diff --git a/Cargo.lock b/Cargo.lock index 7bd5e149..2a2e2a48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,7 +292,11 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", + "pure-rust-locales", + "wasm-bindgen", + "windows-targets 0.52.6", "windows-link", ] @@ -691,6 +695,49 @@ dependencies = [ "rand", ] +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1356,6 +1403,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "plib" version = "0.2.2" @@ -1435,10 +1488,18 @@ dependencies = [ name = "posixutils-display" version = "0.2.2" dependencies = [ + "aho-corasick", + "chrono", "clap", "gettext-rs", "libc", + "once_cell", + "pest", + "pest_derive", "plib", + "regex", + "rstest", + "terminfo 0.9.0", "termion", "thiserror 1.0.69", ] @@ -1559,7 +1620,7 @@ dependencies = [ "clap", "gettext-rs", "libc", - "terminfo", + "terminfo 0.8.0", "termios", ] @@ -1670,6 +1731,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.94" @@ -1699,6 +1769,12 @@ dependencies = [ "unarray", ] +[[package]] +name = "pure-rust-locales" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" + [[package]] name = "quick-error" version = "1.2.3" @@ -1855,6 +1931,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "rowan" version = "0.15.16" @@ -1867,12 +1949,51 @@ dependencies = [ "text-size", ] +[[package]] +name = "rstest" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.89", + "unicode-ident", +] + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.0.3" @@ -1949,6 +2070,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + [[package]] name = "serde" version = "1.0.219" @@ -2021,6 +2148,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.14.0" @@ -2127,6 +2263,18 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + [[package]] name = "termion" version = "4.0.4" @@ -2259,6 +2407,23 @@ dependencies = [ "time-core", ] +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "topological-sort" version = "0.2.2" @@ -2740,13 +2905,22 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr" +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.0" ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d1412a65..958fa218 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "fs", "ftw", "make", + "man", "m4", "m4/test-manager", "gettext-rs", diff --git a/display/test.mdoc b/display/test.mdoc new file mode 100644 index 00000000..a79ba53c --- /dev/null +++ b/display/test.mdoc @@ -0,0 +1,5 @@ +.Dd $Mdocdate: November 9 2023 $ +.Dt FUTEX 2 +.Os +.Sh LIBRARY +.Lb libarm \ No newline at end of file diff --git a/man/Cargo.toml b/man/Cargo.toml new file mode 100644 index 00000000..07f0958e --- /dev/null +++ b/man/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "posixutils-man" +version = "0.2.2" +authors = ["Jeff Garzik"] +repository.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +clap.workspace = true +clap.features = ["env"] +gettext-rs.workspace = true +libc.workspace = true +pest = "2.7" +pest_derive = "2.7" +thiserror = "1.0" +chrono = { version = "0.4", features = ["unstable-locales"] } +regex.workspace = true +terminfo = "0.9.0" +lazy_static = "1.4" + +[dev-dependencies] +plib = { path = "../plib" } +rstest = "0.25.0" + +[lints] +workspace = true + +[[bin]] +name = "man" +path = "./man.rs" diff --git a/man/man.rs b/man/man.rs new file mode 100644 index 00000000..5d62cc6c --- /dev/null +++ b/man/man.rs @@ -0,0 +1,805 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +use clap::{ArgAction, Parser, ValueEnum}; +use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleCategory}; +use man_util::config::{parse_config_file, ManConfig}; +use man_util::formatter::MdocFormatter; +use man_util::parser::MdocParser; +use std::ffi::OsStr; +use std::io::{self, IsTerminal, Write}; +use std::num::ParseIntError; +use std::path::PathBuf; +use std::process::{Command, Output, Stdio}; +use std::str::FromStr; +use std::string::FromUtf8Error; +use thiserror::Error; + +mod man_util; + +// `/usr/share/man` - system provided directory with system documentation. +// `/usr/local/share/man` - user programs provided directory with system documentation. +const MAN_PATHS: [&str; 3] = ["/usr/share/man", "/usr/X11R6/man", "/usr/local/share/man"]; + +// Prioritized order of sections. +const MAN_SECTIONS: [Section; 10] = [ + Section::S1, + Section::S8, + Section::S6, + Section::S2, + Section::S3, + Section::S5, + Section::S7, + Section::S4, + Section::S9, + Section::S3p, +]; + +/// Possible default config file paths to check if `-C` is not provided. +const MAN_CONFS: [&str; 3] = [ + "/etc/man.conf", + "/etc/examples/man.conf", + "/etc/manpath.config", +]; + +#[derive(Parser, Debug, Default)] +#[command( + version, + disable_help_flag = true, + about = gettext("man - display system documentation") +)] +struct Args { + /// Display all matching manual pages + #[arg(short, long, help = "Display all matching manual pages")] + all: bool, + + /// Use the specified file instead of the default configuration file + #[arg( + short = 'C', + long, + help = "Use the specified file instead of the default configuration file" + )] + config_file: Option, + + /// Copy the manual page to the standard output instead of using less(1) to paginate it + #[arg(short, long, help = "Copy the manual page to the standard output")] + copy: bool, + + /// A synonym for whatis(1). It searches for name in manual page names and displays the header lines from all matching pages + #[arg(short = 'f', long, help = "A synonym for whatis(1)")] + whatis: bool, + + /// Display only the SYNOPSIS lines of the requested manual pages + #[arg( + short = 'h', + long, + help = "Display only the SYNOPSIS lines of the requested manual pages" + )] + synopsis: bool, + + /// Displays the header lines of all matching pages. A synonym for apropos(1) + #[arg( + short = 'k', + long, + help = gettext("Interpret name operands as keywords for searching the summary database") + )] + apropos: bool, + + /// A synonym for mandoc(1). Interpret PAGE argument(s) as local filename(s) + #[arg( + short = 'l', + long = "local-file", + help = "interpret PAGE argument(s) as local filename(s)", + num_args = 1.. + )] + local_file: Option>, + + /// Override the list of directories to search for manual pages + #[arg( + short = 'M', + value_delimiter = ':', + help = gettext("Override the list of directories to search for manual pages") + )] + override_paths: Vec, + + /// Augment the list of directories to search for manual pages + #[arg( + short = 'm', + value_delimiter = ':', + help = gettext("Augment the list of directories to search for manual pages") + )] + augment_paths: Vec, + + /// Only show pages for the specified machine(1) architecture + #[arg( + short = 'S', + help = gettext("Only show pages for the specified machine(1) architecture") + )] + subsection: Option, + + /// Only select manuals from the specified section + #[arg( + short = 's', + value_enum, + help = gettext("Only select manuals from the specified section") + )] + section: Option
, + + /// List the pathnames of all matching manual pages instead of displaying any of them + #[arg( + short = 'w', + help = gettext("List the pathnames of all matching manual pages instead of displaying any of them") + )] + list_pathnames: bool, + + #[arg( + long = "help", + action = ArgAction::Help, + help = "Print help information" + )] + help: Option, + + /// Commands names for which documentation search must be performed + #[arg( + help = gettext("Names of the utilities or keywords to display documentation for"), + num_args = 0.. + )] + names: Vec, +} + +/// Common errors that might occur. +#[derive(Error, Debug)] +enum ManError { + /// Search path to man pages isn't exists + #[error("man paths to man pages doesn't exist")] + ManPaths, + + /// Commands for searching documentation isn't exists + #[error("no names specified")] + NoNames, + + /// Man can't find documentation for choosen command + #[error("system documentation for \"{0}\" not found")] + PageNotFound(String), + + /// Configuration file was not found + #[error("configuration file was not found: {0}")] + ConfigFileNotFound(String), + + /// Can't get terminal size + #[error("failed to get terminal size")] + GetTerminalSize, + + /// Man can't find choosen command + #[error("{0} command not found")] + CommandNotFound(String), + + /// Can't execute command; read/write file + #[error("failed to execute command: {0}")] + Io(#[from] io::Error), + + /// Mdoc error + #[error("parsing error: {0}")] + Mdoc(#[from] man_util::parser::MdocError), + + /// Parsing error + #[error("parsing error: {0}")] + ParseError(#[from] ParseError), + + /// Not found error + #[error("file: {0} was not found")] + NotFound(PathBuf), +} + +/// Parsing error types +#[derive(Error, Debug)] +enum ParseError { + #[error("{0}")] + ParseIntError(#[from] ParseIntError), + + #[error("{0}")] + FromUtf8Error(#[from] FromUtf8Error), +} + +/// Manual type +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum)] +pub enum Section { + /// General commands (tools and utilities) + S1, + /// System calls and error numbers + S2, + /// Library functions + S3, + /// perl(1) programmer's reference guide + S3p, + /// Device drivers + S4, + /// File formats + S5, + /// Games + S6, + /// Miscellaneous information + S7, + /// System maintenance and operation commands + S8, + /// Kernel internals + S9, +} + +impl FromStr for Section { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "1" => Ok(Section::S1), + "2" => Ok(Section::S2), + "3" => Ok(Section::S3), + "3p" => Ok(Section::S3p), + "4" => Ok(Section::S4), + "5" => Ok(Section::S5), + "6" => Ok(Section::S6), + "7" => Ok(Section::S7), + "8" => Ok(Section::S8), + "9" => Ok(Section::S9), + _ => Err(format!("Invalid section: {}", s)), + } + } +} + +impl std::fmt::Display for Section { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Section::S1 => "1", + Section::S2 => "2", + Section::S3 => "3", + Section::S3p => "3p", + Section::S4 => "4", + Section::S5 => "5", + Section::S6 => "6", + Section::S7 => "7", + Section::S8 => "8", + Section::S9 => "9", + }; + write!(f, "{}", s) + } +} + +/// Basic formatting settings for manual pages (width, indentation) +#[derive(Debug, Clone, Copy)] +pub struct FormattingSettings { + /// Terminal width + pub width: usize, + /// Lines indentation + pub indent: usize, +} + +impl Default for FormattingSettings { + fn default() -> Self { + Self { + width: 78, + indent: 6, + } + } +} + +// +// ────────────────────────────────────────────────────────────────────────────── +// HELPER FUNCTIONS +// ────────────────────────────────────────────────────────────────────────────── +// + +/// Try to locate the configuration file: +/// - If `path` is Some, check if it exists; error if not. +/// - If `path` is None, try each of MAN_CONFS; return an error if none exist. +fn get_config_file_path(path: &Option) -> Result { + if let Some(user_path) = path { + if user_path.exists() { + Ok(user_path.clone()) + } else { + Err(ManError::ConfigFileNotFound( + user_path.display().to_string(), + )) + } + } else { + // No -C provided, so check defaults: + for default in MAN_CONFS { + let p = PathBuf::from(default); + if p.exists() { + return Ok(p); + } + } + Err(ManError::ConfigFileNotFound( + "No valid man.conf found".to_string(), + )) + } +} + +/// Spawns process with arguments and STDIN if present. +/// +/// # Arguments +/// +/// `name` - [str] name of process. +/// `args` - [IntoIterator>] arguments of process. +/// `stdin` - [Option<&[u8]>] STDIN content of process. +/// +/// # Returns +/// +/// [Output] of spawned process. +/// +/// # Errors +/// +/// [ManError] if process spawn failed or failed to get its output. +fn spawn(name: &str, args: I, stdin: Option<&[u8]>, stdout: Stdio) -> Result +where + I: IntoIterator, + S: AsRef, +{ + let mut process = Command::new(name) + .args(args) + .stdin(Stdio::piped()) + .stdout(stdout) + .spawn() + .map_err(|err| match err.kind() { + io::ErrorKind::NotFound => ManError::CommandNotFound(name.to_string()), + _ => ManError::Io(err), + })?; + + if let Some(stdin) = stdin { + if let Some(mut process_stdin) = process.stdin.take() { + process_stdin.write_all(stdin)?; + } else { + Err(io::Error::new( + io::ErrorKind::Other, + format!("failed to open stdin for {name}"), + ))?; + } + } + + let output = process.wait_with_output().map_err(|_| { + io::Error::new(io::ErrorKind::Other, format!("failed to get {name} stdout")) + })?; + + if !output.status.success() { + Err(io::Error::new( + io::ErrorKind::Other, + format!("{name} failed"), + ))? + } else { + Ok(output) + } +} + +/// Gets page width. +/// +/// # Returns +/// +/// [Option] width value of current terminal. +/// [Option::Some] if working on terminal and receiving terminal size was succesfull. +/// [Option::None] if working not on terminal. +/// +/// # Errors +/// +/// Returns [ManError] if working on terminal and failed to get terminal size. +fn get_pager_settings(config: &ManConfig) -> Result { + let mut settings = FormattingSettings::default(); + + if let Some(Some(val_str)) = config.output_options.get("indent") { + settings.indent = val_str + .parse::() + .map_err(|err| ManError::ParseError(ParseError::ParseIntError(err)))?; + } + + if let Some(Some(val_str)) = config.output_options.get("width") { + settings.width = val_str + .parse::() + .map_err(|err| ManError::ParseError(ParseError::ParseIntError(err)))?; + } + + // If stdout is not a terminal, don't try to ioctl for size + if !io::stdout().is_terminal() { + return Ok(settings); + } + + // If it is a terminal, try to get the window size via ioctl. + let mut winsize = libc::winsize { + ws_row: 0, + ws_col: 0, + ws_xpixel: 0, + ws_ypixel: 0, + }; + + let ret = unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) }; + if ret != 0 { + return Err(ManError::GetTerminalSize); + } + + // If the terminal is narrower than 79 columns, reduce the width setting + if winsize.ws_col < 79 { + settings.width = (winsize.ws_col - 1) as usize; + // If extremely narrow, reduce indent too + if winsize.ws_col < 66 { + settings.indent = 3; + } + } + + Ok(settings) +} + +/// Read a local man page file (possibly .gz), uncompress if needed, and return +/// the raw content. +fn get_man_page_from_path(path: &PathBuf) -> Result, ManError> { + let ext = path.extension().and_then(|ext| ext.to_str()); + let cat_cmd = match ext { + Some("gz") => "zcat", + _ => "cat", + }; + + let output = spawn(cat_cmd, [path], None, Stdio::piped())?; + Ok(output.stdout) +} + +/// Parse and format a man page’s raw content into text suitable for display. +/// +/// # Arguments +/// +/// `man_page` - [Vec] with content that needs to be formatted. +/// +/// # Returns +/// +/// [Vec] STDOUT of called formatter. +/// +/// # Errors +/// +/// [ManError] if failed to execute formatter. +fn format_man_page( + man_bytes: Vec, + formatting: &FormattingSettings, + synopsis: bool, +) -> Result, ManError> { + let content = String::from_utf8(man_bytes) + .map_err(|err| ManError::ParseError(ParseError::FromUtf8Error(err)))?; + + let mut formatter = MdocFormatter::new(*formatting); + + let document = MdocParser::parse_mdoc(&content)?; + let formatted_document = match synopsis { + true => formatter.format_synopsis_section(document), + false => formatter.format_mdoc(document), + }; + + Ok(formatted_document) +} + +/// Write formatted output to either a pager or directly to stdout if `copy = true`. +/// +/// # Arguments +/// +/// `man_page` - [Vec] with content that needs to displayed. +/// +/// # Errors +/// +/// [ManError] if failed to execute pager or failed write to its STDIN. +fn display_pager(man_page: Vec, copy_mode: bool) -> Result<(), ManError> { + if copy_mode { + io::stdout().write_all(&man_page)?; + io::stdout().flush()?; + return Ok(()); + } + + let pager = std::env::var("PAGER").unwrap_or_else(|_| "more".to_string()); + let args = if pager.ends_with("more") { + vec!["-s"] + } else { + vec![] + }; + + spawn(&pager, args, Some(&man_page), Stdio::inherit())?; + + Ok(()) +} + +/// Displays man page summaries for the given keyword. +/// +/// # Arguments +/// +/// `keyword` - [str] name of keyword. +/// +/// # Returns +/// +/// [true] if `apropos` finished successfully, otherwise [false]. +/// +/// # Errors +/// +/// [ManError] if call of `apropros` utility failed. +fn display_summary_database(command: &str, keyword: &str) -> Result { + let status = Command::new(command).arg(keyword).spawn()?.wait()?; + + Ok(status.success()) +} + +/// Man formatting state structure +#[derive(Default)] +struct Man { + args: Args, + search_paths: Vec, + sections: Vec
, + config: ManConfig, + formatting_settings: FormattingSettings, +} + +impl Man { + /// Gets system documentation path by passed name. + /// + /// # Arguments + /// + /// `name` - [str] name of necessary system documentation. + /// + /// # Returns + /// + /// [Vec] of found system documentation. + /// + /// # Errors + /// + /// [ManError] if file not found. + fn get_man_page_paths(&self, name: &str, all: bool) -> Result, ManError> { + let mut path_iter = self.search_paths.iter().flat_map(|path| { + self.sections.iter().flat_map(move |section| { + let base_path = format!("{}/man{section}/{name}.{section}", path.display()); + vec![format!("{base_path}.gz"), base_path] + }) + }); + + if all { + let paths = path_iter + .map(PathBuf::from) + .filter(|path| path.exists()) + .collect::>(); + + if paths.is_empty() { + return Err(ManError::PageNotFound(name.to_string())); + } + + Ok(paths) + } else { + path_iter + .find(|path| PathBuf::from(path).exists()) + .map(|s| vec![PathBuf::from(s)]) + .ok_or_else(|| ManError::PageNotFound(name.to_string())) + } + } + + /// Display a single man page found at `path`. + /// + /// # Arguments + /// + /// `name` - [str] name of system documentation. + /// + /// # Errors + /// + /// [ManError] if man page not found, or any display error happened. + fn display_man_page(&self, path: &PathBuf) -> Result<(), ManError> { + let raw = get_man_page_from_path(path)?; + let formatted = format_man_page(raw, &self.formatting_settings, self.args.synopsis)?; + display_pager(formatted, self.args.copy) + } + + /// Display *all* man pages found for a particular name (when -a is specified). + fn display_all_man_pages(&self, paths: Vec) -> Result<(), ManError> { + if paths.is_empty() { + return Err(ManError::PageNotFound("no matching pages".to_string())); + } + + if paths.iter().any(|path| !path.exists()) { + return Err(ManError::PageNotFound( + "One of the provided files was not found".to_string(), + )); + } + + for path in paths { + self.display_man_page(&path)?; + } + + Ok(()) + } + + /// Display *all* man page pathes (when -w is specified). + fn display_paths(&self, paths: Vec) -> Result<(), ManError> { + if paths.is_empty() { + return Err(ManError::PageNotFound("no matching pages".to_string())); + } + + if paths.iter().any(|path| !path.exists()) { + return Err(ManError::PageNotFound( + "One of the provided files was not found".to_string(), + )); + } + + for path in paths { + println!("{}", path.display()); + } + + Ok(()) + } + + fn new(args: Args) -> Result { + if args.names.is_empty() { + if args.local_file.is_none() { + return Err(ManError::NoNames); + } + + for path in args.local_file.clone().unwrap() { + if !path.exists() { + return Err(ManError::NotFound(path)); + } + } + } + + let config_path = get_config_file_path(&args.config_file)?; + let config = parse_config_file(config_path)?; + + let mut man = Self { + args, + formatting_settings: get_pager_settings(&config)?, + config, + ..Default::default() + }; + + if !man.args.override_paths.is_empty() { + let override_paths = man + .args + .override_paths + .iter() + .filter_map(|path| path.to_str()) + .collect::>() + .join(":"); + + std::env::set_var("MANPATH", OsStr::new(&override_paths)); + } + + if man.args.subsection.is_some() { + std::env::set_var("MACHINE", OsStr::new(&man.args.subsection.clone().unwrap())); + } + + let manpath = std::env::var("MANPATH") + .unwrap_or_default() + .split(":") + .filter_map(|s| PathBuf::from_str(s).ok()) + .collect::>(); + + man.search_paths = [ + man.args.augment_paths.clone(), + manpath, + man.search_paths.clone(), + man.config.manpaths.clone(), + MAN_PATHS + .iter() + .filter_map(|s| PathBuf::from_str(s).ok()) + .collect::>(), + ] + .concat(); + + if man.search_paths.is_empty() { + return Err(ManError::ManPaths); + } + + man.sections = if let Some(section) = man.args.section { + vec![section] + } else { + MAN_SECTIONS.to_vec() + }; + + Ok(man) + } + + // + // ────────────────────────────────────────────────────────────────────────────── + // MAIN LOGIC FUNCTION + // ────────────────────────────────────────────────────────────────────────────── + // + + /// Main function that handles the program logic. It processes the input + /// arguments, and either displays man pages or searches the summary database. + /// + /// # Arguments + /// + /// `args` - [Args] set of incoming arguments. + /// + /// # Returns + /// + /// [true] if no non-critical error happend, otherwise [false]. + /// + /// # Errors + /// + /// [ManError] if critical error happened. + fn man(&mut self) -> Result { + let mut no_errors = true; + + if let Some(paths) = &self.args.local_file { + if self.args.list_pathnames { + let paths = paths + .iter() + .filter(|path| path.exists()) + .cloned() + .collect::>(); + self.display_paths(paths)?; + } else { + self.display_all_man_pages(paths.clone())?; + } + return Ok(no_errors); + } else if self.args.apropos || self.args.whatis { + let command = if self.args.apropos { + "apropos" + } else { + "whatis" + }; + + for keyword in &self.args.names { + let success = display_summary_database(command, keyword)?; + if !success { + no_errors = false; + } + } + + return Ok(no_errors); + } + + for name in &self.args.names { + if self.args.list_pathnames { + let paths = self.get_man_page_paths(name, true)?; + self.display_paths(paths)?; + } else { + let paths = self.get_man_page_paths(name, self.args.all)?; + self.display_all_man_pages(paths)?; + } + } + + Ok(no_errors) + } +} + +// +// ────────────────────────────────────────────────────────────────────────────── +// MAIN ENTRY POINT +// ────────────────────────────────────────────────────────────────────────────── +// + +// Exit code: +// 0 - Successful completion. +// >0 - An error occurred. +fn main() -> Result<(), Box> { + setlocale(LocaleCategory::LcAll, ""); + textdomain("posixutils-rs")?; + bind_textdomain_codeset("posixutils-rs", "UTF-8")?; + + // parse command line arguments + let args = Args::parse(); + + let mut man = match Man::new(args) { + Ok(man) => man, + Err(err) => { + eprintln!("man: {err}"); + std::process::exit(1); + } + }; + + // Run main logic + let exit_code = match man.man() { + // Success, all pages displayed or apropos found something + Ok(true) => 0, + // Some error for specific `name` + Ok(false) => 1, + // Any critical error happened + Err(err) => { + eprintln!("man: {err}"); + 1 + } + }; + + std::process::exit(exit_code) +} diff --git a/man/man.test.conf b/man/man.test.conf new file mode 100644 index 00000000..8dbb42e9 --- /dev/null +++ b/man/man.test.conf @@ -0,0 +1,53 @@ +# $OpenBSD: man.conf,v 1.6 2013/11/01 03:25:48 schwarze Exp $ +# +# Example man.conf file +# This file is read by man(1), apropos(1), and makewhatis(8) on OpenBSD. +# Lines starting with '#' and blank lines are ignored. +# + +###################################################### +# Manpath directives: +# manpath /some/path +# +# Tells man(1) and related utilities to look in this +# directory tree for subdirectories named man[section] +# or cat[section]. +# +manpath /usr/share/man +manpath /usr/X11R6/man +manpath /usr/local/man +manpath /usr/local/share/man + +output width 100 +output indent 10 +###################################################### +# Output directives: +# output option [value] +# +# These can override default formatting options in mandoc(1). +# Common options on OpenBSD might include: +# +# width (integer) -- wrap lines at this text width +# indent (integer) -- left margin for each paragraph +# fragment -- produce only HTML body, omitting +# style -- path to a CSS stylesheet for HTML output +# includes -- path to header files for HTML +# toc -- include a table of contents in HTML output +# +# Examples (currently commented out): +# +# output width 78 +# output indent 5 +# output style /usr/local/share/mandoc-style.css +# output toc + +###################################################### +# You can also include additional options +# specific to your local environment here. +# +# For example, if you installed third-party software +# in /opt, you might add: +# manpath /opt/local/man +# +# If you need a custom style for HTML pages: +# output style /etc/mandoc/custom-style.css diff --git a/man/man_util/config.rs b/man/man_util/config.rs new file mode 100644 index 00000000..6ca67d01 --- /dev/null +++ b/man/man_util/config.rs @@ -0,0 +1,69 @@ +use std::{ + collections::HashMap, + fs::File, + io::{BufRead, BufReader}, + path::PathBuf, +}; + +use crate::ManError; + +/// # ManConfig +/// +/// Parsed configuration file +/// +/// ## Fields: +/// * `manpaths` +/// * `output_options` +#[derive(Debug, Default)] +pub struct ManConfig { + pub manpaths: Vec, + pub output_options: HashMap>, +} + +/// # parse_config_file +/// +/// Parses `man`` cofiguration file. +/// +/// # Params: +/// * path - path to onfiguration file +/// +/// # Errors: +/// * io +pub fn parse_config_file(path: PathBuf) -> Result { + let file = File::open(path)?; + let reader = BufReader::new(file); + + let mut conf = ManConfig::default(); + + for line_result in reader.lines() { + let line = line_result?; + let line = line.trim(); + + if line.is_empty() || line.starts_with("#") { + continue; + } + + let mut parts = line.split_whitespace(); + let directive = match parts.next() { + Some(d) => d, + None => continue, + }; + + match directive { + "manpath" => { + if let Some(path) = parts.next() { + conf.manpaths.push(PathBuf::from(path)); + } + } + "output" => { + if let Some(option_name) = parts.next() { + let value = parts.next().map(|s| s.to_string()); + conf.output_options.insert(option_name.to_string(), value); + } + } + _ => unreachable!("Unexpected directive: {directive}"), + } + } + + Ok(conf) +} diff --git a/man/man_util/formatter.rs b/man/man_util/formatter.rs new file mode 100644 index 00000000..d3bea089 --- /dev/null +++ b/man/man_util/formatter.rs @@ -0,0 +1,7635 @@ +use crate::FormattingSettings; +use lazy_static::lazy_static; +use regex::Regex; +use std::collections::HashMap; +use terminfo::Database; + +use super::{ + mdoc_macro::{text_production::*, types::*, Macro}, + parser::{trim_quotes, Element, MacroNode, MdocDocument}, +}; + +/// Max Bl -width parameter value +const MAX_INDENT: u8 = 20; + +lazy_static! { + pub static ref REGEX_UNICODE: Regex = { + Regex::new( + r"(?x) + (?: + (?P\\\[u(?P[0-9A-F]{4,6})\]) | + (?P\\C'u(?P[0-9A-F]{4,6})') | + (?P\\N'(?P[0-9]+)') | + (?P\\\[char(?P[0-9]+)\]) + ) + " + ).unwrap() + }; + + pub static ref REGEX_NS_MACRO: Regex = { + Regex::new(r"\s*\\\[nsmacroescape\]\s*").unwrap() + }; + + pub static ref SUBSTITUTIONS: HashMap<&'static str, &'static str> = { + let mut m = HashMap::with_capacity(410); + m.insert(r"\[ssindent]", " "); + m.insert(r"\[dq]", "\""); + m.insert(r"\[ti]", "~"); + m.insert(r"\[aq]", "'"); + m.insert(r"\(em", "—"); + m.insert(r"\(en", "–"); + m.insert(r"\(hy", "‐"); + m.insert("\\[pfmacroescape] ", ""); + m.insert("\\[anmacroescape]", "\n"); + // Spaces: + //m.insert(r"\ ", " "); // unpaddable space + m.insert(r"\~", " "); // paddable space + m.insert(r"\0", " "); // digit-width space + m.insert(r"\|", " "); // one-sixth \(em narrow space + m.insert(r"\^", " "); // one-twelfth \(em half-narrow space + m.insert(r"\&", ""); // zero-width space + m.insert(r"\)", ""); // zero-width space (transparent to end-of-sentence detection) + m.insert(r"\%", ""); // zero-width space allowing hyphenation + //m.insert(r"\:", ""); // zero-width space allowing line break + + // Lines: + m.insert(r"\(ba", "|"); // bar + m.insert(r"\(br", "│"); // box rule + m.insert(r"\(ul", "_"); // underscore + m.insert(r"\(ru", "_"); // underscore (width 0.5m) + m.insert(r"\(rn", "‾"); // overline + m.insert(r"\(bb", "¦"); // broken bar + m.insert(r"\(sl", "/"); // forward slash + m.insert(r"\(rs", "\\"); // backward slash + // Text markers: + m.insert(r"\(ci", "○"); // circle + m.insert(r"\(bu", "•"); // bullet + m.insert(r"\(dd", "‡"); // double dagger + m.insert(r"\(dg", "†"); // dagger + m.insert(r"\(lz", "◊"); // lozenge + m.insert(r"\(sq", "□"); // white square + m.insert(r"\(ps", "¶"); // paragraph + m.insert(r"\(sc", "§"); // section + m.insert(r"\(lh", "☜"); // left hand + m.insert(r"\(rh", "☞"); // right hand + m.insert(r"\(at", "@"); // at + m.insert(r"\(sh", "#"); // hash (pound) + m.insert(r"\(CR", "↵"); // carriage return + m.insert(r"\(OK", "✓"); // check mark + m.insert(r"\(CL", "♣"); // club suit + m.insert(r"\(SP", "♠"); // spade suit + m.insert(r"\(HE", "♥"); // heart suit + m.insert(r"\(DI", "♦"); // diamond suit + // Legal symbols: + m.insert(r"\(co", "©"); // copyright + m.insert(r"\(rg", "®"); // registered + m.insert(r"\(tm", "™"); // trademarked + // Punctuation: + m.insert(r"\(em", "—"); // em-dash + m.insert(r"\(en", "–"); // en-dash + m.insert(r"\(hy", "‐"); // hyphen + m.insert(r"\e", "\\"); // back-slash + m.insert(r"\(r!", "¡"); // upside-down exclamation + m.insert(r"\(r?", "¿"); // upside-down question + // Quotes: + m.insert(r"\(Bq", "„"); // right low double-quote + m.insert(r"\(bq", "‚"); // right low single-quote + m.insert(r"\(lq", "“"); // left double-quote + m.insert(r"\(rq", "”"); // right double-quote + m.insert(r"\(oq", "‘"); // left single-quote + m.insert(r"\(cq", "’"); // right single-quote + m.insert(r"\(aq", "'"); // apostrophe quote (ASCII character) + m.insert(r"\(dq", "\""); // double quote (ASCII character) + m.insert(r"\(Fo", "«"); // left guillemet + m.insert(r"\(Fc", "»"); // right guillemet + m.insert(r"\(fo", "‹"); // left single guillemet + m.insert(r"\(fc", "›"); // right single guillemet + // Brackets: + m.insert(r"\(lB", "["); + m.insert(r"\(rB", "]"); + m.insert(r"\(lC", "{"); + m.insert(r"\(rC", "}"); + m.insert(r"\(la", "⟨"); + m.insert(r"\(ra", "⟩"); + m.insert(r"\(bv", "⎪"); + m.insert(r"\[braceex]", "⎪"); + m.insert(r"\[bracketlefttp]", "⎡"); + m.insert(r"\[bracketleftbt]", "⎣"); + m.insert(r"\[bracketleftex]", "⎢"); + m.insert(r"\[bracketrighttp]", "⎤"); + m.insert(r"\[bracketrightbt]", "⎦"); + m.insert(r"\[bracketrightex]", "⎥"); + m.insert(r"\(lt", "⎧"); + m.insert(r"\[bracelefttp]", "⎧"); + m.insert(r"\(lk", "⎨"); + m.insert(r"\[braceleftmid]", "⎨"); + m.insert(r"\(lb", "⎩"); + m.insert(r"\[braceleftbt]", "⎩"); + m.insert(r"\[braceleftex]", "⎪"); + m.insert(r"\(rt", "⎫"); + m.insert(r"\[bracerighttp]", "⎫"); + m.insert(r"\(rk", "⎬"); + m.insert(r"\[bracerightmid]", "⎬"); + m.insert(r"\(rb", "⎭"); + m.insert(r"\[bracerightbt]", "⎭"); + m.insert(r"\[bracerightex]", "⎪"); + m.insert(r"\[parenlefttp]", "⎛"); + m.insert(r"\[parenleftbt]", "⎝"); + m.insert(r"\[parenleftex]", "⎜"); + m.insert(r"\[parenrighttp]", "⎞"); + m.insert(r"\[parenrightbt]", "⎠"); + m.insert(r"\[parenrightex]", "⎟"); + // Arrows: + m.insert(r"\(<-", "←"); + m.insert(r"\(->", "→"); + m.insert(r"\(<>", "↔"); + m.insert(r"\(da", "↓"); + m.insert(r"\(ua", "↑"); + m.insert(r"\(va", "↕"); + m.insert(r"\(lA", "⇐"); + m.insert(r"\(rA", "⇒"); + m.insert(r"\(hA", "⇔"); + m.insert(r"\(uA", "⇑"); + m.insert(r"\(dA", "⇓"); + m.insert(r"\(vA", "⇕"); + m.insert(r"\(an", "⎯"); + // Logical: + m.insert(r"\(AN", "∧"); + m.insert(r"\(OR", "∨"); + m.insert(r"\[tno]", "¬"); + m.insert(r"\(no", "¬"); + m.insert(r"\(te", "∃"); + m.insert(r"\(fa", "∀"); + m.insert(r"\(st", "∋"); + m.insert(r"\(tf", "∴"); + m.insert(r"\(3d", "∴"); + m.insert(r"\(or", "|"); + // Mathematical: + m.insert(r"\-", "-"); + m.insert(r"\(mi", "−"); + m.insert(r"\+", "+"); + m.insert(r"\(pl", "+"); + m.insert(r"\(-+", "∓"); + m.insert(r"\[t+-]", "±"); + m.insert(r"\(+-", "±"); + m.insert(r"\(pc", "·"); + m.insert(r"\[tmu]", "×"); + m.insert(r"\(mu", "×"); + m.insert(r"\(c*", "⊗"); + m.insert(r"\(c+", "⊕"); + m.insert(r"\[tdi]", "÷"); + m.insert(r"\(di", "÷"); + m.insert(r"\(f/", "⁄"); + m.insert(r"\(**", "∗"); + m.insert(r"\(<=", "≤"); + m.insert(r"\(>=", "≥"); + m.insert(r"\(<<", "≪"); + m.insert(r"\(>>", "≫"); + m.insert(r"\(eq", "="); + m.insert(r"\(!=", "≠"); + m.insert(r"\(==", "≡"); + m.insert(r"\(ne", "≢"); + m.insert(r"\(ap", "∼"); + m.insert(r"\(|=", "≃"); + m.insert(r"\(=~", "≅"); + m.insert(r"\(~~", "≈"); + m.insert(r"\(~=", "≈"); + m.insert(r"\(pt", "∝"); + m.insert(r"\(es", "∅"); + m.insert(r"\(mo", "∈"); + m.insert(r"\(nm", "∉"); + m.insert(r"\(sb", "⊂"); + m.insert(r"\(nb", "⊄"); + m.insert(r"\(sp", "⊃"); + m.insert(r"\(nc", "⊅"); + m.insert(r"\(ib", "⊆"); + m.insert(r"\(ip", "⊇"); + m.insert(r"\(ca", "∩"); + m.insert(r"\(cu", "∪"); + m.insert(r"\(/_", "∠"); + m.insert(r"\(pp", "⊥"); + m.insert(r"\(is", "∫"); + m.insert(r"\[integral]", "∫"); + m.insert(r"\[sum]", "∑"); + m.insert(r"\[product]", "∏"); + m.insert(r"\[coproduct]", "∐"); + m.insert(r"\(gr", "∇"); + m.insert(r"\(sr", "√"); + m.insert(r"\[sqrt]", "√"); + m.insert(r"\(lc", "⌈"); + m.insert(r"\(rc", "⌉"); + m.insert(r"\(lf", "⌊"); + m.insert(r"\(rf", "⌋"); + m.insert(r"\(if", "∞"); + m.insert(r"\(Ah", "ℵ"); + m.insert(r"\(Im", "ℑ"); + m.insert(r"\(Re", "ℜ"); + m.insert(r"\(wp", "℘"); + m.insert(r"\(pd", "∂"); + m.insert(r"\(-h", "ℏ"); + m.insert(r"\[hbar]", "ℏ"); + m.insert(r"\(12", "½"); + m.insert(r"\(14", "¼"); + m.insert(r"\(34", "¾"); + m.insert(r"\(18", "⅛"); + m.insert(r"\(38", "⅜"); + m.insert(r"\(58", "⅝"); + m.insert(r"\(78", "⅞"); + m.insert(r"\(S1", "¹"); + m.insert(r"\(S2", "²"); + m.insert(r"\(S3", "³"); + // Ligatures: + m.insert(r"\(ff", "ff"); + m.insert(r"\(fi", "fi"); + m.insert(r"\(fl", "fl"); + m.insert(r"\(Fi", "ffi"); + m.insert(r"\(Fl", "ffl"); + m.insert(r"\(AE", "Æ"); + m.insert(r"\(ae", "æ"); + m.insert(r"\(OE", "Œ"); + m.insert(r"\(oe", "œ"); + m.insert(r"\(ss", "ß"); + m.insert(r"\(IJ", "IJ"); + m.insert(r"\(ij", "ij"); + // Accents: + m.insert(r"\(a-", "¯"); + m.insert(r"\(a.", "˙"); + m.insert(r"\(a^", "^"); + m.insert(r"\(aa", "´"); + m.insert(r"\'", "´"); + m.insert(r"\(ga", "`"); + m.insert(r"\`", "`"); + m.insert(r"\(ab", "˘"); + m.insert(r"\(ac", "¸"); + m.insert(r"\(ad", "¨"); + m.insert(r"\(ah", "ˇ"); + m.insert(r"\(ao", "˚"); + m.insert(r"\(a~", "~"); + m.insert(r"\(ho", "˛"); + m.insert(r"\(ha", "^"); + m.insert(r"\(ti", "~"); + // Accented letters: + m.insert(r"\('A", "Á"); + m.insert(r"\('E", "É"); + m.insert(r"\('I", "Í"); + m.insert(r"\('O", "Ó"); + m.insert(r"\('U", "Ú"); + m.insert(r"\('Y", "Ý"); + m.insert(r"\('a", "á"); + m.insert(r"\('e", "é"); + m.insert(r"\('i", "í"); + m.insert(r"\('o", "ó"); + m.insert(r"\('u", "ú"); + m.insert(r"\('y", "ý"); + m.insert(r"\(`A", "À"); + m.insert(r"\(`E", "È"); + m.insert(r"\(`I", "Ì"); + m.insert(r"\(`O", "Ò"); + m.insert(r"\(`U", "Ù"); + m.insert(r"\(`a", "à"); + m.insert(r"\(`e", "è"); + m.insert(r"\(`i", "ì"); + m.insert(r"\(`o", "ò"); + m.insert(r"\(`u", "ù"); + m.insert(r"\(~A", "Ã"); + m.insert(r"\(~N", "Ñ"); + m.insert(r"\(~O", "Õ"); + m.insert(r"\(~a", "ã"); + m.insert(r"\(~n", "ñ"); + m.insert(r"\(~o", "õ"); + m.insert(r"\(:A", "Ä"); + m.insert(r"\(:E", "Ë"); + m.insert(r"\(:I", "Ï"); + m.insert(r"\(:O", "Ö"); + m.insert(r"\(:U", "Ü"); + m.insert(r"\(:a", "ä"); + m.insert(r"\(:e", "ë"); + m.insert(r"\(:i", "ï"); + m.insert(r"\(:o", "ö"); + m.insert(r"\(:u", "ü"); + m.insert(r"\(:y", "ÿ"); + m.insert(r"\(^A", "Â"); + m.insert(r"\(^E", "Ê"); + m.insert(r"\(^I", "Î"); + m.insert(r"\(^O", "Ô"); + m.insert(r"\(^U", "Û"); + m.insert(r"\(^a", "â"); + m.insert(r"\(^e", "ê"); + m.insert(r"\(^i", "î"); + m.insert(r"\(^o", "ô"); + m.insert(r"\(^u", "û"); + m.insert(r"\(,C", "Ç"); + m.insert(r"\(,c", "ç"); + m.insert(r"\(/L", "Ł"); + m.insert(r"\(/l", "ł"); + m.insert(r"\(/O", "Ø"); + m.insert(r"\(/o", "ø"); + m.insert(r"\(oA", "Å"); + m.insert(r"\(oa", "å"); + // Special letters: + m.insert(r"\(-D", "Ð"); + m.insert(r"\(Sd", "ð"); + m.insert(r"\(TP", "Þ"); + m.insert(r"\(Tp", "þ"); + m.insert(r"\(.i", "ı"); + m.insert(r"\(.j", "ȷ"); + // Currency: + m.insert(r"\(Do", "$"); + m.insert(r"\(ct", "¢"); + m.insert(r"\(Eu", "€"); + m.insert(r"\(eu", "€"); + m.insert(r"\(Ye", "¥"); + m.insert(r"\(Po", "£"); + m.insert(r"\(Cs", "¤"); + m.insert(r"\(Fn", "ƒ"); + // Units: + m.insert(r"\(de", "°"); + m.insert(r"\(%0", "‰"); + m.insert(r"\(fm", "′"); + m.insert(r"\(sd", "″"); + m.insert(r"\(mc", "µ"); + m.insert(r"\(Of", "ª"); + m.insert(r"\(Om", "º"); + // Greek letters: + m.insert(r"\(*A", "Α"); + m.insert(r"\(*B", "Β"); + m.insert(r"\(*G", "Γ"); + m.insert(r"\(*D", "Δ"); + m.insert(r"\(*E", "Ε"); + m.insert(r"\(*Z", "Ζ"); + m.insert(r"\(*Y", "Η"); + m.insert(r"\(*H", "Θ"); + m.insert(r"\(*I", "Ι"); + m.insert(r"\(*K", "Κ"); + m.insert(r"\(*L", "Λ"); + m.insert(r"\(*M", "Μ"); + m.insert(r"\(*N", "Ν"); + m.insert(r"\(*C", "Ξ"); + m.insert(r"\(*O", "Ο"); + m.insert(r"\(*P", "Π"); + m.insert(r"\(*R", "Ρ"); + m.insert(r"\(*S", "Σ"); + m.insert(r"\(*T", "Τ"); + m.insert(r"\(*U", "Υ"); + m.insert(r"\(*F", "Φ"); + m.insert(r"\(*X", "Χ"); + m.insert(r"\(*Q", "Ψ"); + m.insert(r"\(*W", "Ω"); + m.insert(r"\(*a", "α"); + m.insert(r"\(*b", "β"); + m.insert(r"\(*g", "γ"); + m.insert(r"\(*d", "δ"); + m.insert(r"\(*e", "ε"); + m.insert(r"\(*z", "ζ"); + m.insert(r"\(*y", "η"); + m.insert(r"\(*h", "θ"); + m.insert(r"\(*i", "ι"); + m.insert(r"\(*k", "κ"); + m.insert(r"\(*l", "λ"); + m.insert(r"\(*m", "μ"); + m.insert(r"\(*n", "ν"); + m.insert(r"\(*c", "ξ"); + m.insert(r"\(*o", "ο"); + m.insert(r"\(*p", "π"); + m.insert(r"\(*r", "ρ"); + m.insert(r"\(*s", "σ"); + m.insert(r"\(*t", "τ"); + m.insert(r"\(*u", "υ"); + m.insert(r"\(*f", "ϕ"); + m.insert(r"\(*x", "χ"); + m.insert(r"\(*q", "ψ"); + m.insert(r"\(*w", "ω"); + m.insert(r"\(+h", "ϑ"); + m.insert(r"\(+f", "φ"); + m.insert(r"\(+p", "ϖ"); + m.insert(r"\(+e", "ϵ"); + m.insert(r"\(ts", "ς"); + // Predefined strings: + m.insert(r"\*(Ba", "|"); + m.insert(r"\*(Ne", "≠"); + m.insert(r"\*(Ge", "≥"); + m.insert(r"\*(Le", "≤"); + m.insert(r"\*(Gt", ">"); + m.insert(r"\*(Lt", "<"); + m.insert(r"\*(Pm", "±"); + m.insert(r"\*(If", "infinity"); + m.insert(r"\*(Pi", "pi"); + m.insert(r"\*(Na", "NaN"); + m.insert(r"\*(Am", "&"); + m.insert(r"\*R", "®"); + m.insert(r"\*(Tm", "(Tm)"); + m.insert(r"\*q", "\""); + m.insert(r"\*(Rq", "”"); + m.insert(r"\*(Lq", "“"); + m.insert(r"\*(lp", "("); + m.insert(r"\*(rp", ")"); + m.insert(r"\*(lq", "“"); + m.insert(r"\*(rq", "”"); + m.insert(r"\*(ua", "↑"); + m.insert(r"\*(va", "↕"); + m.insert(r"\*(<=", "≤"); + m.insert(r"\*(>=", "≥"); + m.insert(r"\*(aa", "´"); + m.insert(r"\*(ga", "`"); + m.insert(r"\*(Px", "POSIX"); + m.insert(r"\*(Ai", "ANSI"); + m + }; + + pub static ref OUTER_REGEX: Regex = { + let alternation = SUBSTITUTIONS + .keys() + .map(|key| regex::escape(key)) + .collect::>() + .join("|"); + + let pattern = format!( + r#"(?P{})"#, + alternation + ); + + Regex::new(&pattern).unwrap() + }; +} + +pub fn replace_escapes(input: &str) -> String { + let input = OUTER_REGEX + .replace_all(input, |caps: ®ex::Captures| { + if let Some(esc) = caps.name("esc") { + SUBSTITUTIONS + .get(esc.as_str()) + .map(|rep| rep.to_string()) + .unwrap_or_else(|| esc.as_str().to_string()) + } else { + caps.get(0).unwrap().as_str().to_string() + } + }) + .to_string(); + + REGEX_NS_MACRO.replace_all(&input, "").to_string() +} + +/// Formatter state +#[derive(Debug)] +pub struct FormattingState { + /// Utility name; mdoc title + first_name: Option, + /// Header content + header_text: Option, + /// Footer content + footer_text: Option, + /// Space between adjacent macros + spacing: String, + /// Mdoc date for header and footer + date: String, + /// Sm split mode + split_mod: bool, + /// Indentation of current macros nesting level + current_indent: usize, +} + +impl Default for FormattingState { + fn default() -> Self { + Self { + first_name: None, + header_text: None, + footer_text: None, + spacing: " ".to_string(), + date: String::default(), + split_mod: false, + current_indent: 0, + } + } +} + +/// Formatter settings and state +#[derive(Debug)] +pub struct MdocFormatter { + formatting_settings: FormattingSettings, + formatting_state: FormattingState, +} + +// Helper funcitons. +impl MdocFormatter { + pub fn new(settings: FormattingSettings) -> Self { + Self { + formatting_settings: settings, + formatting_state: FormattingState::default(), + } + } + + /// Check if italic is supported for this terminal + fn _supports_italic(&self) -> bool { + if let Ok(info) = Database::from_env() { + return info.raw("sitm").is_some(); + } + false + } + + /// Check if bold is supported for this terminal + fn _supports_bold(&self) -> bool { + if let Ok(info) = Database::from_env() { + return info.raw("bold").is_some(); + } + false + } + + /// Check if undeline is supported for this terminal + fn _supports_underline(&self) -> bool { + if let Ok(info) = Database::from_env() { + return info.raw("smul").is_some(); + } + false + } + + /// Replaces escape sequences in [`text`] [`str`] to true UTF-8 chars + fn replace_unicode_escapes(&self, text: &str) -> String { + REGEX_UNICODE + .replace_all(text, |caps: ®ex::Captures| { + if let Some(hex) = caps + .name("hex1") + .or_else(|| caps.name("hex2")) + .map(|m| m.as_str()) + { + if let Ok(codepoint) = u32::from_str_radix(hex, 16) { + if codepoint < 0x80 { + return "\u{FFFD}".to_string(); + } + if codepoint < 0x10FFFF && !(0xD800..=0xDFFF).contains(&codepoint) { + if let Some(ch) = char::from_u32(codepoint) { + return ch.to_string(); + } + } + } + } else if let Some(dec) = caps + .name("dec1") + .or_else(|| caps.name("dec2")) + .map(|m| m.as_str()) + { + if let Ok(codepoint) = dec.parse::() { + if let Some(ch) = char::from_u32(codepoint) { + return ch.to_string(); + } + } + } + caps.get(0).unwrap().as_str().to_string() + }) + .to_string() + } +} + +// Base formatting functions. +impl MdocFormatter { + /// Append formatted macros on highest mdoc level. + /// Split lines longer than terminal width and + /// adds indentation for new lines + fn append_formatted_text( + &mut self, + formatted: &str, + current_line: &mut String, + lines: &mut Vec, + ) { + let get_indent = |l: &str| { + l.chars() + .take_while(|ch| ch.is_whitespace()) + .collect::() + }; + + let is_one_line = !formatted.contains("\n"); + let max_width = self.formatting_settings.width; + + for line in formatted.split("\n") { + let line = replace_escapes(line); + + if !is_one_line && !current_line.is_empty() { + lines.push(current_line.trim_end().to_string()); + current_line.clear(); + } + + let line_len = current_line.chars().count() + line.chars().count(); + if line_len > max_width || is_one_line { + let indent = get_indent(&line); + let max_width = max_width.saturating_sub(indent.chars().count()); + + for word in line.split_whitespace() { + let line_len = current_line.chars().count() + word.chars().count(); + if line_len > max_width { + lines.push(indent.clone() + current_line.trim()); + current_line.clear(); + } + + current_line.push_str(word); + + if !word.chars().all(|ch| ch.is_control()) { + current_line.push(' '); + } + } + + let is_not_empty = + !(current_line.chars().all(|ch| ch.is_whitespace()) || current_line.is_empty()); + + if is_not_empty { + *current_line = indent + current_line; + } + } else { + lines.push(line.to_string()); + } + } + } + + /// If -h man parameter is enabled this function is used instead + /// [`format_mdoc`] for displaying only `SINOPSYS` section + pub fn format_synopsis_section(&mut self, ast: MdocDocument) -> Vec { + let mut lines = Vec::new(); + let mut current_line = String::new(); + + for node in ast.elements { + let formatted_node = match node { + Element::Macro(macro_node) => { + if let Macro::Sh { ref title } = macro_node.mdoc_macro { + if title.eq_ignore_ascii_case("SYNOPSIS") { + self.format_sh_block(title.clone(), macro_node) + } else { + continue; + } + } else { + continue; + } + } + _ => continue, + }; + + self.append_formatted_text(&formatted_node, &mut current_line, &mut lines); + } + + if !current_line.is_empty() { + lines.push(current_line.trim_end().to_string()); + } + + replace_escapes(&lines.join("\n")).into_bytes() + } + + /// Format full [`MdocDocument`] and returns UTF-8 binary string + pub fn format_mdoc(&mut self, ast: MdocDocument) -> Vec { + let mut lines = Vec::new(); + let mut current_line = String::new(); + + for node in ast.elements { + let mut formatted_node = self.format_node(node.clone()); + + if formatted_node.is_empty() { + continue; + } + + if let Element::Macro(ref macro_node) = node { + if !matches!( + macro_node.mdoc_macro, + Macro::Sh { .. } | Macro::Ss { .. } | Macro::Bd { .. } | Macro::An { .. } + ) && formatted_node.split("\n").count() > 1 + { + formatted_node = formatted_node.trim().to_string(); + } + if matches!(macro_node.mdoc_macro, Macro::Bd { .. }) { + formatted_node.pop(); + formatted_node.remove(0); + } + } + + self.append_formatted_text(&formatted_node, &mut current_line, &mut lines); + } + + if !current_line.is_empty() { + lines.push(current_line.trim_end().to_string()); + } + + let first_empty_count = lines + .iter() + .take_while(|l| l.chars().all(|ch| ch.is_whitespace())) + .count(); + + lines = lines.split_at(first_empty_count).1.to_vec(); + + lines.insert(0, "".to_string()); + + lines.insert( + 0, + self.formatting_state + .header_text + .clone() + .unwrap_or_else(|| self.format_default_header()), + ); + + lines.push(self.format_footer()); + + let content = remove_empty_lines(&lines.join("\n"), 2); + + content.into_bytes() + } + + fn format_default_header(&mut self) -> String { + self.format_dt(None, "", None); + self.formatting_state + .header_text + .clone() + .unwrap_or_default() + } + + fn get_default_footer_text() -> String { + use std::process::Command; + + let mut footer_text = Command::new("uname") + .arg("-o") + .output() + .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) + .unwrap_or_default() + .trim() + .to_string(); + + if footer_text.is_empty() { + footer_text = "()".to_string(); + } + + footer_text + } + + fn format_footer(&mut self) -> String { + let footer_text = self + .formatting_state + .footer_text + .clone() + .unwrap_or(Self::get_default_footer_text()); + + if self.formatting_state.date.is_empty() { + self.formatting_state.date = self.format_dd("$Mdocdate$"); + } + + let mut space_size = self + .formatting_settings + .width + .saturating_sub(2 * footer_text.len() + self.formatting_state.date.len()) + / 2; + + let mut left_footer_text = footer_text.clone(); + let mut right_footer_text = footer_text.clone(); + + if space_size <= 1 { + space_size = self + .formatting_settings + .width + .saturating_sub(self.formatting_state.date.len()) + / 2; + + let space = vec![ + " "; + self.formatting_settings + .width + .saturating_sub(footer_text.len()) + ] + .into_iter() + .collect::(); + + left_footer_text = footer_text.clone() + &space.clone() + "\n"; + right_footer_text = "\n".to_string() + &space.clone() + &footer_text.clone(); + } + + let space = " ".repeat(space_size); + + let mut content = format!( + "\n{}{}{}{}{}", + left_footer_text, + space.clone(), + self.formatting_state.date, + space, + right_footer_text + ); + + let missing_space = self + .formatting_settings + .width + .saturating_sub(content.len() - 1); + + content.insert_str(left_footer_text.len() + 1, &" ".repeat(missing_space)); + + content + } + + /// Convert one [`Element`] AST to [`String`] + fn format_node(&mut self, node: Element) -> String { + let result = match node { + Element::Macro(macro_node) => self.format_macro_node(macro_node), + Element::Text(text) => self.format_text_node(text.as_str()), + Element::Eoi => "".to_string(), + }; + + replace_escapes(&result) + } + + /// Convert one [`MacroNode`] AST to [`String`] + fn format_macro_node(&mut self, macro_node: MacroNode) -> String { + match macro_node.clone().mdoc_macro { + // Block full-explicit + Macro::Bd { + block_type, + offset, + compact, + } => self.format_bd_block(block_type, offset, compact, macro_node), + Macro::Bf(bf_type) => self.format_bf_block(bf_type, macro_node), + Macro::Bk => self.format_bk_block(macro_node), + Macro::Bl { .. } => self.format_bl_blocks(macro_node), + + // Special block macro ta formatting + Macro::Ta => self.format_ta(), + + // Block full-implicit + Macro::It { head } => self.format_it_block(head, macro_node), + Macro::Nd => self.format_nd(macro_node), + Macro::Nm { name } => self.format_nm(name.clone(), macro_node), + Macro::Sh { title } => self.format_sh_block(title, macro_node), + Macro::Ss { title } => self.format_ss_block(title, macro_node), + + // Block partial-explicit. + Macro::Ao => self.format_a_block(macro_node), + Macro::Bo => self.format_b_block(macro_node), + Macro::Bro => self.format_br_block(macro_node), + Macro::Do => self.format_d_block(macro_node), + Macro::Oo => self.format_o_block(macro_node), + Macro::Po => self.format_p_block(macro_node), + Macro::Qo => self.format_q_block(macro_node), + Macro::Rs => self.format_rs_block(macro_node), + Macro::So => self.format_s_block(macro_node), + Macro::Xo => self.format_x_block(macro_node), + Macro::Eo { + opening_delimiter, + closing_delimiter, + } => self.format_e_block(opening_delimiter, closing_delimiter, macro_node), + Macro::Fo { ref funcname } => { + let funcname_copy = funcname.clone(); + self.format_f_block(funcname_copy, macro_node) + } + + // Block partial-implicit. + Macro::Aq => self.format_aq(macro_node), + Macro::Bq => self.format_bq(macro_node), + Macro::Brq => self.format_brq(macro_node), + Macro::D1 => self.format_d1(macro_node), + Macro::Dl => self.format_dl(macro_node), + Macro::Dq => self.format_dq(macro_node), + Macro::En => self.format_en(macro_node), + Macro::Op => self.format_op(macro_node), + Macro::Pq => self.format_pq(macro_node), + Macro::Ql => self.format_ql(macro_node), + Macro::Qq => self.format_qq(macro_node), + Macro::Sq => self.format_sq(macro_node), + Macro::Vt => self.format_vt(macro_node), + + // In-line. + // Rs block macros which can appears outside Rs-Re block. + Macro::B => self.format_b(macro_node), + Macro::T => self.format_t(macro_node), + Macro::U => self.format_u(macro_node), + + // Text production macros. + Macro::At => self.format_at(macro_node), + Macro::Bsx => self.format_bsx(macro_node), + Macro::Bx => self.format_bx(macro_node), + Macro::Dx => self.format_dx(macro_node), + Macro::Ad => self.format_ad(macro_node), + Macro::Ap => self.format_ap(macro_node), + Macro::Ar => self.format_ar(macro_node), + Macro::Bt => self.format_bt(), + Macro::Cd => self.format_cd(macro_node), + Macro::Cm => self.format_cm(macro_node), + Macro::Db => self.format_db(), + Macro::Dv => self.format_dv(macro_node), + Macro::Em => self.format_em(macro_node), + Macro::An { author_name_type } => self.format_an(author_name_type, macro_node), + Macro::Dd => { + match macro_node.nodes.is_empty() { + true => self.formatting_state.date = self.format_dd(""), + false => match ¯o_node.nodes[0] { + Element::Text(l) => self.formatting_state.date = self.format_dd(l.as_str()), + _ => unreachable!(), + }, + }; + + String::new() + } + Macro::Dt { + title, + section, + arch, + } => self.format_dt(title.clone(), section.as_str(), arch.clone()), + + Macro::Er => self.format_er(macro_node), + Macro::Es { + opening_delimiter, + closing_delimiter, + } => self.format_es(opening_delimiter, closing_delimiter, macro_node), + Macro::Ev => self.format_ev(macro_node), + Macro::Ex => self.format_ex(macro_node), + Macro::Fa => self.format_fa(macro_node), + Macro::Fd { + directive, + arguments, + } => self.format_fd(directive.as_str(), &arguments), + Macro::Fl => self.format_fl(macro_node), + Macro::Fn { funcname } => self.format_fn(funcname.as_str(), macro_node), + Macro::Fr => self.format_fr(macro_node), + Macro::Ft => self.format_ft(macro_node), + Macro::Fx => self.format_fx(macro_node), + Macro::Hf => self.format_hf(macro_node), + Macro::Ic => self.format_ic(macro_node), + Macro::In { filename } => self.format_in(filename.as_str(), macro_node), + Macro::Lb { lib_name } => self.format_lb(lib_name.as_str(), macro_node), + Macro::Li => self.format_li(macro_node), + Macro::Lk { ref uri } => self.format_lk(uri.as_str(), macro_node), + Macro::Lp => self.format_lp(), + Macro::Ms => self.format_ms(macro_node), + Macro::Mt => self.format_mt(macro_node), + Macro::No => self.format_no(macro_node), + Macro::Ns => self.format_ns(macro_node), + Macro::Nx => self.format_nx(macro_node), + Macro::Os => self.format_os(macro_node), + Macro::Ox => self.format_ox(macro_node), + Macro::Pa => self.format_pa(macro_node), + Macro::Pf { prefix } => self.format_pf(prefix.as_str(), macro_node), + Macro::Pp => self.format_pp(macro_node), + Macro::Rv => self.format_rv(macro_node), + Macro::Sm(sm_mode) => self.format_sm(sm_mode, macro_node), + Macro::St(st_type) => self.format_st(st_type, macro_node), + Macro::Sx => self.format_sx(macro_node), + Macro::Sy => self.format_sy(macro_node), + Macro::Tg { term } => self.format_tg(term), + Macro::Tn => self.format_tn(macro_node), + Macro::Ud => self.format_ud(), + Macro::Ux => self.format_ux(macro_node), + Macro::Va => self.format_va(macro_node), + Macro::Xr { name, section } => { + self.format_xr(name.as_str(), section.as_str(), macro_node) + } + _ => self.format_inline_macro(macro_node), + } + } + + /// Convert text node to [`String`]. Escape sequences is converted to true UTF-8 chars + fn format_text_node(&self, text: &str) -> String { + self.replace_unicode_escapes(text).trim().to_string() + } + + /// Special block macro ta formatting + fn format_ta(&mut self) -> String { + String::new() + } +} + +/// Split words on lines no longer than [`width`] +fn split_by_width(words: Vec, width: usize) -> Vec { + if width == 0 { + return words.iter().map(|s| s.to_string()).collect::>(); + } + + let mut lines = Vec::new(); + let mut line = String::new(); + let mut i = 0; + while i < words.len() { + let word_len = replace_escapes(&words[i]).len(); + let line_len = replace_escapes(&line).len(); + if line.is_empty() || word_len > width { + lines.extend( + words[i] + .chars() + .collect::>() + .chunks(width) + .map(|ch| ch.iter().collect::()), + ); + if let Some(l) = lines.pop() { + line = l; + } + i += 1; + continue; + } else if line_len + word_len + 1 > width { + lines.push(line.clone()); + line.clear(); + continue; + } + if !line.is_empty() && line_len < width { + if let Some(ch) = line.chars().last() { + if !ch.is_whitespace() { + line.push(' '); + } + } + } + line.push_str(&words[i]); + i += 1; + } + lines.push(line); + + for l in lines.iter_mut() { + *l = l.trim_end().to_string(); + } + + lines +} + +/// Add indentation for every line in [`lines`] according to offset +fn add_indent_to_lines(lines: Vec, width: usize, offset: &OffsetType) -> Vec { + lines + .into_iter() + .map(|line| { + let mut line_indent = width.saturating_sub(line.len()); + match offset { + OffsetType::Left => line, + OffsetType::Right => { + let indent = " ".repeat(line_indent); + indent + &line + } + OffsetType::Center => { + line_indent = (line_indent as f32 / 2.0).floor() as usize; + let indent = " ".repeat(line_indent); + indent.clone() + &line + } + _ => unreachable!(), + } + }) + .collect::>() +} + +/// Returns list symbol [`String`] according [`list_type`]. +/// If [`BlType::Enum`], then this function will return +/// [`String`] with next list number according to [`last_symbol`] +fn get_symbol(last_symbol: &str, list_type: &BlType) -> String { + match list_type { + BlType::Bullet => "•".to_string(), + BlType::Dash => "-".to_string(), + BlType::Enum => { + if last_symbol.is_empty() { + return "0.".to_string(); + } + let mut symbol = last_symbol.to_string(); + symbol.pop(); + let Ok(number) = symbol.parse::() else { + return String::new(); + }; + (number + 1).to_string() + "." + } + _ => String::new(), + } +} + +/// Merges adjacent oneline formatted nodes +fn merge_onelined( + elements: Vec, + line_width: usize, + indent_str: &str, + offset: &OffsetType, +) -> Vec { + fn merge( + v: &mut Vec, + lines: &mut Vec, + line_width: usize, + indent_str: &str, + offset: &OffsetType, + ) { + if !v.is_empty() { + let s = v + .join(" ") + .split_whitespace() + .map(|s| s.to_string()) + .collect::>(); + + let mut content = split_by_width(s, line_width); + content = add_indent_to_lines(content, line_width, offset); + for line in content.iter_mut() { + *line = indent_str.to_string() + line; + } + + lines.extend(content); + v.clear(); + } + } + + let mut lines = Vec::new(); + let mut onelines = Vec::new(); + + for el in elements { + if el.trim().lines().count() > 1 { + merge(&mut onelines, &mut lines, line_width, indent_str, offset); + + let mut el = el.split("\n").map(|s| s.to_string()).collect::>(); + if let Some(s) = el.iter_mut().next() { + if s.is_empty() { + *s = indent_str.to_string() + "\\&"; + } + } + + lines.extend(el); + } else if el.chars().all(|ch| ch.is_whitespace()) { + merge(&mut onelines, &mut lines, line_width, indent_str, offset); + lines.extend(el.lines().map(|_| String::new())); + } else { + onelines.push(el); + } + } + merge(&mut onelines, &mut lines, line_width, indent_str, offset); + + if let Some(first) = lines.first() { + if first.chars().all(|ch| ch.is_whitespace()) { + lines.remove(0); + } + } + + lines +} + +/// If Bl has nested Bl macros, then this function +/// convert it to [`Vec`] flattening super Bl +fn split_nested_bl(bl: MacroNode) -> Vec { + let MacroNode { mdoc_macro, nodes } = bl; + + let super_macros = |nodes: Vec| { + Element::Macro(MacroNode { + mdoc_macro: mdoc_macro.clone(), + nodes, + }) + }; + + let nested_macros = super_macros(nodes.clone()); + + let Macro::Bl { + list_type: super_list_type, + .. + } = mdoc_macro.clone() + else { + return vec![nested_macros]; + }; + let is_not_nested = |list_type: &BlType| { + matches!( + list_type, + BlType::Item | BlType::Inset | BlType::Column | BlType::Ohang + ) + }; + + if !is_not_nested(&super_list_type) { + return vec![nested_macros]; + } + + let mut bl_elements = vec![]; + let mut it_elements = vec![]; + for it in nodes { + let Element::Macro(MacroNode { + mdoc_macro: Macro::It { head }, + nodes: it_nodes, + }) = it + else { + if !it_elements.is_empty() { + bl_elements.push((true, super_macros(it_elements.clone()))); + it_elements.clear(); + } + bl_elements.push((true, it)); + continue; + }; + + let mut head_elements = vec![]; + for element in head { + if matches!( + element, + Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { .. }, + .. + }) + ) { + if !head_elements.is_empty() { + if !it_elements.is_empty() { + bl_elements.push((true, super_macros(it_elements.clone()))); + it_elements.clear(); + } + bl_elements.push(( + true, + super_macros(vec![Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: head_elements.clone(), + }, + nodes: vec![], + })]), + )); + head_elements.clear(); + } + bl_elements.push((false, element)); + } else { + head_elements.push(element); + } + } + + let mut body_elements = vec![]; + for element in it_nodes { + if matches!( + element, + Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { .. }, + .. + }) + ) { + if !head_elements.is_empty() || !body_elements.is_empty() { + it_elements.push(Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: head_elements.clone(), + }, + nodes: body_elements.clone(), + })); + bl_elements.push((true, super_macros(it_elements.clone()))); + + it_elements.clear(); + head_elements.clear(); + body_elements.clear(); + } + bl_elements.push((false, element)); + } else { + body_elements.push(element); + } + } + + if !head_elements.is_empty() || !body_elements.is_empty() { + it_elements.push(Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: head_elements, + }, + nodes: body_elements, + })); + } + } + + if !it_elements.is_empty() { + bl_elements.push((true, super_macros(it_elements))); + } + + if !bl_elements.is_empty() { + bl_elements + .into_iter() + .flat_map(|(checked, bl)| { + if checked { + return vec![bl]; + } + if let Element::Macro(ref node) = bl { + if let MacroNode { + mdoc_macro: Macro::Bl { .. }, + .. + } = node + { + return split_nested_bl(node.clone()); + } + } + vec![] + }) + .collect::>() + } else { + vec![nested_macros] + } +} + +/// Removes reduntant empty lines or lines that contains only whitespaces from [`input`] +fn remove_empty_lines(input: &str, delimiter_size: usize) -> String { + let input = input + .lines() + .map(|line| { + if line.chars().all(|ch| ch.is_whitespace()) { + "" + } else { + line + } + }) + .collect::>() + .join("\n"); + let mut result = String::with_capacity(input.len()); + let mut iter = input.chars().peekable(); + let lines_delimiter_big = "\n".repeat(delimiter_size); + let mut nl_count = 0; + + while let Some(current_char) = iter.next() { + if current_char == '\n' { + if iter.peek() != Some(&'\n') { + let lines_delimiter = if nl_count > 1 { + &lines_delimiter_big.clone() + } else { + "\n" + }; + result.push_str(lines_delimiter); + nl_count = 1; + } else { + nl_count += 1; + } + } else { + result.push(current_char); + } + } + + result +} + +// Formatting block full-explicit. +impl MdocFormatter { + fn get_width_indent(&self, width: &Option) -> usize { + let mut width = width.unwrap_or(0).min(MAX_INDENT) as usize; + if width < 2 { + width = 2; + } + width + } + + fn get_offset_indent(&self, offset: &Option) -> usize { + let Some(offset) = offset else { + return 0; + }; + match offset { + OffsetType::Indent => 6, + OffsetType::IndentTwo => 6 * 2, + _ => self.formatting_settings.indent, + } + } + + /// Converts [`OffsetType`] to block alignment type ([`OffsetType`]). + /// This function exists because [`OffsetType::Indent`] and + /// [`OffsetType::IndentTwo`] exist + fn get_offset_from_offset_type(&self, offset: &Option) -> OffsetType { + let Some(offset) = offset else { + return OffsetType::Left; + }; + match offset.clone() { + OffsetType::Indent | OffsetType::IndentTwo => OffsetType::Left, + OffsetType::Left | OffsetType::Right | OffsetType::Center => offset.clone(), + } + } + + fn format_bd_block( + &mut self, + block_type: BdType, + offset: Option, + _compact: bool, + macro_node: MacroNode, + ) -> String { + let indent = self.get_offset_indent(&offset); + let mut offset = self.get_offset_from_offset_type(&offset); + if block_type == BdType::Centered { + offset = OffsetType::Center; + } + + self.formatting_state.current_indent += indent; + + let current_indent = self.formatting_state.current_indent; + let line_width = self + .formatting_settings + .width + .saturating_sub(current_indent); + + let formatted_elements = macro_node.nodes.into_iter().map(|el| { + let is_aligned_macro = if let Element::Macro(MacroNode { mdoc_macro, .. }) = el.clone() + { + matches!(mdoc_macro, Macro::Bl { .. }) || matches!(mdoc_macro, Macro::Bd { .. }) + } else { + false + }; + + let formatted_node = match el { + Element::Text(text) => text, + _ => self.format_node(el), + }; + + let content = if is_aligned_macro { + vec![formatted_node] + } else { + formatted_node + .split_whitespace() + .map(|s| s.to_string()) + .collect::>() + }; + + (is_aligned_macro, content) + }); + + if line_width == 0 { + let content = formatted_elements + .flat_map(|(_, s)| s) + .collect::>() + .join(" "); + + return content; + } + + let mut lines = vec![]; + let mut is_last_aligned_macro = false; + + for (is_aligned_macro, content) in formatted_elements { + if is_aligned_macro { + lines.extend(content); + } else { + let mut content = content; + + if let BdType::Centered | BdType::Filled | BdType::Ragged = block_type { + if !is_last_aligned_macro { + if let Some(current_line) = lines.last() { + let current_line = current_line + .split_whitespace() + .map(|s| s.to_string()) + .collect::>(); + + content = [current_line, content].concat(); + } + } + } + + let l = split_by_width(content, line_width); + let l = add_indent_to_lines(l, line_width, &offset) + .iter() + .map(|line| format!("{}{}", " ".repeat(current_indent), line)) + .collect::>(); + + lines.extend(l); + } + + is_last_aligned_macro = is_aligned_macro; + } + + self.formatting_state.current_indent = + self.formatting_state.current_indent.saturating_sub(indent); + + let mut content = lines.join("\n"); + content = content.trim_end().to_string(); + + "\n\n".to_string() + &content + "\n\n" + } + + fn format_bf_block(&mut self, bf_type: BfType, macro_node: MacroNode) -> String { + let _font_change = match bf_type { + BfType::Emphasis => { + // if self.supports_italic() { + // "\x1b[3m".to_string() + // } else if self.supports_underline() { + // "\x1b[4m".to_string() + // } else{ + // String::new() + // } + String::new() + } + BfType::Literal => String::new(), + BfType::Symbolic => { + // if self.supports_bold(){ + // "\x1b[1m".to_string() + // }else{ + // String::new() + // } + String::new() + } + }; + + macro_node + .nodes + .into_iter() + .map(|node| { + let mut content = self.format_node(node); + if !content.ends_with('\n') && !content.is_empty() { + content.push_str(&self.formatting_state.spacing); + } + content + }) + .filter(|s| !s.is_empty()) + .collect::>() + .join("") + } + + fn format_bk_block(&mut self, macro_node: MacroNode) -> String { + let indent = self.formatting_state.current_indent; + let max_width = self.formatting_settings.width - indent; + + let mut content = String::new(); + let mut current_len = indent; + + for node in macro_node.nodes.into_iter() { + let formatted_node = self.format_node(node); + let formatted_node_len = formatted_node.chars().count(); + + current_len += formatted_node_len; + + if !content.is_empty() && current_len > max_width { + current_len = indent + formatted_node_len + 1; + content.push_str(&format!("\n{}", " ".repeat(indent))); + } + + content.push_str(&format!("{} ", formatted_node.trim())); + } + + content.trim().to_string() + } + + fn format_bl_symbol_block( + &self, + items: Vec<(String, Vec)>, + width: Option, + offset: Option, + list_type: BlType, + compact: bool, + ) -> String { + let indent = self.get_width_indent(&width); + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let symbol_range = if let BlType::Enum = list_type { + items.len().to_string().len() + 1 + } else { + 1 + }; + let mut full_indent = origin_indent + indent; + if let BlType::Enum = list_type { + full_indent += symbol_range.saturating_sub(2); + } + let line_width = width.saturating_sub(full_indent); + let indent_str = " ".repeat(full_indent); + + let mut symbol = get_symbol("", &list_type); + let mut content = String::new(); + for (_, body) in items { + let mut body = merge_onelined(body, line_width, &indent_str, &offset); + + if let Some(first_line) = body.get_mut(0) { + symbol = get_symbol(symbol.as_str(), &list_type); + if first_line.len() > (origin_indent + symbol_range) { + first_line + .replace_range(origin_indent..(origin_indent + symbol_range), &symbol); + } + } + + content.push_str(&(body.join("\n") + "\n")); + + if !compact { + content.push('\n'); + } + } + + content + } + + fn format_bl_item_block( + &self, + items: Vec<(String, Vec)>, + offset: Option, + compact: bool, + ) -> String { + let indent = self.formatting_settings.indent; + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let line_width = width.saturating_sub(origin_indent + indent); + let origin_indent_str = " ".repeat(origin_indent); + let delimiter = if compact { "\n" } else { "\n\n" }; + + let mut content = String::new(); + for (_, body) in items { + let body = body.join(" "); + let mut body = split_by_width( + body.split_whitespace() + .map(|s| s.to_string()) + .collect::>(), + line_width + indent, + ); + body = add_indent_to_lines(body, line_width + indent, &offset); + for line in body.iter_mut() { + *line = origin_indent_str.clone() + line; + } + content.push_str(&(body.join(delimiter) + delimiter)); + } + + content + } + + fn format_bl_ohang_block( + &self, + items: Vec<(String, Vec)>, + offset: Option, + compact: bool, + ) -> String { + let indent = self.formatting_settings.indent; + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let line_width = width.saturating_sub(origin_indent + indent); + let origin_indent_str = " ".repeat(origin_indent); + + let items = items + .into_iter() + .map(|(head, body)| (head, body.join(" "))) + .collect::>(); + + let delimiter = if compact { "\n" } else { "\n\n" }; + + let mut content = String::new(); + for (head, body) in items { + let mut h = split_by_width( + head.split_whitespace() + .map(|s| s.to_string()) + .collect::>(), + line_width + indent, + ); + let mut body = split_by_width( + body.split_whitespace() + .map(|s| s.to_string()) + .collect::>(), + line_width + indent, + ); + h.extend(body); + body = h; + body = add_indent_to_lines(body, line_width + indent, &offset); + for line in body.iter_mut() { + *line = origin_indent_str.clone() + line; + } + content.push_str(&(body.join(delimiter).trim_end().to_string() + "\n")); + } + + content + } + + fn format_bl_inset_block( + &self, + items: Vec<(String, Vec)>, + offset: Option, + compact: bool, + list_type: BlType, + ) -> String { + let head_space = match list_type { + BlType::Inset => " ", + BlType::Diag => "  ", + _ => " ", + }; + let indent = self.formatting_settings.indent; + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let line_width = width.saturating_sub(origin_indent + indent); + let origin_indent_str = " ".repeat(origin_indent); + + let items = items + .into_iter() + .map(|(head, body)| (head, body.join(" "))) + .collect::>(); + + let get_words = |s: &str| { + s.split_whitespace() + .map(|s| s.to_string()) + .collect::>() + }; + + let mut content = String::new(); + for (head, body) in items { + let mut head = get_words(&head); + let mut body = get_words(&body); + if let Some(word) = head.last_mut() { + *word += head_space; + } + + body = split_by_width([head, body].concat(), line_width + indent); + + body = add_indent_to_lines(body, line_width + indent, &offset); + for line in body.iter_mut() { + *line = origin_indent_str.clone() + line; + } + content.push_str(&(body.join("\n") + "\n")); + if !compact { + content.push('\n'); + } + } + + content + } + + fn format_bl_column_block(&self, items: Vec>, mut columns: Vec) -> String { + fn split_cells(table: Vec>, col_widths: &[usize]) -> Vec> { + let mut splitted_rows_table = vec![]; + for row in table { + let mut splitted_row = vec![]; + for (i, cell) in row.iter().enumerate() { + if i >= col_widths.len() { + break; + } + splitted_row.push(split_by_width( + cell.split_whitespace() + .map(|w| w.to_string()) + .collect::>(), + col_widths[i], + )); + } + splitted_rows_table.push(splitted_row); + } + let mut new_table = vec![]; + for row in splitted_rows_table { + let height = row.iter().map(|c| c.len()).max().unwrap_or(0); + for i in 0..height { + let mut new_row = vec![]; + for cell in &row { + new_row.push(if i >= cell.len() { + "".to_string() + } else { + cell[i].clone() + }); + } + new_table.push(new_row); + } + } + new_table + } + + /// Merges last row cells for rows with length bigger then [`col_count`] + fn merge_row_ends(table: &mut [Vec], col_count: usize) -> Option<(usize, usize)> { + let mut row_len_range: Option<(usize, usize)> = None; + table + .iter_mut() + .for_each(|row| match row.len().cmp(&col_count) { + std::cmp::Ordering::Less => row.resize(col_count, "".to_string()), + std::cmp::Ordering::Greater => { + if row_len_range.is_none() { + row_len_range = Some((usize::MAX, 0)); + } + let end = row.split_off(col_count).join(" "); + let end_len = end.len(); + row_len_range = row_len_range.map(|r| { + if end_len == 0 { + return (r.0, r.1.max(end_len)); + } + (r.0.min(end_len), r.1.max(end_len)) + }); + row.push(trim_quotes(end.trim().to_string())); + } + _ => {} + }); + + row_len_range + } + + fn calculate_col_widths( + table: &Vec>, + total_width: &mut usize, + columns: Vec, + mut row_len_range: Option<(usize, usize)>, + max_line_width: usize, + ) -> (Vec, bool) { + let col_count = columns.len(); + let mut bigger_row_len = None; + if let Some((min, max)) = row_len_range.as_mut() { + let columns_total_width = + max_line_width.saturating_sub(columns.iter().map(|c| c.len()).sum::()); + bigger_row_len = if *max < columns_total_width { + Some(*max) + } else if *min == usize::MAX { + None + } else { + Some(*min) + } + }; + + if let Some(bigger_row_len) = bigger_row_len { + *total_width += bigger_row_len; + } + let columns_suit_by_width = *total_width < max_line_width; + let mut col_widths = vec![0; col_count]; + + if columns_suit_by_width { + for (i, col) in columns.iter().enumerate() { + col_widths[i] = col.len(); + } + if let Some(bigger_row_len) = bigger_row_len { + col_widths.push(bigger_row_len); + } else if let Some(last_col_width) = col_widths.last_mut() { + *last_col_width += max_line_width - *total_width; + } + } else { + for row in table { + for (i, cell) in row.iter().take(col_count).enumerate() { + col_widths[i] = col_widths[i].max(cell.len()); + } + } + if let Some(bigger_row_len) = bigger_row_len { + col_widths.push(bigger_row_len); + } + } + + (col_widths, columns_suit_by_width) + } + + fn format_table( + mut table: Vec>, + columns: Vec, + max_line_width: usize, + ) -> String { + if table.is_empty() { + return String::new(); + } + + let col_count = columns.len(); + let mut total_width: usize = + columns.iter().map(|c| c.len()).sum::() + 2 * (col_count - 1); + let row_len_range = merge_row_ends(&mut table, col_count); + let (col_widths, columns_suit_by_width) = calculate_col_widths( + &table, + &mut total_width, + columns, + row_len_range, + max_line_width, + ); + if columns_suit_by_width { + table = split_cells(table, &col_widths); + } + + let mut result = String::new(); + for row in table { + let mut offset = 0; + let indent_step = 8; + + let items_to_print = col_widths.len().min(row.len()); + if !columns_suit_by_width { + for (i, cell) in row.iter().take(items_to_print).enumerate() { + result.push_str(&" ".repeat(offset)); + result.push_str(&format!("{: max_line_width { + result.push('\n'); + offset += indent_step; + line_width = offset; + result.push_str(&" ".repeat(offset)); + } + result.push_str(&format!("{:>() + .join("\n"); + + if !content.ends_with("\n") { + content.push('\n'); + } + + content + } + + fn format_bl_tag_block( + &self, + items: Vec<(String, Vec)>, + width: Option, + offset: Option, + compact: bool, + ) -> String { + let indent = self.get_width_indent(&width); + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let line_width = width.saturating_sub(origin_indent + indent); + let indent_str = " ".repeat(origin_indent + indent); + let origin_indent_str = " ".repeat(origin_indent); + + let mut content = String::new(); + for (head, body) in items { + let mut body = merge_onelined(body, line_width, &indent_str, &offset); + let head = head.trim().to_string(); + let space = if head.len() < indent.saturating_sub(1) { + if let Some(line) = body.first_mut() { + *line = line.trim_start().to_string(); + } + " ".repeat(indent - head.len()) + } else { + "\n".to_string() + }; + + content.push_str(&(origin_indent_str.clone() + &head + &space + &body.join("\n"))); + if !body.is_empty() || head.len() < indent.saturating_sub(1) { + content.push('\n'); + } + if !compact { + content.push('\n'); + } + } + + content + } + + fn format_bl_hang_block( + &self, + items: Vec<(String, Vec)>, + is_first_block: Vec, + width: Option, + offset: Option, + compact: bool, + ) -> String { + let indent = self.get_width_indent(&width); + let offset = self.get_offset_from_offset_type(&offset); + let origin_indent = self.formatting_state.current_indent; + let width = self.formatting_settings.width; + let line_width = width.saturating_sub(origin_indent + indent); + let indent_str = " ".repeat(origin_indent + indent); + let origin_indent_str = " ".repeat(origin_indent); + let mut content = String::new(); + + for (i, (head, body)) in items.into_iter().enumerate() { + let mut body = body; + let mut head = head.clone(); + + if !is_first_block[i] { + let first_line = body + .first() + .cloned() + .unwrap_or_default() + .split_whitespace() + .map(|s| s.to_string()) + .collect::>(); + let mut i = 0; + let mut j = 0; + if head.len() > indent.saturating_sub(1) { + while head.len() < line_width + indent && i < first_line.len() { + if head.len() + first_line[i].len() >= line_width + indent { + break; + } + head.push_str(&(" ".to_string() + &first_line[i])); + j += first_line[i].len() + 1; + i += 1; + } + } + if let Some(line) = body.get_mut(0) { + line.replace_range(0..j, ""); + } + } + + let mut body = merge_onelined(body, line_width, &indent_str, &offset); + + if head.len() < indent { + if let Some(line) = body.first_mut() { + *line = line.trim_start().to_string(); + } + let space = if is_first_block[i] { + "\n".to_string() + &origin_indent_str.clone() + &" ".repeat(indent) + } else { + " ".repeat(indent - head.len()) + }; + content.push_str( + &(origin_indent_str.clone() + + &head + + &space + + body.join("\n").trim_end() + + "\n"), + ); + } else { + content.push_str( + &(origin_indent_str.clone() + + head.trim_end() + + "\n" + + body.join("\n").trim_end() + + "\n"), + ); + } + if !compact { + content.push('\n'); + } + } + + content + } + + /// Extract head from It macro and format every element in it + fn get_heads(&mut self, macro_node: MacroNode, list_type: &BlType) -> Vec { + macro_node + .nodes + .into_iter() + .filter_map(|el| { + let Element::Macro(MacroNode { + mdoc_macro: Macro::It { head }, + .. + }) = el + else { + return None; + }; + + if list_type == &BlType::Column { + None + } else { + let content = head + .iter() + .map(|element| self.format_node(element.clone())) + .collect::>() + .join(" ") + .trim() + .to_string(); + + Some(content) + } + }) + .collect::>() + } + + /// Extract head from each It macro of Bl macro and format + /// every element in them. Then return [`Vec`] of + /// all formatted It macro heads + fn prepare_rows(&mut self, elements: Vec) -> Vec { + elements + .split(|el| { + matches!( + el, + Element::Macro(MacroNode { + mdoc_macro: Macro::Ta, + .. + }) + ) + }) + .map(|elements| { + elements + .iter() + .map(|el| self.format_node(el.clone())) + .collect::>() + .join(" ") + }) + .collect::>() + } + + /// Extract body from each It macro of Bl macro and format + /// every element in them. Then return [`Vec`] of + /// all formatted It macro bodies + fn get_bodies(&mut self, macro_node: MacroNode, list_type: &BlType) -> Vec> { + macro_node + .nodes + .into_iter() + .filter_map(|el| { + let Element::Macro(MacroNode { + mdoc_macro: Macro::It { head }, + nodes, + }) = el + else { + return None; + }; + + if list_type == &BlType::Column { + Some(self.prepare_rows([head, nodes].concat())) + } else { + Some( + nodes + .iter() + .filter(|el| { + !matches!( + el, + Element::Macro(MacroNode { + mdoc_macro: Macro::Ta, + .. + }) + ) + }) + .map(|element| self.format_node(element.clone())) + .collect::>(), + ) + } + }) + .collect::>() + } + + fn format_bl_block( + &mut self, + list_type: BlType, + width: Option, + offset: Option, + compact: bool, + columns: Vec, + macro_node: MacroNode, + ) -> String { + fn get_symbol_width(list_type: &BlType, macro_node: &MacroNode) -> usize { + if !matches!(list_type, BlType::Bullet | BlType::Dash | BlType::Enum) { + return 0; + } + let it_count = macro_node + .nodes + .iter() + .filter(|n| { + matches!( + n, + Element::Macro(MacroNode { + mdoc_macro: Macro::It { .. }, + .. + }) + ) + }) + .count(); + if let BlType::Enum = list_type { + it_count.to_string().len() + } else { + 0 + } + } + + fn strip_empty_between_nested( + formatted_its: &mut [Vec], + macro_node: &MacroNode, + max_nl: usize, + ) { + for (i, it_node) in macro_node.nodes.iter().enumerate() { + let Element::Macro(MacroNode { + mdoc_macro: Macro::It { .. }, + nodes, + }) = it_node + else { + continue; + }; + let Some(Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { .. }, + .. + })) = nodes.first() + else { + continue; + }; + let Some(element) = formatted_its.get_mut(i) else { + continue; + }; + let Some(first) = element.first_mut() else { + continue; + }; + let leading_nl_count = first + .chars() + .take_while(|ch| ch.is_whitespace()) + .filter(|ch| *ch == '\n') + .count(); + if leading_nl_count >= max_nl { + *first = first + .lines() + .skip(max_nl + 1) + .collect::>() + .join("\n") + .to_string(); + } + } + } + + let heads = self.get_heads(macro_node.clone(), &list_type); + let width_indent = self.get_width_indent(&width); + let offset_indent = self.get_offset_indent(&offset); + + self.formatting_state.current_indent += offset_indent + width_indent; + let symbol_width = get_symbol_width(&list_type, ¯o_node); + let is_symbol = matches!(list_type, BlType::Bullet | BlType::Dash | BlType::Enum); + if is_symbol { + self.formatting_state.current_indent += symbol_width; + } + + let mut bodies = self.get_bodies(macro_node.clone(), &list_type); + + self.formatting_state.current_indent = self + .formatting_state + .current_indent + .saturating_sub(width_indent); + if is_symbol { + self.formatting_state.current_indent = self + .formatting_state + .current_indent + .saturating_sub(symbol_width); + } + + let max_nl = if matches!(list_type, BlType::Hang) { + 1 + } else { + 0 + }; + strip_empty_between_nested(&mut bodies, ¯o_node, max_nl); + + let items: Vec<(String, Vec)> = if heads.is_empty() { + bodies + .clone() + .into_iter() + .map(|body| ("".to_string(), body)) + .collect() + } else { + heads.into_iter().zip(bodies.clone()).collect() + }; + + let mut content = match list_type { + BlType::Bullet | BlType::Dash | BlType::Enum => { + self.format_bl_symbol_block(items, width, offset, list_type, compact) + } + BlType::Item => self.format_bl_item_block(items, offset, compact), + BlType::Ohang => self.format_bl_ohang_block(items, offset, compact), + BlType::Inset | BlType::Diag => { + self.format_bl_inset_block(items, offset, compact, list_type) + } + BlType::Column => self.format_bl_column_block(bodies, columns), + BlType::Tag => self.format_bl_tag_block(items, width, offset, compact), + BlType::Hang => { + let MacroNode { nodes, .. } = macro_node; + let is_first_block = nodes + .iter() + .map(|el| { + if let Element::Macro(MacroNode { nodes, .. }) = el { + if let Some(Element::Macro(MacroNode { mdoc_macro, .. })) = + nodes.first() + { + return matches!(mdoc_macro, &Macro::Bl { .. } | &Macro::Bd { .. }); + } + } + false + }) + .collect::>(); + self.format_bl_hang_block(items, is_first_block, width, offset, compact) + } + }; + + self.formatting_state.current_indent = self + .formatting_state + .current_indent + .saturating_sub(offset_indent); + + content = "\n\n".to_string() + &content + "\n"; + + content + } + + fn format_bl_blocks(&mut self, macro_node: MacroNode) -> String { + split_nested_bl(macro_node) + .into_iter() + .map(|element| { + let Element::Macro(ref macro_node) = element else { + return self.format_node(element); + }; + let MacroNode { mdoc_macro, .. } = macro_node.clone(); + let Macro::Bl { + list_type, + width, + offset, + compact, + columns, + } = mdoc_macro.clone() + else { + return self.format_node(element); + }; + self.format_bl_block( + list_type, + width, + offset, + compact, + columns, + macro_node.clone(), + ) + }) + .collect::>() + .join("") + } +} + +// Formatting block full-implicit. +impl MdocFormatter { + fn format_it_block(&mut self, _head: Vec, _macro_node: MacroNode) -> String { + String::new() + } + + fn format_nd(&mut self, macro_node: MacroNode) -> String { + let content = macro_node + .nodes + .into_iter() + .map(|node| { + let mut content = self.format_node(node); + if !content.ends_with('\n') && !content.is_empty() { + content.push_str(&self.formatting_state.spacing); + } + content + }) + .filter(|s| !s.is_empty()) + .collect::>() + .join(""); + + format!("– {}", content) + } + + fn format_nm(&mut self, name: Option, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + if self.formatting_state.first_name.is_none() { + self.formatting_state.first_name = name; + let first_name = match self.formatting_state.first_name.as_ref() { + Some(name) => name.clone(), + None => "".to_string(), + }; + + if is_first_char_delimiter(&content) { + format!("{}{}", first_name.trim(), content.trim()) + } else { + format!("{} {}", first_name.trim(), content.trim()) + } + } else { + let provided_name = match name { + Some(name) => name, + None => self.formatting_state.first_name.clone().unwrap(), + }; + + let separator = if is_first_char_delimiter(&content) { + "" + } else { + " " + }; + + format!("{}{}{}", provided_name.trim(), separator, content.trim()) + } + } + + /// If line don't have enought indentation according + /// to [`self.formatting_settings.indent`], then + /// indent is added to this line + fn add_missing_indent(&self, content: &mut String) { + *content = content + .split("\n") + .map(|line| { + let indent_is_small = line.chars().take_while(|ch| ch.is_whitespace()).count() + < self.formatting_settings.indent; + + let is_not_empty = !(line.chars().all(|ch| ch.is_whitespace()) || line.is_empty()); + let line = if indent_is_small && is_not_empty && !line.starts_with("\\[ssindent]") { + " ".repeat(self.formatting_settings.indent) + line.trim_start() + } else { + line.to_string() + }; + + line + }) + .collect::>() + .join("\n"); + } + + fn format_sh_block(&mut self, title: String, macro_node: MacroNode) -> String { + fn append_formatted_node( + content: &mut String, + formatted_node: &str, + current_len: &mut usize, + indent: usize, + max_width: usize, + ) { + let formatted_node_len = formatted_node.chars().count(); + *current_len += formatted_node_len; + + if !content.is_empty() && *current_len > max_width { + *current_len = indent + formatted_node_len + 1; + content.push_str(&format!("\n{}", " ".repeat(indent))); + } + + content.push_str(&format!("{} ", formatted_node.trim_end())); + } + + let mut current_lines_count = 0; + let mut prev_node = Macro::Soi; + let mut is_first_an_in_authors_block = true; + + self.formatting_state.current_indent += self.formatting_settings.indent; + + let mut content = if title.eq_ignore_ascii_case("SYNOPSIS") { + let first_name_len = self + .formatting_state + .first_name + .clone() + .unwrap_or_default() + .len(); + let indent = self.formatting_state.current_indent + first_name_len + 1; + + let max_width = self.formatting_settings.width; + + let mut content = String::new(); + let mut current_len = indent; + + for node in macro_node.nodes.into_iter() { + let formatted_node = match &node { + Element::Macro(macro_node) => { + let formatted = match ¯o_node.mdoc_macro { + Macro::Vt => self.format_vt_synopsis(macro_node.clone()), + Macro::Nm { name } => { + let formatted_node = + self.format_nm(name.clone(), macro_node.clone()); + + current_len = indent; + + format!("\n{}", formatted_node.trim_end()) + } + Macro::Ft => { + let formatted_node = self.format_ft_synopsis(macro_node.clone()); + content.push_str(&formatted_node); + + current_len = indent; + + continue; + } + Macro::In { ref filename } => { + let formatted_node = self.format_in_synopsis( + filename.as_str(), + macro_node.clone(), + &prev_node, + ); + content.push_str(&formatted_node); + + current_len = indent; + + continue; + } + Macro::Fd { + directive, + arguments, + } => { + let formatted_node = self.format_fd_synopsis(directive, arguments); + content.push_str(&formatted_node); + + current_len = indent; + + continue; + } + Macro::Fn { funcname } => { + let formatted_node = + self.format_fn_synopsis(funcname, macro_node.clone()); + content.push_str(&formatted_node); + + current_len = indent; + + continue; + } + Macro::Bk => { + for node in macro_node.nodes.clone().into_iter() { + let formatted_node = self.format_node(node); + append_formatted_node( + &mut content, + &formatted_node, + &mut current_len, + indent, + max_width, + ); + continue; + } + + String::new() + } + _ => self.format_macro_node(macro_node.clone()), + }; + + prev_node = macro_node.mdoc_macro.clone(); + formatted + } + Element::Text(text) => self.format_text_node(text), + Element::Eoi => "".to_string(), + }; + + append_formatted_node( + &mut content, + &formatted_node, + &mut current_len, + indent, + max_width, + ); + + current_lines_count += content.lines().count(); + } + + content.trim().to_string() + } else { + macro_node + .nodes + .into_iter() + .map(|node| { + let content = match node { + Element::Macro(ref macro_node) => { + if title.eq_ignore_ascii_case("AUTHORS") { + match ¯o_node.mdoc_macro { + Macro::An { author_name_type } => { + if is_first_an_in_authors_block { + self.formatting_state.split_mod = false; + is_first_an_in_authors_block = false; + } else { + self.formatting_state.split_mod = true; + } + + self.format_an_authors( + author_name_type.clone(), + macro_node.clone(), + ) + } + _ => self.format_macro_node(macro_node.clone()), + } + } else if title.eq_ignore_ascii_case("SEE ALSO") { + match ¯o_node.mdoc_macro { + Macro::Rs => self.format_rs_see_also(macro_node.clone()), + _ => self.format_macro_node(macro_node.clone()), + } + } else { + self.format_macro_node(macro_node.clone()) + } + } + Element::Text(ref text) => self.format_text_node(text), + Element::Eoi => String::new(), + }; + + current_lines_count += content.lines().count(); + content + }) + .filter(|s| !s.is_empty()) + .collect::>() + .join(&self.formatting_state.spacing) + }; + + self.add_missing_indent(&mut content); + + self.formatting_state.current_indent = 0; + + let content = if content.starts_with('\n') { + content.strip_prefix("\n").unwrap().to_string() + } else { + content + }; + + format!("\n{}\n{}", title.to_uppercase(), content.trim_end()) + } + + fn format_ss_block(&mut self, title: String, macro_node: MacroNode) -> String { + if self.formatting_state.current_indent == 0 { + self.formatting_state.current_indent += self.formatting_settings.indent; + } + let mut content = macro_node + .nodes + .into_iter() + .map(|node| { + let mut content = self.format_node(node); + if !content.ends_with('\n') && !content.is_empty() { + content.push_str(&self.formatting_state.spacing); + } + content + }) + .filter(|s| !s.is_empty()) + .collect::>() + .join(""); + + self.add_missing_indent(&mut content); + self.formatting_state.current_indent = 0; + + format!("\n\n\\[ssindent]{title}\n\n{content}\n") + } +} + +// Formatting block partial-explicit. +impl MdocFormatter { + fn format_partial_explicit_block(&mut self, iter: impl Iterator) -> String { + let mut result = String::new(); + let mut prev_was_open = false; + let mut is_first_node = true; + + for node in iter { + match node { + Element::Text(text) => match text.as_str() { + "(" | "[" => { + result.push_str(&text); + prev_was_open = true; + } + ")" | "]" | "." | "," | ":" | ";" | "!" | "?" => { + result.push_str(&text); + prev_was_open = false; + } + _ => { + if prev_was_open { + result.push_str(&self.format_text_node(&text)); + } else { + let offset = if is_first_node { + "" + } else { + self.formatting_state.spacing.as_str() + }; + result.push_str(&format!("{}{}", offset, self.format_text_node(&text))); + } + prev_was_open = false; + } + }, + _ => { + let mut content = self.format_node(node); + if !content.ends_with('\n') && !content.is_empty() { + content.push_str(&self.formatting_state.spacing); + } + result.push_str(&content); + prev_was_open = false; + } + } + + if is_first_node { + is_first_node = false; + } + } + + result.trim().to_string() + } + + fn format_a_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Ac) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Ac) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("⟨{}⟩{}", formatted_body, formatted_tail); + } + + format!("⟨{}⟩ {}", formatted_body, formatted_tail) + } + + fn format_b_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Bc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Bc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("[{}]{}", formatted_body.trim(), formatted_tail.trim()); + } + + format!("[{}] {}", formatted_body.trim(), formatted_tail.trim()) + } + + fn format_br_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Brc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Brc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("{{{}}}{}", formatted_body.trim(), formatted_tail.trim()); + } + + format!("{{{}}} {}", formatted_body, formatted_tail.trim()) + } + + fn format_d_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Dc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Dc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("“{}”{}", formatted_body.trim(), formatted_tail.trim()); + } + + format!("“{}” {}", formatted_body.trim(), formatted_tail.trim()) + } + + fn format_e_block( + &mut self, + opening_delimiter: Option, + closing_delimiter: Option, + macro_node: MacroNode, + ) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Dc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Dc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + match (opening_delimiter, closing_delimiter) { + (Some(open), Some(close)) => { + format!( + "{}{}{} {} ", + open, + formatted_body.trim(), + close, + formatted_tail.trim() + ) + } + (Some(open), None) => { + format!( + "{}{} {} ", + open, + formatted_body.trim(), + formatted_tail.trim() + ) + } + (None, Some(close)) => { + format!( + "{}{} {} ", + formatted_body.trim(), + close, + formatted_tail.trim() + ) + } + (None, None) => format!("{} {}", formatted_body.trim(), formatted_tail.trim()), + } + } + + fn format_f_block(&mut self, funcname: String, macro_node: MacroNode) -> String { + let mut body = macro_node + .nodes + .into_iter() + .map(|node| { + let mut content = self.format_node(node); + if !content.ends_with('\n') && !content.is_empty() { + content.push_str(&format!(",{}", self.formatting_state.spacing)); + } + content + }) + .filter(|s| !s.is_empty()) + .collect::>() + .join(""); + + body.pop(); + body.pop(); + + format!("{}({});", funcname, body) + } + + fn format_o_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Oc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Oc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("[{}]{}", formatted_body, formatted_tail); + } + + format!("[{}] {}", formatted_body, formatted_tail) + } + + fn format_p_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Pc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Pc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("({}){}", formatted_body, formatted_tail); + } + + format!("({}) {} ", formatted_body.trim(), formatted_tail.trim()) + } + + fn format_q_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Qc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Qc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("\"{}\"{} ", formatted_body, formatted_tail); + } + + format!("\"{}\" {} ", formatted_body.trim(), formatted_tail.trim()) + } + + fn format_s_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Sc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Sc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("'{}'{} ", formatted_body, formatted_tail); + } + + format!("'{}' {} ", formatted_body, formatted_tail) + } + + fn format_x_block(&mut self, macro_node: MacroNode) -> String { + let iter = macro_node.nodes.into_iter(); + let body = iter.clone().take_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Xc) + } else { + false + } + }); + + let tail = iter.skip_while(|p| { + if let Element::Macro(ref macro_node) = p { + !matches!(macro_node.mdoc_macro, Macro::Xc) + } else { + false + } + }); + + let formatted_body = self.format_partial_explicit_block(body); + let formatted_tail = self.format_partial_explicit_block(tail); + + if is_first_word_delimiter(&formatted_tail) { + return format!("{}{} ", formatted_body, formatted_tail); + } + + format!("{} {} ", formatted_body, formatted_tail) + } +} + +// Formatting Rs-Re bloock. Can contain only %* macros +impl MdocFormatter { + fn format_rs_block(&self, macro_node: MacroNode) -> String { + let mut iter = macro_node.nodes.into_iter().peekable(); + + let mut items = Vec::new(); + while let Some(el) = iter.peek() { + if let Element::Macro(node) = el { + if node.mdoc_macro == Macro::A { + let el = iter.next().unwrap(); + if let Element::Macro(node) = el { + items.push(self.format_a(node)); + } + } else { + break; + } + } else { + unreachable!("Unexpected rule!"); + } + } + + let formatted_a = match items.len() { + 0 => "".to_string(), + 1 => items[0].clone(), + 2 => format!("{} and {}", items[0], items[1]), + _ => { + let last = items.last().unwrap(); + let all_but_last = &items[..items.len() - 1]; + format!("{}, and {}", all_but_last.join(", "), last) + } + }; + + let formatted_all = iter + .map(|el| match el { + Element::Macro(node) => match node.mdoc_macro { + Macro::B => self.format_b(node), + Macro::C => self.format_c(node), + Macro::D => self.format_d(node), + Macro::I => self.format_i(node), + Macro::J => self.format_j(node), + Macro::N => self.format_n(node), + Macro::O => self.format_o(node), + Macro::P => self.format_p(node), + Macro::Q => self.format_q(node), + Macro::R => self.format_r(node), + Macro::T => self.format_t(node), + Macro::U => self.format_u(node), + Macro::V => self.format_v(node), + _ => unreachable!("Rs can not contain macro: {:?}", node), + }, + _ => unreachable!("Unexpected element type!"), + }) + .collect::>() + .join(", "); + + match (formatted_a.is_empty(), formatted_all.is_empty()) { + (true, true) => "".to_string(), + (true, false) => format!("{}.\n", formatted_all), + (false, true) => format!("{}.\n", formatted_a), + (false, false) => format!("{}, {}.\n", formatted_a, formatted_all), + } + } + + fn format_rs_see_also(&self, macro_node: MacroNode) -> String { + let c = self.format_rs_block(macro_node); + + format!("\n{}", c) + } + + fn format_a(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_b(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_c(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_d(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_i(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_j(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_n(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_o(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_p(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_q(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_r(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_t(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_u(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_v(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } +} + +// Formatting block partial-implicit. +impl MdocFormatter { + fn format_partial_implicit_block( + &mut self, + macro_node: &mut MacroNode, + open_char: &str, + close_char: &str, + ) -> String { + fn is_closing_delimiter(s: &str) -> bool { + matches!(s, ")" | "]" | "." | "," | ":" | ";" | "!" | "?") + } + + fn extract_trailing_delims(node: &mut MacroNode) -> String { + let mut delim_sequence = String::new(); + loop { + if node.nodes.is_empty() { + break; + } + let last_index = node.nodes.len() - 1; + match &mut node.nodes[last_index] { + Element::Text(ref text) if is_closing_delimiter(text) => { + if let Some(Element::Text(delim)) = node.nodes.pop() { + delim_sequence = format!("{}{}", delim, delim_sequence); + } else { + break; + } + } + Element::Macro(ref mut inner_macro_node) => { + let inner_delims = extract_trailing_delims(inner_macro_node); + if inner_delims.is_empty() { + break; + } + delim_sequence = format!("{}{}", inner_delims, delim_sequence); + if inner_macro_node.nodes.is_empty() { + node.nodes.pop(); + } + } + _ => break, + } + } + delim_sequence + } + + let trailing_punctuation = extract_trailing_delims(macro_node); + + let mut result = open_char.to_string(); + let mut prev_was_open = false; + let mut is_first_node = true; + + for node in ¯o_node.nodes { + let content = match node { + Element::Text(text) => match text.as_str() { + "(" | "[" => { + prev_was_open = true; + text.clone() + } + ")" | "]" => { + prev_was_open = false; + text.clone() + } + "." | "," | ":" | ";" | "!" | "?" => { + prev_was_open = false; + String::new() + } + _ => { + let formatted_text = self.format_text_node(text); + let offset = if is_first_node || prev_was_open { + "" + } else { + self.formatting_state.spacing.as_str() + }; + prev_was_open = false; + format!("{}{}", offset, formatted_text) + } + }, + other => { + let mut s = self.format_node(other.clone()); + if !s.is_empty() && !s.ends_with('\n') { + s.push_str(&self.formatting_state.spacing); + } + s + } + }; + + if !content.is_empty() { + result.push_str(&content); + } + is_first_node = false; + } + + result = result.trim().to_string(); + + format!("{}{}{}", result, close_char, trailing_punctuation) + } + + fn format_aq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "⟨", "⟩") + } + + fn format_bq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "[", "]") + } + + fn format_brq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "{{", "}}") + } + + fn format_d1(&mut self, mut macro_node: MacroNode) -> String { + let spaces = " ".repeat(self.formatting_settings.indent); + self.format_partial_implicit_block(&mut macro_node, &spaces, "") + } + + fn format_dl(&mut self, mut macro_node: MacroNode) -> String { + let content = self.format_partial_implicit_block(&mut macro_node, "", ""); + let spaces = + " ".repeat(self.formatting_state.current_indent + self.formatting_settings.indent); + + format!("\n{}{}\n", spaces, content) + } + + fn format_dq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "“", "”") + } + + fn format_en(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "", "") + .trim() + .to_string() + } + + fn format_op(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "[", "]") + } + + fn format_pq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "(", ")") + } + + fn format_ql(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "‘", "’") + } + + fn format_qq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "\"", "\"") + } + + fn format_sq(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "\'", "\'") + } + + fn format_vt(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_vt_synopsis(&mut self, mut macro_node: MacroNode) -> String { + self.format_partial_implicit_block(&mut macro_node, "", "") + .trim() + .to_string() + } +} + +// Format other in-line macros. +impl MdocFormatter { + fn format_inline_macro(&self, macro_node: MacroNode) -> String { + let mut result = String::new(); + let mut prev_was_open = false; + let mut is_first_node = true; + + for node in macro_node.nodes { + match node { + Element::Text(text) => match text.as_str() { + "(" | "[" => { + result.push_str(&text); + prev_was_open = true; + } + ")" | "]" | "." | "," | ":" | ";" | "!" | "?" => { + result.push_str(&text); + prev_was_open = false; + } + _ => { + match prev_was_open { + true => result.push_str(&self.format_text_node(&text)), + false => { + let offset = if is_first_node { + "" + } else { + self.formatting_state.spacing.as_str() + }; + let formatted_node = + format!("{}{}", offset, self.format_text_node(&text)); + result.push_str(&formatted_node); + } + } + prev_was_open = false; + } + }, + _ => unreachable!("macro can't contain macro node or EOI!"), + } + + if is_first_node { + is_first_node = false; + } + } + + result.trim().to_string() + } + + fn format_ad(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ap(&self, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + format!("'{}", content) + } + + fn format_an(&mut self, an_type: AnType, macro_node: MacroNode) -> String { + match an_type { + AnType::NoSplit => { + self.formatting_state.split_mod = false; + String::new() + } + AnType::Split => { + self.formatting_state.split_mod = true; + String::new() + } + AnType::Name => { + let content = self.format_inline_macro(macro_node); + match self.formatting_state.split_mod { + true => format!("\n{}", content), + false => content, + } + } + } + } + + fn format_an_authors(&mut self, an_type: AnType, macro_node: MacroNode) -> String { + match an_type { + AnType::NoSplit => String::new(), + AnType::Split => String::new(), + AnType::Name => { + let content = self.format_inline_macro(macro_node); + match self.formatting_state.split_mod { + true => format!( + "\n{}{}", + " ".repeat(self.formatting_settings.indent), + content + ), + false => format!("{}{}", " ".repeat(self.formatting_settings.indent), content), + } + } + } + } + + fn format_ar(&self, macro_node: MacroNode) -> String { + if macro_node.nodes.is_empty() { + return "file ...".to_string(); + } + + self.format_inline_macro(macro_node) + } + + fn format_bt(&self) -> String { + "is currently in beta test.".to_string() + } + + fn format_cd(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_cm(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_db(&self) -> String { + "".to_string() + } + + fn format_dv(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_em(&self, macro_node: MacroNode) -> String { + // let line = self.format_inline_macro(macro_node); + + // if self.supports_italic() { + // format!("\x1b[3m{line}\x1b[0m") + // } else if self.supports_underline() { + // format!("\x1b[4m{line}\x1b[0m") + // } else { + // line + // } + self.format_inline_macro(macro_node) + } + + fn format_dt(&mut self, title: Option, section: &str, arch: Option) -> String { + let title = match title { + Some(name) => format!("{name}({section})"), + None if section.is_empty() => "UNTITLED".to_string(), + _ => format!("UNTITLED({section})"), + }; + + let section = match section { + "1" => "General Commands Manual", + "2" => "System Calls Manual", + "3" => "Library Functions Manual", + "4" => "Device Drivers Manual", + "5" => "File Formats Manual", + "6" => "Games Manual", + "7" => "Miscellaneous Information Manual", + "8" => "System Manager's Manual", + "9" => "Kernel Developer's Manual", + _ if section.is_empty() => "LOCAL", + _ => section, + }; + + let section = if let Some(val) = arch { + format!("{section} ({val})") + } else { + section.to_string() + }; + + let side_len = title.len(); + let center_len = section.len(); + + let center_start = (self.formatting_settings.width / 2).saturating_sub(center_len / 2); + + let right_start = self.formatting_settings.width.saturating_sub(side_len); + + let mut line = String::with_capacity(self.formatting_settings.width); + + line.push_str(&title); + + if center_start > side_len { + line.push_str(&" ".repeat(center_start - side_len)); + } + line.push_str(§ion); + + let current_len = line.len(); + if right_start > current_len { + line.push_str(&" ".repeat(right_start - current_len)); + } + line.push_str(&title); + + let final_len = line.len(); + if final_len < self.formatting_settings.width { + line.push_str(&" ".repeat(self.formatting_settings.width - final_len)); + } + + self.formatting_state.header_text = Some(line + "\n"); + String::new() + } + + fn format_dx(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_dd(&mut self, line: &str) -> String { + fn parse_month_name(month: &str) -> Option { + let mut months = HashMap::new(); + months.insert("january", 1); + months.insert("february", 2); + months.insert("march", 3); + months.insert("april", 4); + months.insert("may", 5); + months.insert("june", 6); + months.insert("july", 7); + months.insert("august", 8); + months.insert("september", 9); + months.insert("october", 10); + months.insert("november", 11); + months.insert("december", 12); + + months.get(&month.to_lowercase()[..]).copied() + } + + let trimmed = line.trim(); + + if trimmed == "$Mdocdate$" { + return chrono::Utc::now().format("%B %-d, %Y").to_string(); + } + + let prefix = "$Mdocdate: "; + if let Some(remainder) = trimmed.strip_prefix(prefix) { + let mut parts = remainder.split_whitespace(); + + let Some(month_str) = parts.next() else { + return line.to_string(); + }; + let Some(day_str) = parts.next() else { + return line.to_string(); + }; + let Some(year_str) = parts.next() else { + return line.to_string(); + }; + + let Some(month_num) = parse_month_name(month_str) else { + return line.to_string(); + }; + + let Ok(day_num) = day_str.parse::() else { + return line.to_string(); + }; + let Ok(year_num) = year_str.parse::() else { + return line.to_string(); + }; + + let Some(date) = chrono::NaiveDate::from_ymd_opt(year_num, month_num, day_num) else { + return line.to_string(); + }; + + return date.format("%B %-d, %Y").to_string(); + } + + line.to_string() + } + + fn format_bx(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_bsx(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_at(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_er(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_es( + &self, + opening_delimiter: char, + closing_delimiter: char, + macro_node: MacroNode, + ) -> String { + let c = self.format_inline_macro(macro_node); + + format!("{}{} {}", opening_delimiter, closing_delimiter, c) + } + + fn format_ev(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ex(&mut self, macro_node: MacroNode) -> String { + let mut content = macro_node + .nodes + .clone() + .into_iter() + .map(|node| self.format_node(node)) + .filter(|s| !s.is_empty()) + .collect::>() + .join(", "); + + if macro_node.nodes.is_empty() { + content = self.formatting_state.first_name.clone().unwrap_or_default(); + } + + if let Some(pos) = content.rfind(",") { + content.replace_range(pos..(pos + 1), " and"); + } + + let ending = if macro_node.nodes.len() <= 1 { + "y" + } else { + "ies" + }; + + if !content.is_empty() { + format!("The {content} utilit{ending} exits 0 on success, and >0 if an error occurs.") + } else { + String::new() + } + } + + fn format_fa(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_fd(&self, directive: &str, arguments: &[String]) -> String { + format!( + "{directive} {}", + arguments.join(&self.formatting_state.spacing) + ) + } + + fn format_fd_synopsis(&self, directive: &str, arguments: &[String]) -> String { + format!("{}\n", self.format_fd(directive, arguments)) + } + + fn format_fl(&mut self, macro_node: MacroNode) -> String { + if macro_node.nodes.is_empty() { + return "-\\[nsmacroescape]".to_string(); + } + + let mut result = String::new(); + let mut prev_was_open = false; + let mut is_first_node = true; + + for node in macro_node.nodes { + match node { + Element::Text(text) => match text.as_str() { + "(" | "[" => { + result.push_str(&text); + prev_was_open = true; + } + ")" | "]" | "." | "," | ":" | ";" | "!" | "?" => { + result.push_str(&text); + prev_was_open = false; + } + _ => { + let fmtd = self.format_text_node(&text); + let fmtd = match is_first_char_alnum(&fmtd) { + true => format!("-{}", fmtd), + false => fmtd, + }; + + match prev_was_open { + true => result.push_str(&fmtd), + false => { + let offset = if is_first_node { + "" + } else { + self.formatting_state.spacing.as_str() + }; + result.push_str(&format!("{}{}", offset, fmtd)); + } + } + prev_was_open = false; + } + }, + _ => unreachable!("macro can't contain macro node or EOI!"), + } + + if is_first_node { + is_first_node = false; + } + } + + result + } + + fn format_fn(&mut self, funcname: &str, macro_node: MacroNode) -> String { + let mut result = format!("{funcname}("); + let mut iter = macro_node.nodes.iter(); + + if let Some(node) = iter.next() { + match node { + Element::Text(arg) => match arg.as_str() { + "(" | "[" | ")" | "]" | "." | "," | ":" | ";" | "!" | "?" => { + let c = iter + .map(|n| self.format_node(n.clone())) + .collect::(); + return format!("{}){} {}", result, arg, c); + } + _ => { + let mut prev_was_open = false; + let mut is_first_node = true; + + for node in macro_node.nodes { + match node { + Element::Text(text) => match text.as_str() { + "(" | "[" => { + result.push_str(&text); + prev_was_open = true; + } + ")" | "]" | "." | "," | ":" | ";" | "!" | "?" => { + result.push_str(&text); + prev_was_open = false; + } + _ => { + match prev_was_open { + true => result.push_str(&self.format_text_node(&text)), + false => { + let offset = if is_first_node { + "" + } else { + self.formatting_state.spacing.as_str() + }; + let formatted_node = format!( + "{}{},", + offset, + self.format_text_node(&text) + ); + result.push_str(&formatted_node); + } + } + prev_was_open = false; + } + }, + _ => unreachable!("macro can't contain macro node or EOI!"), + } + + if is_first_node { + is_first_node = false; + } + } + + if result.ends_with(",") { + result.pop(); + } + + result.push(')'); + + return result; + } + }, + _ => unreachable!(), + } + }; + + let c = iter + .map(|n| self.format_node(n.clone())) + .collect::(); + format!("{}){}", result, c.trim()) + } + + fn format_fn_synopsis(&mut self, funcname: &str, macro_node: MacroNode) -> String { + format!("{};\n", &self.format_fn(funcname, macro_node)) + } + + fn format_fr(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ft(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ft_synopsis(&mut self, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + format!("\n{}\n", content) + } + + fn format_fx(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_hf(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ic(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_in(&self, filename: &str, macro_node: MacroNode) -> String { + let mut result = if is_first_char_delimiter(filename) { + let mut filename = filename.to_string(); + let del = filename.remove(0); + format!("{}<{}>", del, filename) + } else { + format!("<{}>", filename) + }; + + if let Some(node) = macro_node.nodes.into_iter().next() { + match node { + Element::Text(close_del) => result.push_str(close_del.as_str()), + _ => unreachable!(), + } + } + + result + } + + fn format_in_synopsis( + &self, + filename: &str, + macro_node: MacroNode, + prev_node: &Macro, + ) -> String { + let in_formatted = self.format_in(filename, macro_node); + let start = match prev_node { + Macro::Fn { .. } => "\n#include".to_string(), + _ => "#include".to_string(), + }; + + format!("{} {}\n", start, in_formatted) + } + + fn format_lb(&self, lib_name: &str, macro_node: MacroNode) -> String { + let mut result = String::new(); + let mut iter = macro_node.nodes.into_iter(); + + if let Some(node) = iter.next() { + match node { + Element::Text(open_del) => result.push_str(open_del.as_str()), + _ => unreachable!(), + } + } + + result.push_str(&format!("library “{lib_name}”")); + + if let Some(node) = iter.next() { + match node { + Element::Text(close_del) => result.push_str(close_del.as_str()), + _ => unreachable!(), + } + } + + result + } + + fn format_li(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_lk(&mut self, uri: &str, macro_node: MacroNode) -> String { + let content = macro_node + .nodes + .clone() + .into_iter() + .map(|node| self.format_node(node)) + .collect::>() + .join(&self.formatting_state.spacing); + + format!("{content}: {uri}") + } + + fn format_lp(&self) -> String { + "\n\n".to_string() + } + + fn format_ms(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_mt(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_no(&mut self, macro_node: MacroNode) -> String { + // self.formatting_state.suppress_space = false; + self.format_inline_macro(macro_node) + } + + fn format_ns(&mut self, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + format!("\\[nsmacroescape]{}", content) + } + + fn format_nx(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_os(&mut self, macro_node: MacroNode) -> String { + let content = macro_node + .nodes + .into_iter() + .map(|node| self.format_node(node)) + .collect::>() + .join(&self.formatting_state.spacing); + + if !content.is_empty() { + self.formatting_state.footer_text = Some(content); + } + String::new() + } + + fn format_ox(&self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_pa(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_pf(&mut self, prefix: &str, macro_node: MacroNode) -> String { + format!( + "{}\\[pfmacroescape]{}", + prefix, + &self.format_inline_macro(macro_node) + ) + } + + fn format_pp(&self, _macro_node: MacroNode) -> String { + "\n\n".to_string() + } + + fn format_rv(&mut self, macro_node: MacroNode) -> String { + let mut content = macro_node + .nodes + .clone() + .into_iter() + .take(macro_node.nodes.len().saturating_sub(1)) + .map(|node| self.format_node(node)) + .filter(|s| !s.is_empty()) + .collect::>() + .join("(), "); + + if macro_node.nodes.is_empty() { + content = self.formatting_state.first_name.clone().unwrap_or_default(); + } else if let Some(formatted_node) = macro_node.nodes.iter().last() { + let formatted_node = self.format_node(formatted_node.clone()); + if macro_node.nodes.len() == 1 { + content = format!("{formatted_node}()"); + } else { + content.push_str(&format!("(), and {formatted_node}()")); + } + } + + let ending_1 = if macro_node.nodes.len() <= 1 { "" } else { "s" }; + + let ending_2 = if macro_node.nodes.len() <= 1 { "s" } else { "" }; + + format!("The {content} function{ending_1} return{ending_2} the value 0 if successful; otherwise the value -1 is returned and the global variable errno is set to indicate the error.") + } + + fn format_sm(&mut self, sm_mode: Option, macro_node: MacroNode) -> String { + self.formatting_state.spacing = match sm_mode { + Some(SmMode::On) => " ".to_string(), + Some(SmMode::Off) => "".to_string(), + None => match self.formatting_state.spacing.as_str() { + "" => "".to_string(), + " " => "".to_string(), + _ => " ".to_string(), + }, + }; + + let c = self.format_inline_macro(macro_node); + + format!("{}{}", c, self.formatting_state.spacing) + } + + fn format_st(&self, st_type: StType, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + if is_first_char_delimiter(&content) { + return format!("{}{}", st_type, content); + } + + format!("{} {}", st_type, content) + } + + fn format_sx(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_sy(&mut self, macro_node: MacroNode) -> String { + // let line = self.format_inline_macro(macro_node); + + // if self.supports_bold() { + // format!("\x1b[1m{line}\x1b[0m") + // } else { + // line + // } + self.format_inline_macro(macro_node) + } + + fn format_tg(&self, _term: Option) -> String { + String::new() + } + + fn format_tn(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_ud(&self) -> String { + "currently under development.".to_string() + } + + fn format_ux(&self, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + if is_first_char_delimiter(&content) { + return format!("UNIX{content}"); + } + + format!("UNIX {content}") + } + + fn format_va(&mut self, macro_node: MacroNode) -> String { + self.format_inline_macro(macro_node) + } + + fn format_xr(&self, name: &str, section: &str, macro_node: MacroNode) -> String { + let content = self.format_inline_macro(macro_node); + + if is_first_char_delimiter(&content) { + return format!("{name}({section}){content}"); + } + + format!("{}({}) {}", name, section, content.trim()) + } +} + +/// Check if first char of [`s`] string is ASCII digit or letter +fn is_first_char_alnum(s: &str) -> bool { + s.chars() + .next() + .map(|c| c.is_ascii_alphanumeric()) + .unwrap_or(false) +} + +fn is_first_char_delimiter(s: &str) -> bool { + s.chars() + .next() + .map(|c| matches!(c, '(' | '[' | ')' | ']' | '.' | ',' | ':' | ';' | '!' | '?')) + .unwrap_or(false) +} + +fn is_first_word_delimiter(s: &str) -> bool { + s.split_whitespace() + .next() + .map(|c| matches!(c, "(" | "[" | ")" | "]" | "." | "," | ":" | ";" | "!" | "?")) + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use crate::{man_util::formatter::MdocDocument, FormattingSettings, MdocFormatter, MdocParser}; + + /// Test settings + const FORMATTING_SETTINGS: FormattingSettings = FormattingSettings { + width: 78, + indent: 5, + }; + + /// Parse [`input`] into AST + fn get_ast(input: &str) -> MdocDocument { + MdocParser::parse_mdoc(input).unwrap() + } + + /// Universal function for all tests + pub fn test_formatting(input: &str, output: &str) { + let ast = get_ast(input); + let mut formatter = MdocFormatter::new(FORMATTING_SETTINGS); + let result = String::from_utf8(formatter.format_mdoc(ast)).unwrap(); + println!( + "Formatted document:\nTarget:\n{}\n{}\nReal:\n{}\n", + output, + vec!['-'; formatter.formatting_settings.width] + .iter() + .collect::(), + result + ); + assert_eq!(output, result); + } + + mod special_chars { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn spaces() { + let input = r".Dd January 1, 1970 +.Os footer text +\~\0\|\^\&\)\%"; //not used: "\ ", "\:" + let output = r"UNTITLED LOCAL UNTITLED + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn lines() { + let input = r".Dd January 1, 1970 +.Os footer text +\(ba \(br \(ul \(ru \(rn \(bb \(sl \(rs"; + let output = r"UNTITLED LOCAL UNTITLED + +| │ _ _ ‾ ¦ / \ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn text_markers() { + let input = r".Dd January 1, 1970 +.Os footer text +\(ci \(bu \(dd \(dg \(lz \(sq \(ps \(sc \(lh \(rh \(at \(sh \(CR \(OK \(CL \(SP \(HE \(DI"; + let output = r"UNTITLED LOCAL UNTITLED + +○ • ‡ † ◊ □ ¶ § ☜ ☞ @ # ↵ ✓ ♣ ♠ ♥ ♦ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn legal_symbols() { + let input = r".Dd January 1, 1970 +.Os footer text +\(co \(rg \(tm"; + let output = r"UNTITLED LOCAL UNTITLED + +© ® ™ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn punctuation() { + let input = r".Dd January 1, 1970 +.Os footer text +\(em \(en \(hy \e \(r! \(r?"; + let output = r"UNTITLED LOCAL UNTITLED + +— – ‐ \ ¡ ¿ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn quotes() { + let input = r".Dd January 1, 1970 +.Os footer text +\(Bq \(bq \(lq \(rq \(oq \(cq \(aq \(dq \(Fo \(Fc \(fo \(fc"; + let output = + "UNTITLED LOCAL UNTITLED + +„ ‚ “ ” ‘ ’ ' \" « » ‹ › + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn brackets() { + let input = r".Dd January 1, 1970 +.Os footer text +\(lB \(rB \(lC \(rC \(la \(ra \(bv \[braceex] \[bracketlefttp] \[bracketleftbt] +\[bracketleftex] \[bracketrighttp] \[bracketrightbt] \[bracketrightex] +\(lt \[bracelefttp] \(lk \[braceleftmid] \(lb \[braceleftbt] \[braceleftex] +\(rt \[bracerighttp] \(rk \[bracerightmid] \(rb \[bracerightbt] \[bracerightex] +\[parenlefttp] \[parenleftbt] \[parenleftex] \[parenrighttp] \[parenrightbt] \[parenrightex] +"; + let output = r"UNTITLED LOCAL UNTITLED + +[ ] { } ⟨ ⟩ ⎪ ⎪ ⎡ ⎣ ⎢ ⎤ ⎦ ⎥ ⎧ ⎧ ⎨ ⎨ ⎩ ⎩ ⎪ ⎫ ⎫ ⎬ ⎬ ⎭ ⎭ ⎪ ⎛ ⎝ ⎜ ⎞ ⎠ ⎟ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn arrows() { + let input = r".Dd January 1, 1970 +.Os footer text +\(<- \(-> \(<> \(da \(ua \(va \(lA \(rA \(hA \(uA \(dA \(vA \(an"; + let output = r"UNTITLED LOCAL UNTITLED + +← → ↔ ↓ ↑ ↕ ⇐ ⇒ ⇔ ⇑ ⇓ ⇕ ⎯ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn logical() { + let input = r".Dd January 1, 1970 +.Os footer text +\(AN \(OR \[tno] \(no \(te \(fa \(st \(tf \(3d \(or"; + let output = r"UNTITLED LOCAL UNTITLED + +∧ ∨ ¬ ¬ ∃ ∀ ∋ ∴ ∴ | + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn mathematical() { + let input = r".Dd January 1, 1970 +.Os footer text +\- \(mi \+ \(pl \(-+ \[t+-] \(+- \(pc \[tmu] +\(mu \(c* \(c+ \[tdi] \(di \(f/ \(** \(<= \(>= \(<< \(>> \(eq \(!= \(== +\(ne \(ap \(|= \(=~ \(~~ \(~= \(pt \(es \(mo \(nm \(sb \(nb \(sp +\(nc \(ib \(ip \(ca \(cu \(/_ \(pp \(is \[integral] \[sum] \[product] +\[coproduct] \(gr \(sr \[sqrt] \(lc \(rc \(lf \(rf \(if \(Ah \(Im \(Re +\(wp \(pd \(-h \[hbar] \(12 \(14 \(34 \(18 \(38 \(58 \(78 \(S1 \(S2 \(S3 +"; + let output = r"UNTITLED LOCAL UNTITLED + +- − + + ∓ ± ± · × × ⊗ ⊕ ÷ ÷ ⁄ ∗ ≤ ≥ ≪ ≫ = ≠ ≡ ≢ ∼ ≃ ≅ ≈ ≈ ∝ ∅ ∈ ∉ ⊂ ⊄ ⊃ ⊅ ⊆ ⊇ +∩ ∪ ∠ ⊥ ∫ ∫ ∑ ∏ ∐ ∇ √ √ ⌈ ⌉ ⌊ ⌋ ∞ ℵ ℑ ℜ ℘ ∂ ℏ ℏ ½ ¼ ¾ ⅛ ⅜ ⅝ ⅞ ¹ ² ³ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ligatures() { + let input = r".Dd January 1, 1970 +.Os footer text +\(ff \(fi \(fl \(Fi \(Fl \(AE \(ae \(OE \(oe \(ss \(IJ \(ij"; + let output = r"UNTITLED LOCAL UNTITLED + +ff fi fl ffi ffl Æ æ Œ œ ß IJ ij + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn accents() { + let input = ".Dd January 1, 1970 +.Os footer text +\\(a- \\(a. \\(a^ \\(aa \\\' \\(ga \\` \\(ab \\(ac \\(ad \\(ah \\(ao \\(a~ \\(ho \\(ha \\(ti"; + let output = r"UNTITLED LOCAL UNTITLED + +¯ ˙ ^ ´ ´ ` ` ˘ ¸ ¨ ˇ ˚ ~ ˛ ^ ~ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn accented_letters() { + let input = r".Dd January 1, 1970 +.Os footer text +\('A \('E \('I \('O \('U \('Y \('a \('e +\('i \('o \('u \('y \(`A \(`E \(`I \(`O \(`U \(`a \(`e \(`i \(`o \(`u +\(~A \(~N \(~O \(~a \(~n \(~o \(:A \(:E \(:I \(:O \(:U \(:a \(:e \(:i +\(:o \(:u \(:y \(^A \(^E \(^I \(^O \(^U \(^a \(^e \(^i \(^o \(^u \(,C +\(,c \(/L \(/l \(/O \(/o \(oA \(oa +"; + let output = r"UNTITLED LOCAL UNTITLED + +Á É Í Ó Ú Ý á é í ó ú ý À È Ì Ò Ù à è ì ò ù Ã Ñ Õ ã ñ õ Ä Ë Ï Ö Ü ä ë ï ö ü ÿ +Â Ê Î Ô Û â ê î ô û Ç ç Ł ł Ø ø Å å + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn special_letters() { + let input = r".Dd January 1, 1970 +.Os footer text +\(-D \(Sd \(TP \(Tp \(.i \(.j"; + let output = r"UNTITLED LOCAL UNTITLED + +Ð ð Þ þ ı ȷ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn currency() { + let input = r".Dd January 1, 1970 +.Os footer text +\(Do \(ct \(Eu \(eu \(Ye \(Po \(Cs \(Fn"; + let output = r"UNTITLED LOCAL UNTITLED + +$ ¢ € € ¥ £ ¤ ƒ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn units() { + let input = r".Dd January 1, 1970 +.Os footer text +\(de \(%0 \(fm \(sd \(mc \(Of \(Om"; + let output = r"UNTITLED LOCAL UNTITLED + +° ‰ ′ ″ µ ª º + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn greek_leters() { + let input = r".Dd January 1, 1970 +.Os footer text +\(*A \(*B \(*G \(*D \(*E \(*Z +\(*Y \(*H \(*I \(*K \(*L \(*M \(*N \(*C \(*O \(*P \(*R \(*S +\(*T \(*U \(*F \(*X \(*Q \(*W \(*a \(*b \(*g \(*d \(*e \(*z +\(*y \(*h \(*i \(*k \(*l \(*m \(*n \(*c \(*o \(*p \(*r \(*s +\(*t \(*u \(*f \(*x \(*q \(*w \(+h \(+f \(+p \(+e \(ts +"; + let output = r"UNTITLED LOCAL UNTITLED + +Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω α β γ δ ε ζ η θ ι κ λ μ ν ξ ο +π ρ σ τ υ ϕ χ ψ ω ϑ φ ϖ ϵ ς + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn predefined_strings() { + let input = r".Dd January 1, 1970 +.Os footer text +\*(Ba \*(Ne \*(Ge \*(Le \*(Gt \*(Lt \*(Pm \*(If \*(Pi \*(Na \*(Am \*R \*(Tm \*q \*(Rq \*(Lq \*(lp \*(rp \*(lq \*(rq \*(ua \*(va \*(<= \*(>= \*(aa \*(ga \*(Px \*(Ai"; + let output = + "UNTITLED LOCAL UNTITLED + +| ≠ ≥ ≤ > < ± infinity pi NaN & ® (Tm) \" ” “ ( ) “ ” ↑ ↕ ≤ ≥ ´ ` POSIX ANSI + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn unicode() { + let input = r".Dd January 1, 1970 +.Os footer text +\[u0100] \C'u01230' \[u025600]"; + let output = + "UNTITLED LOCAL UNTITLED + +Ā ሰ 𥘀 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn numbered() { + let input = r".Dd January 1, 1970 +.Os footer text +\N'34' \[char43]"; + let output = + "UNTITLED LOCAL UNTITLED + +\" + + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + mod full_explicit { + use crate::man_util::formatter::tests::test_formatting; + + mod bd { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn bd_filled() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bd -filled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bd_unfilled() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bd_centered() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bd -centered -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bd_offset_right() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bd -filled -offset right +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bd_compact() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bd -literal -offset indent -compact +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bd_nested_blocks() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Bd -unfilled -offset indent +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.Ed +.Ed +.Ed +.Ed +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo + consequat. + + Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + #[test] + fn bf() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bf -emphasis +Line 1 +Line 2 +.Ef"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Line 1 Line 2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bf_macro() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bf Em +Line 1 +Line 2 +.Ef"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Line 1 Line 2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bk() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bk -words +Line 1 +Line 2 +.Ek"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Line 1 Line 2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + mod bl { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn bl_bullet() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +• Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +• Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +• Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_column() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -column -width 8 -compact \"long column1\" \"long column2\" \"long column3\" +.It Cell 1 Ta Cell 2 Ta Cell 3 +Line 1 +.It Cell 4 Ta Cell 5 Ta Cell 6 +Line 2 +.It Cell 7 Ta Cell 8 Ta Cell 9 +Line 3 +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Cell 1 Cell 2 Cell 3 Line 1 +Cell 4 Cell 5 Cell 6 Line 2 +Cell 7 Cell 8 Cell 9 Line 3 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_column_long_content() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -column -width 8 -compact \"very big super long column1\" \"very big super long column2\" \"very big super long column3\" +.It AAAAAA AAAAAAAAAAAA AAAAA Ta BBBBBB BBBBBBBBB BBBBBB Ta CCCCCC CCCCCCCCCC CCCCCCC +Line 1 +.It DDDDDD DDDDDDDDDDDD DDDDD Ta EEEEEE EEEEEEEEE EEEEEE Ta FFFFFF FFFFFFFFFF FFFFFFF +Line 2 +.It RRRRRR RRRRRRRRRRRR RRRRR Ta VVVVVV VVVVVVVVV VVVVVV Ta WWWWWW WWWWWWWWWW WWWWWWW +Line 3 +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +AAAAAA AAAAAAAAAAAA AAAAA + BBBBBB BBBBBBBBB BBBBBB + CCCCCC CCCCCCCCCC CCCCCCC Line 1 +DDDDDD DDDDDDDDDDDD DDDDD + EEEEEE EEEEEEEEE EEEEEE + FFFFFF FFFFFFFFFF FFFFFFF Line 2 +RRRRRR RRRRRRRRRRRR RRRRR + VVVVVV VVVVVVVVV VVVVVV + WWWWWW WWWWWWWWWW WWWWWWW Line 3 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_dash() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -dash -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_diag() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -diag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +head1  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do +eiusmod tempor incididunt ut labore et dolore magna aliqua. +head2  Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris +nisi ut aliquip ex ea commodo consequat. +head3  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum +dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_enum() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -enum -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +1. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +2. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +3. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_item() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -item -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_hang() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -hang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +head3 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_inset() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -inset -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. +head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +head3 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum +dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_ohang() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -ohang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo consequat. +head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_tag() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -tag -width 12 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +head3 Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_hang_long_head() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -hang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Item head title1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Item head title2 Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. +Item head title3 Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_inset_long_head() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -inset -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Item head title1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed +do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Item head title2 Ut enim ad minim veniam, quis nostrud exercitation ullamco +laboris nisi ut aliquip ex ea commodo consequat. +Item head title3 Duis aute irure dolor in reprehenderit in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_ohang_long_head() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -ohang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo consequat. +Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_tag_long_head() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +.Bl -tag -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El"; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Item head title1 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +Item head title2 + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +Item head title3 + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_symbol_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +• Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +• Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +• Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + • Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + • Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. + • Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + • + • Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. + • Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + • Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. + • + • Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. + • Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. + • Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat + nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_item_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -item -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -item -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -item -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -item -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_ohang_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -ohang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -ohang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -ohang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -ohang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo consequat. +head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore +eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + head1 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + head2 + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + head3 + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + head4 + + head1 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + head2 + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + head3 + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + head4 + + head1 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. + head2 + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi + ut aliquip ex ea commodo consequat. + head3 + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_inset_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -inset -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -inset -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -inset -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -inset -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. +head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +head3 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum +dolore eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + head4 + + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + head4 + + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_column_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -column -width 8 -compact \"col1_ _ _ _ _ _ col1\" \"col2_ _ _ _ _ _ col2\" \"col3_ _ _ _ _ _ col3\" \"col4_ _ _ _ _ _ col4\" +.It head1 Ta Lorem ipsum dolor sit amet, Ta consectetur adipiscing elit, Ta sed do eiusmod tempor incididunt ut Ta labore et dolore magna aliqua. +.It head2 Ta Ut enim ad minim veniam, Ta quis nostrud exercitation ullamco Ta laboris nisi ut aliquip ex Ta ea commodo consequat. +.It head3 Ta Duis aute irure dolor in Ta reprehenderit in voluptate velit Ta esse cillum dolore eu Ta fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -column -width 8 -compact col1 col2 col3 col4 +.It head1 Ta Lorem ipsum dolor sit amet, Ta consectetur adipiscing elit, Ta sed do eiusmod tempor incididunt ut Ta labore et dolore magna aliqua. +.It head2 Ta Ut enim ad minim veniam, Ta quis nostrud exercitation ullamco Ta laboris nisi ut aliquip ex Ta ea commodo consequat. +.It head3 Ta Duis aute irure dolor in Ta reprehenderit in voluptate velit Ta esse cillum dolore eu Ta fugiat nulla pariatur. +.It head4 +.Bl -column -width 8 -compact \"col1_ _ _ _ _ _ col1\" \"col2_ _ _ _ _ _ col2\" \"col3_ _ _ _ _ _ col3\" \"col4_ _ _ _ _ _ col4\" +.It head1 Ta Lorem ipsum dolor sit amet, Ta consectetur adipiscing elit, Ta sed do eiusmod tempor incididunt ut Ta labore et dolore magna aliqua. +.It head2 Ta Ut enim ad minim veniam, Ta quis nostrud exercitation ullamco Ta laboris nisi ut aliquip ex Ta ea commodo consequat. +.It head3 Ta Duis aute irure dolor in Ta reprehenderit in voluptate velit Ta esse cillum dolore eu Ta fugiat nulla pariatur. +.It head4 +.Bl -column -width 8 -compact col1 col2 col3 col4 +.It head1 Ta Lorem ipsum dolor sit amet, Ta consectetur adipiscing elit, Ta sed do eiusmod tempor incididunt ut Ta labore et dolore magna aliqua. +.It head2 Ta Ut enim ad minim veniam, Ta quis nostrud exercitation ullamco Ta laboris nisi ut aliquip ex Ta ea commodo consequat. +.It head3 Ta Duis aute irure dolor in Ta reprehenderit in voluptate velit Ta esse cillum dolore eu Ta fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +head1 + Lorem ipsum dolor sit amet, + consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut + labore et dolore magna aliqua. +head2 + Ut enim ad minim veniam, + quis nostrud exercitation ullamco + laboris nisi ut aliquip ex + ea commodo consequat. +head3 + Duis aute irure dolor in + reprehenderit in voluptate velit + esse cillum dolore eu + fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + head Lore cons sed labore et dolore magna aliqua. + 1 ipsu ecte eius + dolo adip temp + r isci inci + amet elit didu + , , nt + ut + head Ut nost labo ea commodo consequat. + 2 enim exer ris + mini cita nisi + veni ulla aliq + am, mco uip + ex + head Duis repr cill fugiat nulla pariatur. + 3 irur ehen dolo + dolo deri re + r in volu eu + ptat + veli + t + head + 4 + + head1 + Lorem ipsum dolor sit amet, + consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut + labore et dolore magna aliqua. + head2 + Ut enim ad minim veniam, + quis nostrud exercitation ullamco + laboris nisi ut aliquip ex + ea commodo consequat. + head3 + Duis aute irure dolor in + reprehenderit in voluptate velit + esse cillum dolore eu + fugiat nulla pariatur. + head4 + + head Lore cons sed labore et dolore magna aliqua. + 1 ipsu ecte eius + dolo adip temp + r isci inci + amet elit didu + , , nt + ut + head Ut nost labo ea commodo consequat. + 2 enim exer ris + mini cita nisi + veni ulla aliq + am, mco uip + ex + head Duis repr cill fugiat nulla pariatur. + 3 irur ehen dolo + dolo deri re + r in volu eu + ptat + veli + t + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_tag_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -tag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -tag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -tag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -tag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +head3 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + head4 + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. + head4 + head1 Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat + nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_hang_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -hang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -hang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It Item head title4 +.Bl -hang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It Item head title4 +.Bl -hang -width 8 -compact +.It Item head title1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It Item head title2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It Item head title3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +Item head title1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Item head title2 Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. +Item head title3 Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + Item head title1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. + Item head title2 Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + Item head title3 Duis aute irure dolor in reprehenderit in voluptate + velit esse cillum dolore eu fugiat nulla pariatur. + Item head title4 + Item head title1 Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt ut + labore et dolore magna aliqua. + Item head title2 Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. + Item head title3 Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat nulla + pariatur. + Item head title4 + Item head title1 Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. + Item head title2 Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. + Item head title3 Duis aute irure dolor in reprehenderit + in voluptate velit esse cillum dolore eu fugiat + nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bl_mixed_nested_lists() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME 1 +.Os footer text +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms +.Sh DESCRIPTION +.Ss SUBSECTION +Adssdf sdfmsdpf sdfm sdfmsdpf +.Ms +.Bl -bullet -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -hang -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.It head4 +.Bl -tag -width 8 -compact +.It head1 +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +.It head2 +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +.It head3 +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +.El +.El +.El +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg g wefwefwer werwe rwe r wer +.Ms "; + let output = + "PROGNAME(1) General Commands Manual PROGNAME(1) + +Adssdf sdfmsdpf sdfm sdfmsdpf +• Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +• Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. +• Duis aute irure dolor in reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. +Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg dfg +g wefwefwer werwe rwe r wer + +DESCRIPTION + + SUBSECTION + + Adssdf sdfmsdpf sdfm sdfmsdpf + + • Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + • Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. + • Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. + • + head1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. + head4 + head1 Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. + head2 Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. + head3 Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat + nulla pariatur. + + Adssdf sdfmsdpf sdfm sdfmsdpf sgsdgsdg sdfg sdfg sdfg fdsg d gdfg df gdfg + dfg g wefwefwer werwe rwe r wer + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + } + + mod full_implicit { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn it() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bl -bullet +.It +Line 1 +.It +Line 2 +.El"; + let output = + "PROGNAME(section) section PROGNAME(section) + +• Line 1 + +• Line 2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn nd() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Nd short description of the manual"; + let output = + "PROGNAME(section) section PROGNAME(section) + +– short description of the manual + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn nm() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Nm command_name"; + let output = + "PROGNAME(section) section PROGNAME(section) + + command_name + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn sh() { + let input = ".Dd $Mdocdate: October 28 2016 $ +.Dt REV 1 +.Os footer text +.Sh NAME +.Nm rev +.Nd reverse lines of a file +.Sh SYNOPSIS +.Nm rev +.Op Ar +.Sh DESCRIPTION +The +.Nm rev +utility copies the specified files to the standard output, reversing the +order of characters in every line. +If no files are specified, the standard input is read."; + let output = + "REV(1) General Commands Manual REV(1) + +NAME + rev – reverse lines of a file + +SYNOPSIS + rev [file ...] + +DESCRIPTION + The rev utility copies the specified files to the standard output, + reversing the order of characters in every line. If no files are + specified, the standard input is read. + +footer text October 28, 2016 footer text"; + test_formatting(input, output); + } + + #[test] + fn ss() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ss Options +These are the available options."; + let output = + "PROGNAME(section) section PROGNAME(section) + + Options + + These are the available options. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + #[test] + fn ta() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bl -column \"A col\" \"B col\" +.It item1 Ta item2 +.It item1 Ta item2 +.El"; + let output = + "PROGNAME(section) section PROGNAME(section) + +item1 item2 +item1 item2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + mod inline { + use crate::man_util::formatter::tests::test_formatting; + + mod rs_submacro { + use super::*; + + #[test] + fn a() { + let input = r".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%A author name +.Re +.Rs +.%A author name1 +.%A author name2 +.Re +.Rs +.%A author name1 +.%A author name2 +.%A author name3 +.Re +.Rs +.%A ( author ) name1 +.%A author , name2 +.%A author name3 ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +author name. author name1 and author name2. author name1, author name2, and +author name3. (author) name1, author, name2, and author name3!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn b() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%B book title +.Re +.Rs +.%B book title +.%B book title +.Re +.Rs +.%B ( book ) title +.%B book , title +.%B book title ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +book title. book title, book title. (book) title, book, title, book title!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn c() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%C Publication city +.Re +.Rs +.%C Publication city +.%C Publication city +.Re +.Rs +.%C ( Publication ) city +.%C Publication , city +.%C Publication city ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Publication city. Publication city, Publication city. (Publication) city, +Publication, city, Publication city!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn d() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%D January 1, 1970 +.Re +.Rs +.%D January 1 1970 +.%D first january 1970 +.Re +.Rs +.%D ( March ) 1189 +.%D 12 , 1900 +.%D 12 of March, 1970 ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +January 1, 1970. January 1 1970, first january 1970. (March) 1189, 12, 1900, +12 of March, 1970!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn i() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%I issuer name +.Re +.Rs +.%I issuer name +.%I issuer name +.Re +.Rs +.%I ( issuer ) name +.%I issuer , name +.%I issuer name ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +issuer name. issuer name, issuer name. (issuer) name, issuer, name, issuer +name!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn j() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%J Journal name +.Re +.Rs +.%J Journal name +.%J Journal name +.Re +.Rs +.%J ( Journal ) name +.%J Journal , name +.%J Journal name ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Journal name. Journal name, Journal name. (Journal) name, Journal, name, +Journal name!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn n() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%N Issue number +.Re +.Rs +.%N Issue number +.%N Issue number +.Re +.Rs +.%N ( Issue ) number +.%N Issue , number +.%N Issue number ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Issue number. Issue number, Issue number. (Issue) number, Issue, number, Issue +number!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn o() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%O Optional information +.Re +.Rs +.%O Optional information +.%O Optional information +.Re +.Rs +.%O ( Optional ) information +.%O Optional , information +.%O Optional information ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Optional information. Optional information, Optional information. (Optional) +information, Optional, information, Optional information!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn p() { + let input = r".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%P pp. 42\(en47 +.Re +.Rs +.%P pp. 42\(en47 +.%P p. 42 +.Re +.Rs +.%P ( p. 42 ) p. 43 +.%P pp. 42 , 47 +.%P pp. 42\(en47 ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +pp. 42–47. pp. 42–47, p. 42. (p. 42) p. 43, pp. 42, 47, pp. 42–47!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn q() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%Q Institutional author +.Re +.Rs +.%Q Institutional author +.%Q Institutional author +.Re +.Rs +.%Q ( Institutional ) author +.%Q Institutional , author +.%Q Institutional author ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Institutional author. Institutional author, Institutional author. +(Institutional) author, Institutional, author, Institutional author!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn r() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%R Technical report +.Re +.Rs +.%R Technical report +.%R Technical report +.Re +.Rs +.%R ( Technical report ) Technical report +.%R Technical report , Technical report +.%R Technical report ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Technical report. Technical report, Technical report. (Technical report) +Technical report, Technical report, Technical report, Technical report!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn t() { + let input = r".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%T Article title +.Re +.Rs +.%T Article title +.%T Article title +.Re +.Rs +.%T ( Article title ) Article title +.%T Article title , Article title +.%T Article title ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Article title. Article title, Article title. (Article title) Article title, +Article title, Article title, Article title!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn u() { + let input = r".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%U Article title +.Re +.Rs +.%U Article title +.%U Article title +.Re +.Rs +.%U ( Article title ) Article title +.%U Article title , Article title +.%U Article title ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Article title. Article title, Article title. (Article title) Article title, +Article title, Article title, Article title!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn v() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rs +.%V Volume number +.Re +.Rs +.%V Volume number +.%V Volume number +.Re +.Rs +.%V ( Volume number ) Volume number +.%V Volume number , Volume number +.%V Volume number ! +.Re"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Volume number. Volume number, Volume number. (Volume number) Volume number, +Volume number, Volume number, Volume number!. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + #[test] + fn ad() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ad [0,$] +.Ad 0x00000000 +.Ad [ 0,$ ]"; + let output = + "PROGNAME(section) section PROGNAME(section) + +[0,$] 0x00000000 [0,$] + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ap() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ap Text Line"; + let output = + "PROGNAME(section) section PROGNAME(section) + +'Text Line + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ar() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ar +.Ar arg1 , arg2 ."; + let output = + "PROGNAME(section) section PROGNAME(section) + +file ... arg1, arg2. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn at() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.At +.At III +.At V.1 +.At ( V.1 ) +.At ( V.1 ) subnode Ad ( addr )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +AT&T UNIX AT&T System III UNIX AT&T System V Release 1 UNIX (AT&T System V +Release 1 UNIX) (AT&T System V Release 1 UNIX) subnode (addr) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bsx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bsx 1.0 +.Bsx +.Bsx ( 1.0 )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +BSD/OS 1.0 BSD/OS (BSD/OS 1.0) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bt() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bt"; + let output = + "PROGNAME(section) section PROGNAME(section) + +is currently in beta test. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn bx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bx 4.3 Tahoe +.Bx 4.4 +.Bx +.Bx ( 4.3 Tahoe )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +4.3BSD-Tahoe 4.4BSD BSD (4.3BSD-Tahoe) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn cd() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Cd device le0 at scode?"; + + let output = + "PROGNAME(section) section PROGNAME(section) + +device le0 at scode? + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn cm() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Cm file bind"; + let output = + "PROGNAME(section) section PROGNAME(section) + +file bind + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn db() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Db +"; + let output = + "PROGNAME(section) section PROGNAME(section) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn dd() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text"; + let output = + "PROGNAME(section) section PROGNAME(section) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn dt() { + let input = ".Dd January 1, 1970 +.Dt TITLE 7 arch +.Os footer text"; + let output = + "TITLE(7) Miscellaneous Information Manual (arch) TITLE(7) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn dv() { + let input = ".Dd January 1, 1970 +.Dt TITLE 7 arch +.Os footer text +.Dv NULL +.Dv BUFSIZ +.Dv STDOUT_FILEnmo"; + let output = + "TITLE(7) Miscellaneous Information Manual (arch) TITLE(7) + +NULL BUFSIZ STDOUT_FILEnmo + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn dx() { + let input = ".Dd January 1, 1970 +.Dt TITLE 7 arch +.Os footer text +.Dx 2.4.1 +.Dx ( 2.4.1 ) +"; + let output = + "TITLE(7) Miscellaneous Information Manual (arch) TITLE(7) + +DragonFly 2.4.1 (DragonFly 2.4.1) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn em() { + let input = ".Dd January 1, 1970 +.Dt TITLE 7 arch +.Os footer text +Selected lines are those +.Em not +matching any of the specified patterns. +Some of the functions use a +.Em hold space +to save the pattern space for subsequent retrieval."; + let output = + "TITLE(7) Miscellaneous Information Manual (arch) TITLE(7) + +Selected lines are those not matching any of the specified patterns. Some of +the functions use a hold space to save the pattern space for subsequent +retrieval. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn er() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Er ERROR ERROR2"; + let output = + "PROGNAME(section) section PROGNAME(section) + +ERROR ERROR2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn es() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Es ( )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +() + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ev() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ev DISPLAY"; + let output = + "PROGNAME(section) section PROGNAME(section) + +DISPLAY + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ex() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ex -std grep"; + let output = + "PROGNAME(section) section PROGNAME(section) + +The grep utility exits 0 on success, and >0 if an error occurs. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn fa() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fa funcname Ft const char *"; + let output = + "PROGNAME(section) section PROGNAME(section) + +funcname const char * + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn fd() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fd #define sa_handler __sigaction_u.__sa_handler"; + let output = + "PROGNAME(section) section PROGNAME(section) + +#define sa_handler __sigaction_u.__sa_handler + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn fl() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fl H | L | P inet"; + let output = + "PROGNAME(section) section PROGNAME(section) + +-H | -L | -P -inet + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[allow(non_snake_case)] + #[test] + fn Fn() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fn funcname arg arg2 arg3"; + let output = + "PROGNAME(section) section PROGNAME(section) + +funcname(arg, arg2, arg3) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn fr() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fr 32"; + let output = + "PROGNAME(section) section PROGNAME(section) + +32 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ft() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ft int32 void"; + let output = + "PROGNAME(section) section PROGNAME(section) + +int32 void + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn fx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fx 1.0"; + let output = + "PROGNAME(section) section PROGNAME(section) + +FreeBSD 1.0 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn hf() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Hf file/path file2/path"; + let output = + "PROGNAME(section) section PROGNAME(section) + +file/path file2/path + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ic() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ic :wq"; + let output = + "PROGNAME(section) section PROGNAME(section) + +:wq + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[allow(non_snake_case)] + #[test] + fn In() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.In stdatomic.h"; + let output = + "PROGNAME(section) section PROGNAME(section) + + + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn lb() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Lb libname"; + let output = + "PROGNAME(section) section PROGNAME(section) + +library “libname” + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn li() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Li Book Antiqua"; + let output = + "PROGNAME(section) section PROGNAME(section) + +Book Antiqua + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn lk() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Lk https://bsd.lv The BSD.lv Project"; + let output = + "PROGNAME(section) section PROGNAME(section) + +The BSD.lv Project: https://bsd.lv + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ms() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ms alpha beta"; + let output = + "PROGNAME(section) section PROGNAME(section) + +alpha beta + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn mt() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Mt abc@gmail.com abc@gmail.com"; + let output = + "PROGNAME(section) section PROGNAME(section) + +abc@gmail.com abc@gmail.com + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn no() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.No a b c"; + let output = + "PROGNAME(section) section PROGNAME(section) + +a b c + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn nx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Nx Version 1.0"; + let output = + "PROGNAME(section) section PROGNAME(section) + +NetBSD Version 1.0 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn os() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text"; + let output = + "PROGNAME(section) section PROGNAME(section) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ot() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ot functype"; + let output = + "PROGNAME(section) section PROGNAME(section) + +functype + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ox() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ox Version 1.0"; + let output = + "PROGNAME(section) section PROGNAME(section) + +OpenBSD Version 1.0 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn pa() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Pa name1 name2"; + let output = + "PROGNAME(section) section PROGNAME(section) + +name1 name2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn rv() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rv -std f1 f2 Ar value"; + let output = + "PROGNAME(section) section PROGNAME(section) + +The f1(), f2(), Ar(), and value() functions return the value 0 if successful; +otherwise the value -1 is returned and the global variable errno is set to +indicate the error. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn rv_std() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Rv -std"; + let output = + "PROGNAME(section) section PROGNAME(section) + +The function returns the value 0 if successful; otherwise the value -1 is +returned and the global variable errno is set to indicate the error. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn sm() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Sm off A B C D +.Sm on A B C D"; + let output = + "PROGNAME(section) section PROGNAME(section) + +ABCD A B C D + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn st() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.St -ansiC word +.St -iso9945-1-96"; + let output = + "PROGNAME(section) section PROGNAME(section) + +ANSI X3.159-1989 (“ANSI C89”) word ISO/IEC 9945-1:1996 (“POSIX.1”) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn sx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Sx MANUAL STRUCTURE"; + let output = + "PROGNAME(section) section PROGNAME(section) + +MANUAL STRUCTURE + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn sy() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Sy word1 word2"; + let output = + "PROGNAME(section) section PROGNAME(section) + +word1 word2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn tn() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Tn word1 word2"; + let output = + "PROGNAME(section) section PROGNAME(section) + +word1 word2 + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ud() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ud"; + let output = + "PROGNAME(section) section PROGNAME(section) + +currently under development. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn ux() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Ux"; + let output = + "PROGNAME(section) section PROGNAME(section) + +UNIX + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn va() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Va const char *bar"; + let output = + "PROGNAME(section) section PROGNAME(section) + +const char *bar + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn xr() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Xr mandoc 1"; + let output = + "PROGNAME(section) section PROGNAME(section) + +mandoc(1) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + mod partial_implicit { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn block_empty() { + let input = r#".Dd January 1, 1970 +.Os footer text +.Aq"#; + let output = + "UNTITLED LOCAL UNTITLED + +⟨⟩ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn block_single_line() { + let input = r#".Dd January 1, 1970 +.Os footer text +.Aq Ad addr addr Ad addr Ad addr"#; + let output = + "UNTITLED LOCAL UNTITLED + +⟨addr addr addr addr⟩ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + mod partial_explicit { + use crate::man_util::formatter::tests::test_formatting; + + #[test] + fn block_empty() { + let input = r#".Dd January 1, 1970 +.Os footer text +.Ao +.Ac"#; + let output = + "UNTITLED LOCAL UNTITLED + +⟨⟩ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn block_single_line() { + let input = r#".Dd January 1, 1970 +.Os footer text +.Ao +.Ad addr addr +.Ad addr +.Ad addr +.Ac"#; + let output = + "UNTITLED LOCAL UNTITLED + +⟨addr addr addr addr⟩ + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn multi_line() { + let input = r#".Dd January 1, 1970 +.Os footer text +.Ao +.Ad addr +.Ad addr +.Ad addr +Text loooooooong line +Text loooooooong line +Text loooooooong line +Text loooooooong line +Text loooooooong line +Text loooooooong line +.Ac"#; + let output = r#"UNTITLED LOCAL UNTITLED + +⟨addr addr addr⟩ Text loooooooong line Text loooooooong line Text loooooooong +line Text loooooooong line Text loooooooong line Text loooooooong line + +footer text January 1, 1970 footer text"#; + test_formatting(input, output); + } + + #[test] + fn block_overlong_line() { + let input = r#".Dd January 1, 1970 +.Os Debian +.Aq Ad addr Ad addr Ad addr Text looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"#; + let output = r#"UNTITLED LOCAL UNTITLED + +⟨addr addr addr Text +looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong +line⟩ + +Debian January 1, 1970 Debian"#; + test_formatting(input, output); + } + + #[test] + fn rs_block() { + let input = ".Dd January 1, 1970 +.Dt TITLE 7 arch +.Os footer text +.Rs +.%A J. E. Hopcroft +.%A J. D. Ullman +.%B Introduction to Automata Theory, Languages, and Computation +.%I Addison-Wesley +.%C Reading, Massachusetts +.%D 1979 +.Re"; + let output = + "TITLE(7) Miscellaneous Information Manual (arch) TITLE(7) + +J. E. Hopcroft and J. D. Ullman, Introduction to Automata Theory, Languages, +and Computation, Addison-Wesley, Reading, Massachusetts, 1979. + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } + + #[test] + fn zero_width() { + let input = r".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Xr mandoc 1 \&Ns \&( s \&) behaviour +Text Line \&Ns \&( s \&) behaviour"; + let output = + "PROGNAME(section) section PROGNAME(section) + +mandoc(1) Ns ( s ) behaviour Text Line Ns ( s ) behaviour + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + mod delimiters { + use super::*; + + #[test] + fn delimiters_inline_common() { + fn test(macro_str: &str) { + let input = vec![ + format!(".Dd January 1, 1970\n.Dt PROGNAME section\n.Os footer text"), + format!(".{} {} text {}", macro_str, "(", ")"), + format!(".{} {} text {}", macro_str, "[", "]"), + format!(".{} text {}", macro_str, "."), + format!(".{} text {}", macro_str, ","), + format!(".{} text {}", macro_str, "?"), + format!(".{} text {}", macro_str, "!"), + format!(".{} text {}", macro_str, ":"), + format!(".{} text {}", macro_str, ";"), + ] + .join("\n"); + + let output = + "PROGNAME(section) section PROGNAME(section) + +(text) [text] text. text, text? text! text: text; + +footer text January 1, 1970 footer text"; + + test_formatting(&input, &output); + } + + let inline_macros = vec![ + "Ad", "An", "Ar", "Cd", "Cm", "Dv", "Er", "Ev", "Fa", "Fr", "Ft", "Hf", "Ic", "Li", + "Ms", "Mt", "No", "Ot", "Pa", "Sx", "Tn", "Va", + ]; + + for macro_str in inline_macros { + println!("Macro: {macro_str}"); + + test(macro_str); + } + } + + #[test] + fn delimiters_text_production() { + fn test(macro_str: &str) { + let placeholder = match macro_str { + "At" => "AT&T UNIX", + "Bsx" => "BSD/OS", + "Dx" => "DragonFly", + "Fx" => "FreeBSD", + "Nx" => "NetBSD", + "Ox" => "OpenBSD", + _ => unreachable!(), + }; + + let input = vec![ + format!(".Dd January 1, 1970\n.Dt PROGNAME section\n.Os footer text"), + format!(".{} {} text {}", macro_str, "(", ")"), + format!(".{} {} text {}", macro_str, "[", "]"), + format!(".{} text {}", macro_str, "."), + ] + .join("\n"); + + let output = format!( + "PROGNAME(section) section PROGNAME(section) + +({placeholder} text) [{placeholder} text] {placeholder} text. + +footer text January 1, 1970 footer text", + ); + test_formatting(&input, &output); + } + + let macros = vec!["At", "Bsx", "Ox", "Dx", "Fx", "Nx"]; + + for macro_str in macros { + println!("Macro: {}", macro_str); + + test(macro_str) + } + } + + #[test] + fn delimiters_bx() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Bx ( random ) +.Bx random !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(randomBSD) randomBSD! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_em() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Em ( random ) text !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(random) text! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_fn() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fn ( random ) text !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(random()) text! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_sy() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Sy ( random ) text !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(random) text! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_fl() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Fl ( random ) text !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(-random) -text! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_in() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.In ( random )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +() + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_lb() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Lb ( random )"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(library “random”) + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + + #[test] + fn delimiters_vt() { + let input = ".Dd January 1, 1970 +.Dt PROGNAME section +.Os footer text +.Vt ( random ) text !"; + let output = + "PROGNAME(section) section PROGNAME(section) + +(random) text! + +footer text January 1, 1970 footer text"; + test_formatting(input, output); + } + } +} + +/* +/// Use this mod for testing whole mdoc files. +/// Test results may differ from original mdoc +/// formatter. So this tests will not pass +/// successfully. This is expected behavior +#[cfg(test)] +mod test_mdoc { + use crate::man_util::formatter::tests::test_formatting; + use std::process::Command; + use rstest::rstest; + + #[rstest] + // Small + #[case("./test_files/mdoc/rev.1")] + #[case("./test_files/mdoc/adjfreq.2")] + #[case("./test_files/mdoc/getgroups.2")] + #[case("./test_files/mdoc/sigreturn.2")] + #[case("./test_files/mdoc/size.1")] + #[case("./test_files/mdoc/fgen.1")] + #[case("./test_files/mdoc/getrtable.2")] + #[case("./test_files/mdoc/wall.1")] + #[case("./test_files/mdoc/getsid.2")] + #[case("./test_files/mdoc/ypconnect.2")] + #[case("./test_files/mdoc/closefrom.2")] + #[case("./test_files/mdoc/moptrace.1")] + + //Other + #[case("./test_files/mdoc/rlog.1")] + #[case("./test_files/mdoc/access.2")] + #[case("./test_files/mdoc/munmap.2")] + #[case("./test_files/mdoc/ipcs.1")] + #[case("./test_files/mdoc/atq.1")] + #[case("./test_files/mdoc/brk.2")] + #[case("./test_files/mdoc/cal.1")] + #[case("./test_files/mdoc/minherit.2")] + #[case("./test_files/mdoc/cat.1")] + #[case("./test_files/mdoc/file.1")] + #[case("./test_files/mdoc/mkdir.1")] + #[case("./test_files/mdoc/getsockname.2")] + #[case("./test_files/mdoc/mlockall.2")] + #[case("./test_files/mdoc/cut.1")] + + //without bl + #[case("./test_files/mdoc/umask.2")] + #[case("./test_files/mdoc/sched_yield.2")] + #[case("./test_files/mdoc/sigsuspend.2")] + #[case("./test_files/mdoc/mopa.out.1")] + #[case("./test_files/mdoc/fsync.2")] + #[case("./test_files/mdoc/shar.1")] + #[case("./test_files/mdoc/sysarch.2")] + + //word as macro + #[case("./test_files/mdoc/fork.2")] + #[case("./test_files/mdoc/symlink.2")] + #[case("./test_files/mdoc/sync.2")] + #[case("./test_files/mdoc/futex.2")] + #[case("./test_files/mdoc/reboot.2")] + #[case("./test_files/mdoc/id.1")] + #[case("./test_files/mdoc/rename.2")] + #[case("./test_files/mdoc/cu.1")] + #[case("./test_files/mdoc/getfh.2")] + #[case("./test_files/mdoc/ioctl.2")] + #[case("./test_files/mdoc/dup.2")] + #[case("./test_files/mdoc/getpeername.2")] + #[case("./test_files/mdoc/lpq.1")] + #[case("./test_files/mdoc/nm.1")] + #[case("./test_files/mdoc/truncate.2")] + #[case("./test_files/mdoc/chdir.2")] + #[case("./test_files/mdoc/mkfifo.2")] + #[case("./test_files/mdoc/quotactl.2")] + #[case("./test_files/mdoc/send.2")] + #[case("./test_files/mdoc/getpriority.2")] + #[case("./test_files/mdoc/select.2")] + #[case("./test_files/mdoc/w.1")] + #[case("./test_files/mdoc/chflags.2")] + #[case("./test_files/mdoc/flock.2")] + + // Bl -column + #[case("./test_files/mdoc/shutdown.2")] + #[case("./test_files/mdoc/tmux.1")] + #[case("./test_files/mdoc/nl.1")] + #[case("./test_files/mdoc/bc.1")] + #[case("./test_files/mdoc/mg.1")] + #[case("./test_files/mdoc/snmp.1")] + #[case("./test_files/mdoc/rdist.1")] + + //Block 1 + #[case("./test_files/mdoc/chmod.2")] + #[case("./test_files/mdoc/cvs.1")] + #[case("./test_files/mdoc/dc.1")] + #[case("./test_files/mdoc/flex.1")] + #[case("./test_files/mdoc/getdents.2")] + #[case("./test_files/mdoc/getitimer.2")] + #[case("./test_files/mdoc/getrusage.2")] + #[case("./test_files/mdoc/getsockopt.2")] + + #[case("./test_files/mdoc/gettimeofday.2")] + #[case("./test_files/mdoc/ktrace.2")] + #[case("./test_files/mdoc/msgrcv.2")] + #[case("./test_files/mdoc/msgsnd.2")] + #[case("./test_files/mdoc/mv.1")] + #[case("./test_files/mdoc/poll.2")] + #[case("./test_files/mdoc/profil.2")] + #[case("./test_files/mdoc/rcs.1")] + #[case("./test_files/mdoc/read.2")] + #[case("./test_files/mdoc/rup.1")] + #[case("./test_files/mdoc/semget.2")] + #[case("./test_files/mdoc/shmctl.2")] + #[case("./test_files/mdoc/signify.1")] + #[case("./test_files/mdoc/statfs.2")] + #[case("./test_files/mdoc/t11.2")] + #[case("./test_files/mdoc/talk.1")] + #[case("./test_files/mdoc/write.2")] + + #[case("./test_files/mdoc/diff.1")] + #[case("./test_files/mdoc/top.1")] + #[case("./test_files/mdoc/execve.2")] + #[case("./test_files/mdoc/open.2")] + #[case("./test_files/mdoc/scp.1")] + #[case("./test_files/mdoc/socket.2")] + #[case("./test_files/mdoc/socketpair.2")] + #[case("./test_files/mdoc/setuid.2")] + #[case("./test_files/mdoc/shmget.2")] + #[case("./test_files/mdoc/sftp.1")] + #[case("./test_files/mdoc/grep.1")] + fn format_mdoc_file(#[case] path: &str){ + let input = std::fs::read_to_string(path).unwrap(); + let output = Command::new("mandoc") + .args(["-T", "locale", path]) + .output() + .unwrap() + .stdout; + let output = String::from_utf8(output).unwrap(); + println!("Current path: {}", path); + test_formatting(&input, &output); + } +} +*/ diff --git a/man/man_util/mdoc.pest b/man/man_util/mdoc.pest new file mode 100644 index 00000000..c6a395d8 --- /dev/null +++ b/man/man_util/mdoc.pest @@ -0,0 +1,747 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +// ----- Basic rules +// -- Shortened synonym + +WHITESPACE = _{ " " | "\t" } +ws = _{ WHITESPACE } +NEWLINE = _{ "\r"? ~ "\n" } + +// -- Macro name separator to not allow merging macro name with arguments + +comment_start = _{ "\\" ~ "\"" } +comment_macro = _{ comment_start ~ (!(NEWLINE | EOI) ~ ANY)* } + +text_non_comment = ${ (!comment_start ~ ws* ~ word ~ ws*)+ } +text_line = { !"." ~ (comment_macro | text_non_comment)+ ~ (NEWLINE+ | EOI) } +line = { !"." ~ (comment_macro | text_non_comment)+ } + +word = @{ (!(NEWLINE | ws) ~ ANY)+ } +text_arg = @{ + ("\\&" ~ (!(ws | NEWLINE) ~ ANY)+) + | ("\"" ~ (!("\"" | NEWLINE) ~ ANY)+ ~ "\"") + | (!(ws | NEWLINE | macro_arg | comment_start) ~ ANY)+ +} + +// -- List of Callable macros +macro_arg = { + !( + d1_block | + dl_block | + rs_submacro | + bt | st | db | dd | dt | ex | fd | hf | + lb | lp | os | pp | rv | sm | tg | ud | + rs_block + ) + ~ + ( + block_full_implicit | + block_partial_implicit | + block_partial_explicit | + inline | ta + ) +} + +arg = { macro_arg | text_arg } + +// ----- Macro types + +block_full_explicit = { + bd_block | + bf_block | + bk_block | + bl_block +} + +block_full_implicit = { + nd_block | + nm_block | + sh_block | + ss_block +} + +block_partial_implicit = { + aq_block | + bq_block | + brq_block | + d1_block | + dl_block | + dq_block | + en_block | + op_block | + pq_block | + ql_block | + qq_block | + sq_block | + vt_block +} + +block_partial_explicit = { + ao_block | + ac | + bo_block | + bc | + bro_block | + brc | + do_block | + dc | + eo_block | + ec | + fo_block | + fc | + oo_block | + oc | + po_block | + pc | + qo_block | + qc | + rs_block | + re | + so_block | + sc | + xo_block | + xc +} + +rs_submacro = { + a | + b | + c | + d | + i | + j | + n | + o | + p | + q | + r | + t | + u | + v +} + +text_production = { at | bsx | bx | dx | ex | fx | nx | ox | st | rv } + +inline = { + rs_submacro + | ad | an | ap | ar + | bt + | cd | cm + | db | dd | dt | dv + | em | er | es | ev + | fa | fd | fl | fr | ft | Fn + | hf + | ic | In + | lb | li | lk | lp + | ms | mt + | no | ns + | os | ot + | pa | pf | pp + | sm | sx | sy + | tg | tn + | ud | ux + | va + | xr + | text_production +} + +// ----- Mdoc document + +element = { + ( + ((ws | NEWLINE)* ~ ".")* ~ + ( ta + | block_full_explicit + | block_full_implicit + | block_partial_implicit + | block_partial_explicit + | inline + ) ~ NEWLINE? + ) + | text_line +} + +mdoc = { SOI ~ (("." ~ comment_macro ~ NEWLINE)* ~ element)* ~ EOI? } +args = { SOI ~ ws* ~ arg* ~ ws* ~ EOI? } + +// ----- Block full-explicit macros + +// -- Bd + +bd_centered = { "-centered" } +bd_filled = { "-filled" } +bd_literal = { "-literal" } +bd_ragged = { "-ragged" } +bd_unfilled = { "-unfilled" } +bd_type = { + bd_centered + | bd_filled + | bd_literal + | bd_ragged + | bd_unfilled +} + +// ! Try to parse "indent-two" before "indent" +off_indent_two = { "indent-two" ~ "."? } +off_indent = { "indent" ~ "."? } +off_left = { "left" ~ "."? } +off_right = { "right" ~ "."? } +off_center = { "center" ~ "."? } +offset = { + off_indent_two + | off_indent + | off_left + | off_right + | off_center + | word +} + +compact = { "-compact" } + +bd_offset = { ws+ ~ "-offset" ~ ws+ ~ offset } +bd_compact = { ws+ ~ compact } +bd_open = ${ "Bd" ~ ws+ ~ bd_type ~ (bd_offset | bd_compact){,2} ~ ws* ~ NEWLINE } +ed_close = { ".Ed" ~ NEWLINE? } +bd_block = { bd_open ~ (("." ~ comment_macro ~ NEWLINE)* ~ element)* ~ ed_close } + +// -- Bf + +bf_emphasis = { "-emphasis" } +bf_literal = { "-literal" } +bf_symbolic = { "-symbolic" } +bf_em = { "Em" } +bf_li = { "Li" } +bf_sy = { "Sy" } +bf_type = { + bf_emphasis + | bf_literal + | bf_symbolic + | bf_em + | bf_li + | bf_sy +} + +bf_open = ${ "Bf" ~ ws+ ~ bf_type ~ ws* ~ NEWLINE } +ef_close = { ".Ef" ~ NEWLINE? } +bf_block = { bf_open ~ (("." ~ comment_macro ~ NEWLINE)* ~ element)* ~ ef_close } + +// -- Bk + +bk_words = { "-words" } + +bk_open = ${ "Bk" ~ ws+ ~ bk_words ~ (ws+ ~ text_arg)* ~ ws* ~ NEWLINE } +ek_close = { ".Ek" ~ NEWLINE? } +bk_block = { bk_open ~ (("." ~ comment_macro ~ NEWLINE)* ~ element)* ~ ek_close } + +// -- Bl + +bl_bullet = { "-bullet" } +bl_column = { "-column" } +bl_dash = { "-dash" } +bl_diag = { "-diag" } +bl_enum = { "-enum" } +bl_hang = { "-hang" } +bl_hyphen = { "-hyphen" } +bl_inset = { "-inset" } +bl_item = { "-item" } +bl_ohang = { "-ohang" } +bl_tag = { "-tag" } +bl_type = { + bl_bullet + | bl_column + | bl_dash + | bl_diag + | bl_enum + | bl_hang + | bl_hyphen + | bl_inset + | bl_item + | bl_ohang + | bl_tag +} + +bl_width = { "-width" ~ ws+ ~ word } +bl_offset = { "-offset" ~ ws+ ~ offset } +column = @{ + ("\\&" ~ (!(ws | NEWLINE) ~ ANY)+) + | ("\"" ~ (!("\"" | NEWLINE) ~ ANY)+ ~ "\"") + | (!(ws | NEWLINE) ~ ANY)+ +} +bl_param = { bl_width | bl_offset | compact | column } +bl_skip = { !( it_block | comment_start | el_close ) ~ element } + +bl_open = ${ "Bl" ~ ws+ ~ bl_type ~ (ws+ ~ bl_param)* ~ ws* ~ NEWLINE } +el_close = { ".El" ~ NEWLINE? } +bl_block = { bl_open ~ bl_skip* ~ (it_block | comment_start)* ~ el_close } + +// ----- Block full-implicit macros + +block_line = { (!NEWLINE ~ ANY)+ } + +// -- It +ta_head = ${ (!"." ~ "Ta") | " " | "\t" } +ta = ${ "Ta" ~ !text_arg ~ ws* ~ NEWLINE? } + +it_head = ${ (ws* ~ ".")* ~ "It" ~ !text_arg ~ (ws* ~ (ta_head | macro_arg | word))* ~ ws* ~ NEWLINE? } +it_body = ${ (!(".It") ~ !(".El") ~ ("." ~ comment_macro ~ NEWLINE)* ~ element ~ NEWLINE?)* } +it_block = ${ it_head ~ it_body } + +// -- Nd + +nd_open = ${ "Nd" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +nd_block_element = { !("." ~ sh_block) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +nd_block = { nd_open ~ (NEWLINE ~ nd_block_element*)? } + +// -- Nm + +nm_block = ${ "Nm" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* ~ NEWLINE? } + +// -- Sh + +sh_open = ${ "Sh" ~ ws+ ~ block_line ~ ws* } +sh_block_element = { !("." ~ sh_block) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +sh_block = { sh_open ~ (NEWLINE ~ sh_block_element*)? } + +// -- Ss + +ss_open = ${ "Ss" ~ ws+ ~ block_line ~ ws* } +ss_block_element = { !("." ~ (sh_block | ss_block)) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +ss_block = { ss_open ~ (NEWLINE ~ ss_block_element*)? } + +// ----- Block partial-explicit + +// --- Ao & Ac + +ac = ${ "Ac" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +ao_head = ${ "Ao" ~ !text_arg ~ (ws+ ~ !ac ~ text_arg)* ~ ws* ~ NEWLINE? } +ao_body = ${ !("."? ~ ac) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +ao_block = ${ ao_head ~ ao_body* ~ "."? ~ ac } + +// --- Bo & Bc +bc = ${ "Bc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +bo_head = ${ "Bo" ~ !text_arg ~ (ws+ ~ !bc ~ text_arg)* ~ ws* ~ NEWLINE? } +bo_body = ${ !("."? ~ bc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +bo_block = ${ bo_head ~ bo_body* ~ "."? ~ bc } + +// --- Bro & Brc +brc = ${ "Brc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +bro_head = ${ "Bro" ~ !text_arg ~ (ws+ ~ !brc ~ text_arg)* ~ ws* ~ NEWLINE? } +bro_body = ${ !("."? ~ brc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +bro_block = ${ bro_head ~ bro_body* ~ "."? ~ brc } + +// --- Do & Dc +dc = ${ "Dc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +do_head = ${ "Do" ~ !text_arg ~ (ws+ ~ !dc ~ text_arg)* ~ ws* ~ NEWLINE? } +do_body = ${ !("."? ~ dc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +do_block = ${ do_head ~ do_body* ~ "."? ~ dc } + +// --- Eo & Ec +ec = ${ "Ec" ~ !text_arg ~ (ws+ ~ closing_delimiter)? ~ ws* } +eo_head = ${ "Eo" ~ !text_arg ~ (ws+ ~ opening_delimiter)? ~ (ws+ ~ !ec ~ text_arg)* ~ ws* ~ NEWLINE? } +eo_body = ${ !("."? ~ ec) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +eo_block = ${ eo_head ~ eo_body* ~ "."? ~ ec } + +// --- Fo & Fc +fc = ${ "Fc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +fo_head = ${ "Fo" ~ !text_arg ~ ws+ ~ word ~ (ws+ ~ !fc ~ (comment_macro | line))? ~ ws* ~ NEWLINE? } +fo_body = ${ !("."? ~ fc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +fo_block = ${ fo_head ~ fo_body* ~ "."? ~ fc } + +// --- Oo & Oc +oc = ${ "Oc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +//oc = ${ "Oc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +oo_head = ${ "Oo" ~ !text_arg ~ (ws+ ~ !oc ~ text_arg)* ~ ws* ~ NEWLINE? } +oo_body = ${ !("."? ~ oc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +oo_block = ${ oo_head ~ oo_body* ~ "."? ~ oc } + +// --- Po & Pc +pc = ${ "Pc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +po_head = ${ "Po" ~ !text_arg ~ (ws+ ~ !pc ~ text_arg)* ~ ws* ~ NEWLINE? } +po_body = ${ !("."? ~ pc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +po_block = ${ po_head ~ po_body* ~ "."? ~ pc } + +// --- Qo & Qc +qc = ${ "Qc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +qo_head = ${ "Qo" ~ !text_arg ~ (ws+ ~ !qc ~ text_arg)* ~ ws* ~ NEWLINE? } +qo_body = ${ !("."? ~ qc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +qo_block = ${ qo_head ~ qo_body* ~ "."? ~ qc } + +// --- Rs & Re +re = ${ "Re" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +rs_head = ${ "Rs" ~ !text_arg ~ (ws+ ~ (rs_submacro | comment_macro))* ~ ws* ~ NEWLINE? } +rs_body = ${ "."? ~ (rs_submacro | comment_macro) ~ ws* ~ NEWLINE? } +rs_block = ${ rs_head ~ rs_body* ~ "."? ~ re } + +// --- So & Sc +sc = ${ "Sc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +so_head = ${ "So" ~ !text_arg ~ (ws+ ~ !sc ~ text_arg)* ~ ws* ~ NEWLINE? } +so_body = ${ !("."? ~ sc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +so_block = ${ so_head ~ so_body* ~ "."? ~ sc } + +// --- Xo & Xc +xc = ${ "Xc" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +xo_head = ${ "Xo" ~ !text_arg ~ (ws+ ~ !xc ~ text_arg)* ~ ws* ~ NEWLINE? } +xo_body = ${ !("."? ~ xc) ~ ("." ~ comment_macro ~ NEWLINE)* ~ element } +xo_block = ${ xo_head ~ xo_body* ~ "."? ~ xc } + +// ----- Block partial-implicit + +partial_implicit_element = { + (( ta | block_full_explicit | block_full_implicit | block_partial_implicit | block_partial_explicit | inline)) | + text_arg +} + +aq_block = ${ "Aq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +bq_block = ${ "Bq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +brq_block = ${ "Brq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +d1_block = ${ "D1" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +dl_block = ${ "Dl" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +dq_block = ${ "Dq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +en_block = ${ "En" ~ !text_arg ~ (ws* ~ partial_implicit_element)+ ~ ws* ~ NEWLINE? } +op_block = ${ "Op" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +pq_block = ${ "Pq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +ql_block = ${ "Ql" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +qq_block = ${ "Qq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +sq_block = ${ "Sq" ~ !text_arg ~ (ws* ~ partial_implicit_element)* ~ ws* ~ NEWLINE? } +vt_block = ${ "Vt" ~ !text_arg ~ (ws* ~ partial_implicit_element)+ ~ ws* ~ NEWLINE? } + +// ----- In-line + +// -- Rs submacros + +// -- Additional rules + +month = { (!("." | NEWLINE | ws) ~ ANY)* } +day = @{ (!(WHITESPACE | NEWLINE) ~ ASCII_DIGIT)+ } +month_day = { month ~ ws+ ~ day ~ ws* ~ "," } +year = { ASCII_DIGIT+ } +uri = @{ (!"://" ~ ANY)+ ~ "://" ~ word* } + +a = ${ "%A" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +b = ${ "%B" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +c = ${ "%C" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +d = ${ "%D" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +i = ${ "%I" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +j = ${ "%J" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +n = ${ "%N" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +o = ${ "%O" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +p = ${ "%P" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +q = ${ "%Q" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +r = ${ "%R" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +t = ${ "%T" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +u = ${ "%U" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } +v = ${ "%V" ~ !text_arg ~ ws+ ~ line ~ ws* ~ NEWLINE? } + +// -- Text production + +at_version = @{ "v" ~ '1'..'7' } +at_32v = { "32v" } +at_3 = { "III" } +at_system_v = @{ "V" ~ (!"." | ("." ~ '1'..'4')) } +at_type = { + at_version + | at_32v + | at_3 + | at_system_v +} + +line_start = _{ SOI | NEWLINE } +not_line_start = _{ !line_start } + +at = ${ + not_line_start + ~ "At" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ (at_type | text_arg))? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +bsx = ${ + "Bsx" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +bx = ${ + "Bx" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +dx = ${ + "Dx" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +fx = ${ + "Fx" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +nx = ${ + "Nx" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +ox = ${ + "Ox" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)* + ~ (ws+ ~ !closing_delimiter ~ text_arg)? + ~ (ws+ ~ closing_delimiter)* + ~ (ws+ ~ text_arg)* + ~ ws* +} + +rv = ${ "Rv" ~ ws+ ~ "-std" ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } + +// C Language Standards +st_ansiC = { "-ansiC" } +st_ansiC_89 = { "-ansiC-89" } +st_isoC = { "-isoC" } +st_isoC_90 = { "-isoC-90" } +st_isoC_amd1 = { "-isoC-amd1" } +st_isoC_tcor1 = { "-isoC-tcor1" } +st_isoC_tcor2 = { "-isoC-tcor2" } +st_isoC_99 = { "-isoC-99" } +st_isoC_2011 = { "-isoC-2011" } +// POSIX.1 Standards before XPG4.2 +st_p1003_1_88 = { "-p1003.1-88" } +st_p1003_1 = { "-p1003.1" } +st_p1003_1_90 = { "-p1003.1-90" } +st_iso9945_1_90 = { "-iso9945-1-90" } +st_p1003_1b_93 = { "-p1003.1b-93" } +st_p1003_1b = { "-p1003.1b" } +st_p1003_1c_95 = { "-p1003.1c-95" } +st_p1003_1i_95 = { "-p1003.1i-95" } +st_p1003_1_96 = { "-p1003.1-96" } +st_iso9945_1_96 = { "-iso9945-1-96" } +// X/Open Portability Guide before XPG4.2 +st_xpg3 = { "-xpg3" } +st_p1003_2 = { "-p1003.2" } +st_p1003_2_92 = { "-p1003.2-92" } +st_iso9945_2_93 = { "-iso9945-2-93" } +st_p1003_2a_92 = { "-p1003.2a-92" } +st_xpg4 = { "-xpg4" } +// X/Open Portability Guide Issue 4 Version 2 and Related Standards +st_susv1 = { "-susv1" } +st_xpg4_2 = { "-xpg4.2" } +st_xcurses4_2 = { "-xcurses4.2" } +st_p1003_1g_2000 = { "-p1003.1g-2000" } +st_svid4 = { "-svid4" } +// X/Open Portability Guide Issue 5 and Related Standards +st_susv2 = { "-susv2" } +st_xbd5 = { "-xbd5" } +st_xsh5 = { "-xsh5" } +st_xcu5 = { "-xcu5" } +st_xns5 = { "-xns5" } +st_xns5_2 = { "-xns5.2" } +// POSIX Issue 6 Standards +st_p1003_1_2001 = { "-p1003.1-2001" } +st_susv3 = { "-susv3" } +st_p1003_1_2004 = { "-p1003.1-2004" } +// POSIX Issues 7 and 8 Standards +st_p1003_1_2008 = { "-p1003.1-2008" } +st_susv4 = { "-susv4" } +st_p1003_1_2024 = { "-p1003.1-2024" } +// Other Standards +st_ieee754 = { "-ieee754" } +st_iso8601 = { "-iso8601" } +st_iso8802_3 = { "-iso8802-3" } +st_ieee1275_94 = { "-ieee1275-94" } +// ! This is neccessacy to be reversally sorted +st_abbreviation = { + st_ansiC_89 + | st_ansiC + | st_ieee1275_94 + | st_ieee754 + | st_iso8802_3 + | st_iso8601 + | st_isoC_2011 + | st_isoC_99 + | st_isoC_90 + | st_isoC_tcor2 + | st_isoC_tcor1 + | st_isoC_amd1 + | st_isoC + | st_iso9945_2_93 + | st_iso9945_1_96 + | st_iso9945_1_90 + | st_p1003_2a_92 + | st_p1003_2_92 + | st_p1003_2 + | st_p1003_1_2024 + | st_p1003_1_2008 + | st_p1003_1_2004 + | st_p1003_1_2001 + | st_p1003_1_96 + | st_p1003_1i_95 + | st_p1003_1g_2000 + | st_p1003_1c_95 + | st_p1003_1b_93 + | st_p1003_1b + | st_p1003_1_90 + | st_p1003_1_88 + | st_p1003_1 + | st_svid4 + | st_xpg4_2 + | st_xpg4 + | st_xpg3 + | st_xcurses4_2 + | st_xns5_2 + | st_xns5 + | st_xsh5 + | st_xcu5 + | st_xbd5 + | st_susv1 + | st_susv4 + | st_susv3 + | st_susv2 +} +st = ${ "St" ~ !text_arg ~ ws+ ~ st_abbreviation ~ (ws+ ~ text_arg)* ~ ws* ~ NEWLINE? } + +// -- Other in-line macros +ad = ${ "Ad" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } + +an_split = { "-split" } +an_no_split = { "-nosplit" } +an_name = ${ text_arg ~ (ws+ ~ text_arg)* } +an = ${ "An" ~ !text_arg ~ ws+ ~ (an_split | an_no_split | an_name) ~ ws* } + +ap = ${ "Ap" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +ar = ${ "Ar" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +bt = ${ "Bt" ~ !text_arg ~ (ws+ ~ line)? ~ ws* ~ NEWLINE? } +cd = ${ "Cd" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +cm = ${ "Cm" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +db = ${ "Db" ~ !text_arg ~ (ws+ ~ text_arg)? ~ NEWLINE? } + +// -- Dd + +dd = ${ "Dd" ~ !text_arg ~ (ws+ ~ line)? ~ ws* ~ NEWLINE? } + +// -- Dt + +title = ${ !ASCII_DIGIT ~ word } +section = ${ word } +arch = ${ text_arg } +dt = ${ "Dt" ~ !text_arg ~ (ws+ ~ title)? ~ ws+ ~ section ~ (ws+ ~ arch)? ~ ws* ~ NEWLINE? } + +// -- Fd + +directive = ${ "#" ~ word } +fd = ${ "Fd" ~ !text_arg ~ ws+ ~ directive ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } + +// -- Other in-line macros + +dv = ${ "Dv" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +em = ${ "Em" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +er = ${ "Er" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +es = ${ + "Es" + ~ !text_arg + ~ ws+ ~ opening_delimiter + ~ ws+ ~ closing_delimiter + ~ (ws+ ~ text_arg)* + ~ ws* +} +ev = ${ "Ev" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +ex = ${ "Ex" ~ !text_arg ~ ws+ ~ "-std" ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } +fa = ${ "Fa" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +fl = ${ "Fl" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } +Fn = ${ "Fn" ~ !text_arg ~ (ws+ ~ opening_delimiter)? ~ (ws+ ~ text_arg)+ ~ ws* } +fr = ${ "Fr" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +ft = ${ "Ft" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +hf = ${ "Hf" ~ !text_arg ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } +ic = ${ "Ic" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +In = ${ + "In" + ~ !text_arg + ~ (ws+ ~ opening_delimiter)? + ~ ws+ ~ word + ~ (ws+ ~ closing_delimiter)? + ~ (ws+ ~ text_arg)* + ~ ws* +} +lb = ${ "Lb" ~ !text_arg ~ (ws+ ~ opening_delimiter)? ~ ws+ ~ word ~ (ws+ ~ closing_delimiter)? ~ ws* ~ NEWLINE? } +li = ${ "Li" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +lk = ${ "Lk" ~ !text_arg ~ ws+ ~ uri ~ (ws+ ~ text_arg)* ~ ws* } +lp = ${ "Lp" ~ !text_arg ~ (ws+ ~ line)? ~ ws* ~ NEWLINE? } + +// -- Delimeters +separated_delimiter = { ws+ ~ delimiter ~ ws+ } +delimiter = { opening_delimiter | closing_delimiter } +opening_delimiter = { "(" | "[" } +closing_delimiter = { "." | "," | ":" | ";" | ")" | "]" | "?" | "!" } + +// -- Document preamble and NAME section macros +os = ${ "Os" ~ !text_arg ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } + +// -- Sections and cross references +sx = ${ "Sx" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +xr = ${ "Xr" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +tg = ${ "Tg" ~ !text_arg ~ (ws+ ~ arg){, 1} ~ ws* ~ NEWLINE? } +pp = ${ "Pp" ~ !text_arg ~ (ws+ ~ word)* ~ ws* ~ NEWLINE? } + +// -- Spacing control +pf = ${ "Pf" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +ns = ${ "Ns" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } + +sm_on = { "on" } +sm_off = { "off" } +spacing_mode = { sm_on | sm_off } +sm = ${ "Sm" ~ !text_arg ~ (ws+ ~ spacing_mode)? ~ (ws+ ~ text_arg)* ~ ws* ~ NEWLINE? } + +// -- Semantic markup for command line utilities +pa = ${ "Pa" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } + +// -- Semantic markup for function libraries +ot = ${ "Ot" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +va = ${ "Va" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } + +// -- Various semantic markup +mt = ${ "Mt" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +ms = ${ "Ms" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } + +// -- Physical markup +sy = ${ "Sy" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +no = ${ "No" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } + +tn = ${ "Tn" ~ !text_arg ~ (ws+ ~ text_arg)+ ~ ws* } +// Prints “currently under development” +ud = ${ "Ud" ~ !text_arg ~ ws* ~ NEWLINE? } +// Prints “UNIX” +ux = ${ "Ux" ~ !text_arg ~ (ws+ ~ text_arg)* ~ ws* } \ No newline at end of file diff --git a/man/man_util/mdoc_macro/mod.rs b/man/man_util/mdoc_macro/mod.rs new file mode 100644 index 00000000..26b91374 --- /dev/null +++ b/man/man_util/mdoc_macro/mod.rs @@ -0,0 +1,193 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +use crate::man_util::parser::Element; +use text_production::StType; +use types::*; + +pub mod text_production; +pub mod types; + +/// Mdoc language units +#[derive(Debug, Clone, PartialEq)] +pub enum Macro { + Soi, + A, + B, + C, + D, + I, + J, + N, + O, + P, + Q, + R, + T, + U, + V, + Ad, + An { + author_name_type: AnType, + }, + Ao, // Begin a block enclosed by angle brackets + Ac, // Close an Ao block + Ap, + Aq, + Ar, + At, + Bd { + block_type: BdType, + offset: Option, + compact: bool, + }, + Bk, + Bf(BfType), + Bl { + list_type: BlType, + width: Option, + offset: Option, + compact: bool, + columns: Vec, + }, + Bo, + Bc, // Close a Bo block + Bq, + Bro, + Brc, // Close a Bro block + Brq, + Bsx, + Bt, + Bx, + Cd, + Cm, + D1, + Db, // Obsolete + Dd, + Dl, + Do, + Dc, // Close a Do block + Dq, + Dt { + title: Option, + section: String, + arch: Option, + }, + Dv, + Dx, + Em, + En, + Eo { + opening_delimiter: Option, + closing_delimiter: Option, + }, + Ec, + Er, + Es { + // Obsolete + opening_delimiter: char, + closing_delimiter: char, + }, + Ev, + Ex, + Fa, + Fd { + directive: String, + arguments: Vec, + }, + Fl, + Fn { + funcname: String, + }, + Fo { + funcname: String, + }, + Fc, // End a function context started by Fo + Fr, // Obsolete + Ft, + Fx, + Hf, + Ic, + In { + filename: String, + }, + It { + head: Vec, + }, + Lb { + lib_name: String, + }, + Li, + Lk { + uri: String, + }, + Lp, + Ms, + Mt, + Nd, + Nm { + name: Option, + }, + No, + Ns, + Nx, + Oo, + Oc, // Close multi-line Oo context + Op, + Os, + Ox, + Pa, + Pf { + prefix: String, + }, + Po, + Pc, // Close parenthesised context opened by Po + Pp, + Pq, + Ql, + Qo, + Qc, // Close quoted context opened by Qo + Qq, + Rs, + Re, // Close an Rs block + Rv, + Sh { + title: String, + }, + Sm(Option), + So, + Sc, + Sq, + Ss { + title: String, + }, + St(StType), + Sx, + Sy, + Ta, + Tg { + term: Option, + }, + Tn, + Ud, + Ux, + Va, + Vt, + Xo, + Xc, // Close a scope opened by Xo + Xr { + name: String, + section: String, + }, + _Ed, // End a display context started by Bd + _Ef, // End a display context started by Bf + _Ek, // End a keep context started by Bk + _El, // End a list context started by Bl + _Ot, // Deprecated +} diff --git a/man/man_util/mdoc_macro/text_production.rs b/man/man_util/mdoc_macro/text_production.rs new file mode 100644 index 00000000..f1725404 --- /dev/null +++ b/man/man_util/mdoc_macro/text_production.rs @@ -0,0 +1,331 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +use std::fmt::Display; + +use pest::iterators::Pair; + +use crate::man_util::parser::Rule; + +/// Types of formatting AT&T UNIX version +#[derive(Debug, Clone, PartialEq)] +pub enum AtType { + General, + Version(String), + V32, + SystemIII, + SystemV(Option), +} + +impl From> for AtType { + fn from(pair: Pair<'_, Rule>) -> Self { + let at_type = pair.into_inner().next().unwrap(); + match at_type.as_rule() { + Rule::at_version => Self::Version(at_type.as_str().chars().nth(1).unwrap().to_string()), + Rule::at_32v => Self::V32, + Rule::at_3 => Self::SystemIII, + Rule::at_system_v => { + Self::SystemV(at_type.as_str().chars().nth(2).map(|c| c.to_string())) + } + Rule::text_arg => Self::General, + _ => unreachable!(), + } + } +} + +impl Display for AtType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let at_n_t_unix = match self { + AtType::General => "AT&T UNIX".to_string(), + AtType::Version(v) => format!("Version {v} AT&T UNIX"), + AtType::V32 => "AT&T UNIX v32".to_string(), + AtType::SystemIII => "AT&T System III UNIX".to_string(), + AtType::SystemV(None) => "AT&T System V UNIX".to_string(), + AtType::SystemV(Some(v)) => format!("AT&T System V Release {v} UNIX"), + }; + + write!(f, "{at_n_t_unix}") + } +} + +impl Default for AtType { + fn default() -> Self { + Self::General + } +} + +/// Used for incapsulating formatting BSD/OS version logic +pub struct BsxType; + +impl BsxType { + pub fn format(version: &str) -> String { + format!("BSD/OS {}", version) + } + + pub fn format_default() -> String { + "BSD/OS".to_string() + } +} + +/// Used for incapsulating formatting BSD version logic +pub struct BxType; + +impl BxType { + pub fn format(version: &str, variant: Option<&str>) -> String { + match variant { + Some(var) => format!("{}BSD-{}", version, var), + None => format!("{}BSD", version), + } + } + + pub fn format_default() -> String { + "BSD".to_string() + } +} + +/// Used for incapsulating formatting DragonFly version logic +pub struct DxType; + +impl DxType { + pub fn format(version: &str) -> String { + format!("DragonFly {}", version) + } + + pub fn format_default() -> String { + "DragonFly".to_string() + } +} + +/// Used for incapsulating formatting FreeBSD version logic +pub struct FxType; + +impl FxType { + pub fn format(version: &str) -> String { + format!("FreeBSD {}", version) + } + + pub fn format_default() -> String { + "FreeBSD".to_string() + } +} + +/// Used for incapsulating formatting NetBSD version logic +pub struct NxType; + +impl NxType { + pub fn format(version: &str) -> String { + format!("NetBSD {}", version) + } + + pub fn format_default() -> String { + "NetBSD".to_string() + } +} + +/// Used for incapsulating formatting OpenBSD version logic +pub struct OxType; + +impl OxType { + pub fn format(version: &str) -> String { + format!("OpenBSD {}", version) + } + + pub fn format_default() -> String { + "OpenBSD".to_string() + } +} + +/// Used for incapsulating formatting C language standards logic +#[derive(Debug, Clone, PartialEq)] +pub enum StType { + // C Language Standards + AnsiC, + AnsiC89, + IsoC, + IsoC90, + IsoCAmd1, + IsoCTcor1, + IsoCTcor2, + IsoC99, + IsoC2011, + // POSIX.1 Standards before XPG4.2 + P1003188, + P10031, + P1003190, + Iso9945190, + P10031B93, + P10031B, + P10031C95, + P10031I95, + P1003196, + Iso9945196, + // X/Open Portability Guide before XPG4.2 + Xpg3, + P10032, + P1003292, + Iso9945293, + P10032A92, + Xpg4, + // X/Open Portability Guide Issue 4 Version 2 and Related Standards + Susv1, + Xpg42, + XCurses42, + P10031G2000, + Svid4, + // X/Open Portability Guide Issue 5 and Related Standards + Susv2, + Xbd5, + Xsh5, + Xcu5, + Xns5, + Xns52, + // POSIX Issue 6 Standards + P100312001, + Susv3, + P100312004, + // POSIX Issues 7 and 8 Standards + P100312008, + Susv4, + P100312024, + // Other Standards + Ieee754, + Iso8601, + Iso88023, + Ieee127594, +} + +impl From> for StType { + fn from(pair: Pair<'_, Rule>) -> Self { + match pair.into_inner().next().unwrap().as_rule() { + // C Language Standards + Rule::st_ansiC => Self::AnsiC, + Rule::st_ansiC_89 => Self::AnsiC89, + Rule::st_isoC => Self::IsoC, + Rule::st_isoC_90 => Self::IsoC90, + Rule::st_isoC_amd1 => Self::IsoCAmd1, + Rule::st_isoC_tcor1 => Self::IsoCTcor1, + Rule::st_isoC_tcor2 => Self::IsoCTcor2, + Rule::st_isoC_99 => Self::IsoC99, + Rule::st_isoC_2011 => Self::IsoC2011, + // POSIX.1 Standards before XPG4.2 + Rule::st_p1003_1_88 => Self::P1003188, + Rule::st_p1003_1 => Self::P10031, + Rule::st_p1003_1_90 => Self::P1003190, + Rule::st_iso9945_1_90 => Self::Iso9945190, + Rule::st_p1003_1b_93 => Self::P10031B93, + Rule::st_p1003_1b => Self::P10031B, + Rule::st_p1003_1c_95 => Self::P10031C95, + Rule::st_p1003_1i_95 => Self::P10031I95, + Rule::st_p1003_1_96 => Self::P1003196, + Rule::st_iso9945_1_96 => Self::Iso9945196, + // X/Open Portability Guide before XPG4.2 + Rule::st_xpg3 => Self::Xpg3, + Rule::st_p1003_2 => Self::P10032, + Rule::st_p1003_2_92 => Self::P1003292, + Rule::st_iso9945_2_93 => Self::Iso9945293, + Rule::st_p1003_2a_92 => Self::P10032A92, + Rule::st_xpg4 => Self::Xpg4, + // X/Open Portability Guide Issue 4 Version 2 and Related Standards + Rule::st_susv1 => Self::Susv1, + Rule::st_xpg4_2 => Self::Xpg42, + Rule::st_xcurses4_2 => Self::XCurses42, + Rule::st_p1003_1g_2000 => Self::P10031G2000, + Rule::st_svid4 => Self::Svid4, + // X/Open Portability Guide Issue 5 and Related Standards + Rule::st_susv2 => Self::Susv2, + Rule::st_xbd5 => Self::Xbd5, + Rule::st_xsh5 => Self::Xsh5, + Rule::st_xcu5 => Self::Xcu5, + Rule::st_xns5 => Self::Xns5, + Rule::st_xns5_2 => Self::Xns52, + // POSIX Issue 6 Standards + Rule::st_p1003_1_2001 => Self::P100312001, + Rule::st_susv3 => Self::Susv3, + Rule::st_p1003_1_2004 => Self::P100312004, + // POSIX Issues 7 and 8 Standards + Rule::st_p1003_1_2008 => Self::P100312008, + Rule::st_susv4 => Self::Susv4, + Rule::st_p1003_1_2024 => Self::P100312024, + // Other Standards + Rule::st_ieee754 => Self::Ieee754, + Rule::st_iso8601 => Self::Iso8601, + Rule::st_iso8802_3 => Self::Iso88023, + Rule::st_ieee1275_94 => Self::Ieee127594, + _ => unreachable!(), + } + } +} + +impl Display for StType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let standard = match self { + // C Language Standards + StType::AnsiC => "ANSI X3.159-1989 (“ANSI C89”)".to_string(), + StType::AnsiC89 => "ANSI X3.159-1989 (“ANSI C89”)".to_string(), + StType::IsoC => "ISO/IEC 9899:1990 (“ISO C90”)".to_string(), + StType::IsoC90 => "ISO/IEC 9899:1990 (“ISO C90”)".to_string(), + StType::IsoCAmd1 => "ISO/IEC 9899/AMD1:1995 (“ISO C90, Amendment 1”)".to_string(), + StType::IsoCTcor1 => { + "ISO/IEC 9899/TCOR1:1994 (“ISO C90, Technical Corrigendum 1”)".to_string() + } + StType::IsoCTcor2 => { + "ISO/IEC 9899/TCOR2:1995 (“ISO C90, Technical Corrigendum 2”)".to_string() + } + StType::IsoC99 => "ISO/IEC 9899:1999 (“ISO C99”)".to_string(), + StType::IsoC2011 => "ISO/IEC 9899:2011 (“ISO C11”)".to_string(), + // POSIX.1 Standards before XPG4.2 + StType::P1003188 => "IEEE Std 1003.1-1988 (“POSIX.1”)".to_string(), + StType::P10031 => "IEEE Std 1003.1 (“POSIX.1”)".to_string(), + StType::P1003190 => "IEEE Std 1003.1-1990 (“POSIX.1”)".to_string(), + StType::Iso9945190 => "ISO/IEC 9945-1:1990 (“POSIX.1”)".to_string(), + StType::P10031B93 => "IEEE Std 1003.1b-1993 (“POSIX.1b”)".to_string(), + StType::P10031B => "IEEE Std 1003.1b (“POSIX.1b”)".to_string(), + StType::P10031C95 => "IEEE Std 1003.1c-1995 (“POSIX.1c”)".to_string(), + StType::P10031I95 => "IEEE Std 1003.1i-1995 (“POSIX.1i”)".to_string(), + StType::P1003196 => "ISO/IEC 9945-1:1996 (“POSIX.1”)".to_string(), + StType::Iso9945196 => "ISO/IEC 9945-1:1996 (“POSIX.1”)".to_string(), + // X/Open Portability Guide before XPG4.2 + StType::Xpg3 => "X/Open Portability Guide Issue 3 (“XPG3”)".to_string(), + StType::P10032 => "IEEE Std 1003.2 (“POSIX.2”)".to_string(), + StType::P1003292 => "IEEE Std 1003.2-1992 (“POSIX.2”)".to_string(), + StType::Iso9945293 => "ISO/IEC 9945-2:1993 (“POSIX.2”)".to_string(), + StType::P10032A92 => "IEEE Std 1003.2a-1992 (“POSIX.2”)".to_string(), + StType::Xpg4 => "X/Open Portability Guide Issue 4 (“XPG4”)".to_string(), + // X/Open Portability Guide Issue 4 Version 2 and Related Standards + StType::Susv1 => "Version 1 of the Single UNIX Specification (“SUSv1”)".to_string(), + StType::Xpg42 => "X/Open Portability Guide Issue 4, Version 2 (“XPG4.2”)".to_string(), + StType::XCurses42 => "X/Open Curses Issue 4, Version 2 (“XCURSES4.2”)".to_string(), + StType::P10031G2000 => "IEEE Std 1003.1g-2000 (“POSIX.1g”)".to_string(), + StType::Svid4 => "System V Interface Definition, Fourth Edition (“SVID4”)".to_string(), + // X/Open Portability Guide Issue 5 and Related Standards + StType::Susv2 => "Version 2 of the Single UNIX Specification (“SUSv2”)".to_string(), + StType::Xbd5 => "X/Open Base Definitions Issue 5 (“XBD5”)".to_string(), + StType::Xsh5 => "X/Open System Interfaces and Headers Issue 5 (“XSH5”)".to_string(), + StType::Xcu5 => "X/Open Commands and Utilities Issue 5 (“XCU5”)".to_string(), + StType::Xns5 => "X/Open Networking Services Issue 5 (“XNS5”)".to_string(), + StType::Xns52 => "X/Open Networking Services Issue 5.2 (“XNS5.2”)".to_string(), + // POSIX Issue 6 Standards + StType::P100312001 => "IEEE Std 1003.1-2001 (“POSIX.1”)".to_string(), + StType::Susv3 => "Version 3 of the Single UNIX Specification (“SUSv3”)".to_string(), + StType::P100312004 => "IEEE Std 1003.1-2004 (“POSIX.1”)".to_string(), + // POSIX Issues 7 and 8 Standards + StType::P100312008 => "IEEE Std 1003.1-2008 (“POSIX.1”)".to_string(), + StType::Susv4 => "Version 4 of the Single UNIX Specification (“SUSv4”)".to_string(), + // TODO: documentation doesn't containt needed text. + StType::P100312024 => "".to_string(), + // Other Standards + StType::Ieee754 => "IEEE Std 754-1985".to_string(), + StType::Iso8601 => "ISO 8601".to_string(), + StType::Iso88023 => "ISO 8802-3: 1989".to_string(), + StType::Ieee127594 => "IEEE Std 1275-1994 (“Open Firmware”)".to_string(), + }; + + write!(f, "{standard}") + } +} diff --git a/man/man_util/mdoc_macro/types.rs b/man/man_util/mdoc_macro/types.rs new file mode 100644 index 00000000..a9f85f66 --- /dev/null +++ b/man/man_util/mdoc_macro/types.rs @@ -0,0 +1,142 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +use pest::iterators::Pair; + +use crate::man_util::parser::Rule; + +/// Bd block variants +#[derive(Debug, Clone, PartialEq)] +pub enum BdType { + Centered, + Filled, + Literal, + Ragged, + Unfilled, +} + +impl From> for BdType { + fn from(pair: Pair<'_, Rule>) -> Self { + match pair.into_inner().next().unwrap().as_rule() { + Rule::bd_centered => Self::Centered, + Rule::bd_filled => Self::Filled, + Rule::bd_literal => Self::Literal, + Rule::bd_ragged => Self::Ragged, + Rule::bd_unfilled => Self::Unfilled, + _ => unreachable!(), + } + } +} + +/// Bd, Bl blocks offset variants +#[derive(Debug, Clone, PartialEq)] +pub enum OffsetType { + Indent, + /// 2x [`OffsetType::Indent`] + IndentTwo, + Left, + Right, + Center, +} + +impl From> for OffsetType { + fn from(pair: Pair<'_, Rule>) -> Self { + match pair.into_inner().next().unwrap().as_rule() { + Rule::off_indent => Self::Indent, + Rule::off_indent_two => Self::IndentTwo, + Rule::off_left => Self::Left, + Rule::off_right => Self::Right, + Rule::off_center => Self::Center, + Rule::word => Self::Indent, + _ => unreachable!(), + } + } +} + +/// Bf block font style variants +#[derive(Debug, Clone, PartialEq)] +pub enum BfType { + /// Enables italic font mode + Emphasis, + /// Enables typewriter font mode + Literal, + /// Enables boldface font mode + Symbolic, +} + +impl From> for BfType { + fn from(pair: Pair<'_, Rule>) -> Self { + match pair.into_inner().next().unwrap().as_rule() { + Rule::bf_emphasis | Rule::bf_em => Self::Emphasis, + Rule::bf_literal | Rule::bf_li => Self::Literal, + Rule::bf_symbolic | Rule::bf_sy => Self::Symbolic, + _ => unreachable!(), + } + } +} + +/// Bl block variants +#[derive(Debug, Clone, PartialEq)] +pub enum BlType { + /// No item heads can be specified, but a bullet will be printed at the head of each item + Bullet, + /// A columnated list + Column, + /// Like -bullet, except that dashes are used in place of bullets + Dash, + /// Like -inset, except that item heads are not parsed for macro invocations + Diag, + /// A numbered list + Enum, + /// Like -tag, except that the first lines of item bodies are not indented, but follow the item heads like in -inset lists + Hang, + /// Item bodies follow items heads on the same line, using normal inter-word spacing + Inset, + /// No item heads can be specified, and none are printed + Item, + /// Item bodies start on the line following item heads and are not indented + Ohang, + /// Item bodies are indented according to the -width argument + Tag, +} + +impl From> for BlType { + fn from(pair: Pair<'_, Rule>) -> Self { + match pair.into_inner().next().unwrap().as_rule() { + Rule::bl_bullet => Self::Bullet, + Rule::bl_column => Self::Column, + Rule::bl_dash | Rule::bl_hyphen => Self::Dash, + Rule::bl_diag => Self::Diag, + Rule::bl_enum => Self::Enum, + Rule::bl_hang => Self::Hang, + Rule::bl_inset => Self::Inset, + Rule::bl_item => Self::Item, + Rule::bl_ohang => Self::Ohang, + Rule::bl_tag => Self::Tag, + _ => unreachable!(), + } + } +} + +/// Defines how split authors names +#[derive(Debug, Clone, PartialEq)] +pub enum AnType { + Split, + NoSplit, + Name, +} + +/// Spacing mode for output generated from macros +#[derive(Debug, Clone, PartialEq)] +pub enum SmMode { + /// Space is inserted between macro arguments and between the output generated from adjacent macros + On, + /// No white space is inserted between macro arguments and between the output generated from adjacent macros + Off, +} diff --git a/man/man_util/mod.rs b/man/man_util/mod.rs new file mode 100644 index 00000000..2d7a9be3 --- /dev/null +++ b/man/man_util/mod.rs @@ -0,0 +1,17 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +/// Handle mdoc config file +pub mod config; +/// Converts AST to [`String`] and print it to terminal +pub mod formatter; +/// Store [`Macro`] enum +pub mod mdoc_macro; +/// Converts input mdoc file macros to AST +pub mod parser; diff --git a/man/man_util/parser.rs b/man/man_util/parser.rs new file mode 100644 index 00000000..69a4a83f --- /dev/null +++ b/man/man_util/parser.rs @@ -0,0 +1,12052 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +use pest::{iterators::Pair, Parser}; +use pest_derive::Parser; +use text_production::{AtType, BsxType}; +use thiserror::Error; +use types::{BdType, BfType, OffsetType, SmMode}; + +use crate::man_util::mdoc_macro::text_production::{ + BxType, DxType, FxType, NxType, OxType, StType, +}; + +use super::mdoc_macro::types::*; +use super::mdoc_macro::*; + +use std::mem::discriminant; +use std::sync::LazyLock; + +/// Rs submacros sorting order +static RS_SUBMACRO_ORDER: LazyLock> = LazyLock::new(|| { + vec![ + Macro::A, + Macro::T, + Macro::B, + Macro::I, + Macro::J, + Macro::R, + Macro::N, + Macro::V, + Macro::U, + Macro::P, + Macro::Q, + Macro::C, + Macro::D, + Macro::O, + ] +}); + +static BLOCK_PARTIAL_IMPLICIT: &[&str] = &[ + "Aq", "Bq", "Brq", "D1", "Dl", "Dq", "En", "Op", "Pq", "Ql", "Qq", "Sq", "Vt", +]; + +#[allow(unreachable_patterns)] +fn does_start_with_macro(word: &str) -> bool { + matches!( + word, + "Bd" | "Bf" + | "Bk" + | "Bl" + | "Ed" + | "Ef" + | "Ek" + | "El" + | "It" + | "Nd" + | "Nm" + | "Sh" + | "Ss" + | "Ac" + | "Ao" + | "Bc" + | "Bo" + | "Brc" + | "Bro" + | "Dc" + | "Do" + | "Ec" + | "Eo" + | "Fc" + | "Fo" + | "Oc" + | "Oo" + | "Pc" + | "Po" + | "Qc" + | "Qo" + | "Re" + | "Rs" + | "Sc" + | "So" + | "Xc" + | "Xo" + | "Aq" + | "Bq" + | "Brq" + | "D1" + | "Dl" + | "Dq" + | "En" + | "Op" + | "Pq" + | "Ql" + | "Qq" + | "Sq" + | "Vt" + | "Ta" + | "%A" + | "%B" + | "%C" + | "%D" + | "%I" + | "%J" + | "%N" + | "%O" + | "%P" + | "%Q" + | "%R" + | "%T" + | "%U" + | "%V" + | "Ad" + | "An" + | "Ap" + | "Ar" + | "At" + | "Bsx" + | "Bt" + | "Bx" + | "Cd" + | "Cm" + | "Db" + | "Dd" + | "Dt" + | "Dv" + | "Dx" + | "Em" + | "Er" + | "Es" + | "Ev" + | "Ex" + | "Fa" + | "Fd" + | "Fl" + | "Fn" + | "Fr" + | "Ft" + | "Fx" + | "Hf" + | "Ic" + | "In" + | "Lb" + | "Li" + | "Lk" + | "Lp" + | "Ms" + | "Mt" + | "Nm" + | "No" + | "Ns" + | "Nx" + | "Os" + | "Ot" + | "Ox" + | "Pa" + | "Pf" + | "Pp" + | "Rv" + | "Sm" + | "St" + | "Sx" + | "Sy" + | "Tg" + | "Tn" + | "Ud" + | "Ux" + | "Va" + | "Vt" + | "Xr" + ) +} + +pub fn prepare_document(text: &str) -> String { + let mut is_bd_literal_block = false; + + text.lines() + .filter(|l| !l.trim_start().starts_with(".Tg")) + .map(|l| { + let line = if l.contains(".It") { + l.replace('\t', " Ta ").replace(" ", " Ta ") + } else { + l.to_string() + }; + + if line.contains(".Bd") && (line.contains("-literal") || line.contains("-unfilled")) { + is_bd_literal_block = true; + } + + if is_bd_literal_block && line.contains(".Ed") { + is_bd_literal_block = false; + } + + let transformed_line = if is_bd_literal_block { + let mut leading_spaces = if line.is_empty() { 1 } else { 0 }; + let mut index = 0; + for (i, ch) in line.char_indices() { + if !ch.is_whitespace() { + break; + } + leading_spaces += if ch == '\t' { 4 } else { 1 }; + index = i + ch.len_utf8(); + } + + format!("{}{}", "\\^".repeat(leading_spaces), &line[index..]) + } else { + line.clone() + }; + + let mut processed_line = if let Some(first_word) = line.split_whitespace().next() { + if does_start_with_macro(first_word) { + format!("\\&{}", transformed_line) + } else { + transformed_line + } + } else { + transformed_line + }; + + let count_partials = processed_line + .split_whitespace() + .filter(|word| BLOCK_PARTIAL_IMPLICIT.contains(word)) + .count(); + + if count_partials > 0 { + processed_line.push_str(&"\n".repeat(count_partials)); + } + + processed_line + }) + .collect::>() + .join("\n") +} + +/// Mdoc files parser +#[derive(Parser)] +#[grammar = "./man_util/mdoc.pest"] +pub struct MdocParser; + +/// Stores macro parameters and subnodes +#[derive(Debug, Clone, PartialEq)] +pub struct MacroNode { + /// Macro type + pub mdoc_macro: Macro, + /// Sub nodes of current node + pub nodes: Vec, +} + +/// Mdoc language units +#[derive(Debug, Clone, PartialEq)] +pub enum Element { + /// Text node + Text(String), + /// Macro node + Macro(MacroNode), + /// "End of input" marker + Eoi, +} + +impl From for String { + fn from(element: Element) -> Self { + match element { + Element::Text(text) => text, + Element::Macro(macro_node) => format!("{:?}", macro_node), + Element::Eoi => "EOI".to_string(), + } + } +} + +impl From for Element { + fn from(value: String) -> Self { + Element::Text(value) + } +} + +/// Stores full mdoc AST +#[derive(Debug, Clone, PartialEq)] +pub struct MdocDocument { + pub elements: Vec, +} + +/// Mdoc parsing errors +#[derive(Error, Debug, PartialEq)] +pub enum MdocError { + /// Pest rules violation + #[error("mdoc: {0}")] + Pest(#[from] Box>), +} + +impl MdocParser { + fn parse_element(pair: Pair) -> Element { + match pair.as_rule() { + Rule::element => Self::parse_element(pair.into_inner().next().unwrap()), + Rule::block_full_explicit => Self::parse_block_full_explicit(pair), + Rule::block_full_implicit => Self::parse_block_full_implicit(pair), + Rule::block_partial_implicit => Self::parse_block_partial_implicit(pair), + Rule::partial_implicit_element => { + Self::parse_element(pair.into_inner().next().unwrap()) + } + Rule::block_partial_explicit => Self::parse_block_partial_explicit(pair), + Rule::inline => Self::parse_inline(pair), + Rule::arg => Self::parse_arg(pair.into_inner().next().unwrap()), + Rule::macro_arg => Self::parse_element(pair.into_inner().next().unwrap()), + Rule::ta | Rule::ta_head => Self::parse_ta(pair), + Rule::text_line | Rule::line => Element::Text(trim_quotes( + pair.into_inner().next().unwrap().as_str().to_string(), + )), + Rule::EOI => Element::Eoi, + _ => Element::Text(trim_quotes(pair.as_str().to_string())), + } + } + + fn parse_arg(pair: Pair) -> Element { + match pair.as_rule() { + Rule::text_arg => Element::Text(pair.as_str().to_string()), + Rule::macro_arg => Self::parse_element(pair.into_inner().next().unwrap()), + _ => unreachable!(), + } + } + + fn parse_ta(_pair: Pair) -> Element { + Element::Macro(MacroNode { + mdoc_macro: Macro::Ta, + nodes: vec![], + }) + } + + /// Parses full mdoc file + pub fn parse_mdoc(input: &str) -> Result { + let input = prepare_document(input); + let pairs = MdocParser::parse(Rule::mdoc, input.as_ref()) + .map_err(|err| MdocError::Pest(Box::new(err)))?; + + // Iterate each pair (macro or text element) + let mut elements: Vec = pairs + .flat_map(|p| { + let inner_rules = p.into_inner(); + inner_rules.map(Self::parse_element) + }) + .collect(); + + if let Some(Element::Eoi) = elements.last() { + elements.pop(); // Remove `Element::Eoi` element + } + + let mdoc = MdocDocument { elements }; + + Ok(mdoc) + } +} + +// Block full-explicit macros parsing +impl MdocParser { + /// Parses (`Bd`)[https://man.openbsd.org/mdoc#Bd]: + /// `Bd -type [-offset width] [-compact]` + fn parse_bd_block(pair: Pair) -> Element { + fn parse_bd_open(pair: Pair) -> Macro { + let mut inner = pair.into_inner(); + + // -type + let block_type = BdType::from(inner.next().unwrap()); + + let mut offset: Option = None; + let mut compact = false; + + for arg_pair in inner { + if !matches!(arg_pair.as_rule(), Rule::bd_offset | Rule::bd_compact) { + unreachable!() + } + for arg_pair in arg_pair.into_inner() { + match arg_pair.as_rule() { + Rule::offset => offset = Some(OffsetType::from(arg_pair)), + Rule::compact => compact = true, + _ => unreachable!(), + } + } + } + + Macro::Bd { + block_type, + offset, + compact, + } + } + + let mut pairs = pair.into_inner(); + + let bd_macro = parse_bd_open(pairs.next().unwrap()); + + let nodes = pairs + .take_while(|p| p.as_rule() != Rule::ed_close) + .map(Self::parse_element) + .collect(); + // .map(|p| parse_bd_body(bd_macro.clone(), p)) + + Element::Macro(MacroNode { + mdoc_macro: bd_macro, + nodes, + }) + } + + /// Parses (`Bf`)[https://man.openbsd.org/mdoc#Bf]: + /// `Bf -emphasis | -literal | -symbolic | Em | Li | Sy` + fn parse_bf_block(pair: Pair) -> Element { + fn parse_bf_open(pair: Pair) -> Macro { + let mut inner = pair.into_inner(); + + // -type + let block_type = BfType::from(inner.next().unwrap()); + + Macro::Bf(block_type) + } + + let mut pairs = pair.into_inner(); + + let bf_macro = parse_bf_open(pairs.next().unwrap()); + + let nodes = pairs + .take_while(|p| p.as_rule() != Rule::ef_close) + .map(Self::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: bf_macro, + nodes, + }) + } + + /// Parses (`Bk`)[https://man.openbsd.org/mdoc#Bk]: + /// `Bk -words` + fn parse_bk_block(pair: Pair) -> Element { + let mut pairs = pair.into_inner(); + + // `bk_open` + let _ = pairs.next().unwrap(); + + let nodes = pairs + .take_while(|p| p.as_rule() != Rule::ek_close) + .map(Self::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Bk, + nodes, + }) + } + + // Parses (`Bl`)[https://man.openbsd.org/mdoc#Bl] + // `Bl -type [-width val] [-offset val] [-compact] [col ...]` + fn parse_bl_block(pair: Pair) -> Element { + fn parse_bl_parameter( + pair: Pair, + width: &mut Option, + offset: &mut Option, + compact: &mut bool, + columns: &mut Vec, + count: &mut (usize, usize, usize), + ) -> bool { + match pair.as_rule() { + Rule::bl_width => { + if count.0 > 0 { + return true; + } + count.0 += 1; + let mut width_p = pair + .into_inner() + .find(|p| Rule::word == p.as_rule()) + .map(|p| p.as_str().to_string()) + .unwrap_or("".to_string()); + + if width_p.is_empty() { + *width = None; + } else if width_p.chars().next().unwrap().is_ascii_digit() { + width_p = width_p + .chars() + .take_while(|ch| ch.is_ascii_digit()) + .collect::(); + if let Ok(w) = str::parse::(&width_p) { + *width = Some(w); + } + } else { + *width = match width_p.as_str() { + "Er" => Some(19), + "Ds" => Some(8), + "Ev" => Some(17), + "Fl" => Some(12), + _ => width_p.len().try_into().ok(), + } + } + } + Rule::bl_offset => { + if count.1 > 0 { + return true; + } + count.1 += 1; + let offset_p = pair + .into_inner() + .find(|p| Rule::offset == p.as_rule()) + .unwrap(); + *offset = Some(OffsetType::from(offset_p)); + } + Rule::compact => { + if count.2 > 0 { + return true; + } + count.2 += 1; + *compact = true; + } + _ => columns.push(pair.as_str().to_string()), + } + false + } + + fn parse_bl_open(pair: Pair) -> Macro { + let mut inner = pair.into_inner(); + + // -type + let bl_type_pair = inner.next().unwrap(); + let list_type = BlType::from(bl_type_pair); + + let mut offset: Option = None; + let mut width: Option = None; + let mut compact = false; + let mut columns = vec![]; + let mut count = (0, 0, 0); + + for opt_pair in inner { + match opt_pair.as_rule() { + Rule::bl_param => { + for parameter in opt_pair.into_inner() { + let has_repeat = parse_bl_parameter( + parameter.clone(), + &mut width, + &mut offset, + &mut compact, + &mut columns, + &mut count, + ); + + if has_repeat { + columns.extend( + parameter + .as_str() + .split(" ") + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()) + .collect::>(), + ); + continue; + } + } + } + _ => columns.push(opt_pair.as_str().to_string()), + } + } + + Macro::Bl { + list_type, + width, + offset, + compact, + columns, + } + } + + let mut pairs = pair.into_inner(); + + let bl_macro = parse_bl_open(pairs.next().unwrap()); + + let nodes = pairs + .take_while(|p| p.as_rule() != Rule::el_close) + .filter(|p| p.as_rule() != Rule::bl_skip) + .map(Self::parse_it_block) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: bl_macro, + nodes, + }) + } + + fn parse_block_full_explicit(pair: Pair) -> Element { + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::bd_block => Self::parse_bd_block(pair), + Rule::bf_block => Self::parse_bf_block(pair), + Rule::bk_block => Self::parse_bk_block(pair), + Rule::bl_block => Self::parse_bl_block(pair), + _ => unreachable!(), + } + } +} + +// Block full-implicit macros parsing +impl MdocParser { + // Parses (`It`)[https://man.openbsd.org/mdoc#It] + // `It [head]` + fn parse_it_block(pair: Pair) -> Element { + fn string_to_elements(input: &str) -> Vec { + if let Ok(pairs) = MdocParser::parse(Rule::args, input) { + pairs + .flat_map(|p| { + let inner_rules = p.into_inner(); + inner_rules.map(MdocParser::parse_element) + }) + .filter(|el| !matches!(el, Element::Eoi)) + .collect() + } else { + vec![] + } + } + + let mut inner_pairs = pair.into_inner(); + + let mut head: Vec<_> = inner_pairs + .next() + .unwrap() + .into_inner() + .map(Self::parse_element) + .collect(); + + let mut parse_buffer = String::new(); + let mut new_head = vec![]; + for element in head { + match element { + Element::Text(text) => { + parse_buffer.push_str(&(text + " ")); + } + _ => { + new_head.extend(string_to_elements(&parse_buffer)); + parse_buffer.clear(); + new_head.push(element); + } + } + } + + new_head.extend(string_to_elements(&parse_buffer)); + head = new_head; + + let nodes = inner_pairs + .next() + .unwrap() + .into_inner() + .map(Self::parse_element) + .collect::>(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::It { head }, + nodes, + }) + } + + // Parses (`Nd`)[https://man.openbsd.org/mdoc#Nd] + // `Nd line` + fn parse_nd(pair: Pair) -> Element { + let mut inner_nodes = pair.into_inner(); + + let mut nodes: Vec<_> = inner_nodes + .next() + .unwrap() + .into_inner() + .map(Self::parse_element) + .collect(); + + for body in inner_nodes { + let mut inner = body.into_inner(); + for pair in inner.by_ref() { + nodes.push(Self::parse_element(pair)); + } + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes, + }) + } + + // Parses (`Nm`)[https://man.openbsd.org/mdoc#Nm] + // `Nm [name]` + fn parse_nm(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + + let mut name = None; + let mut nodes = vec![]; + + if let Some(val) = inner_pairs.next() { + let val = val.as_str().to_string(); + if val.chars().all(|ch| ch.is_alphanumeric()) { + name = Some(val); + } else { + nodes.push(Element::Text(val)); + } + } + + nodes.extend(inner_pairs.map(Self::parse_element)); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Nm { name }, + nodes, + }) + } + + // Parses (`Sh`)[https://man.openbsd.org/mdoc#Sh] + // `Sh TITLE LINE` + fn parse_sh_block(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let title = inner + .next() // `sh_block` -> `sh_open` + .unwrap() + .into_inner() + .next() // `sh_open` -> `sh_title_line` + .expect("Expected title for 'Sh' block") + .as_str() + .trim_end() + .to_string(); + + // Parse `sh_block_element` + let nodes = inner + .filter_map(|p| p.into_inner().next().map(Self::parse_element)) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { title }, + nodes, + }) + } + + /// Parses (`Ss`)[https://man.openbsd.org/mdoc#Ss]: + /// `Ss Title line` + fn parse_ss_block(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let title = inner + .next() // `ss_block` -> `ss_open` + .unwrap() + .into_inner() + .next() // `ss_open` -> `ss_title_line` + .expect("Expected title for 'Ss' block") + .as_str() + .trim_end() + .to_string(); + + // Parse `ss_block_element` + let nodes = inner + .filter_map(|p| p.into_inner().next().map(Self::parse_element)) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { title }, + nodes, + }) + } + + fn parse_block_full_implicit(pair: Pair) -> Element { + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::it_block => Self::parse_it_block(pair), + Rule::nd_block => Self::parse_nd(pair), + Rule::nm_block => Self::parse_nm(pair), + Rule::sh_block => Self::parse_sh_block(pair), + Rule::ss_block => Self::parse_ss_block(pair), + _ => unreachable!(), + } + } +} + +// Block partial-implicit macros parsing +impl MdocParser { + // Parses (`Aq`)[https://man.openbsd.org/mdoc#Aq]: + // `Aq line` + fn parse_aq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Aq, + nodes, + }) + } + + // Parses (`Bq`)[https://man.openbsd.org/mdoc#Bq]: + // `Bq line` + fn parse_bq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Bq, + nodes, + }) + } + + // Parses (`Brq`)[https://man.openbsd.org/mdoc#Brq]: + // `Brq line` + fn parse_brq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Brq, + nodes, + }) + } + + // Parses (`D1`)[https://man.openbsd.org/mdoc#D1]: + // `D1 line` + fn parse_d1_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::D1, + nodes, + }) + } + + // Parses (`Dl`)[https://man.openbsd.org/mdoc#Dl]: + // `Dl line` + fn parse_dl_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dl, + nodes, + }) + } + + // Parses (`Dq`)[https://man.openbsd.org/mdoc#Dq]: + // `Dq line` + fn parse_dq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dq, + nodes, + }) + } + + // Parses (`En`)[https://man.openbsd.org/mdoc#En]: + // `En word ...` + fn parse_en_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::En, + nodes, + }) + } + + // Parses (`Op`)[https://man.openbsd.org/mdoc#Op]: + // `Op line` + fn parse_op_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Op, + nodes, + }) + } + + // Parses (`Pq`)[https://man.openbsd.org/mdoc#Pq]: + // `Pq line` + fn parse_pq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Pq, + nodes, + }) + } + + // Parses (`Ql`)[https://man.openbsd.org/mdoc#Ql]: + // `Ql line` + fn parse_ql_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ql, + nodes, + }) + } + + // Parses (`Qq`)[https://man.openbsd.org/mdoc#Qq]: + // `Qq line` + fn parse_qq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Qq, + nodes, + }) + } + + // Parses (`Sq`)[https://man.openbsd.org/mdoc#Sq]: + // `Sq line` + fn parse_sq_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sq, + nodes, + }) + } + + // Parses (`Vt`)[https://man.openbsd.org/mdoc#Vt]: + // `Vt type [identifier] ...` + fn parse_vt_block(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Vt, + nodes, + }) + } + + fn parse_block_partial_implicit(pair: Pair) -> Element { + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::aq_block => Self::parse_aq_block(pair), + Rule::bq_block => Self::parse_bq_block(pair), + Rule::brq_block => Self::parse_brq_block(pair), + Rule::d1_block => Self::parse_d1_block(pair), + Rule::dl_block => Self::parse_dl_block(pair), + Rule::dq_block => Self::parse_dq_block(pair), + Rule::en_block => Self::parse_en_block(pair), + Rule::op_block => Self::parse_op_block(pair), + Rule::pq_block => Self::parse_pq_block(pair), + Rule::ql_block => Self::parse_ql_block(pair), + Rule::qq_block => Self::parse_qq_block(pair), + Rule::sq_block => Self::parse_sq_block(pair), + Rule::vt_block => Self::parse_vt_block(pair), + _ => unreachable!(), + } + } +} + +// Block partial-explicit parsing +impl MdocParser { + // Parses (`Ao`)[https://man.openbsd.org/mdoc#Ao]: + // `Ao block` + fn parse_ao_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::ac) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let ac = inner_pairs + .skip_while(|p| p.as_rule() != Rule::ac) + .map(Self::parse_ac); + + nodes.extend(ac); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes, + }) + } + + // Parses (`Ac`)[https://man.openbsd.org/mdoc#Ac]: + // `Ac` + fn parse_ac(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes, + }) + } + + // Parses (`Bo`)[https://man.openbsd.org/mdoc#Bo]: + // `Bo block` + fn parse_bo_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::bc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let bc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::bc) + .map(Self::parse_bc); + + nodes.extend(bc); + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes, + }) + } + + // Parses (`Bc`)[https://man.openbsd.org/mdoc#Bc]: + // `Bc` + fn parse_bc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes, + }) + } + + // Parses (`Bro`)[https://man.openbsd.org/mdoc#Bro]: + // `Bro` + fn parse_bro_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::brc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let brc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::brc) + .map(Self::parse_brc); + + nodes.extend(brc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes, + }) + } + + // Parses (`Brc`)[https://man.openbsd.org/mdoc#Brc]: + // `Brc` + fn parse_brc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes, + }) + } + + // Parses (`Do`)[https://man.openbsd.org/mdoc#Do]: + // `Do` + fn parse_do_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::dc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let dc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::dc) + .map(Self::parse_dc); + + nodes.extend(dc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes, + }) + } + + // Parses (`Dc`)[https://man.openbsd.org/mdoc#Dc]: + // `Dc block` + fn parse_dc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes, + }) + } + + // Parses (`Eo`)[https://man.openbsd.org/mdoc#Eo]: + // `Eo block` + fn parse_eo_block(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + + let head = inner_pairs.next().unwrap().into_inner(); + + let mut nodes = Vec::new(); + let mut opening_delimiter = None; + let mut closing_delimiter = None; + + for arg in head { + if arg.as_rule() == Rule::opening_delimiter { + opening_delimiter = Some(arg.as_str().parse::().unwrap()); + } else { + nodes.push(Self::parse_element(arg)); + } + } + + let next_arg = inner_pairs.next().unwrap(); + match next_arg.as_rule() { + Rule::ec => { + if let Some(arg) = next_arg.into_inner().next() { + closing_delimiter = Some(arg.as_str().parse::().unwrap()); + } + } + Rule::eo_body => { + let iter = next_arg + .into_inner() + .take_while(|p| p.as_rule() != Rule::ec) + .map(Self::parse_element); + + nodes.extend(iter); + + if let Some(arg) = inner_pairs.next().unwrap().into_inner().next() { + closing_delimiter = Some(arg.as_str().parse::().unwrap()); + } + } + _ => unreachable!(), + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter, + closing_delimiter, + }, + nodes, + }) + } + + // Parses (`Ec`)[https://man.openbsd.org/mdoc#Ec]: + // `Ec` + fn parse_ec(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ec, + nodes, + }) + } + + // Parses (`Fo`)[https://man.openbsd.org/mdoc#Fo]: + // `Fo block` + fn parse_fo_block(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + let mut head = inner_pairs.next().unwrap().into_inner(); + + let funcname = head.next().unwrap().as_str().to_string(); + let mut nodes: Vec<_> = head.map(Self::parse_element).collect(); + + nodes.extend(inner_pairs.filter_map(|p| p.into_inner().next().map(Self::parse_element))); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { funcname }, + nodes, + }) + } + + // Parses (`Fc`)[https://man.openbsd.org/mdoc#Fc]: + // `Fc` + fn parse_fc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fc, + nodes, + }) + } + + // Parses (`Oo`)[https://man.openbsd.org/mdoc#Oo]: + // `Oo block` + fn parse_oo_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::oc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let oc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::oc) + .map(Self::parse_oc); + + nodes.extend(oc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes, + }) + } + + // Parses (`Oc`)[https://man.openbsd.org/mdoc#Oc]: + // `Oc` + fn parse_oc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes, + }) + } + + // Parses (`Po`)[https://man.openbsd.org/mdoc#Po]: + // `Po block` + fn parse_po_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::pc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let pc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::pc) + .map(Self::parse_pc); + + nodes.extend(pc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes, + }) + } + + // Parses (`Pc`)[https://man.openbsd.org/mdoc#Pc]: + // `Pc` + fn parse_pc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes, + }) + } + + // Parses (`Qo`)[https://man.openbsd.org/mdoc#Qo]: + // `Qo block` + fn parse_qo_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::qc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let qc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::qc) + .map(Self::parse_qc); + + nodes.extend(qc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes, + }) + } + + // Parses (`Qc`)[https://man.openbsd.org/mdoc#Qc]: + // `Qc` + fn parse_qc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes, + }) + } + + // Parses (`Rs`)[https://man.openbsd.org/mdoc#Rs]: + // `Rs` + fn parse_rs_block(pair: Pair) -> Element { + fn rs_submacro_cmp(a: &Element, b: &Element) -> std::cmp::Ordering { + let get_macro_order_position = |n| { + RS_SUBMACRO_ORDER + .iter() + .position(|m| discriminant(m) == discriminant(n)) + .unwrap_or(RS_SUBMACRO_ORDER.len()) + }; + + let Element::Macro(MacroNode { + mdoc_macro: macro_a, + .. + }) = a + else { + return std::cmp::Ordering::Greater; + }; + + let Element::Macro(MacroNode { + mdoc_macro: macro_b, + .. + }) = b + else { + return std::cmp::Ordering::Greater; + }; + + let a_pos = get_macro_order_position(macro_a); + let b_pos = get_macro_order_position(macro_b); + + a_pos.cmp(&b_pos) + } + + let mut nodes: Vec<_> = pair + .into_inner() + .skip_while(|p| p.as_rule() == Rule::rs_head) + .take_while(|p| p.as_rule() != Rule::re) + .filter_map(|p| p.into_inner().next().map(Self::parse_rs_submacro)) + .collect(); + + nodes.sort_by(rs_submacro_cmp); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Rs, + nodes, + }) + } + + // Parses (`Re`)[https://man.openbsd.org/mdoc#Re]: + // `Re` + fn parse_re(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Re, + nodes, + }) + } + + // Parses (`So`)[https://man.openbsd.org/mdoc#So]: + // `So block` + fn parse_so_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::sc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let sc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::sc) + .map(Self::parse_sc); + + nodes.extend(sc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes, + }) + } + + // Parses (`Sc`)[https://man.openbsd.org/mdoc#Sc]: + // `Sc` + fn parse_sc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes, + }) + } + + // Parses (`Xo`)[https://man.openbsd.org/mdoc#Xo]: + // `Xo block` + fn parse_xo_block(pair: Pair) -> Element { + let inner_pairs = pair.into_inner(); + let mut nodes: Vec<_> = inner_pairs + .clone() + .take_while(|p| p.as_rule() != Rule::xc) + .flat_map(|p| p.into_inner().map(Self::parse_element).collect::>()) + .collect(); + + let xc = inner_pairs + .skip_while(|p| p.as_rule() != Rule::xc) + .map(Self::parse_xc); + + nodes.extend(xc); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes, + }) + } + + // Parses (`Xc`)[https://man.openbsd.org/mdoc#Xc]: + // `Xc` + fn parse_xc(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes, + }) + } + + fn parse_block_partial_explicit(pair: Pair) -> Element { + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::ao_block => Self::parse_ao_block(pair), + Rule::bo_block => Self::parse_bo_block(pair), + Rule::bro_block => Self::parse_bro_block(pair), + Rule::do_block => Self::parse_do_block(pair), + Rule::eo_block => Self::parse_eo_block(pair), + Rule::fo_block => Self::parse_fo_block(pair), + Rule::oo_block => Self::parse_oo_block(pair), + Rule::po_block => Self::parse_po_block(pair), + Rule::qo_block => Self::parse_qo_block(pair), + Rule::rs_block => Self::parse_rs_block(pair), + Rule::so_block => Self::parse_so_block(pair), + Rule::xo_block => Self::parse_xo_block(pair), + Rule::ac => Self::parse_ac(pair), + Rule::bc => Self::parse_bc(pair), + Rule::brc => Self::parse_brc(pair), + Rule::dc => Self::parse_dc(pair), + Rule::ec => Self::parse_ec(pair), + Rule::fc => Self::parse_fc(pair), + Rule::oc => Self::parse_oc(pair), + Rule::pc => Self::parse_pc(pair), + Rule::qc => Self::parse_qc(pair), + Rule::re => Self::parse_re(pair), + Rule::sc => Self::parse_sc(pair), + Rule::xc => Self::parse_xc(pair), + _ => unreachable!(), + } + } +} + +/// Trim `"` quotes from [`String`] +pub fn trim_quotes(mut s: String) -> String { + if !s.starts_with("\\&\"") { + if let Some(stripped) = s.strip_prefix("\"") { + s = stripped.to_string(); + } + } + if !s.ends_with("\\&\"") { + if let Some(stripped) = s.strip_suffix("\"") { + s = stripped.to_string(); + } + } + + s +} + +// In-line macros parsing +impl MdocParser { + fn parse_rs_submacro(pair: Pair) -> Element { + // Parses (`%A`)[https://man.openbsd.org/mdoc#_A]: + // `%A first_name ... last_name` + fn parse_a(pair: Pair) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes, + }) + } + + // Parses (`%B`)[https://man.openbsd.org/mdoc#_B]: + // `%B title` + fn parse_b(pair: Pair) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes, + }) + } + + // Parses (`%C`)[https://man.openbsd.org/mdoc#_C]: + // `%C location` + fn parse_c(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes, + }) + } + + // Parses (`%D`)[https://man.openbsd.org/mdoc#_D]: + // `%D [month day,] year` + fn parse_d(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes, + }) + } + + // Parses (`%I`)[https://man.openbsd.org/mdoc#_I]: + // `%I name` + fn parse_i(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes, + }) + } + + // Parses (`%J`)[https://man.openbsd.org/mdoc#_J]: + // `%J name` + fn parse_j(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes, + }) + } + + // Parses (`%N`)[https://man.openbsd.org/mdoc#_N]: + // `%N number` + fn parse_n(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes, + }) + } + + // Parses (`%O`)[https://man.openbsd.org/mdoc#_O]: + // `%O line` + fn parse_o(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes, + }) + } + + // Parses (`%P`)[https://man.openbsd.org/mdoc#_P]: + // `%P number` + fn parse_p(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::P, + nodes, + }) + } + + // Parses (`%Q`)[https://man.openbsd.org/mdoc#_Q]: + // `%Q name` + fn parse_q(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes, + }) + } + + // Parses (`%R`)[https://man.openbsd.org/mdoc#_R]: + // `%R name` + fn parse_r(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes, + }) + } + + // Parses (`%T`)[https://man.openbsd.org/mdoc#_T]: + // `%T title` + fn parse_t(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes, + }) + } + + // Parses (`%U`)[https://man.openbsd.org/mdoc#_U]: + // `%U protocol://path` + fn parse_u(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes, + }) + } + + // Parses (`%V`)[https://man.openbsd.org/mdoc#_V]: + // `%V number` + fn parse_v(pair: Pair<'_, Rule>) -> Element { + let nodes = pair + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .map(MdocParser::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes, + }) + } + + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::a => parse_a(pair), + Rule::b => parse_b(pair), + Rule::c => parse_c(pair), + Rule::d => parse_d(pair), + Rule::i => parse_i(pair), + Rule::j => parse_j(pair), + Rule::n => parse_n(pair), + Rule::o => parse_o(pair), + Rule::p => parse_p(pair), + Rule::q => parse_q(pair), + Rule::r => parse_r(pair), + Rule::t => parse_t(pair), + Rule::u => parse_u(pair), + Rule::v => parse_v(pair), + _ => unreachable!(), + } + } + + fn process_delimiters(inner: &[Pair], mut i: usize, rule: Rule) -> (Vec, usize) { + let mut nodes = Vec::new(); + while i < inner.len() && inner[i].as_rule() == rule { + nodes.push(MdocParser::parse_element(inner[i].clone())); + i += 1; + } + (nodes, i) + } + + fn parse_text_production(pair: Pair) -> Element { + fn parse_x_args( + pair: Pair, + macro_value: Macro, + format: F, + format_default: D, + ) -> Element + where + F: Fn(&str) -> String, + D: Fn() -> String, + { + let inner: Vec<_> = pair.into_inner().collect(); + + if inner.is_empty() { + return Element::Macro(MacroNode { + mdoc_macro: macro_value, + nodes: vec![Element::Text(format_default())], + }); + } + + let mut nodes = Vec::new(); + let mut i = 0; + + // Process opening delimiters. + let (open_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::opening_delimiter); + nodes.extend(open_nodes); + i = new_i; + + // Process the middle argument if it exists. + if i < inner.len() { + match inner[i].as_rule() { + Rule::text_arg => { + nodes.push(Element::Text(format(inner[i].as_str()))); + i += 1; + } + Rule::closing_delimiter => { + nodes.push(Element::Text(format_default())); + nodes.push(Element::Text(inner[i].as_str().to_string())); + i += 1; + } + _ => unreachable!(), + } + } + + // Process closing delimiters. + let (close_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::closing_delimiter); + nodes.extend(close_nodes); + + i = new_i; + while i < inner.len() { + nodes.push(MdocParser::parse_element(inner[i].clone())); + i += 1; + } + + Element::Macro(MacroNode { + mdoc_macro: macro_value, + nodes, + }) + } + + // Parses (`At`)[https://man.openbsd.org/mdoc#At]: + // `At [version]` + fn parse_at(pair: Pair) -> Element { + let inner: Vec<_> = pair.into_inner().collect(); + + if inner.is_empty() { + return Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![Element::Text(AtType::default().to_string())], + }); + } + + let mut i = 0; + let mut nodes = Vec::new(); + + let (open_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::opening_delimiter); + nodes.extend(open_nodes); + i = new_i; + + if i < inner.len() { + match inner[i].as_rule() { + Rule::text_arg => { + nodes.push(Element::Text(AtType::default().to_string())); + nodes.push(MdocParser::parse_element(inner[i].clone())); + i += 1; + } + Rule::at_type => { + nodes.push(Element::Text(AtType::from(inner[i].clone()).to_string())); + i += 1; + } + Rule::closing_delimiter => { + nodes.push(Element::Text(AtType::default().to_string())); + } + _ => unreachable!(), + } + } + + let (close_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::closing_delimiter); + nodes.extend(close_nodes); + + i = new_i; + while i < inner.len() { + nodes.push(MdocParser::parse_element(inner[i].clone())); + i += 1; + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes, + }) + } + + // Parses (`Bsx`)[https://man.openbsd.org/mdoc#Bsx]: + // `Bsx [version]` + fn parse_bsx(pair: Pair) -> Element { + parse_x_args(pair, Macro::Bsx, BsxType::format, BsxType::format_default) + } + + // Parses (`Bx`)[https://man.openbsd.org/mdoc#Bx]: + // `Bx [version [variant]]` + fn parse_bx(pair: Pair) -> Element { + let inner: Vec<_> = pair.into_inner().collect(); + + if inner.is_empty() { + return Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![Element::Text(BxType::format_default())], + }); + } + + let mut nodes = Vec::new(); + let mut i = 0; + + let (open_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::opening_delimiter); + nodes.extend(open_nodes); + i = new_i; + + if i < inner.len() { + match inner[i].as_rule() { + Rule::text_arg => { + let version = inner[i].as_str(); + + i += 1; + + let variant = match i < inner.len() && inner[i].as_rule() == Rule::text_arg + { + true => { + let res = Some(inner[i].as_str()); + i += 1; + res + } + false => None, + }; + + nodes.push(Element::Text(BxType::format(version, variant))); + } + Rule::closing_delimiter => nodes.push(Element::Text(BxType::format_default())), + _ => unreachable!(), + } + } + + let (close_nodes, new_i) = + MdocParser::process_delimiters(&inner, i, Rule::closing_delimiter); + nodes.extend(close_nodes); + + i = new_i; + while i < inner.len() { + nodes.push(MdocParser::parse_element(inner[i].clone())); + i += 1; + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes, + }) + } + + // Parses (`Dx`)[https://man.openbsd.org/mdoc#Dx]: + // `Dx [version]` + fn parse_dx(pair: Pair) -> Element { + parse_x_args(pair, Macro::Dx, DxType::format, DxType::format_default) + } + + // Parses (`Fx`)[https://man.openbsd.org/mdoc#Fx]: + // `Fx [version]` + fn parse_fx(pair: Pair) -> Element { + parse_x_args(pair, Macro::Fx, FxType::format, FxType::format_default) + } + + // Parses (`Ex`)[https://man.openbsd.org/mdoc#Ex] + // .Ex VAR, ... + fn parse_ex(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ex, + nodes, + }) + } + + // Parses (`Nx`)[http://man.openbsd.org/mdoc#Nx]: + // `Nx [version]` + fn parse_nx(pair: Pair) -> Element { + parse_x_args(pair, Macro::Nx, NxType::format, NxType::format_default) + } + + // Parses (`Ox`)[https://man.openbsd.org/mdoc#Ox]: + // `Ox [version]` + fn parse_ox(pair: Pair) -> Element { + parse_x_args(pair, Macro::Ox, OxType::format, OxType::format_default) + } + + // Parses (`St`)[https://man.openbsd.org/mdoc#St]: + // `St -abbreviation` + fn parse_st(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let st_type = StType::from(inner.next().unwrap()); + let nodes: Vec<_> = inner.map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::St(st_type), + nodes, + }) + } + + // Parses (`Rv`)[https://man.openbsd.org/mdoc#Rv]: + // `Rv -std [function ...]` + fn parse_rv(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Rv, + nodes, + }) + } + + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::at => parse_at(pair), + Rule::bsx => parse_bsx(pair), + Rule::bx => parse_bx(pair), + Rule::dx => parse_dx(pair), + Rule::fx => parse_fx(pair), + Rule::ex => parse_ex(pair), + Rule::nx => parse_nx(pair), + Rule::ox => parse_ox(pair), + Rule::st => parse_st(pair), + Rule::rv => parse_rv(pair), + _ => unreachable!(), + } + } + + // Parses (`Ad`)[https://man.openbsd.org/mdoc#Ad]: + // `Ad address` + fn parse_ad(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes, + }) + } + + // Parses (`An`)[https://man.openbsd.org/mdoc#An]: + // `An -split | -nosplit | first_name ... last_name` + fn parse_an(pair: Pair) -> Element { + let an_arg = pair.into_inner().next().unwrap(); + let (author_name_type, nodes) = match an_arg.as_rule() { + Rule::an_split => (AnType::Split, vec![]), + Rule::an_no_split => (AnType::NoSplit, vec![]), + Rule::an_name => ( + AnType::Name, + an_arg.into_inner().map(Self::parse_element).collect(), + ), + _ => unreachable!(), + }; + + Element::Macro(MacroNode { + mdoc_macro: Macro::An { author_name_type }, + nodes, + }) + } + + // Parses (`Ap`)[https://man.openbsd.org/mdoc#Ap]: + // `Ap` + fn parse_ap(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ap, + nodes, + }) + } + + // Parses (`Ar`)[https://man.openbsd.org/mdoc#Ar]: + // `Ar [placeholder ...]` + fn parse_ar(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes, + }) + } + + // Parses (`Bt`)[https://man.openbsd.org/mdoc#Bt]: + // `Bt` + fn parse_bt(_pair: Pair) -> Element { + Element::Macro(MacroNode { + mdoc_macro: Macro::Bt, + nodes: vec![], + }) + } + + // Parses (`Cd`)[https://man.openbsd.org/mdoc#Cd]: + // `Cd line` + fn parse_cd(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Cd, + nodes, + }) + } + + // Parses (`Cd`)[https://man.openbsd.org/mdoc#Cm]: + // `Cm keyword ...` + fn parse_cm(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes, + }) + } + + // Parses (`Db`)[https://man.openbsd.org/mdoc#Db] + // Obsolete + fn parse_db(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Db, + nodes, + }) + } + + // Parses (`Dd`)[https://man.openbsd.org/mdoc#Dd] + // `Dd [date]` + fn parse_dd(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let nodes = match inner.next() { + Some(line) => vec![Element::Text(line.as_str().to_string())], + None => Vec::new(), + }; + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dd, + nodes, + }) + } + + // Parses (`Dt`)[https://man.openbsd.org/mdoc#Dt] + fn parse_dt(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let (title, section) = if let Some(arg) = inner.next() { + if matches!(arg.as_rule(), Rule::title) { + let title = Some(arg.as_str().to_string()); + let section = inner.next().unwrap().as_str().to_string(); + + (title, section) + } else { + let section = arg.as_str().to_string(); + + (None, section) + } + } else { + unreachable!() + }; + + let arch = inner.next().map(|arch| arch.as_str().trim().to_string()); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dt { + title, + section, + arch, + }, + nodes: vec![], + }) + } + + // Parses (`Dv`)[https://man.openbsd.org/mdoc#Dv] + fn parse_dv(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes, + }) + } + + // Parses (`Em`)[https://man.openbsd.org/mdoc#Em] + // .Em word ... + fn parse_em(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes, + }) + } + + // Parses (`Er`)[https://man.openbsd.org/mdoc#Er] + // .Er CONSTANT ... + fn parse_er(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Er, + nodes, + }) + } + + // Parses (`Es`)[https://man.openbsd.org/mdoc#Es] + // .Es opening_delimiter closing_delimiter + fn parse_es(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + + let opening_delimiter = inner_pairs + .next() + .unwrap() + .as_str() + .parse::() + .unwrap(); + let closing_delimiter = inner_pairs + .next() + .unwrap() + .as_str() + .parse::() + .expect("Macro Es expected closing delimiter as the second argument"); + + let nodes = inner_pairs.map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Es { + opening_delimiter, + closing_delimiter, + }, + nodes, + }) + } + + // Parses (`Ev`)[https://man.openbsd.org/mdoc#Ev] + // .Ev VAR, ... + fn parse_ev(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes, + }) + } + + // Parses (`Fa`)[https://man.openbsd.org/mdoc#Fa] + // .Fa [args] + fn parse_fa(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes, + }) + } + + // Parses (`Fd`)[https://man.openbsd.org/mdoc#Fd] + // .Fd directive [args] + fn parse_fd(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let directive = inner.next().unwrap().as_str().to_string(); + + let mut args = vec![]; + + for arg in inner { + args.push(arg.as_str().to_string()); + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fd { + directive, + arguments: args, + }, + nodes: vec![], + }) + } + + // Parses (`Fl`)[https://man.openbsd.org/mdoc#Fl] + fn parse_fl(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes, + }) + } + + // Parses (`Fn`)[https://man.openbsd.org/mdoc#Fn] + fn parse_fn(pair: Pair) -> Element { + let mut inner_nodes = pair.into_inner(); + let mut funcname = String::new(); + let arg = inner_nodes.next().unwrap(); + + match arg.as_rule() { + Rule::opening_delimiter => { + funcname.push_str(arg.as_str()); + let name = inner_nodes.next().unwrap(); + funcname.push_str(name.as_str()); + } + Rule::text_arg => funcname.push_str(arg.as_str()), + _ => unreachable!(), + }; + + let nodes = inner_nodes + .map(|n| { + if n.as_rule() == Rule::text_arg { + return Element::Text(trim_quotes(n.as_str().to_string())); + } + Self::parse_element(n) + }) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { funcname }, + nodes, + }) + } + + // Parses (`Fr`)[https://man.openbsd.org/mdoc#Fr] + // Obsolete + // .Fr num + fn parse_fr(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Fr, + nodes, + }) + } + + // Parses (`Ft`)[https://man.openbsd.org/mdoc#Ft] + fn parse_ft(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes, + }) + } + + // Parses (`Hf`)[https://man.openbsd.org/mdoc#Hf] + // .Hf filename + fn parse_hf(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Hf, + nodes, + }) + } + + // Parses (`Ic`)[https://man.openbsd.org/mdoc#Ic] + // .Ic keyword + fn parse_ic(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ic, + nodes, + }) + } + + // Parses (`In`)[https://man.openbsd.org/mdoc#In] + // .In filename + fn parse_in(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + // let mut filename = String::new(); + // let mut nodes = Vec::new(); + let arg = inner_pairs.next().unwrap(); + + let filename = match arg.as_rule() { + Rule::opening_delimiter => { + // nodes.push(Element::Text(arg.as_str().to_string())); + let name = inner_pairs.next().unwrap().as_str(); + // filename.push_str(name); + format!("{}{}", arg.as_str(), name) + } + Rule::word => arg.as_str().to_string(), + _ => unreachable!(), + }; + + // let iter = inner_pairs.map(Self::parse_element); + // nodes.extend(iter); + let nodes = inner_pairs.map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::In { filename }, + nodes, + }) + } + + // Parses (`Lb`)[https://man.openbsd.org/mdoc#Lb] + // .Lb libname + fn parse_lb(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + let mut lib_name = String::new(); + let mut nodes = Vec::new(); + let arg = inner_pairs.next().unwrap(); + + match arg.as_rule() { + Rule::opening_delimiter => { + nodes.push(Element::Text(arg.as_str().to_string())); + let name = inner_pairs.next().unwrap().as_str(); + lib_name.push_str(name); + } + Rule::word => lib_name.push_str(arg.as_str()), + _ => unreachable!(), + } + + if let Some(del) = inner_pairs.next() { + nodes.push(Element::Text(del.as_str().to_string())); + } + + Element::Macro(MacroNode { + mdoc_macro: Macro::Lb { lib_name }, + nodes, + }) + } + + // Parses (`Li`)[https://man.openbsd.org/mdoc#Li] + // .Li word ... + fn parse_li(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Li, + nodes, + }) + } + + // Parses (`Lk`)[https://man.openbsd.org/mdoc#Lk] + // .Lk link [display_name] + fn parse_lk(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let uri = inner.next().unwrap().as_str().to_string(); + let nodes = inner.map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Lk { uri }, + nodes, + }) + } + + // Parses (`Lp`)[https://man.openbsd.org/mdoc#Lp] + // Deprecated + fn parse_lp(_pair: Pair) -> Element { + Element::Macro(MacroNode { + mdoc_macro: Macro::Lp, + nodes: vec![], + }) + } + + // --------------------------------------------------------------------------- + + // Parses (`Ms`)[https://man.openbsd.org/mdoc#Ms]: + // `Ms name` + fn parse_ms(pair: Pair) -> Element { + let nodes = pair + .into_inner() + .take_while(|p| p.as_rule() == Rule::text_arg) + .map(Self::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ms, + nodes, + }) + } + + // Parses (`Mt`)[https://man.openbsd.org/mdoc#Mt]: + // `Mt localpart@domain` + fn parse_mt(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Mt, + nodes, + }) + } + + // Parses (`No`)[https://man.openbsd.org/mdoc#No]: + // `No word ...` + + fn parse_no(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::No, + nodes, + }) + } + + // Parses (`Ns`)[https://man.openbsd.org/mdoc#Ns]: + // `Ns` + fn parse_ns(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ns, + nodes, + }) + } + + // Parses (`Os`)[https://man.openbsd.org/mdoc#Os]: + // `Os [footer text]` + fn parse_os(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Os, + nodes, + }) + } + + // Parses (`Ot`)[https://man.openbsd.org/mdoc#Ot]: + // `Ot functype` + fn parse_ot(pair: Pair) -> Element { + let nodes = pair + .into_inner() + .take_while(|p| p.as_rule() == Rule::text_arg) + .map(Self::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes, + }) + } + + // Parses (`Pa`)[https://man.openbsd.org/mdoc#Pa]: + // `Pa name ...` + fn parse_pa(pair: Pair) -> Element { + let nodes = pair + .into_inner() + .take_while(|p| p.as_rule() == Rule::text_arg) + .map(Self::parse_element) + .collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Pa, + nodes, + }) + } + + // Parses (`Pf`)[https://man.openbsd.org/mdoc#Pf]: + // `Pf prefix macro [argument ...]` + fn parse_pf(pair: Pair) -> Element { + let mut inner_pairs = pair.into_inner(); + + let prefix = inner_pairs.next().unwrap().as_str().to_string(); + + let nodes = inner_pairs.map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Pf { prefix }, + nodes, + }) + } + + // Parses (`Pp`)[https://man.openbsd.org/mdoc#Pp]: + // `Pp` + fn parse_pp(pair: Pair) -> Element { + let nodes = pair.into_inner().map(MdocParser::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Pp, + nodes, + }) + } + + // Parses (`Sm`)[https://man.openbsd.org/mdoc#Sm]: + // `Sm [on | off]` + fn parse_sm(pair: Pair) -> Element { + fn parse_spacing_mode(pair: Pair) -> SmMode { + match pair.as_rule() { + Rule::sm_on => SmMode::On, + Rule::sm_off => SmMode::Off, + _ => unreachable!(), + } + } + + let mut inner = pair.into_inner(); + + let spacing_mode = if let Some(sm_arg) = inner.next() { + match sm_arg.as_rule() { + Rule::spacing_mode => { + let sm_arg = sm_arg.into_inner().next().unwrap(); + Some(parse_spacing_mode(sm_arg)) + } + _ => None, + } + } else { + None + }; + + let nodes = inner.map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sm(spacing_mode), + nodes, + }) + } + + // Parses (`Sx`)[https://man.openbsd.org/mdoc#Sx]: + // `Sx Title line` + fn parse_sx(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sx, + nodes, + }) + } + + // Parses (`Sy`)[https://man.openbsd.org/mdoc#Sy]: + // `Sy word ...` + fn parse_sy(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Sy, + nodes, + }) + } + + // Parses (`Tg`)[https://man.openbsd.org/mdoc#Tg]: + // `Tg [term]` + fn parse_tg(pair: Pair) -> Element { + let mut nodes = pair.into_inner().map(Self::parse_element); + + let term = match nodes.next() { + Some(Element::Text(term)) => { + if term.is_empty() { + None + } else { + Some(term) + } + } + None => None, + _ => unreachable!(), + }; + + Element::Macro(MacroNode { + mdoc_macro: Macro::Tg { term }, + nodes: vec![], + }) + } + + // Parses (`Tn`)[https://man.openbsd.org/mdoc#Tn]: + // `Tn word ...` + + fn parse_tn(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Tn, + nodes, + }) + } + + // Parses (`Ud`)[https://man.openbsd.org/mdoc#Ud]: + // `Ud` + fn parse_ud(_pair: Pair) -> Element { + Element::Macro(MacroNode { + mdoc_macro: Macro::Ud, + nodes: vec![], + }) + } + + // Parses (`Ux`)[https://man.openbsd.org/mdoc#Ux]: + // `Ux` + fn parse_ux(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes, + }) + } + + // Parses (`Va`)[https://man.openbsd.org/mdoc#Va]: + // `Va [type] identifier ...` + + fn parse_va(pair: Pair) -> Element { + let nodes = pair.into_inner().map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Va, + nodes, + }) + } + + // Parses (`Xr`)[https://man.openbsd.org/mdoc#Xr]: + // `Xr name section` + fn parse_xr(pair: Pair) -> Element { + let mut inner = pair.into_inner(); + + let name = inner.next().unwrap(); + let name = match name.as_rule() { + Rule::text_arg => name.as_str().to_string(), + _ => unreachable!(), + }; + + let section = inner.next().unwrap(); + let section = match section.as_rule() { + Rule::text_arg => section.as_str().to_string(), + _ => unreachable!(), + }; + + let nodes = inner.map(Self::parse_element).collect(); + + Element::Macro(MacroNode { + mdoc_macro: Macro::Xr { name, section }, + nodes, + }) + } + + fn parse_inline(pair: Pair) -> Element { + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::rs_submacro => Self::parse_rs_submacro(pair), + Rule::text_production => Self::parse_text_production(pair), + Rule::ad => Self::parse_ad(pair), + Rule::an => Self::parse_an(pair), + Rule::ap => Self::parse_ap(pair), + Rule::ar => Self::parse_ar(pair), + Rule::bt => Self::parse_bt(pair), + Rule::cd => Self::parse_cd(pair), + Rule::cm => Self::parse_cm(pair), + Rule::db => Self::parse_db(pair), + Rule::dd => Self::parse_dd(pair), + Rule::dt => Self::parse_dt(pair), + Rule::dv => Self::parse_dv(pair), + Rule::em => Self::parse_em(pair), + Rule::er => Self::parse_er(pair), + Rule::es => Self::parse_es(pair), + Rule::ev => Self::parse_ev(pair), + Rule::fa => Self::parse_fa(pair), + Rule::fd => Self::parse_fd(pair), + Rule::fl => Self::parse_fl(pair), + Rule::Fn => Self::parse_fn(pair), + Rule::fr => Self::parse_fr(pair), + Rule::ft => Self::parse_ft(pair), + Rule::hf => Self::parse_hf(pair), + Rule::ic => Self::parse_ic(pair), + Rule::In => Self::parse_in(pair), + Rule::lb => Self::parse_lb(pair), + Rule::li => Self::parse_li(pair), + Rule::lk => Self::parse_lk(pair), + Rule::lp => Self::parse_lp(pair), + Rule::ms => Self::parse_ms(pair), + Rule::mt => Self::parse_mt(pair), + Rule::no => Self::parse_no(pair), + Rule::ns => Self::parse_ns(pair), + Rule::os => Self::parse_os(pair), + Rule::ot => Self::parse_ot(pair), + Rule::pa => Self::parse_pa(pair), + Rule::pf => Self::parse_pf(pair), + Rule::pp => Self::parse_pp(pair), + Rule::sm => Self::parse_sm(pair), + Rule::sx => Self::parse_sx(pair), + Rule::sy => Self::parse_sy(pair), + Rule::tg => Self::parse_tg(pair), + Rule::tn => Self::parse_tn(pair), + Rule::ud => Self::parse_ud(pair), + Rule::ux => Self::parse_ux(pair), + Rule::va => Self::parse_va(pair), + Rule::xr => Self::parse_xr(pair), + _ => unreachable!(), + } + } +} + +#[cfg(test)] +mod tests { + use crate::man_util::parser::*; + + #[test] + fn text_line() { + let content = "Line 1\nLine 2\nLine 3\n"; + let elements = vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + Element::Text("Line 3".to_string()), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + mod block_full_explicit { + use std::collections::HashMap; + + use crate::man_util::parser::*; + + #[test] + fn bd() { + let content = ".Bd -literal -offset indent -compact\nLine 1\nLine 2\n.Ed"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: BdType::Literal, + offset: Some(OffsetType::Indent), + compact: true, + }, + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bd_no_closing_macro() { + let input = ".Bd -literal -offset indent -compact\nLine 1\nLine 2\n"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bd_foreign_closing_macros() { + let closing_macros = vec![".Ef", ".Ek", ".El"]; + let content = ".Bd -literal -offset indent -compact\nLine 1\nLine 2\n"; + + for closing_macro in closing_macros { + let input = format!("{content}.{closing_macro}"); + assert_eq!(MdocParser::parse_mdoc(&input).unwrap().elements, vec![]); + } + } + + #[test] + fn bd_no_body() { + let content = ".Bd -literal\n.Ed"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: BdType::Literal, + offset: None, + compact: false, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bd_type() { + let mut bd_types: HashMap<&str, BdType> = Default::default(); + bd_types.insert("-centered", BdType::Centered); + bd_types.insert("-filled", BdType::Filled); + bd_types.insert("-literal", BdType::Literal); + bd_types.insert("-ragged", BdType::Ragged); + bd_types.insert("-unfilled", BdType::Unfilled); + + for (str_type, enum_type) in bd_types { + let content = format!(".Bd {str_type}\n.Ed"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: enum_type, + offset: None, + compact: false, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bd type: {str_type}"); + } + } + + #[test] + fn bd_offset() { + let mut offset_types: HashMap<&str, OffsetType> = Default::default(); + offset_types.insert("indent", OffsetType::Indent); + offset_types.insert("indent-two", OffsetType::IndentTwo); + offset_types.insert("left", OffsetType::Left); + offset_types.insert("right", OffsetType::Right); + + for (str_type, enum_type) in offset_types { + let content = format!(".Bd -literal -offset {str_type}\n.Ed"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: BdType::Literal, + offset: Some(enum_type), + compact: false, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bd offset: {str_type}"); + } + } + + #[test] + fn bd_invalid_offset() { + let input = ".Bd -literal -offset invalid_offset\n.Ed"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: BdType::Literal, + offset: Some(OffsetType::Indent), + compact: false, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bd_compact() { + let content = ".Bd -literal -compact\n.Ed"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bd { + block_type: BdType::Literal, + offset: None, + compact: true, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bd_not_parsed() { + let input = ".Bd -literal -compact Ad addr1\n.Ed"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bd_not_callable() { + let input = ".Ad addr1 Bd -literal\n.Ed"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Bd".to_string()), + Element::Text("-literal".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bf() { + let content = ".Bf -emphasis\nLine 1\nLine 2\n.Ef"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bf(BfType::Emphasis), + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bf_no_closing_macro() { + let input = ".Bf -emphasis\nLine 1\nLine 2\n"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bf_foreign_closing_macros() { + let closing_macros = vec![".Ed", ".Ek", ".El"]; + let content = ".Bf -emphasis\nLine 1\nLine 2\n"; + + for closing_macro in closing_macros { + let input = format!("{content}.{closing_macro}"); + assert_eq!(MdocParser::parse_mdoc(&input).unwrap().elements, vec![]); + } + } + + #[test] + fn bf_type() { + let mut bf_types: HashMap<&str, BfType> = Default::default(); + bf_types.insert("-emphasis", BfType::Emphasis); + bf_types.insert("Em", BfType::Emphasis); + bf_types.insert("-literal", BfType::Literal); + bf_types.insert("Li", BfType::Literal); + bf_types.insert("-symbolic", BfType::Symbolic); + bf_types.insert("Sy", BfType::Symbolic); + + for (str_type, enum_type) in bf_types { + let content = format!(".Bf {str_type}\n.Ef"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bf(enum_type), + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bf type: {str_type}"); + } + } + + #[test] + fn bf_invalid_type() { + let input = ".Bf -invalid\n.Ef"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bf_not_parsed() { + let input = ".Bf Em Ad addr1\n.Ef"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bf_not_callable() { + let input = ".Ad addr1 Bf Em\n.Ef"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Bf".to_string()), + Element::Text("Em".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bk() { + let content = ".Bk -words\nLine 1\nLine 2\n.Ek"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bk, + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bk_no_body() { + let content = ".Bk -words\n.Ek"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bk, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bk_no_words() { + let input = ".Bk\n.Ek"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bk_not_parsed() { + let content = ".Bk -words Ad\n.Ek"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bk, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bk_not_callable() { + let input = ".Ad addr1 Bk -words\n.Ek"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Bk".to_string()), + Element::Text("-words".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl() { + let content = r#".Bl -bullet -width 15 -offset indent-two -compact col1 col2 col3 +.It Line 1 +.It Line 2 +.El"#; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: Some(15), + offset: Some(OffsetType::IndentTwo), + compact: true, + columns: vec!["col1".to_string(), "col2".to_string(), "col3".to_string()], + }, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: vec![ + Element::Text("Line".to_string()), + Element::Text("2".to_string()), + ], + }, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_no_closing_macro() { + let input = ".Bl -bullet\nLine 1\nLine 2\n"; + assert_eq!(MdocParser::parse_mdoc(input).unwrap().elements, vec![]); + } + + #[test] + fn bl_foreign_closing_macros() { + let closing_macros = vec![".Ed", ".Ef", ".Ek"]; + let content = ".Bl -bullet\nLine 1\nLine 2\n"; + + for closing_macro in closing_macros { + let input = format!("{content}.{closing_macro}"); + assert_eq!(MdocParser::parse_mdoc(&input).unwrap().elements, vec![]); + } + } + + #[test] + fn bl_no_body() { + let content = ".Bl -bullet\n.El"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_types() { + let mut macro_types: HashMap<&str, BlType> = Default::default(); + macro_types.insert("-bullet", BlType::Bullet); + macro_types.insert("-column", BlType::Column); + macro_types.insert("-dash", BlType::Dash); + macro_types.insert("-hyphen", BlType::Dash); + macro_types.insert("-diag", BlType::Diag); + macro_types.insert("-enum", BlType::Enum); + macro_types.insert("-hang", BlType::Hang); + macro_types.insert("-inset", BlType::Inset); + macro_types.insert("-item", BlType::Item); + macro_types.insert("-ohang", BlType::Ohang); + macro_types.insert("-tag", BlType::Tag); + + for (str_type, enum_type) in macro_types { + let content = format!(".Bl {str_type}\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: enum_type, + width: None, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bl type: {str_type}"); + } + } + + #[test] + fn bl_width() { + let mut width_types: HashMap<&str, Option> = Default::default(); + width_types.insert("15", Some(15)); + width_types.insert("300", None); + width_types.insert("left", Some(4)); + + for (str_type, width_result) in width_types { + let content = format!(".Bl -bullet -width {str_type}\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: width_result, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bl width: {str_type}"); + } + } + + #[test] + fn bl_offset() { + let mut offset_types: HashMap<&str, OffsetType> = Default::default(); + offset_types.insert("indent", OffsetType::Indent); + offset_types.insert("indent-two", OffsetType::IndentTwo); + offset_types.insert("left", OffsetType::Left); + offset_types.insert("right", OffsetType::Right); + + for (str_type, enum_type) in offset_types { + let content = format!(".Bl -bullet -offset {str_type}\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: Some(enum_type), + compact: false, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bl offset: {str_type}"); + } + } + + #[test] + fn bl_invalid_offset() { + // Because of invalid offset, it is considered as column + let content = ".Bl -bullet -offset invalid_offset\n.El"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: Some(OffsetType::Indent), + compact: false, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_compact() { + let content = format!(".Bl -bullet -compact\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: None, + compact: true, + columns: vec![], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_columns() { + let content = format!(".Bl -bullet col1 col2 col3\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: None, + compact: false, + columns: vec!["col1".to_string(), "col2".to_string(), "col3".to_string()], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_parameters() { + let mut parameters_cases: HashMap< + &str, + (Option, Option, bool, Vec), + > = Default::default(); + parameters_cases.insert( + "-width 15 -offset indent-two -compact col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-width 15 -compact -offset indent-two col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-offset indent-two -width 15 -compact col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-offset indent-two -compact -width 15 col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-compact -width 15 -offset indent-two col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-compact -offset indent-two -width 15 col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-width 15 -offset indent-two col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + false, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-width 15 -compact col1 col2", + ( + Some(15), + None, + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-offset indent-two -width 15 col1 col2", + ( + Some(15), + Some(OffsetType::IndentTwo), + false, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-offset indent-two -compact col1 col2", + ( + None, + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-compact -offset indent-two col1 col2", + ( + None, + Some(OffsetType::IndentTwo), + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-compact -width 15 col1 col2", + ( + Some(15), + None, + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-width 15 col1 col2", + ( + Some(15), + None, + false, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-offset indent-two col1 col2", + ( + None, + Some(OffsetType::IndentTwo), + false, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert( + "-compact col1 col2", + ( + None, + None, + true, + vec!["col1".to_string(), "col2".to_string()], + ), + ); + parameters_cases.insert("-width 8 -compact", (Some(8), None, true, vec![])); + + for (input, output) in parameters_cases { + let (width, offset, compact, columns) = output; + let content = format!(".Bl -bullet {input}\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width, + offset, + compact, + columns, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bl parameters: {input}"); + } + } + + #[test] + fn bl_invalid_parameters() { + let mut parameters_cases: HashMap< + &str, + (Option, Option, bool, Vec<&str>), + > = Default::default(); + parameters_cases.insert( + "-width 15 -width 15 -offset indent", + ( + Some(15), + Some(OffsetType::Indent), + false, + "-width 15".split(" ").collect::>(), + ), + ); + parameters_cases.insert( + "-offset indent -offset indent -compact", + ( + None, + Some(OffsetType::Indent), + true, + "-offset indent".split(" ").collect::>(), + ), + ); + parameters_cases.insert( + "-width 15 word -width 15 -offset indent", + ( + Some(15), + Some(OffsetType::Indent), + false, + "word -width 15".split(" ").collect::>(), + ), + ); + parameters_cases.insert( + "-compact -width 15 -offset indent -width 15", + ( + Some(15), + Some(OffsetType::Indent), + true, + "-width 15".split(" ").collect::>(), + ), + ); + parameters_cases.insert( + "-compact -compact -width 15", + ( + Some(15), + None, + true, + "-compact".split(" ").collect::>(), + ), + ); + parameters_cases.insert( + "-compact word -width 15", + (Some(15), None, true, "word".split(" ").collect::>()), + ); + + for (input, output) in parameters_cases { + let (width, offset, compact, columns) = output; + let content = format!(".Bl -bullet {input}\n.El"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width, + offset, + compact, + columns: columns.iter().map(|s| s.to_string()).collect::>(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bl parameters: {input}"); + } + } + + #[test] + fn bl_not_parsed() { + // Callable macro as opaque text + let content = ".Bl -bullet Ad\n.El"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: None, + compact: false, + columns: vec!["Ad".to_string()], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bl_not_callable() { + let content = ".Ad addr1 Bl Em\n.El"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Bl".to_string()), + Element::Text("Em".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod block_full_implicit { + use crate::man_util::parser::*; + + #[test] + fn it_first_variant() { + let input = r#".Bl -hang +.It arg Ad addr1 +Some text +.It arg1 arg2 +.Ad addr +Some text +.El +"#; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Hang, + width: None, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ], + }, + nodes: vec![Element::Text("Some text".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + ], + }, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Text("Some text".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements) + } + + #[test] + fn it_second_variant() { + let input = r#".Bl -bullet +.It +Line +.It +.Ad addr Ad addr +.El +"#; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Bullet, + width: None, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::It { head: vec![] }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::It { head: vec![] }, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements) + } + + #[test] + fn it_column_variant() { + let input = r#".Bl -column +.It Em Command Ta Em External Ta Ad addr +.El"#; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bl { + list_type: BlType::Column, + width: None, + offset: None, + compact: false, + columns: vec![], + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::It { + head: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes: vec![Element::Text("Command".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ta, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes: vec![Element::Text("External".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ta, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + ], + }, + nodes: vec![], + })], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements) + } + + #[test] + fn nd() { + let content = ".Nd short description"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![ + Element::Text("short".to_string()), + Element::Text("description".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nd_with_line_whitespaces_and_tabs() { + let content = ".Nd short description\t \t"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![ + Element::Text("short".to_string()), + Element::Text("description".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nd_surrounded_by_text() { + let content = "Line 1\n.Nd short description\nLine 2\n"; + let elements = vec![ + Element::Text("Line 1".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![ + Element::Text("short".to_string()), + Element::Text("description".to_string()), + Element::Text("Line 2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nd_with_sh_closure() { + let content = ".Nd short description\nLine 1\nLine 2\n.Sh SECTION"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![ + Element::Text("short".to_string()), + Element::Text("description".to_string()), + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION".to_string(), + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nd_not_parsed() { + let content = ".Nd name Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![Element::Text("name".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh() { + let content = ".Sh SECTION +This is the SECTION section."; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION".to_string(), + }, + nodes: vec![Element::Text("This is the SECTION section.".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_with_multiple_lines() { + let content = ".Sh SECTION\nLine 1\nLine 2\nLine 3\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION".to_string(), + }, + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + Element::Text("Line 3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_without_title() { + assert_eq!( + MdocParser::parse_mdoc(".Sh\nLine 1\n").unwrap().elements, + vec![] + ); + } + + #[test] + fn sh_without_body() { + let content = ".Sh SECTION"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_title_line() { + let content = ".Sh TITLE LINE\nLine 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "TITLE LINE".to_string(), + }, + nodes: vec![Element::Text("Line 1".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_with_multiple_chapters() { + let content = ".Sh SECTION 1\nLine 1\n.Sh SECTION 2\nLine 2\n"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION 1".to_string(), + }, + nodes: vec![Element::Text("Line 1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION 2".to_string(), + }, + nodes: vec![Element::Text("Line 2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_name_with_nd() { + let content = ".Sh NAME\nLine 1\n.Nd short description"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "NAME".to_string(), + }, + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nd, + nodes: vec![ + Element::Text("short".to_string()), + Element::Text("description".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sh_parsed() { + // Although this macro is parsed, it should not consist of child + // node or it may not be linked with Sx. + let content = ".Sh SECTION Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION Ad addr1".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss() { + let content = ".Ss Options\nThese are the available options."; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Options".to_string(), + }, + nodes: vec![Element::Text( + "These are the available options.".to_string(), + )], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_with_multiple_lines() { + let content = ".Ss Options\nLine 1\nLine 2\nLine 3\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Options".to_string(), + }, + nodes: vec![ + Element::Text("Line 1".to_string()), + Element::Text("Line 2".to_string()), + Element::Text("Line 3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_without_title() { + assert_eq!( + MdocParser::parse_mdoc(".Ss\nLine 1").unwrap().elements, + vec![] + ); + } + + #[test] + fn ss_without_body() { + let content = ".Ss Options"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Options".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_title_line() { + let content = ".Ss TITLE LINE\nLine 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "TITLE LINE".to_string(), + }, + nodes: vec![Element::Text("Line 1".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_nested_in_sh() { + let content = ".Sh SECTION\n.Ss Subsection\nLine 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sh { + title: "SECTION".to_string(), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Subsection".to_string(), + }, + nodes: vec![Element::Text("Line 1".to_string())], + })], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_with_multiple_subchapters() { + let content = ".Ss Subchapter 1\nLine 1\n.Ss Subchapter 2\nLine 2\n"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Subchapter 1".to_string(), + }, + nodes: vec![Element::Text("Line 1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Subchapter 2".to_string(), + }, + nodes: vec![Element::Text("Line 2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ss_parsed() { + // Although this macro is parsed, it should not consist of child + // node or it may not be linked with Sx. + let content = ".Ss Subchapter Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ss { + title: "Subchapter Ad addr1".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod block_partial_implicit { + use crate::man_util::parser::*; + + #[test] + fn aq_empty() { + let content = ".Aq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Aq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn aq_text_line() { + let content = ".Aq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Aq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn aq_parsed() { + let content = ".Aq Text Ad addr1 addr2 Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Aq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn aq_callable() { + let content = ".Ad addr1 Aq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Aq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bq_empty() { + let content = ".Bq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bq_text_line() { + let content = ".Bq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bq_parsed() { + let content = ".Bq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bq_callable() { + let content = ".Ad addr1 Bq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn brq_empty() { + let content = ".Brq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Brq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn brq_text_line() { + let content = ".Brq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Brq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn brq_parsed() { + let content = ".Brq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Brq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn brq_callable() { + let content = ".Ad addr1 Brq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d1_empty() { + let content = ".D1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D1, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d1_text_line() { + let content = ".D1 Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D1, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d1_parsed() { + let content = ".D1 Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D1, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d1_not_callable() { + let content = ".Ad addr1 D1 addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("D1".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dl_empty() { + let content = ".Dl"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dl, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dl_text_line() { + let content = ".Dl Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dl, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dl_parsed() { + let content = ".Dl Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dl, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dl_not_callable() { + let content = ".Ad addr1 Dl addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Dl".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dq_empty() { + let content = ".Dq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dq_text_line() { + let content = ".Dq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dq_parsed() { + let content = ".Dq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dq_callable() { + let content = ".Ad addr1 Dq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn en() { + let content = ".En word1 word2 word3"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::En, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + Element::Text("word3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn en_no_words() { + assert_eq!(MdocParser::parse_mdoc(".En").unwrap().elements, vec![]); + } + + #[test] + fn en_parsed() { + let content = ".En Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::En, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn en_callable() { + let content = ".Ad addr1 En addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::En, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn op_empty() { + let content = ".Op"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Op, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn op_text_line() { + let content = ".Op Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Op, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn op_parsed() { + let content = ".Op Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Op, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn op_callable() { + let content = ".Ad addr1 Op addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Op, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pq_empty() { + let content = ".Pq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pq_text_line() { + let content = ".Pq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pq_parsed() { + let content = ".Pq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pq_callable() { + let content = ".Ad addr1 Pq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ql_empty() { + let content = ".Ql"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ql, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ql_text_line() { + let content = ".Ql Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ql, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ql_parsed() { + let content = ".Ql Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ql, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ql_callable() { + let content = ".Ad addr1 Ql addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ql, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qq_empty() { + let content = ".Qq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Qq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qq_text_line() { + let content = ".Qq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Qq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qq_parsed() { + let content = ".Qq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Qq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qq_callable() { + let content = ".Ad addr1 Qq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sq_empty() { + let content = ".Sq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sq, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sq_text_line() { + let content = ".Sq Line 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sq, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sq_parsed() { + let content = ".Sq Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sq, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sq_callable() { + let content = ".Ad addr1 Sq addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sq, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn vt() { + let content = ".Vt type some identifier"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Vt, + nodes: vec![ + Element::Text("type".to_string()), + Element::Text("some".to_string()), + Element::Text("identifier".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn vt_empty() { + assert_eq!(MdocParser::parse_mdoc(".Vt").unwrap().elements, vec![]); + } + + #[test] + fn vt_only_type() { + let content = ".Vt type"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Vt, + nodes: vec![Element::Text("type".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn vt_parsed() { + let content = ".Vt Text Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Vt, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn vt_callable() { + let content = ".Ad addr1 Vt addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Vt, + nodes: vec![Element::Text("addr2".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod block_partial_explicit { + use crate::man_util::parser::*; + + #[test] + fn ao() { + let input = r#".Ao +Line1 +Line2 +.Ac +.Ao El1 El2 El3 Ac +.Ao arg Ac +.Ao +.Dv ARG +.Ac +.Ao arg +.Dv ARG Ac +.Ao Dv ARG Ac +.Ao +Line +.Ac +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ao_not_args() { + let input = r#".Ao Ac +.Ao +.Ac"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + #[test] + fn ao_callable() { + let input = ".Ao Ao arg Ac\n.Ac"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bo() { + let input = r#".Bo +Line1 +Line2 +.Bc +.Bo El1 El2 El3 Bc +.Bo arg Bc +.Bo +.Dv ARG +.Bc +.Bo arg +.Dv ARG Bc +.Bo Dv ARG Bc +.Bo +Line +.Bc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bo_not_args() { + let input = r#".Bo Bc +.Bo +.Bc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bo_callable() { + let input = ".Bo Bo arg Bc\n.Bc"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bro() { + let input = r#".Bro +Line1 +Line2 +.Brc +.Bro El1 El2 El3 Brc +.Bro arg Brc +.Bro +.Dv ARG +.Brc +.Bro arg +.Dv ARG Brc +.Bro Dv ARG Brc +.Bro +Line +.Brc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bro_not_args() { + let input = r#".Bro Brc +.Bro +.Brc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bro_callable() { + let input = ".Bo Bro arg Brc\n.Bc"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bro, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Brc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r#do() { + let input = r#".Do +Line1 +Line2 +.Dc +.Do El1 El2 El3 Dc +.Do arg Dc +.Do +.Dv ARG +.Dc +.Do arg +.Dv ARG Dc +.Do Dv ARG Dc +.Do +Line +.Dc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn do_not_args() { + let input = r#".Do Dc +.Do +.Dc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn do_callable() { + let input = ".Bo Do arg Dc\n.Bc"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Do, + nodes: vec![ + Element::Text("arg".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn eo() { + let input = r#".Eo +Line +.Ec +.Eo [ +Line +.Ec ] +.Eo +Line +.Ec . +.Eo [ arg1 arg2 Ec ] +.Eo arg1 arg2 Ec +.Eo arg1 arg2 Ec . +.Eo [ arg1 +.Ec ] +.Eo +Line +.Ec +"#; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: Some('.'), + }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: Some('.'), + }, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Text("arg1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Text("Line".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn eo_not_args() { + let input = ".Eo Ec"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn eo_parsed() { + let input = r#".Eo +.Dv ARG +.Ec +.Eo +.Dv ARG +.Ec . +.Eo [ +.Dv ARG +.Ec ] +.Eo Dv ARG +.Ec +.Eo Dv ARG +.Ec . +.Eo [ Dv ARG +.Ec ] +.Eo Dv ARG Ec +.Eo Dv ARG Ec . +.Eo [ Dv ARG Ec ] +.Eo +Text +.Ec +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: Some('.'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: Some('.'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: Some('.'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("ARG".to_string())], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: None, + closing_delimiter: None, + }, + nodes: vec![Element::Text("Text".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn eo_callable() { + let input = ".Bo Eo [ arg Ec ]\n.Bc"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Eo { + opening_delimiter: Some('['), + closing_delimiter: Some(']'), + }, + nodes: vec![Element::Text("arg".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bc, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fo() { + let input = r#".Fo funcname +Line +.Fc +.Fo funcname Fc +.Fo funcname arg1 +arg2 Fc +.Fc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { + funcname: "funcname".to_string(), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { + funcname: "funcname".to_string(), + }, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2 Fc".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fo_not_args() { + assert_eq!(MdocParser::parse_mdoc(".Fo.Fc").unwrap().elements, vec![]); + } + + #[test] + fn fo_not_parsed() { + let input = r#".Fo funcname Dv ARG +.Fc +"#; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("Dv ARG".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Oo ----------------------------------------------------------- + + #[test] + fn oo() { + let input = r#".Oo +Line1 +Line2 +.Oc +.Oo El1 El2 El3 Oc +.Oo +Line +.Oc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn oo_no_args() { + let input = r#".Oo Oc +.Oo +.Oc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn oo_parsed() { + let input = r#".Oo +.Ad addr +.Oc +.Oo Dv CONSTANT +.Oc +.Oo +Line +.Oc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn oo_called() { + let input = r#".Ao +.Oo +.Ad addr +.Oc +.Oo Dv CONSTANT +.Oc +.Oo +Line +.Oc +.Ac +"#; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ao, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Oc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ac, + nodes: vec![], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Po ----------------------------------------------------------- + + #[test] + fn po() { + let input = r#".Po +Line1 +Line2 +.Pc +.Po El1 El2 El3 Pc +.Po +Line +.Pc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn po_no_args() { + let input = r#".Po Pc +.Po +.Pc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn po_parsed() { + let input = r#".Po +.Ad addr +.Pc +.Po Dv CONSTANT +.Pc +.Po +Line +.Pc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Po, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Qo ----------------------------------------------------------- + + #[test] + fn qo() { + let input = r#".Qo +Line1 +Line2 +.Qc +.Qo El1 El2 El3 Qc +.Qo +Line +.Qc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qo_no_args() { + let input = r#".Qo Qc +.Qo +.Qc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn qo_parsed() { + let input = r#".Qo +.Ad addr +.Qc +.Qo Dv CONSTANT +.Qc +.Qo +Line +.Qc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Qc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Rs ----------------------------------------------------------- + + #[test] + fn rs() { + let input = r#".Rs +.%A John Doe +.%B Title Line Ad addr1 +.%D January 1, 1970 +.%U protocol://path +.Re +.Rs %A John Doe %B Title Line Ad addr1 %D January 1, 1970 %U protocol://path +.Re +.Rs %A John Doe +.%B Title Line Ad addr1 +.%D January 1, 1970 +.%U protocol://path +.Re"#; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Rs, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes: vec![Element::Text("protocol://path".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Rs, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Rs, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes: vec![Element::Text("protocol://path".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + + fn rs_wrong_args() { + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs +Line1 +Line2 +.Re +"# + ) + .unwrap() + .elements, + vec![] + ); + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs El1 El2 El3 +Re"# + ) + .unwrap() + .elements, + vec![] + ); + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs +arg Re"# + ) + .unwrap() + .elements, + vec![] + ); + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs +Line +.Re +"# + ) + .unwrap() + .elements, + vec![] + ); + } + + #[test] + fn rs_no_args() { + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs + .Re"# + ) + .unwrap() + .elements, + vec![] + ); + } + + #[test] + fn rs_not_parsed() { + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs +.%A John Doe +.%B Title Line Ad addr1 +.%D January 1, 1970 +.%U protocol://path +.Ad addr +.Re"# + ) + .unwrap() + .elements, + vec![] + ); + assert_eq!( + MdocParser::parse_mdoc( + r#".Rs %A John Doe +.%B Title Line Ad addr1 +.%D January 1, 1970 +.%U protocol://path +.Ad addr +.Re"# + ) + .unwrap() + .elements, + vec![] + ); + } + + #[test] + fn rs_submacro_sorting() { + let input = r#".Rs +.%O Optional information +.%D January 1, 1970 +.%C Location line +.%Q John Doe +.%P pp. 1-100 +.%U protocol://path +.%V Volume No. 1 +.%N Issue No. 1 +.%R Technical report No. 1 +.%J Journal Name Line +.%I John Doe +.%B Title Line +.%T Article title line +.%A John Doe +.Re"#; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Rs, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes: vec![ + Element::Text("Article".to_string()), + Element::Text("title".to_string()), + Element::Text("line".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes: vec![ + Element::Text("Journal".to_string()), + Element::Text("Name".to_string()), + Element::Text("Line".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes: vec![ + Element::Text("Technical".to_string()), + Element::Text("report".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes: vec![ + Element::Text("Issue".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes: vec![ + Element::Text("Volume".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes: vec![Element::Text("protocol://path".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::P, + nodes: vec![ + Element::Text("pp.".to_string()), + Element::Text("1-100".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes: vec![ + Element::Text("Location".to_string()), + Element::Text("line".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes: vec![ + Element::Text("Optional".to_string()), + Element::Text("information".to_string()), + ], + }), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .So ----------------------------------------------------------- + + #[test] + fn so() { + let input = r#".So +Line1 +Line2 +.Sc +.So El1 El2 El3 Sc +.So +Line +.Sc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn so_no_args() { + let input = r#".So Sc +.So +.Sc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn so_parsed() { + let input = r#".So +.Ad addr +.Sc +.So Dv CONSTANT +.Sc +.So +Line +.Sc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::So, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Xo ----------------------------------------------------------- + + #[test] + fn xo() { + let input = r#".Xo +Line1 +Line2 +.Xc +.Xo El1 El2 El3 Xc +.Xo +Line +.Xc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Text("Line1".to_string()), + Element::Text("Line2".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Text("El1".to_string()), + Element::Text("El2".to_string()), + Element::Text("El3".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn xo_no_args() { + let input = r#".Xo Xc +.Xo +.Xc"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + })], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + })], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn xo_parsed() { + let input = r#".Xo +.Ad addr +.Xc +.Xo Dv CONSTANT +.Xc +.Xo +Line +.Xc +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONSTANT".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xo, + nodes: vec![ + Element::Text("Line".to_string()), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xc, + nodes: vec![], + }), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod inline { + use crate::man_util::parser::*; + + mod rs_submacros { + use crate::man_util::parser::*; + + #[test] + fn a() { + let content = ".%A John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn a_with_whitespaces() { + let content = ".%A John \t Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn a_no_args() { + let content = ".%A"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn a_not_parsed() { + let content = ".%A John Doe Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn a_not_callable() { + let content = ".Ad addr1 %A John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%A".to_string()), + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn b() { + let content = ".%B Title Line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn b_with_whitespaces() { + let content = ".%B Title \t Line\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn b_no_args() { + let content = ".%B"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn b_not_parsed() { + let content = ".%B Title Line Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![ + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn b_not_callable() { + let content = ".Ad addr1 %B Title Line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%B".to_string()), + Element::Text("Title".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn c() { + let content = ".%C Location line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes: vec![ + Element::Text("Location".to_string()), + Element::Text("line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn c_with_whitespaces() { + let content = ".%C Location \t Line\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes: vec![ + Element::Text("Location".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn c_no_args() { + let content = ".%C"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn c_not_parsed() { + let content = ".%C Location Line Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes: vec![ + Element::Text("Location".to_string()), + Element::Text("Line".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn c_not_callable() { + let content = ".Ad addr1 %C Location Line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%C".to_string()), + Element::Text("Location".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d() { + let content = ".%D January 1, 1970"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d_with_whitespaces() { + let content = ".%D January \t 1, \t 1970\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d_no_month_day() { + let content = ".%D 1970"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![Element::Text("1970".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d_no_args() { + let content = ".%D"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d_not_parsed() { + let input = ".%D Ad 1, 1970"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::D, + nodes: vec![ + Element::Text("Ad".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn d_not_callable() { + let content = ".Ad addr1 %D January 1, 1970"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%D".to_string()), + Element::Text("January".to_string()), + Element::Text("1,".to_string()), + Element::Text("1970".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn i() { + let content = ".%I John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn i_with_whitespaces() { + let content = ".%I John \t Doe\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn i_no_args() { + let content = ".%I"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn i_not_parsed() { + let content = ".%I John Doe Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn i_not_callable() { + let content = ".Ad addr1 %I John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%I".to_string()), + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn j() { + let content = ".%J Journal Name Line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes: vec![ + Element::Text("Journal".to_string()), + Element::Text("Name".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn j_with_whitespaces() { + let content = ".%J Journal \t Name \t Line\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes: vec![ + Element::Text("Journal".to_string()), + Element::Text("Name".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn j_no_args() { + let content = ".%J"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn j_not_parsed() { + let content = ".%J Journal Name Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes: vec![ + Element::Text("Journal".to_string()), + Element::Text("Name".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn j_not_callable() { + let content = ".Ad addr1 %J Journal Name"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%J".to_string()), + Element::Text("Journal".to_string()), + Element::Text("Name".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn n() { + let content = ".%N Issue No. 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes: vec![ + Element::Text("Issue".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn n_with_whitespaces() { + let content = ".%N Issue \t No. \t 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes: vec![ + Element::Text("Issue".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn n_no_args() { + let content = ".%N"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn n_not_parsed() { + let content = ".%N Issue No. 1 Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes: vec![ + Element::Text("Issue".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn n_not_callable() { + let content = ".Ad addr1 %N Issue No. 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%N".to_string()), + Element::Text("Issue".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn o() { + let content = ".%O Optional information line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes: vec![ + Element::Text("Optional".to_string()), + Element::Text("information".to_string()), + Element::Text("line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn o_with_whitespaces() { + let content = ".%O Optional \t information \t line\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes: vec![ + Element::Text("Optional".to_string()), + Element::Text("information".to_string()), + Element::Text("line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn o_no_args() { + let content = ".%O"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn o_not_parsed() { + let content = ".%O Optional information Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes: vec![ + Element::Text("Optional".to_string()), + Element::Text("information".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn o_not_callable() { + let content = ".Ad addr1 %O Optional information"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%O".to_string()), + Element::Text("Optional".to_string()), + Element::Text("information".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn p() { + let content = ".%P pp. 1-100"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::P, + nodes: vec![ + Element::Text("pp.".to_string()), + Element::Text("1-100".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn p_with_whitespaces() { + let content = ".%P pp. \t 1-100\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::P, + nodes: vec![ + Element::Text("pp.".to_string()), + Element::Text("1-100".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn p_no_args() { + let content = ".%P"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn p_not_parsed() { + let content = ".%P pp. 1-100 Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::P, + nodes: vec![ + Element::Text("pp.".to_string()), + Element::Text("1-100".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn p_not_callable() { + let content = ".Ad addr1 %P pp. 1-100"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%P".to_string()), + Element::Text("pp.".to_string()), + Element::Text("1-100".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn q() { + let content = ".%Q John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn q_with_whitespaces() { + let content = ".%Q John \t Doe\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn q_no_args() { + let content = ".%Q"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn q_not_parsed() { + let content = ".%Q John Doe Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn q_not_callable() { + let content = ".Ad addr1 %Q John Doe"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%Q".to_string()), + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r() { + let content = ".%R Technical report No. 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes: vec![ + Element::Text("Technical".to_string()), + Element::Text("report".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r_with_whitespaces() { + let content = ".%R Technical \t report \t No. \t 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes: vec![ + Element::Text("Technical".to_string()), + Element::Text("report".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r_no_args() { + let content = ".%R"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r_not_parsed() { + let content = ".%R Technical report Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes: vec![ + Element::Text("Technical".to_string()), + Element::Text("report".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r_not_callable() { + let content = ".Ad addr1 %R Technical report"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%R".to_string()), + Element::Text("Technical".to_string()), + Element::Text("report".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn t() { + let content = ".%T Article title line"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes: vec![ + Element::Text("Article".to_string()), + Element::Text("title".to_string()), + Element::Text("line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn t_with_whitespaces() { + let content = ".%T Article \t title \t line\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes: vec![ + Element::Text("Article".to_string()), + Element::Text("title".to_string()), + Element::Text("line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn t_no_args() { + let content = ".%T"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn t_not_parsed() { + let content = ".%T Article title Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes: vec![ + Element::Text("Article".to_string()), + Element::Text("title".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn t_not_callable() { + let content = ".Ad addr1 %T Article title"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%T".to_string()), + Element::Text("Article".to_string()), + Element::Text("title".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn u() { + let content = ".%U protocol://path"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes: vec![Element::Text("protocol://path".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn u_no_args() { + let content = ".%U"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn u_not_parsed() { + let content = ".%U Ad addr1"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::U, + nodes: vec![ + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn u_not_callable() { + let content = ".Ad addr1 %U protocol://path"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%U".to_string()), + Element::Text("protocol://path".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn v() { + let content = ".%V Volume No. 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes: vec![ + Element::Text("Volume".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn v_with_whitespaces() { + let content = ".%V Volume \t No. \t 1\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes: vec![ + Element::Text("Volume".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn v_no_args() { + let content = ".%V"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn v_not_parsed() { + let content = ".%V Volume No. 1 Ad addr1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes: vec![ + Element::Text("Volume".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn v_not_callable() { + let content = ".Ad addr1 %V Volume No. 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("%V".to_string()), + Element::Text("Volume".to_string()), + Element::Text("No.".to_string()), + Element::Text("1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod text_production { + use std::collections::HashMap; + + use crate::man_util::parser::*; + + #[test] + fn at() { + let mut at_types: HashMap<&str, AtType> = Default::default(); + at_types.insert("", AtType::General); + at_types.insert("v1", AtType::Version("1".to_string())); + at_types.insert("v2", AtType::Version("2".to_string())); + at_types.insert("v3", AtType::Version("3".to_string())); + at_types.insert("v4", AtType::Version("4".to_string())); + at_types.insert("v5", AtType::Version("5".to_string())); + at_types.insert("v6", AtType::Version("6".to_string())); + at_types.insert("v7", AtType::Version("7".to_string())); + at_types.insert("32v", AtType::V32); + at_types.insert("III", AtType::SystemIII); + at_types.insert("V", AtType::SystemV(None)); + at_types.insert("V.1", AtType::SystemV(Some("1".to_string()))); + at_types.insert("V.2", AtType::SystemV(Some("2".to_string()))); + at_types.insert("V.3", AtType::SystemV(Some("3".to_string()))); + at_types.insert("V.4", AtType::SystemV(Some("4".to_string()))); + + for (str_type, enum_type) in at_types { + let content = format!(".At {str_type}"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![Element::Text(enum_type.to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "At type: {str_type}"); + } + } + + #[test] + fn at_other_text_values() { + let at_args = vec![ + "v0".to_string(), + "v8".to_string(), + "V.0".to_string(), + "V.5".to_string(), + "word".to_string(), + ]; + + for arg in at_args { + let content = format!(".At {arg} word\n"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text(AtType::General.to_string()), + Element::Text(arg.clone()), + Element::Text("word".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "At type: {arg}"); + } + } + + #[test] + fn at_parsed() { + let content = ".At v1 Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![Element::Text(AtType::Version("1".to_string()).to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn at_callable() { + let content = ".Ad addr1 At v1 word\n"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text(AtType::Version(1.to_string()).to_string()), + Element::Text("word".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn at_with_delimiters() { + let input = r#".At ( v1 ) +.At ( v2 +.At v3 ) +.At , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(AtType::Version("1".to_string()).to_string()), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(AtType::Version("2".to_string()).to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text(AtType::Version("3".to_string()).to_string()), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text(AtType::default().to_string()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bsx() { + let content = ".Bsx 1.0"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![Element::Text(BsxType::format("1.0"))], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bsx_no_args() { + let content = ".Bsx"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![Element::Text(BsxType::format_default())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bsx_parsed() { + let content = ".Bsx 1.0 Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![Element::Text(BsxType::format("1.0"))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bsx_callable() { + let content = ".Ad addr1 Bsx 1.0"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![Element::Text(BsxType::format("1.0"))], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bsx_with_delimiters() { + let input = r#".Bsx ( v1 ) +.Bsx ( v2 +.Bsx v3 ) +.Bsx , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(BsxType::format("v1")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(BsxType::format("v2")), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![ + Element::Text(BsxType::format("v3")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bsx, + nodes: vec![ + Element::Text(BsxType::format_default()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bx() { + let mut bx_args: HashMap<&str, (&str, Option<&str>)> = Default::default(); + bx_args.insert("", ("", None)); + bx_args.insert("4.3", ("4.3", None)); + bx_args.insert("4.3 Tahoe", ("4.3", Some("Tahoe"))); + + for (args, (version, variant)) in bx_args { + let content = format!(".Bx {args}"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![Element::Text(BxType::format(version, variant))], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "Bx args: {args}"); + } + } + + #[test] + fn bx_parsed() { + let content = ".Bx 4.3 Tahoe Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![Element::Text(BxType::format("4.3", Some("Tahoe")))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bx_callable_with_arg() { + let content = ".Ad addr1 Bx 4.3 Tahoe Example\n"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![ + Element::Text(BxType::format("4.3", Some("Tahoe"))), + Element::Text("Example".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bx_callable_without_arg() { + let content = ".Ad addr1 Bx"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![Element::Text(BxType::format_default())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bx_with_delimiters() { + let input = r#".Bx ( v1 ) +.Bx ( v2 +.Bx v3 ) +.Bx , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(BxType::format("v1", None)), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(BxType::format("v2", None)), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![ + Element::Text(BxType::format("v3", None)), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Bx, + nodes: vec![ + Element::Text(BxType::format_default()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx() { + let content = ".Dx 1.0"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![Element::Text(DxType::format("1.0"))], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx_no_args() { + let content = ".Dx"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![Element::Text(DxType::format_default())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx_parsed() { + let content = ".Dx 1.0 Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![Element::Text(DxType::format("1.0"))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx_callable_with_arg() { + let content = ".Ad addr1 Dx 1.0 text"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![ + Element::Text(DxType::format("1.0")), + Element::Text("text".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx_callable_without_arg() { + let content = ".Ad addr1 Dx"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![Element::Text(DxType::format_default())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dx_with_delimiters() { + let input = r#".Dx ( v1 ) +.Dx ( v2 +.Dx v3 ) +.Dx , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(DxType::format("v1")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(DxType::format("v2")), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![ + Element::Text(DxType::format("v3")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dx, + nodes: vec![ + Element::Text(DxType::format_default()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx() { + let content = ".Nx 1.0"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![Element::Text(NxType::format("1.0"))], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx_no_args() { + let content = ".Nx"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![Element::Text(NxType::format_default())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx_parsed() { + let content = ".Nx 1.0 Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![Element::Text(NxType::format("1.0"))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx_callable_with_arg() { + let content = ".Ad addr1 Nx 1.0"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![Element::Text(NxType::format("1.0"))], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx_callable_without_arg() { + let content = ".Ad addr1 Nx"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![Element::Text(NxType::format_default())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn nx_with_delimiters() { + let input = r#".Nx ( v1 ) +.Nx ( v2 +.Nx v3 ) +.Nx , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(NxType::format("v1")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(NxType::format("v2")), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![ + Element::Text(NxType::format("v3")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Nx, + nodes: vec![ + Element::Text(NxType::format_default()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox() { + let content = ".Ox 1.0"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![Element::Text(OxType::format("1.0"))], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox_no_args() { + let content = ".Ox"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![Element::Text(OxType::format_default())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox_parsed() { + let content = ".Ox 1.0 Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![Element::Text(OxType::format("1.0"))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox_callable_with_arg() { + let content = ".Ad addr1 Ox 1.0"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![Element::Text(OxType::format("1.0"))], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox_callable_without_arg() { + let content = ".Ad addr1 Ox"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![Element::Text(OxType::format_default())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ox_with_delimiters() { + let input = r#".Ox ( v1 ) +.Ox ( v2 +.Ox v3 ) +.Ox , v1 +"#; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(OxType::format("v1")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![ + Element::Text("(".to_string()), + Element::Text(OxType::format("v2")), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![ + Element::Text(OxType::format("v3")), + Element::Text(")".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ox, + nodes: vec![ + Element::Text(OxType::format_default()), + Element::Text(",".to_string()), + Element::Text("v1".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn st() { + let mut st_types: HashMap<&str, StType> = Default::default(); + // C Language Standards + st_types.insert("-ansiC", StType::AnsiC); + st_types.insert("-ansiC-89", StType::AnsiC89); + st_types.insert("-isoC", StType::IsoC); + st_types.insert("-isoC-90", StType::IsoC90); + st_types.insert("-isoC-amd1", StType::IsoCAmd1); + st_types.insert("-isoC-tcor1", StType::IsoCTcor1); + st_types.insert("-isoC-tcor2", StType::IsoCTcor2); + st_types.insert("-isoC-99", StType::IsoC99); + st_types.insert("-isoC-2011", StType::IsoC2011); + // POSIX.1 Standards before XPG4.2 + st_types.insert("-p1003.1-88", StType::P1003188); + st_types.insert("-p1003.1", StType::P10031); + st_types.insert("-p1003.1-90", StType::P1003190); + st_types.insert("-iso9945-1-90", StType::Iso9945190); + st_types.insert("-p1003.1b-93", StType::P10031B93); + st_types.insert("-p1003.1b", StType::P10031B); + st_types.insert("-p1003.1c-95", StType::P10031C95); + st_types.insert("-p1003.1i-95", StType::P10031I95); + st_types.insert("-p1003.1-96", StType::P1003196); + st_types.insert("-iso9945-1-96", StType::Iso9945196); + // X/Open Portability Guide before XPG4.2 + st_types.insert("-xpg3", StType::Xpg3); + st_types.insert("-p1003.2", StType::P10032); + st_types.insert("-p1003.2-92", StType::P1003292); + st_types.insert("-iso9945-2-93", StType::Iso9945293); + st_types.insert("-p1003.2a-92", StType::P10032A92); + st_types.insert("-xpg4", StType::Xpg4); + // X/Open Portability Guide Issue 4 Version 2 and Related Standards + st_types.insert("-susv1", StType::Susv1); + st_types.insert("-xpg4.2", StType::Xpg42); + st_types.insert("-xcurses4.2", StType::XCurses42); + st_types.insert("-p1003.1g-2000", StType::P10031G2000); + st_types.insert("-svid4", StType::Svid4); + // X/Open Portability Guide Issue 5 and Related Standards + st_types.insert("-susv2", StType::Susv2); + st_types.insert("-xbd5", StType::Xbd5); + st_types.insert("-xsh5", StType::Xsh5); + st_types.insert("-xcu5", StType::Xcu5); + st_types.insert("-xns5", StType::Xns5); + st_types.insert("-xns5.2", StType::Xns52); + // POSIX Issue 6 Standards + st_types.insert("-p1003.1-2001", StType::P100312001); + st_types.insert("-susv3", StType::Susv3); + st_types.insert("-p1003.1-2004", StType::P100312004); + // POSIX Issues 7 and 8 Standards + st_types.insert("-p1003.1-2008", StType::P100312008); + st_types.insert("-susv4", StType::Susv4); + st_types.insert("-p1003.1-2024", StType::P100312024); + // Other Standards + st_types.insert("-ieee754", StType::Ieee754); + st_types.insert("-iso8601", StType::Iso8601); + st_types.insert("-iso8802-3", StType::Iso88023); + st_types.insert("-ieee1275-94", StType::Ieee127594); + + for (str_type, enum_type) in st_types { + let content = format!(".St {str_type} word"); + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::St(enum_type), + nodes: vec![Element::Text("word".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(&content).unwrap(); + assert_eq!(mdoc.elements, elements, "St type: {str_type}"); + } + } + + #[test] + fn st_no_abbreviation() { + assert_eq!(MdocParser::parse_mdoc(".St word").unwrap().elements, vec![]) + } + + #[test] + fn st_parsed() { + let content = ".St -ansiC Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::St(StType::AnsiC), + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn st_not_callable() { + let content = ".Ad addr1 St -ansiC word"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("St".to_string()), + Element::Text("-ansiC".to_string()), + Element::Text("word".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + #[test] + fn ad() { + let content = ".Ad addr1 addr2 addr3"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + Element::Text("addr3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + + fn ad_no_args() { + let content = ".Ad"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ad_parsed() { + let content = ".Ad addr1 Ad arg1 arg2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ad_callable() { + let content = ".Ad word1 Ad addr1 addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("word1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn an_split() { + let content = ".An -split"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::An { + author_name_type: AnType::Split, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn an_nosplit() { + let content = ".An -nosplit"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::An { + author_name_type: AnType::NoSplit, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn an_name() { + let content = ".An John Doe"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::An { + author_name_type: AnType::Name, + }, + nodes: vec![ + Element::Text("John".to_string()), + Element::Text("Doe".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + + fn an_no_args() { + let content = ".An"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn an_parsed() { + let content = ".An Name Ad addr1 addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::An { + author_name_type: AnType::Name, + }, + nodes: vec![Element::Text("Name".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn an_callable() { + let content = ".Ad word1 An -nosplit"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("word1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::An { + author_name_type: AnType::NoSplit, + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ap() { + let content = ".Ap Text Line"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ap, + nodes: vec![ + Element::Text("Text".to_string()), + Element::Text("Line".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ap_no_args() { + let content = ".Ap"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ap, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ap_parsed() { + let content = ".Ap some text Ad addr1 addr2"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ap, + nodes: vec![ + Element::Text("some".to_string()), + Element::Text("text".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ap_callable() { + let content = ".Ad addr1 Ap word1 word2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ap, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ar() { + let content = ".Ar arg1 arg2 arg3"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![ + Element::Text("arg1".to_string()), + Element::Text("arg2".to_string()), + Element::Text("arg3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ar_no_args() { + let content = ".Ar"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ar_parsed() { + let content = ".Ar arg1 Ad addr1 addr2"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("arg1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ar_callable() { + let content = ".Ad addr1 Ar word1 word2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bt() { + // "Text Line" will be ignored + let content = ".Bt Text Line"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bt, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bt_no_args() { + let content = ".Bt"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bt, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bt_not_parsed() { + // "Ad" macro will be ignored + let content = ".Bt Ad addr1 addr2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Bt, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn bt_not_callable() { + let content = ".Ad addr1 Bt addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Bt".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cd() { + let content = ".Cd kernel configuration declaration"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Cd, + nodes: vec![ + Element::Text("kernel".to_string()), + Element::Text("configuration".to_string()), + Element::Text("declaration".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cd_no_args() { + let content = ".Cd"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cd_parsed() { + let content = ".Cd declaration Ad addr1 addr2"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Cd, + nodes: vec![Element::Text("declaration".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cd_callable() { + let content = ".Ad addr1 Cd configuration declaration"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Cd, + nodes: vec![ + Element::Text("configuration".to_string()), + Element::Text("declaration".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cm() { + let content = ".Cm mod1 mod2 mod3"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes: vec![ + Element::Text("mod1".to_string()), + Element::Text("mod2".to_string()), + Element::Text("mod3".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cm_no_args() { + let content = ".Cm"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cm_parsed() { + let content = ".Cm cmdm1 cmdm2 Ad addr1 addr2"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes: vec![ + Element::Text("cmdm1".to_string()), + Element::Text("cmdm2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn cm_callable() { + let content = ".Ad addr1 Cm mod1 mod2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes: vec![ + Element::Text("mod1".to_string()), + Element::Text("mod2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn db() { + let content = ".Db text_argument"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Db, + nodes: vec![Element::Text("text_argument".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn db_not_callable() { + let content = ".Ad addr1 Db addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Db".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn db_not_parsed() { + let content = ".Db Ad"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Db, + nodes: vec![Element::Text("Ad".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn db_no_args() { + let content = ".Db"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Db, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dd() { + let content = ".Dd $Mdocdate: July 2 2018$"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dd, + nodes: vec![Element::Text("$Mdocdate: July 2 2018$".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dd_no_date() { + let content = ".Dd $Mdocdate$"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dd, + nodes: vec![Element::Text("$Mdocdate$".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dd_no_args() { + let content = ".Dd"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dd, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dd_not_callable() { + let content = ".Ad addr1 Dd addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Dd".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dd_not_parsed() { + let content = ".Dd Ad 2, 2018"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dd, + nodes: vec![Element::Text("Ad 2, 2018".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dt() { + let content = ".Dt PROGNAME 1 i386\n.Dt 1 i386 \n.Dt PROGNAME 1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dt { + title: Some("PROGNAME".to_string()), + section: "1".to_string(), + arch: Some("i386".to_string()), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dt { + title: None, + section: "1".to_string(), + arch: Some("i386".to_string()), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dt { + title: Some("PROGNAME".to_string()), + section: "1".to_string(), + arch: None, + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dt_not_callable() { + let content = ".Ad addr1 Dt addr2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Dt".to_string()), + Element::Text("addr2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dt_not_parsed() { + let content = ".Dt Ad 1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dt { + title: Some("Ad".to_string()), + section: "1".to_string(), + arch: None, + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dt_no_args() { + let content = ".Dt"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dv() { + let content = ".Dv CONSTANT1 CONSTANT2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![ + Element::Text("CONSTANT1".to_string()), + Element::Text("CONSTANT2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dv_no_args() { + let content = ".Dv"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn dv_callable() { + let content = ".Ad addr1 addr2 Dv CONST1"; + let elemenets = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONST1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elemenets) + } + + #[test] + fn dv_parsed() { + let content = ".Dv CONST1 Ad addr1 addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Dv, + nodes: vec![Element::Text("CONST1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn em() { + let input = ".Em word1 word2"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn em_no_args() { + let input = ".Em"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn em_parsed() { + let input = ".Em word1 Ad addr1 addr2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes: vec![Element::Text("word1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn em_callable() { + let input = ".Ad addr1 addr2 Em word1"; + let elemenets = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Em, + nodes: vec![Element::Text("word1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elemenets) + } + + #[test] + fn er() { + let input = ".Er ERROR"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Er, + nodes: vec![Element::Text("ERROR".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn er_no_args() { + let input = ".Er"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn er_parsed() { + let input = ".Er ERROR Ad addr1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Er, + nodes: vec![Element::Text("ERROR".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn er_callable() { + let input = ".Ad addr1 addr2 Er ERROR ERROR2"; + let elemenets = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Er, + nodes: vec![ + Element::Text("ERROR".to_string()), + Element::Text("ERROR2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elemenets) + } + + #[test] + fn es() { + let input = ".Es ( )"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Es { + opening_delimiter: '(', + closing_delimiter: ')', + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn es_bad_args() { + assert_eq!(MdocParser::parse_mdoc(".Es").unwrap().elements, vec![]); + assert_eq!(MdocParser::parse_mdoc(".Es (").unwrap().elements, vec![]); + assert_eq!(MdocParser::parse_mdoc(".Es { }").unwrap().elements, vec![]); + } + + #[test] + fn es_parsed() { + let input = ".Es [ ] At 2.32"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Es { + opening_delimiter: '[', + closing_delimiter: ']', + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::At, + nodes: vec![ + Element::Text(AtType::General.to_string()), + Element::Text("2.32".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn es_callable() { + let input = ".Ad addr1 addr2 Es ( )"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("addr2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Es { + opening_delimiter: '(', + closing_delimiter: ')', + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Ev ----------------------------------------------------------- + + #[test] + fn ev() { + let input = ".Ev DISPLAY"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes: vec![Element::Text("DISPLAY".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ev_no_args() { + let input = ".Ev"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ev_parsed() { + let input = ".Ev DISPLAY Ad ADDRESS"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes: vec![Element::Text("DISPLAY".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("ADDRESS".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ev_callable() { + let input = ".Ad addr1 Ev ADDRESS"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes: vec![Element::Text("ADDRESS".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Ex ----------------------------------------------------------- + + #[test] + fn ex() { + let input = ".Ex -std grep"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ex, + nodes: vec![Element::Text("grep".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ex_no_args() { + let input = ".Ex"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ex_not_parsed() { + let input = ".Ex -std grep Ad addr"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ex, + nodes: vec![ + Element::Text("grep".to_string()), + Element::Text("Ad".to_string()), + Element::Text("addr".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ex_not_callable() { + let input = ".Ad addr Ex -std grep"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr".to_string()), + Element::Text("Ex".to_string()), + Element::Text("-std".to_string()), + Element::Text("grep".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Fa ----------------------------------------------------------- + + #[test] + fn fa() { + let input = ".Fa size_t"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes: vec![Element::Text("size_t".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fa_no_args() { + let input = ".Fa"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fa_parsed() { + let input = ".Fa funcname Ft const char *"; + let elemets = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes: vec![Element::Text("funcname".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![ + Element::Text("const".to_string()), + Element::Text("char".to_string()), + Element::Text("*".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elemets); + } + + #[test] + fn fa_callable() { + let input = ".Ft functype Fa int"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes: vec![Element::Text("int".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fd() { + let input = ".Fd #define sa_handler __sigaction_u.__sa_handler\n.Fd #define SIO_MAXNFDS\n.Fd #endif"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fd { + directive: "#define".to_string(), + arguments: vec![ + "sa_handler".to_string(), + "__sigaction_u.__sa_handler".to_string(), + ], + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fd { + directive: "#define".to_string(), + arguments: vec!["SIO_MAXNFDS".to_string()], + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fd { + directive: "#endif".to_string(), + arguments: vec![], + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fd_no_args() { + let input = ".Fd"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fd_not_parsed() { + let input = ".Fd #define Ad addr"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fd { + directive: "#define".to_string(), + arguments: vec!["Ad".to_string(), "addr".to_string()], + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fd_not_callable() { + let input = ".Ad Fd #define ADDRESS"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("Fd".to_string()), + Element::Text("#define".to_string()), + Element::Text("ADDRESS".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fl() { + let input = ".Fl H | L | P\n.Fl inet"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes: vec![ + Element::Text("H".to_string()), + Element::Text("|".to_string()), + Element::Text("L".to_string()), + Element::Text("|".to_string()), + Element::Text("P".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes: vec![Element::Text("inet".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fl_no_args() { + let input = ".Fl"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fl_parsed() { + let input = ".Fl inet Ar destination gateway"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes: vec![Element::Text("inet".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![ + Element::Text("destination".to_string()), + Element::Text("gateway".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fl_callable() { + let input = ".Cm add Fl inet"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes: vec![Element::Text("add".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fl, + nodes: vec![Element::Text("inet".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Fn ----------------------------------------------------------- + + #[test] + fn r#fn() { + let input = ".Fn \"int funcname\" \"int arg0\" \"int arg1\"\n.Fn funcname \"int arg0\"\n.Fn funcname arg0\n.Fn ( funcname )"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "\"int funcname\"".to_string(), + }, + nodes: vec![ + Element::Text("int arg0".to_string()), + Element::Text("int arg1".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("int arg0".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("arg0".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "(funcname".to_string(), + }, + nodes: vec![Element::Text(")".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fn_no_args() { + let input = ".Fn"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fn_parsed() { + let input = ".Fn funcname arg Ft int"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("arg".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("int".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fn_callable() { + let input = ".Ft functype Fn funcname"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fn { + funcname: "funcname".to_string(), + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fr() { + let input = ".Fr 32"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fr, + nodes: vec![Element::Text("32".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fr_no_args() { + let input = ".Fr"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fr_parsed() { + let input = ".Fr 32 Ad addr"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fr, + nodes: vec![Element::Text("32".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fr_callable() { + let input = ".Ft functype Fr 12"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fr, + nodes: vec![Element::Text("12".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // .Ft ----------------------------------------------------------- + + #[test] + fn ft() { + let input = ".Ft int32 void"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![ + Element::Text("int32".to_string()), + Element::Text("void".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ft_no_args() { + let input = ".Ft"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ft_parsed() { + let input = ".Ft functype Fa arg"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes: vec![Element::Text("arg".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ft_callable() { + let input = ".Fa funcname Ft const char *"; + let elemets = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fa, + nodes: vec![Element::Text("funcname".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![ + Element::Text("const".to_string()), + Element::Text("char".to_string()), + Element::Text("*".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elemets); + } + + #[test] + fn fx() { + let input = ".Fx 1.0 arg\n"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fx, + nodes: vec![ + Element::Text(FxType::format("1.0")), + Element::Text("arg".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fx_no_args() { + let input = ".Fx"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Fx, + nodes: vec![Element::Text(FxType::format_default())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fx_parsed() { + let input = ".Fx 1.0 Ad addr"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Fx, + nodes: vec![Element::Text(FxType::format("1.0"))], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn fx_callable() { + let input = ".Ad addr Fx 1.0"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fx, + nodes: vec![Element::Text(FxType::format("1.0"))], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn hf() { + let input = ".Hf file/path file2/path"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Hf, + nodes: vec![ + Element::Text("file/path".to_string()), + Element::Text("file2/path".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn hf_no_args() { + let input = ".Hf"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Hf, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn hf_not_parsed() { + let input = ".Hf Ad addr"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Hf, + nodes: vec![ + Element::Text("Ad".to_string()), + Element::Text("addr".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn hf_not_callable() { + let input = ".Ad Hf path/to/some/file"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("Hf".to_string()), + Element::Text("path/to/some/file".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ic() { + let input = ".Ic :wq"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ic, + nodes: vec![Element::Text(":wq".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ic_no_args() { + let input = ".Ic"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ic_parsed() { + let input = ".Ic lookup Cm file bind"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ic, + nodes: vec![Element::Text("lookup".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Cm, + nodes: vec![ + Element::Text("file".to_string()), + Element::Text("bind".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ic_callable() { + let input = ".Ad addr Ic :wq"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ic, + nodes: vec![Element::Text(":wq".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn r#in() { + let input = ".In stdatomic.h"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::In { + filename: "stdatomic.h".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn in_no_args() { + let input = ".In"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn in_parsed() { + let input = ".In stdio.h Ad addr"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::In { + filename: "stdio.h".to_string(), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn in_callable() { + let input = ".Ad addr In stdatomic.c"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::In { + filename: "stdatomic.c".to_string(), + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lb() { + let input = ".Lb libname"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Lb { + lib_name: "libname".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lb_wrong_args() { + assert_eq!(MdocParser::parse_mdoc(".Lb").unwrap().elements, vec![]); + } + + #[test] + fn lb_not_parsed() { + let input = ".Lb Ar"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Lb { + lib_name: "Ar".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lb_not_callable() { + let input = ".Ad Lb stdio.h"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("Lb".to_string()), + Element::Text("stdio.h".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn li() { + let input = ".Li Book Antiqua"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Li, + nodes: vec![ + Element::Text("Book".to_string()), + Element::Text("Antiqua".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn li_no_args() { + let input = ".Li"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn li_parsed() { + let input = ".Li font Ev DEFAULT_FONT"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Li, + nodes: vec![Element::Text("font".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes: vec![Element::Text("DEFAULT_FONT".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn li_callable() { + let input = ".Ad addr Li font"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Li, + nodes: vec![Element::Text("font".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lk() { + let input = ".Lk https://bsd.lv The BSD.lv Project"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Lk { + uri: "https://bsd.lv".to_string(), + }, + nodes: vec![ + Element::Text("The".to_string()), + Element::Text("BSD.lv".to_string()), + Element::Text("Project".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lk_no_args() { + let input = ".Lk"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lk_parsed() { + let input = ".Lk https://bsd.lv Ev NAME"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Lk { + uri: "https://bsd.lv".to_string(), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ev, + nodes: vec![Element::Text("NAME".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lk_callable() { + let input = ".Ad addr Lk https://bsd.lv"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Lk { + uri: "https://bsd.lv".to_string(), + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lp() { + let input = ".Lp"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Lp, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lp_not_parsed() { + let input = ".Lp Ad addr"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Lp, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn lp_not_callable() { + let input = ".Ad addr Lp"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr".to_string()), + Element::Text("Lp".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Ms -------------------------------------------------------------------------- + + #[test] + fn ms() { + let content = ".Ms alpha beta"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ms, + nodes: vec![ + Element::Text("alpha".to_string()), + Element::Text("beta".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ms_no_args() { + let content = ".Ms"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ms_parsed() { + let content = ".Ms beta Ux"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ms, + nodes: vec![Element::Text("beta".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ms_callable() { + let content = ".No / Ms aleph"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::No, + nodes: vec![Element::Text("/".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ms, + nodes: vec![Element::Text("aleph".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Mt -------------------------------------------------------------------------- + + #[test] + fn mt() { + let content = ".Mt abc@gmail.com abc@gmail.com"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Mt, + nodes: vec![ + Element::Text("abc@gmail.com".to_string()), + Element::Text("abc@gmail.com".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn mt_no_args() { + let content = ".Mt"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn mt_parsed() { + let content = ".Mt abc@gmail.com Ux"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Mt, + nodes: vec![Element::Text("abc@gmail.com".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn mt_callable() { + let content = ".Ad address1 Mt abc@gmail.com"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("address1".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Mt, + nodes: vec![Element::Text("abc@gmail.com".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // No -------------------------------------------------------------------------- + + #[test] + fn no() { + let content = ".No a b c"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::No, + nodes: vec![ + Element::Text("a".to_string()), + Element::Text("b".to_string()), + Element::Text("c".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements) + } + + #[test] + fn no_no_args() { + let content = ".No"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn no_parsed() { + let content = ".No a Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::No, + nodes: vec![Element::Text("a".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn no_callable() { + let content = ".Ar value No a"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::No, + nodes: vec![Element::Text("a".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Ns -------------------------------------------------------------------------- + + #[test] + fn ns() { + let content = ".Ns"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ns, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ns_parsed() { + let content = ".Ns Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ns, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ns_callable() { + let content = ".Ar value Ns"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ns, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Os -------------------------------------------------------------------------- + + #[test] + fn os() { + let content = ".Os footer text"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Os, + nodes: vec![ + Element::Text("footer".to_string()), + Element::Text("text".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn os_no_args() { + let content = ".Os"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Os, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn os_not_parsed() { + let content = ".Os Ar value"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Os, + nodes: vec![ + Element::Text("Ar".to_string()), + Element::Text("value".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn os_not_callable() { + let content = ".Ad addr1 Os"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Os".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Ot -------------------------------------------------------------------------- + + #[test] + fn ot() { + let content = ".Ot functype"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ot_no_args() { + let content = ".Ot"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ot_parsed() { + let content = ".Ot functype Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ot_callable() { + let content = ".Ar value Ot functype"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ft, + nodes: vec![Element::Text("functype".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Pa -------------------------------------------------------------------------- + + #[test] + fn pa() { + let content = ".Pa name1 name2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pa, + nodes: vec![ + Element::Text("name1".to_string()), + Element::Text("name2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pa_no_args() { + let content = ".Pa"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pa_parsed() { + let content = ".Pa name1 name2 Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Pa, + nodes: vec![ + Element::Text("name1".to_string()), + Element::Text("name2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pa_callable() { + let content = ".Ar value Pa name1 name2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pa, + nodes: vec![ + Element::Text("name1".to_string()), + Element::Text("name2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Pf -------------------------------------------------------------------------- + + #[test] + fn pf() { + let content = ".Pf $ Ar variable_name"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Pf { + prefix: "$".to_string(), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("variable_name".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pf_no_args() { + let content = ".Pf"; + let elements = vec![]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pf_callable() { + let content = ".Ar value Pf $ Ar variable_name"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Pf { + prefix: "$".to_string(), + }, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("variable_name".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Pp -------------------------------------------------------------------------- + + #[test] + fn pp() { + let content = ".Pp"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pp, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pp_not_parsed() { + // "Ar" macro will be ignored + let content = ".Pp Ar value"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Pp, + nodes: vec![ + Element::Text("Ar".to_string()), + Element::Text("value".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn pp_not_callable() { + let content = ".Ad addr1 Pp"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Pp".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Rv -------------------------------------------------------------------------- + + #[test] + fn rv() { + let content = ".Rv -std f1 f2 Ar value"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Rv, + nodes: vec![ + Element::Text("f1".to_string()), + Element::Text("f2".to_string()), + Element::Text("Ar".to_string()), + Element::Text("value".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn rv_no_std() { + assert_eq!( + MdocParser::parse_mdoc(".Rv f1 f2").unwrap().elements, + vec![] + ); + } + + #[test] + fn rv_no_args() { + let content = ".Rv -std"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Rv, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn rv_no_std_and_args() { + assert_eq!(MdocParser::parse_mdoc(".Rv").unwrap().elements, vec![]); + } + + #[test] + fn rv_not_parsed() { + // "Ar" macro will be ignored + let content = ".Rv -std f1 Ar value"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Rv, + nodes: vec![ + Element::Text("f1".to_string()), + Element::Text("Ar".to_string()), + Element::Text("value".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn rv_not_callable() { + let content = ".Ad addr1 Rv -std f1"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Rv".to_string()), + Element::Text("-std".to_string()), + Element::Text("f1".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Sm -------------------------------------------------------------------------- + + #[test] + fn sm_on() { + let content = ".Sm on"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sm(Some(SmMode::On)), + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sm_off() { + let content = ".Sm off"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sm(Some(SmMode::Off)), + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sm_no_args() { + let content = ".Sm"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sm(None), + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sm_not_parsed() { + // "Ar" macro will be ignored + let content = ".Sm Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Sm(None), + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sm_not_callable() { + let content = ".Ad addr1 Sm"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Sm".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Sx -------------------------------------------------------------------------- + + #[test] + fn sx() { + let content = ".Sx MANUAL STRUCTURE"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sx, + nodes: vec![ + Element::Text("MANUAL".to_string()), + Element::Text("STRUCTURE".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sx_no_args() { + assert_eq!(MdocParser::parse_mdoc(".Sx").unwrap().elements, vec![]); + } + + #[test] + fn sx_wrong_args() { + assert_eq!( + MdocParser::parse_mdoc(".Sx Ar value").unwrap().elements, + vec![] + ); + } + + #[test] + fn sx_parsed() { + let content = ".Sx MANUAL STRUCTURE Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Sx, + nodes: vec![ + Element::Text("MANUAL".to_string()), + Element::Text("STRUCTURE".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sx_callable() { + let content = ".Ar value Sx MANUAL STRUCTURE"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sx, + nodes: vec![ + Element::Text("MANUAL".to_string()), + Element::Text("STRUCTURE".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Sy -------------------------------------------------------------------------- + + #[test] + fn sy() { + let content = ".Sy word1 word2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Sy, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sy_no_args() { + assert_eq!(MdocParser::parse_mdoc(".Sy").unwrap().elements, vec![]); + } + + #[test] + fn sy_parsed() { + let content = ".Sy word1 word2 Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Sy, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn sy_callable() { + let content = ".Ar value Sy word1 word2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Sy, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Tn -------------------------------------------------------------------------- + + #[test] + fn tn() { + let content = ".Tn word1 word2"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Tn, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn tn_no_args() { + assert_eq!(MdocParser::parse_mdoc(".Tn").unwrap().elements, vec![]); + } + + #[test] + fn tn_parsed() { + let content = ".Tn word1 word2 Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Tn, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn tn_callable() { + let content = ".Ar value Tn word1 word2"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Tn, + nodes: vec![ + Element::Text("word1".to_string()), + Element::Text("word2".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Ud -------------------------------------------------------------------------- + + #[test] + fn ud() { + let content = ".Ud"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ud, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ud_not_parsed() { + // "Ar" macro will be ignored + let content = ".Ud Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ud, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ud_not_callable() { + let content = ".Ad addr1 Ud"; + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![ + Element::Text("addr1".to_string()), + Element::Text("Ud".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Ux -------------------------------------------------------------------------- + + #[test] + fn ux() { + let content = ".Ux"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ux_parsed() { + let content = ".Ux Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes: vec![], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn ux_callable() { + let content = ".Ar value Ux"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ux, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Va -------------------------------------------------------------------------- + + #[test] + fn va() { + let content = ".Va const char *bar"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Va, + nodes: vec![ + Element::Text("const".to_string()), + Element::Text("char".to_string()), + Element::Text("*bar".to_string()), + ], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn va_without_type() { + let content = ".Va foo"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Va, + nodes: vec![Element::Text("foo".to_string())], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn va_no_args() { + assert_eq!(MdocParser::parse_mdoc(".Va").unwrap().elements, vec![]); + } + + #[test] + fn va_parsed() { + let content = ".Va bool foo Ar value"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Va, + nodes: vec![ + Element::Text("bool".to_string()), + Element::Text("foo".to_string()), + ], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn va_callable() { + let content = ".Ar value Va char foo"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Va, + nodes: vec![ + Element::Text("char".to_string()), + Element::Text("foo".to_string()), + ], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + // Xr -------------------------------------------------------------------------- + + #[test] + fn xr() { + let content = ".Xr mandoc 1"; + + let elements = vec![Element::Macro(MacroNode { + mdoc_macro: Macro::Xr { + name: "mandoc".to_string(), + section: "1".to_string(), + }, + nodes: vec![], + })]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + #[should_panic] + fn xr_no_args() { + assert!(MdocParser::parse_mdoc(".Xr mandoc").is_err()); + assert!(MdocParser::parse_mdoc(".Xr 1").is_err()); + assert!(MdocParser::parse_mdoc(".Xr").is_err()); + } + + #[test] + fn xr_parsed() { + let content = ".Xr mandoc 1 test Ns"; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Xr { + name: "mandoc".to_string(), + section: "1".to_string(), + }, + nodes: vec![Element::Text("test".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Ns, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn xr_callable() { + let content = ".Ar value Xr mandoc 1"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ar, + nodes: vec![Element::Text("value".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Xr { + name: "mandoc".to_string(), + section: "1".to_string(), + }, + nodes: vec![], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(content).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } + + mod general { + use crate::man_util::parser::*; + + #[test] + fn comment_in_text_line() { + let input = r#".\" comment +.\" Still comment1 +.\" Still comment2 +Line \" comment +.\" Still comment2 +Line \" comment +"#; + let elements = vec![ + Element::Text("Line ".to_string()), + Element::Text("Line ".to_string()), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn comment_in_lines() { + let input = r#".%A John \" Doe +.Fo funcname \" comment +Line +.Fc +.%B John \" Doe +.%C John \" Doe +.%I John \" Doe +.%J John \" Doe +.%N John \" Doe +.%O John \" Doe +.%Q John \" Doe +.%R John \" Doe +.%T John \" Doe +.%V John \" Doe +"#; + + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::A, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Fo { + funcname: "funcname".to_string(), + }, + nodes: vec![Element::Text("Line".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::B, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::C, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::I, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::J, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::N, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::O, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::Q, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::R, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::T, + nodes: vec![Element::Text("John".to_string())], + }), + Element::Macro(MacroNode { + mdoc_macro: Macro::V, + nodes: vec![Element::Text("John".to_string())], + }), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + + #[test] + fn comment_in_macros() { + let input = ".Ad addr \\\"comment"; + let elements = vec![ + Element::Macro(MacroNode { + mdoc_macro: Macro::Ad, + nodes: vec![Element::Text("addr".to_string())], + }), + Element::Text("".to_string()), + ]; + + let mdoc = MdocParser::parse_mdoc(input).unwrap(); + assert_eq!(mdoc.elements, elements); + } + } +} diff --git a/man/test_files/mdoc/access.2 b/man/test_files/mdoc/access.2 new file mode 100644 index 00000000..fdb6537e --- /dev/null +++ b/man/test_files/mdoc/access.2 @@ -0,0 +1,240 @@ +.\" $OpenBSD: access.2,v 1.27 2023/09/28 01:51:00 jsg Exp $ +.\" $NetBSD: access.2,v 1.7 1995/02/27 12:31:44 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)access.2 8.2 (Berkeley) 4/1/94 +.\" +.Dd $Mdocdate: September 28 2023 $ +.Dt ACCESS 2 +.Os +.Sh NAME +.Nm access , +.Nm faccessat +.Nd check access permissions of a file or pathname +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn access "const char *path" "int amode" +.In fcntl.h +.In unistd.h +.Ft int +.Fn faccessat "int fd" "const char *path" "int amode" "int flag" +.Sh DESCRIPTION +The +.Fn access +function checks the accessibility of the file named by +.Fa path +for the access permissions indicated by +.Fa amode . +The +.Fa amode +argument is either the bitwise OR of one or more of the access permissions +to be checked +.Pf ( Dv R_OK +for read permission, +.Dv W_OK +for write permission, and +.Dv X_OK +for execute/search permission) or the existence test, +.Dv F_OK . +All components of the pathname +.Fa path +are checked for access permissions (including +.Dv F_OK ) . +.Pp +The real user ID is used in place of the effective user ID +and the real group access list +(including the real group ID) is +used in place of the effective ID for verifying permission. +.Pp +If the invoking process has superuser privileges, +.Fn access +will always indicate success for +.Dv R_OK +and +.Dv W_OK , +regardless of the actual file permission bits. +Likewise, for +.Dv X_OK , +if the file has any of the execute bits set and +.Fa path +is not a directory, +.Fn access +will indicate success. +.Pp +The +.Fn faccessat +function is equivalent to +.Fn access +except that where +.Fa path +specifies a relative path, +the file whose accessibility is checked is determined relative to +the directory associated with file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn faccessat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used. +If +.Fa flag +is also zero, the behavior is identical to a call to +.Fn access . +.Pp +The +.Fa flag +argument is the bitwise OR of zero or more of the following values: +.Pp +.Bl -tag -width AT_EACCESS -offset indent -compact +.It Dv AT_EACCESS +The checks for accessibility are performed using the effective user +and group IDs instead of the real user and group IDs. +.El +.Sh RETURN VALUES +If +.Fa path +cannot be found or if any of the desired access modes would not be granted, +then a \-1 value is returned; otherwise a 0 value is returned. +.Sh ERRORS +Access to the file is denied if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EROFS +Write access is requested for a file on a read-only file system. +.It Bq Er ETXTBSY +Write access is requested for a pure procedure (shared text) +file presently being executed. +.It Bq Er EACCES +Permission bits of the file mode do not permit the requested access, +or search permission is denied on a component of the path prefix. +The owner of a file has permission checked with respect to the +.Dq owner +read, write, and execute mode bits, members of the file's group other +than the owner have permission checked with respect to the +.Dq group +mode bits, and all others have permissions checked with respect to the +.Dq other +mode bits. +.It Bq Er EPERM +Write access has been requested and the named file has its immutable +flag set (see +.Xr chflags 2 ) . +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.It Bq Er EINVAL +An invalid value was specified for +.Fa amode . +.El +.Pp +Additionally, +.Fn faccessat +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The value of the +.Fa flag +argument was neither zero nor +.Dv AT_EACCESS . +.It Bq Er EBADF +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is a valid file descriptor but it does not reference a directory. +.It Bq Er EACCES +The +.Fa path +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fd +file descriptor references. +.El +.Sh SEE ALSO +.Xr chmod 2 , +.Xr stat 2 +.Sh STANDARDS +The +.Fn access +and +.Fn faccessat +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +.Fn access +first appeared as an internal kernel function in +.At v1 . +It became a system call, +first appearing outside of Bell Labs in the +.Dq 50 changes +tape for +.At v6 . +The first official release with the system call was PWB/UNIX 1.0. +It was also included in +.Bx 2 . +.Pp +The +.Fn faccessat +function appeared in +.Ox 5.0 . +.Sh CAVEATS +.Fn access +and +.Fn faccessat +should never be used for actual access control. +Doing so can result in a time of check vs. time of use security hole. diff --git a/man/test_files/mdoc/adjfreq.2 b/man/test_files/mdoc/adjfreq.2 new file mode 100644 index 00000000..183e65c6 --- /dev/null +++ b/man/test_files/mdoc/adjfreq.2 @@ -0,0 +1,76 @@ +.\" $OpenBSD: adjfreq.2,v 1.8 2020/07/09 02:17:07 cheloha Exp $ +.\" +.\" Copyright (c) 2006 Otto Moerbeek +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd $Mdocdate: July 9 2020 $ +.Dt ADJFREQ 2 +.Os +.Sh NAME +.Nm adjfreq +.Nd correct the rate of the system clock +.Sh SYNOPSIS +.In sys/types.h +.In sys/time.h +.Ft int +.Fn adjfreq "const int64_t *freq" "int64_t *oldfreq" +.Sh DESCRIPTION +.Fn adjfreq +adjusts the rate in which time progresses if +.Fa freq +is non-null. +The unit of the rate of adjustment is nanoseconds per second, +shifted left 32 bits to allow for fractional values. +.Pp +If +.Fa oldfreq +is non-null, the current value is returned. +.Pp +Only the superuser may adjust the frequency. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn adjfreq +will fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +Either of the arguments point outside the process's allocated address space. +.It Bq Er EPERM +The +.Fa freq +argument is non-null and the process's effective user ID is not that +of the superuser. +.It Bq Er EINVAL +.Fa freq +is less than -500000 ppm or greater than 500000 ppm. +.El +.Sh SEE ALSO +.Xr date 1 , +.Xr adjtime 2 , +.Xr gettimeofday 2 , +.Xr ntpd 8 +.Sh HISTORY +The +.Fn adjfreq +function call first appeared in +.Ox 4.0 . diff --git a/man/test_files/mdoc/atq.1 b/man/test_files/mdoc/atq.1 new file mode 100644 index 00000000..23214da3 --- /dev/null +++ b/man/test_files/mdoc/atq.1 @@ -0,0 +1,103 @@ +.\" $OpenBSD: atq.1,v 1.7 2015/09/09 21:23:30 schwarze Exp $ +.\" +.\" Copyright (c) 1985, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)atq.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: September 9 2015 $ +.Dt ATQ 1 +.Os +.Sh NAME +.Nm atq +.Nd display the at(1) job queue +.Sh SYNOPSIS +.Nm atq +.Op Fl cnv +.Op Fl q Ar queue +.Op Ar name ... +.Sh DESCRIPTION +.Nm atq +displays the queue of jobs, created by the +.Xr at 1 +command, which are currently awaiting execution. +Unless the user is the superuser, only the user's own jobs will be displayed. +With no flags, the queue is sorted in the order that +the jobs will be executed. +.Pp +The options are as follows: +.Bl -tag -width "-q queueX" +.It Fl c +Sort the queue by the time that the jobs were submitted (created). +By default, +.Nm +will sort the queue by the time that the jobs will run. +.It Fl n +Only print the total number of files that are currently in the queue. +.It Fl q Ar queue +Restrict output to jobs in the specified +.Ar queue . +A queue designation consists of a single letter. +Valid queue designations range from +.Sy a +to +.Sy z +and +.Sy A +to +.Sy Z . +The +.Sy c +queue is the default for +.Xr at 1 +and the +.Sy E +queue for +.Xr batch 1 . +By default, +.Nm +will display jobs in all queues. +.It Fl v +Jobs that have completed but have not yet been removed are also displayed. +.El +.Pp +If a name(s) is provided, only those files belonging to that user(s) are +displayed. +.Sh FILES +.Bl -tag -width /var/cron/atjobs -compact +.It Pa /var/cron/atjobs +directory containing job files +.El +.Sh SEE ALSO +.Xr at 1 , +.Xr atrm 1 , +.Xr cron 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/man/test_files/mdoc/bc.1 b/man/test_files/mdoc/bc.1 new file mode 100644 index 00000000..260ca5da --- /dev/null +++ b/man/test_files/mdoc/bc.1 @@ -0,0 +1,409 @@ +.\" $OpenBSD: bc.1,v 1.36 2024/07/31 05:36:13 jmc Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, +.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)bc.1 6.8 (Berkeley) 8/8/91 +.\" +.Dd $Mdocdate: July 31 2024 $ +.Dt BC 1 +.Os +.Sh NAME +.Nm bc +.Nd arbitrary-precision arithmetic language and calculator +.Sh SYNOPSIS +.Nm bc +.Op Fl cl +.Op Fl e Ar expression +.Op Ar file ... +.Sh DESCRIPTION +.Nm +is an interactive processor for a language which resembles +C but provides unlimited precision arithmetic. +It takes input from any expressions on the command line and +any files given, then reads the standard input. +.Pp +Options available: +.Bl -tag -width Ds +.It Fl c +.Nm +is actually a preprocessor for +.Xr dc 1 , +which it invokes automatically, unless the +.Fl c +.Pq compile only +option is present. +In this case the generated +.Xr dc 1 +instructions are sent to the standard output, +instead of being interpreted by a running +.Xr dc 1 +process. +.It Fl e Ar expression +Evaluate +.Ar expression . +If multiple +.Fl e +options are specified, they are processed in the order given, +separated by newlines. +.It Fl l +Include an arbitrary precision math library. +The definitions in the library are available to command line expressions +and are documented below. +.El +.Pp +The syntax for +.Nm +programs is as follows: +.Sq L +means letter a-z; +.Sq E +means expression; +.Sq S +means statement. +As a non-portable extension, it is possible to use long names +in addition to single letter names. +A long name is a sequence starting with a lowercase letter +followed by any number of lowercase letters and digits. +The underscore character +.Pq Sq _ +counts as a letter. +.Pp +Comments +.Bd -unfilled -offset indent -compact +are enclosed in /* and */ +are enclosed in # and the next newline +.Ed +.Pp +The newline is not part of the line comment, +which in itself is a non-portable extension. +.Pp +Names +.Bd -unfilled -offset indent -compact +simple variables: L +array elements: L [ E ] +The words `ibase', `obase', and `scale' +The word `last' or a single dot +.Ed +.Pp +Other operands +.Bd -unfilled -offset indent -compact +arbitrarily long numbers with optional sign and decimal point +( E ) +sqrt ( E ) +length ( E ) number of significant decimal digits +scale ( E ) number of digits right of decimal point +L ( E , ... , E ) +.Ed +.Pp +The sequence +.Sq \e +is ignored within numbers. +.Pp +Operators +.Pp +The following arithmetic and logical operators can be used. +The semantics of the operators is the same as in the C language. +They are listed in order of decreasing precedence. +Operators in the same group have the same precedence. +.Bl -column "= += \-= *= /= %= ^=" "Associativity" "multiply, divide, modulus" -offset indent +.It Sy "Operator" Ta Sy "Associativity" Ta Sy "Description" +.It "++ \-\-" Ta "none" Ta "increment, decrement" +.It "\-" Ta "none" Ta "unary minus" +.It "^" Ta "right" Ta "power" +.It "* / %" Ta "left" Ta "multiply, divide, modulus" +.It "+ \-" Ta "left" Ta "plus, minus" +.It "= += -= *= /= %= ^=" Ta "right" Ta "assignment" +.It "== <= >= != < >" Ta "none" Ta "relational" +.It "!" Ta "none" Ta "boolean not" +.It "&&" Ta "left" Ta "boolean and" +.It "||" Ta "left" Ta "boolean or" +.El +.Pp +Note the following: +.Bl -bullet -offset indent +.It +The relational operators may appear in any expression. +The +.St -p1003.1-2008 +standard only allows them in the conditional expression of an +.Sq if , +.Sq while +or +.Sq for +statement. +.It +The relational operators have a lower precedence than the assignment +operators. +This has the consequence that the expression +.Sy a = b < c +is interpreted as +.Sy (a = b) < c , +which is probably not what the programmer intended. +.It +In contrast with the C language, the relational operators all have +the same precedence, and are non-associative. +The expression +.Sy a < b < c +will produce a syntax error. +.It +The boolean operators (!, && and ||) are non-portable extensions. +.It +The boolean not +(!) operator has much lower precedence than the same operator in the +C language. +This has the consequence that the expression +.Sy !a < b +is interpreted as +.Sy !(a < b) . +Prudent programmers use parentheses when writing expressions involving +boolean operators. +.El +.Pp +Statements +.Bd -unfilled -offset indent -compact +E +{ S ; ... ; S } +if ( E ) S +if ( E ) S else S +while ( E ) S +for ( E ; E ; E ) S +null statement +break +continue +quit +a string of characters, enclosed in double quotes +print E ,..., E +.Ed +.Pp +A string may contain any character, except double quote. +The if statement with an else branch is a non-portable extension. +All three E's in a for statement may be empty. +This is a non-portable extension. +The continue and print statements are also non-portable extensions. +.Pp +The print statement takes a list of comma-separated expressions. +Each expression in the list is evaluated and the computed +value is printed and assigned to the variable `last'. +No trailing newline is printed. +The expression may also be a string enclosed in double quotes. +Within these strings the following escape sequences may be used: +.Sq \ea +for bell (alert), +.Sq \eb +for backspace, +.Sq \ef +for formfeed, +.Sq \en +for newline, +.Sq \er +for carriage return, +.Sq \et +for tab, +.Sq \eq +for double quote and +.Sq \e\e +for backslash. +Any other character following a backslash will be ignored. +Strings will not be assigned to `last'. +.Pp +Function definitions +.Bd -unfilled -offset indent +define L ( L ,..., L ) { + auto L, ... , L + S; ... S + return ( E ) +} +.Ed +.Pp +As a non-portable extension, the opening brace of the define statement +may appear on the next line. +The return statement may also appear in the following forms: +.Bd -unfilled -offset indent +return +return () +return E +.Ed +.Pp +The first two are equivalent to the statement +.Dq return 0 . +The last form is a non-portable extension. +Not specifying a return statement is equivalent to writing +.Dq return (0) . +.Pp +Functions available in the math library, which is loaded by specifying the +.Fl l +flag on the command line: +.Pp +.Bl -tag -width j(n,x) -offset indent -compact +.It s(x) +sine +.It c(x) +cosine +.It e(x) +exponential +.It l(x) +log +.It a(x) +arctangent +.It j(n,x) +Bessel function +.El +.Pp +All function arguments are passed by value. +.Pp +The value of a statement that is an expression is printed +unless the main operator is an assignment. +The value printed is assigned to the special variable `last'. +This is a non-portable extension. +A single dot may be used as a synonym for `last'. +Either semicolons or newlines may separate statements. +Assignment to +.Ar scale +influences the number of digits to be retained on arithmetic +operations in the manner of +.Xr dc 1 . +Assignments to +.Ar ibase +or +.Ar obase +set the input and output number radix respectively. +.Pp +The same letter may be used as an array, a function, +and a simple variable simultaneously. +All variables are global to the program. +`Auto' variables are pushed down during function calls. +When using arrays as function arguments +or defining them as automatic variables, +empty square brackets must follow the array name. +.Pp +For example +.Bd -literal -offset indent +scale = 20 +define e(x){ + auto a, b, c, i, s + a = 1 + b = 1 + s = 1 + for(i=1; 1==1; i++){ + a = a*x + b = b*i + c = a/b + if(c == 0) return(s) + s = s+c + } +} +.Ed +.Pp +defines a function to compute an approximate value of +the exponential function and +.Pp +.Dl for(i=1; i<=10; i++) e(i) +.Pp +prints approximate values of the exponential function of +the first ten integers. +.Bd -literal -offset indent +$ bc -l -e 'scale = 500; 4 * a(1)' -e quit +.Ed +.Pp +prints an approximation of pi. +.Sh COMMAND LINE EDITING +.Nm +supports interactive command line editing, via the +.Xr editline 3 +library. +It is enabled by default if input is from a tty. +Previous lines can be recalled and edited with the arrow keys, +and other GNU Emacs-style editing keys may be used as well. +.Pp +The +.Xr editline 3 +library is configured with a +.Pa .editrc +file \- refer to +.Xr editrc 5 +for more information. +.Sh FILES +.Bl -tag -width /usr/share/misc/bc.library -compact +.It Pa /usr/share/misc/bc.library +math library, read when the +.Fl l +option is specified on the command line. +.El +.Sh SEE ALSO +.Xr dc 1 +.Rs +.\" 4.4BSD USD:6 +.%A L. L. Cherry +.%A R. H. Morris +.%T "BC \(em An Arbitrary Precision Desk-Calculator Language" +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl ce , +as well as the parts noted above, +are extensions to that specification. +.Sh HISTORY +The +.Nm +command first appeared in +.At v6 . +A complete rewrite of the +.Nm +command first appeared in +.Ox 3.5 . +.Sh AUTHORS +.An -nosplit +The original version of the +.Nm +command was written by +.An Robert Morris +and +.An Lorinda Cherry . +The current version of the +.Nm +utility was written by +.An Otto Moerbeek . +.Sh BUGS +The +.Ql quit +statement is interpreted when read, not when executed. +.Pp +Some non-portable extensions, as found in the GNU version of the +.Nm +utility are not implemented (yet). diff --git a/man/test_files/mdoc/brk.2 b/man/test_files/mdoc/brk.2 new file mode 100644 index 00000000..970e4217 --- /dev/null +++ b/man/test_files/mdoc/brk.2 @@ -0,0 +1,154 @@ +.\" $OpenBSD: brk.2,v 1.24 2019/09/08 22:50:59 schwarze Exp $ +.\" $NetBSD: brk.2,v 1.7 1995/02/27 12:31:57 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)brk.2 8.2 (Berkeley) 12/11/93 +.\" +.Dd $Mdocdate: September 8 2019 $ +.Dt BRK 2 +.Os +.Sh NAME +.Nm brk , +.Nm sbrk +.Nd change data segment size +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn brk "void *addr" +.Ft void * +.Fn sbrk "int incr" +.Sh DESCRIPTION +.Bf -symbolic +The +.Fn brk +and +.Fn sbrk +functions are historical curiosities +left over from earlier days before the advent of virtual memory management. +.Ef +The +.Fn brk +function sets the break or lowest address +of a process's data segment (uninitialized data) to +.Fa addr +(immediately above bss). +Data addressing is restricted between +.Fa addr +and the lowest stack pointer to the stack segment. +Memory is allocated by +.Fn brk +in page size pieces; +if +.Fa addr +is not evenly divisible by the system page size, it is +increased to the next page boundary. +.Pp +.\" The +.\" .Nm sbrk +.\" function +.\" allocates chunks of +.\" .Fa incr +.\" bytes +.\" to the process's data space +.\" and returns an address pointer. +.\" The +.\" .Xr malloc 3 +.\" function utilizes +.\" .Nm sbrk . +.\" .Pp +The current value of the program break is reliably returned by +.Dq Li sbrk(0) +(see also +.Xr end 3 ) . +The +.Xr getrlimit 2 +system call may be used to determine +the maximum permissible size of the +.Em data +segment; +it will not be possible to set the break +beyond the +.Fa rlim_max +value returned from a call to +.Xr getrlimit 2 , +e.g., +.Ql etext + rlp->rlim_max +(see +.Xr end 3 +for the definition of +.Em etext ) . +.Sh RETURN VALUES +.Rv -std brk +.Pp +The +.Fn sbrk +function returns a pointer to the base of the new storage if successful; +otherwise \-1 with +.Va errno +set to indicate why the allocation failed. +.Sh ERRORS +.Fn sbrk +will fail and no additional memory will be allocated if +one of the following are true: +.Bl -tag -width Er +.It Bq Er ENOMEM +The limit, as set by +.Xr setrlimit 2 , +was exceeded. +.It Bq Er ENOMEM +The maximum possible size of a data segment (compiled into the +system) was exceeded. +.It Bq Er ENOMEM +Insufficient space existed in the swap area +to support the expansion. +.El +.Sh SEE ALSO +.Xr execve 2 , +.Xr getrlimit 2 , +.Xr mmap 2 , +.Xr end 3 , +.Xr malloc 3 +.Sh HISTORY +A predecessor +.Fn break +appeared in +.At v1 . +The +.Fn sbrk +function call first appeared in +.At v4 +and +.Fn brk +in +.At v6 . +.Sh BUGS +Setting the break may fail due to a temporary lack of swap space. +It is not possible to distinguish this from a failure caused by exceeding +the maximum size of the data segment without consulting +.Xr getrlimit 2 . diff --git a/man/test_files/mdoc/cal.1 b/man/test_files/mdoc/cal.1 new file mode 100644 index 00000000..1e0e4fbb --- /dev/null +++ b/man/test_files/mdoc/cal.1 @@ -0,0 +1,132 @@ +.\" $OpenBSD: cal.1,v 1.33 2024/07/31 17:09:23 jmc Exp $ +.\" $NetBSD: cal.1,v 1.6 1995/09/02 05:34:20 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kim Letkeman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cal.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd $Mdocdate: July 31 2024 $ +.Dt CAL 1 +.Os +.Sh NAME +.Nm cal +.Nd displays a calendar +.Sh SYNOPSIS +.Nm cal +.Op Fl jmwy +.Op Ar month +.Op Ar year +.Sh DESCRIPTION +.Nm +displays a simple calendar. +Calendars may be displayed by month or by year. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl j +Display Julian dates (days one-based, numbered from January 1). +The options +.Fl j +and +.Fl w +are mutually exclusive. +.It Fl m +Display weeks starting on Monday instead of Sunday. +.It Fl w +Display week numbers in the month display. +If +.Fl m +is specified, the ISO week format is assumed. +The options +.Fl j +and +.Fl w +are mutually exclusive. +.It Fl y +Display a calendar for the current year. +.El +.Pp +A single numerical parameter specifies the +.Ar year +(1 \- 9999) +to be displayed. +The year must be fully specified: +.Dq Li cal 89 +will +.Em not +display a calendar for 1989. +Two parameters denote the +.Ar month +(1 \- 12, or a month name or abbreviation thereof) +and +.Ar year . +Alternatively, +a single parameter may be given specifying +the name or abbreviated name of a month: +in that case a calendar is displayed for that month of the current year. +If no parameters are specified, the current month's calendar is +displayed. +.Pp +A year starts on January 1st. +.Pp +The Gregorian Reformation is assumed to have occurred in 1752 after the 2nd +of September. +By this time, most countries had recognized the Reformation (although a +few did not recognize it until the early 1900s). +Eleven days following that date were eliminated by the Reformation, so the +calendar for that month is a bit unusual. +.Sh EXIT STATUS +.Ex -std cal +.Sh SEE ALSO +.Xr calendar 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +X/Open System Interfaces option of the +.St -p1003.1-2024 +specification. +.Pp +The flags +.Op Fl jmwy , +as well as the ability to specify a month name as a single argument, +are extensions to that specification. +.Pp +The week number computed by +.Fl mw +is compliant with the +.St -iso8601 +specification. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/man/test_files/mdoc/cat.1 b/man/test_files/mdoc/cat.1 new file mode 100644 index 00000000..47f37d10 --- /dev/null +++ b/man/test_files/mdoc/cat.1 @@ -0,0 +1,185 @@ +.\" $OpenBSD: cat.1,v 1.37 2024/08/01 14:08:07 jmc Exp $ +.\" $NetBSD: cat.1,v 1.12 1995/09/27 05:38:55 cgd Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cat.1 8.3 (Berkeley) 5/2/95 +.\" +.Dd $Mdocdate: August 1 2024 $ +.Dt CAT 1 +.Os +.Sh NAME +.Nm cat +.Nd concatenate and print files +.Sh SYNOPSIS +.Nm cat +.Op Fl benstuv +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility reads files sequentially, writing them to the standard output. +The +.Ar file +operands are processed in command-line order. +If +.Ar file +is a single dash +.Pq Sq - +or absent, +.Nm +reads from the standard input. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b +Number the lines, but don't count blank lines. +.It Fl e +Print a dollar sign +.Pq Ql \&$ +at the end of each line. +Implies the +.Fl v +option to display non-printing characters. +.It Fl n +Number the output lines, starting at 1. +.It Fl s +Squeeze multiple adjacent empty lines, causing the output to be +single spaced. +.It Fl t +Print tab characters as +.Ql ^I . +Implies the +.Fl v +option to display non-printing characters. +.It Fl u +The output is guaranteed to be unbuffered (see +.Xr setvbuf 3 ) . +.It Fl v +Displays non-printing characters so they are visible. +Control characters print as +.Ql ^X +for control-X, with the exception of the tab and EOL characters, +which are displayed normally. +The DEL character (octal 0177) prints as +.Ql ^? . +Non-ASCII characters (with the high bit set) are printed as +.Ql M- +(for meta) followed by the character for the low 7 bits. +.El +.Sh EXIT STATUS +.Ex -std cat +.Sh EXAMPLES +Print the contents of +.Ar file1 +to the standard output: +.Pp +.Dl $ cat file1 +.Pp +Sequentially print the contents of +.Ar file1 +and +.Ar file2 +to the file +.Ar file3 , +truncating +.Ar file3 +if it already exists. +See the manual page for your shell (e.g., +.Xr sh 1 ) +for more information on redirection. +.Pp +.Dl $ cat file1 file2 > file3 +.Pp +Print the contents of +.Ar file1 , +print data it receives from the standard input until it receives an +.Dv EOF +.Pq Sq ^D +character, print the contents of +.Ar file2 , +read and output contents of the standard input again, then finally output +the contents of +.Ar file3 . +Note that if the standard input referred to a file, the second dash +on the command line would have no effect, since the entire contents of the file +would have already been read and printed by +.Nm +when it encountered the first +.Ql \&- +operand. +.Pp +.Dl $ cat file1 - file2 - file3 +.Sh SEE ALSO +.Xr head 1 , +.Xr less 1 , +.Xr more 1 , +.Xr pr 1 , +.Xr sh 1 , +.Xr tail 1 , +.Xr vis 1 , +.Xr setvbuf 3 +.Rs +.%A Rob Pike +.%T "UNIX Style, or cat -v Considered Harmful" +.%J "USENIX Summer Conference Proceedings" +.%D 1983 +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2024 +specification. +.Pp +The flags +.Op Fl benstv +are extensions to that specification. +.Sh HISTORY +A +.Nm +utility appeared in +.At v1 . +.Sh CAVEATS +Because of the shell language mechanism used to perform output +redirection, the following command will cause the original data in +.Ar file1 +to be destroyed: +.Pp +.Dl $ cat file1 file2 > file1 +.Pp +To append +.Ar file2 +to +.Ar file1 , +instead use: +.Pp +.Dl $ cat file2 >> file1 diff --git a/man/test_files/mdoc/chdir.2 b/man/test_files/mdoc/chdir.2 new file mode 100644 index 00000000..5250bc64 --- /dev/null +++ b/man/test_files/mdoc/chdir.2 @@ -0,0 +1,130 @@ +.\" $OpenBSD: chdir.2,v 1.14 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: chdir.2,v 1.7 1995/02/27 12:32:00 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chdir.2 8.2 (Berkeley) 12/11/93 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt CHDIR 2 +.Os +.Sh NAME +.Nm chdir , +.Nm fchdir +.Nd change current working directory +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn chdir "const char *path" +.Ft int +.Fn fchdir "int fd" +.Sh DESCRIPTION +The +.Fa path +argument points to the pathname of a directory. +The +.Fn chdir +function causes the named directory to become the current working directory, +that is, the starting point for path searches of pathnames not beginning with +a slash +.Pq Ql / . +.Pp +The +.Fn fchdir +function causes the directory referenced by +.Fa fd +to become the current working directory, +the starting point for path searches of pathnames not beginning with +a slash +.Pq Ql / . +.Pp +In order for a directory to become the current directory, +a process must have execute (search) access to the directory. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn chdir +will fail and the current working directory will be unchanged if +one or more of the following are true: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named directory does not exist. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EACCES +Search permission is denied for any component of the pathname. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EIO +An I/O error occurred while reading from the file system. +.El +.Pp +.Fn fchdir +will fail and the current working directory will be unchanged if +one or more of the following are true: +.Bl -tag -width Er +.It Bq Er EACCES +Search permission is denied for the directory referenced by the +file descriptor. +.It Bq Er ENOTDIR +The file descriptor does not reference a directory. +.It Bq Er EBADF +The argument +.Fa fd +is not a valid file descriptor. +.It Bq Er EIO +An I/O error occurred while reading from the file system. +.El +.Sh SEE ALSO +.Xr chroot 2 +.Sh STANDARDS +The +.Fn chdir +and +.Fn fchdir +functions are expected to conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn chdir +system call first appeared in +.At v1 , +and +.Fn fchdir +in +.Bx 4.3 Reno . diff --git a/man/test_files/mdoc/chflags.2 b/man/test_files/mdoc/chflags.2 new file mode 100644 index 00000000..5f9b8470 --- /dev/null +++ b/man/test_files/mdoc/chflags.2 @@ -0,0 +1,228 @@ +.\" $OpenBSD: chflags.2,v 1.29 2022/08/04 06:20:24 jsg Exp $ +.\" $NetBSD: chflags.2,v 1.6 1995/02/27 12:32:03 cgd Exp $ +.\" +.\" Copyright (c) 1989, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chflags.2 8.1 (Berkeley) 6/9/93 +.\" +.Dd $Mdocdate: August 4 2022 $ +.Dt CHFLAGS 2 +.Os +.Sh NAME +.Nm chflags , +.Nm chflagsat , +.Nm fchflags +.Nd set file flags +.Sh SYNOPSIS +.In sys/stat.h +.Ft int +.Fn chflags "const char *path" "unsigned int flags" +.Ft int +.Fn fchflags "int fd" "unsigned int flags" +.In sys/stat.h +.In fcntl.h +.Ft int +.Fn chflagsat "int fd" "const char *path" "unsigned int flags" "int atflags" +.Sh DESCRIPTION +The file whose name is given by +.Fa path +or referenced by the descriptor +.Fa fd +has its flags changed to +.Fa flags . +.Pp +The flags are the bitwise OR of zero or more of the following values: +.Pp +.Bl -tag -width "SF_IMMUTABLE" -compact -offset indent +.It Dv UF_NODUMP +Do not dump the file. +.It Dv UF_IMMUTABLE +The file may not be changed. +.It Dv UF_APPEND +The file may only be appended to. +.It Dv SF_ARCHIVED +The file may be archived. +.It Dv SF_IMMUTABLE +The file may not be changed. +.It Dv SF_APPEND +The file may only be appended to. +.El +.Pp +The +.Dv UF_IMMUTABLE +and +.Dv UF_APPEND +flags may be set or unset by either the owner of a file or the superuser. +.Pp +The +.Dv SF_ARCHIVED , +.Dv SF_IMMUTABLE +and +.Dv SF_APPEND +flags may only be set or unset by the superuser. +They may be set at any time, but normally may only be unset when +the system is in single-user mode. +(See +.Xr init 8 +for details.) +.Pp +The +.Fn chflagsat +function is equivalent to +.Fn chflags +except in the case where +.Fa path +specifies a relative path. +In this case the file to be changed is determined relative to the directory +associated with the file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn chflagsat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used. +If +.Fa atflags +is also zero, the behavior is identical to a call to +.Fn chflags . +.Pp +The +.Fa atflags +argument is the bitwise OR of zero or more of the following values: +.Pp +.Bl -tag -width AT_SYMLINK_NOFOLLOW -offset indent -compact +.It Dv AT_SYMLINK_NOFOLLOW +If +.Fa path +names a symbolic link, then the flags of the symbolic link are changed. +.El +.Pp +The +.Fn fchflags +function is equivalent to +.Fn chflags +except that the file whose flags are changed is specified +by the file descriptor +.Fa fd . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn chflags +will fail if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EPERM +The effective user ID does not match the owner of the file and +the effective user ID is not the superuser, or the effective user ID +is not the superuser and at least one of the super-user-only flags +for the named file would be changed. +.It Bq Er EOPNOTSUPP +The named file resides on a file system that does not support file +flags. +.It Bq Er EROFS +The named file resides on a read-only file system. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.It Bq Er EINVAL +The +.Fa flags +value is invalid. +.It Bq Er EINVAL +The descriptor references a block or character device and the effective +user ID is not the superuser. +.El +.Pp +.Fn fchflags +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The descriptor is not valid. +.It Bq Er EINVAL +.Fa fd +refers to a socket, not to a file. +.It Bq Er EINVAL +The descriptor references a block or character device and the effective +user ID is not the superuser. +.It Bq Er EINVAL +The +.Fa flags +value is invalid. +.It Bq Er EPERM +The effective user ID does not match the owner of the file and +the effective user ID is not the superuser, or the effective user ID +is not the superuser and at least one of the super-user-only flags +for the named file would be changed. +.It Bq Er EOPNOTSUPP +The named file resides on a file system that does not support file +flags. +.It Bq Er EROFS +The file resides on a read-only file system. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Sh SEE ALSO +.Xr chflags 1 , +.Xr init 8 +.Sh HISTORY +The +.Fn chflags +and +.Fn fchflags +functions first appeared in +.Bx 4.3 Reno . +The +.Fn chflagsat +function first appeared in +.Fx 10.0 . +It was added to +.Ox +in +.Ox 5.7 . diff --git a/man/test_files/mdoc/chmod.2 b/man/test_files/mdoc/chmod.2 new file mode 100644 index 00000000..e67e8c41 --- /dev/null +++ b/man/test_files/mdoc/chmod.2 @@ -0,0 +1,275 @@ +.\" $OpenBSD: chmod.2,v 1.28 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: chmod.2,v 1.7 1995/02/27 12:32:06 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chmod.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt CHMOD 2 +.Os +.Sh NAME +.Nm chmod , +.Nm fchmodat , +.Nm fchmod +.Nd change mode of file +.Sh SYNOPSIS +.In sys/stat.h +.Ft int +.Fn chmod "const char *path" "mode_t mode" +.Ft int +.Fn fchmod "int fd" "mode_t mode" +.In sys/stat.h +.In fcntl.h +.Ft int +.Fn fchmodat "int fd" "const char *path" "mode_t mode" "int flag" +.Sh DESCRIPTION +The +.Fn chmod +function sets the file permission bits of the file specified by the pathname +.Fa path +to +.Fa mode . +.Fn chmod +verifies that the process owner (user) either owns the specified file +or is the superuser. +.Pp +The +.Fa mode +argument is the bitwise OR of zero or more of the permission bit masks +from the following list: +.Bd -literal -offset indent +#define S_IRWXU 0000700 /* RWX mask for owner */ +#define S_IRUSR 0000400 /* R for owner */ +#define S_IWUSR 0000200 /* W for owner */ +#define S_IXUSR 0000100 /* X for owner */ + +#define S_IRWXG 0000070 /* RWX mask for group */ +#define S_IRGRP 0000040 /* R for group */ +#define S_IWGRP 0000020 /* W for group */ +#define S_IXGRP 0000010 /* X for group */ + +#define S_IRWXO 0000007 /* RWX mask for other */ +#define S_IROTH 0000004 /* R for other */ +#define S_IWOTH 0000002 /* W for other */ +#define S_IXOTH 0000001 /* X for other */ + +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISVTX 0001000 /* save swapped text even after use */ +.Ed +.Pp +If mode +.Dv ISVTX +(the +.Em sticky bit ) +is set on a file, it is ignored. +.Pp +If mode +.Dv ISVTX +(the +.Em sticky bit ) +is set on a directory, an unprivileged user may not delete or rename +files of other users in that directory. +The sticky bit may be set by any user on a directory which the user owns +or has appropriate permissions. +For more details of the properties of the sticky bit, see +.Xr sticky 8 . +.Pp +Writing or changing the owner of a file turns off the set-user-ID and +set-group-ID bits unless the user is the superuser. +This makes the system somewhat more secure by protecting +set-user-ID (set-group-ID) files from remaining set-user-ID (set-group-ID) +if they are modified, at the expense of a degree of compatibility. +.Pp +The +.Fn fchmodat +function is equivalent to +.Fn chmod +except in the case where +.Fa path +specifies a relative path. +In this case the file to be changed is determined relative to the directory +associated with the file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn fchmodat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used. +If +.Fa flag +is also zero, the behavior is identical to a call to +.Fn chmod . +.Pp +The +.Fa flag +argument is the bitwise OR of zero or more of the following values: +.Pp +.Bl -tag -width AT_SYMLINK_NOFOLLOW -offset indent -compact +.It Dv AT_SYMLINK_NOFOLLOW +If +.Fa path +names a symbolic link, then the mode of the symbolic link is changed. +.El +.Pp +The +.Fn fchmod +function is equivalent to +.Fn chmod +except that the file whose permissions are changed is specified +by the file descriptor +.Fa fd . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn chmod +and +.Fn fchmodat +functions will fail and the file mode will be unchanged if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er EINVAL +.Fa mode +contains bits other than the file type and those described above. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EPERM +The effective user ID does not match the owner of the file and +the effective user ID is not the superuser. +.It Bq Er EROFS +The named file resides on a read-only file system. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Pp +Additionally, the +.Fn fchmodat +function will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The value of the +.Fa flag +argument was neither zero nor +.Dv AT_SYMLINK_NOFOLLOW . +.It Bq Er EBADF +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is a valid file descriptor but it does not reference a directory. +.It Bq Er EOPNOTSUPP +The +.Fa flag +argument specifies +.Dv AT_SYMLINK_NOFOLLOW +on a symbolic link and the file system does not support that operation. +.It Bq Er EACCES +The +.Fa path +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fd +file descriptor references. +.El +.Pp +.Fn fchmod +will fail and the file mode will be unchanged if: +.Bl -tag -width Er +.It Bq Er EBADF +The descriptor is not valid. +.It Bq Er EINVAL +.Fa fd +refers to a socket, not to a file. +.It Bq Er EINVAL +.Fa mode +contains bits other than the file type and those described above. +.It Bq Er EPERM +The effective user ID does not match the owner of the file and +the effective user ID is not the superuser. +.It Bq Er EROFS +The file resides on a read-only file system. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Sh SEE ALSO +.Xr chmod 1 , +.Xr chown 2 , +.Xr open 2 , +.Xr stat 2 , +.Xr sticky 8 +.Sh STANDARDS +The +.Fn chmod , +.Fn fchmod , +and +.Fn fchmodat +functions are expected to conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn chmod +system call first appeared in +.At v1 ; +.Fn fchmod +in +.Bx 4.1c ; +and +.Fn fchmodat +has been available since +.Ox 5.0 . diff --git a/man/test_files/mdoc/closefrom.2 b/man/test_files/mdoc/closefrom.2 new file mode 100644 index 00000000..f1166083 --- /dev/null +++ b/man/test_files/mdoc/closefrom.2 @@ -0,0 +1,67 @@ +.\" $OpenBSD: closefrom.2,v 1.10 2019/05/31 18:36:58 cheloha Exp $ +.\" +.\" Copyright (c) 2004 Ted Unangst. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.Dd $Mdocdate: May 31 2019 $ +.Dt CLOSEFROM 2 +.Os +.Sh NAME +.Nm closefrom +.Nd delete many descriptors +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn closefrom "int fd" +.Sh DESCRIPTION +The +.Fn closefrom +call deletes all descriptors numbered +.Fa fd +and higher from the per-process file descriptor table. +It is effectively the same as calling +.Xr close 2 +on each descriptor. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn closefrom +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa fd +is greater than all open file descriptors. +.It Bq Er EINTR +An interrupt was received. +.El +.Sh SEE ALSO +.Xr close 2 +.Sh STANDARDS +.Fn closefrom +is a +.Bx +and Solaris extension. +.Sh HISTORY +The +.Fn closefrom +function first appeared in Solaris 9 and has been available since +.Ox 3.5 . diff --git a/man/test_files/mdoc/cu.1 b/man/test_files/mdoc/cu.1 new file mode 100644 index 00000000..2b03115e --- /dev/null +++ b/man/test_files/mdoc/cu.1 @@ -0,0 +1,224 @@ +.\" $OpenBSD: cu.1,v 1.25 2023/10/03 05:20:38 jmc Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd $Mdocdate: October 3 2023 $ +.Dt CU 1 +.Os +.Sh NAME +.Nm cu +.Nd serial terminal emulator +.Sh SYNOPSIS +.Nm +.Op Fl dr +.Op Fl E Ar escape_char +.Op Fl l Ar line +.Op Fl s Ar speed | Fl Ar speed +.Nm +.Op Ar host +.Sh DESCRIPTION +.Nm +is used to connect to another system over a serial link. +In the era before modern networks, it was typically used to +connect to a modem in order to dial in to a remote host. +It is now frequently used for tasks such as attaching to the +serial console of another machine for administrative or +debugging purposes. +.Pp +The options are as follows: +.Bl -tag -width 4n +.It Fl d +Specify that the line is directly connected and +.Nm +should not allow the driver to block waiting for a carrier to be detected. +.It Fl E Ar escape_char +Specify an escape character to use instead of the default tilde. +.It Fl l Ar line +Specify the line to use. +Any of the forms +.Pa cua00 , +.Pa /dev/cua00 , +or +.Pa usb0.1.00002.3 +are permitted. +.Pp +The default is +.Pa /dev/cua00 . +See +.Xr cua 4 +for information on terminal devices. +Users in group +.Dq dialer +are permitted to use +.Xr cua 4 +devices by default. +.Pp +See +.Xr sysctl 2 +.Va hw.ucomnames +for available USB serial lines. +.It Fl r +Start +.Nm +in restricted mode. +This prevents all local filesystem operations +.Po +.Cm ~R , +.Cm ~X , +and +.Cm ~> +.Pc +and command executions +.Po +.Cm ~C +and +.Cm ~$ +.Pc . +.It Fl s Ar speed | Fl Ar speed +Set the speed of the connection. +The default is 9600. +.El +.Pp +If +.Ar host +is given, +.Nm +uses the +.Xr remote 5 +database to retrieve the +.Sy dc Pq directly connected , +.Sy dv Pq device +and +.Sy br Pq baud rate +capabilities for that host. +The +.Nm +utility ignores other capabilities found in that database. +.Pp +Typed characters are normally transmitted directly to the remote +machine (which does the echoing as well). +A tilde +.Pq Ql ~ +appearing as the first character of a line is an escape signal; the +following are recognized: +.Bl -tag -offset indent -width Fl +.It Ic ~^D No or Ic ~. +Drop the connection and exit. +Only the connection is dropped \(en the login session is not terminated. +.It Ic ~> +Copy file from local to remote. +.Nm +prompts for the name of a local file to transmit. +.It Ic ~$ +Pipe the output from a local +.Ux +process to the remote host. +The command string sent to the local +.Ux +system is processed by the shell. +.It Ic ~# +Send a +.Dv BREAK +to the remote system. +.It Ic ~^Z +Stop +.Nm +(only available with job control). +.It Ic ~C +Fork a child process on the local system to perform special protocols +such as XMODEM. +The child program will be run with the following arrangement of +file descriptors: +.Pp +.Bl -item -compact -offset indent +.It +0 \(<> remote tty in +.It +1 \(<> remote tty out +.It +2 \(<> local tty stderr +.El +.It Ic ~D +Deassert the data terminal ready (DTR) line briefly. +.It Ic ~R +Record all output from the remote system to a file. +If the given file already exists, it is appended to. +If no file is specified, any existing recording is stopped. +.It Ic ~S +Change the speed of the connection. +.It Ic ~X +Send a file with the XMODEM protocol. +.It Ic ~? +Get a summary of the tilde escapes. +.El +.Pp +When +.Nm +prompts for an argument, for example during setup of a file transfer, +the line typed may be edited with the standard erase and kill characters. +A null line in response to a prompt, or an interrupt, will abort the +dialogue and return the user to the remote machine. +.Pp +.Nm +guards against multiple users connecting to a remote system by opening +modems and terminal lines with exclusive access. +.Sh ENVIRONMENT +.Bl -tag -width REMOTEXXX +.It Ev HOST +The default value for +.Ar host +if none is specified via the command line. +.It Ev REMOTE +A system description, or an absolute path to a +.Xr remote 5 +system description database. +.El +.Sh FILES +.Bl -tag -width /etc/remote +.It Pa /etc/remote +host description file +.El +.Sh EXIT STATUS +.Ex -std cu +.Sh SEE ALSO +.Xr sysctl 2 , +.Xr cua 4 , +.Xr remote 5 +.Sh HISTORY +The +.Nm +.Pq Dq Call Unix +command first appeared outside of Bell Labs in PWB/UNIX 1.0. +It was reimplemented as part of the +.Nm tip +command in +.Bx 4.1c . +The current version was written for +.Ox 5.4 . +.Sh AUTHORS +.An Nicholas Marriott Aq Mt nicm@openbsd.org diff --git a/man/test_files/mdoc/cut.1 b/man/test_files/mdoc/cut.1 new file mode 100644 index 00000000..5dfeff59 --- /dev/null +++ b/man/test_files/mdoc/cut.1 @@ -0,0 +1,184 @@ +.\" $OpenBSD: cut.1,v 1.28 2022/08/04 15:38:33 schwarze Exp $ +.\" $NetBSD: cut.1,v 1.6 1995/10/02 20:19:26 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cut.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: August 4 2022 $ +.Dt CUT 1 +.Os +.Sh NAME +.Nm cut +.Nd select portions of each line of a file +.Sh SYNOPSIS +.Nm cut +.Fl b Ar list +.Op Fl n +.Op Ar +.Nm cut +.Fl c Ar list +.Op Ar +.Nm cut +.Fl f Ar list +.Op Fl s +.Op Fl d Ar delim +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility selects portions of each line (as specified by +.Ar list ) +from each +.Ar file +and writes them to the standard output. +If no +.Ar file +arguments are specified, or a file argument is a single dash +.Pq Sq \- , +.Nm +reads from the standard input. +The items specified by +.Ar list +can be in terms of column position or in terms of fields delimited +by a special character. +Column and field numbering starts from 1; +output is in the same order as input, not in the order selected. +.Pp +.Ar list +is a comma or whitespace separated set of numbers and/or +number ranges. +Number ranges consist of a number, a dash +.Pq Sq \- , +and a second number +which select the fields or columns from the first number to the second, +inclusive. +Numbers or number ranges may be preceded by a dash, which selects all +fields or columns from 1 to the first number. +Numbers or number ranges may be followed by a dash, which selects all +fields or columns from the last number to the end of the line. +Numbers and number ranges may be repeated, overlapping, and in any order. +It is not an error to select fields or columns not present in the +input line. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar list +The +.Ar list +specifies byte positions. +.It Fl c Ar list +The +.Ar list +specifies character positions. +.It Fl d Ar delim +Use the first character of +.Ar delim +as the field delimiter character. +The default is the +.Aq TAB +character. +.It Fl f Ar list +The +.Ar list +specifies fields, separated by the field delimiter character. +The selected fields are output, +separated by the field delimiter character. +.It Fl n +Do not split multi-byte characters. +A character is written to standard output if and only if the byte +position holding its last byte is selected. +.It Fl s +Suppresses lines with no field delimiter characters. +Unless specified, lines with no delimiters are passed through unmodified. +.El +.Sh ENVIRONMENT +.Bl -tag -width LC_CTYPE +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 . +It decides which byte sequences form characters. +If unset or set to +.Qq C , +.Qq POSIX , +or an unsupported value, +.Fl c +does the same as +.Fl b , +.Fl n +has no effect, and +.Fl d +uses the first byte of +.Ar delim . +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 if all input files are output successfully, +and >0 if an error occurs. +.Sh EXAMPLES +Extract login names and shells from the system +.Xr passwd 5 +file as +.Dq name:shell +pairs: +.Pp +.Dl "$ cut -d : -f 1,7 /etc/passwd" +.Pp +Show the names and login times of logged in users: +.Pp +.Dl "$ who | cut -c 1-8,18-30" +.Sh SEE ALSO +.Xr awk 1 , +.Xr paste 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Sh HISTORY +A +.Nm +command first appeared outside Bell Labs in +.At III +and has been available since +.Bx 4.3 Reno . +.Sh AUTHORS +.An -nosplit +The original Bell Labs version was written by +.An Gottfried W. R. Luderer +and the +.Bx +version by +.An Adam S. Moskowitz +and +.An Marciano Pitargue . diff --git a/man/test_files/mdoc/cvs.1 b/man/test_files/mdoc/cvs.1 new file mode 100644 index 00000000..9873e396 --- /dev/null +++ b/man/test_files/mdoc/cvs.1 @@ -0,0 +1,1987 @@ +.\" $OpenBSD: cvs.1,v 1.128 2015/12/24 16:54:37 mmcc Exp $ +.\" +.\" Copyright (c) 2004 Jean-Francois Brousseau +.\" Copyright (c) 2004-2008 Xavier Santolaria +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: December 24 2015 $ +.Dt CVS 1 +.Os +.Sh NAME +.Nm cvs +.Nd OpenCVS Concurrent Versioning System +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl flnQqRrtVvw +.Op Fl d Ar root +.Op Fl e Ar editor +.Xo +.Oo Fl s +.Ar var Ns = Ns Ar val Oc +.Xc +.Op Fl T Ar tmpdir +.Op Fl z Ar level +.Ar command ... +.Ek +.Sh DESCRIPTION +The +.Nm +program acts as both client and server for the use of and administration of +a CVS source repository. +CVS is used to maintain version information on files that are kept in a +repository. +Although it is more commonly used to track changes in source code, there +are no real limitations to the type of files that can be stored in a +repository. +For a general introduction to CVS, see +.Xr cvsintro 7 . +.Pp +.Nm +reads its startup configuration file, +.Pa .cvsrc , +from the home directory of the user who invoked it. +This file is used to specify implicit options passed to +.Nm +or one of its commands whenever it is invoked. +The defaults in the configuration file can be overridden with the +.Fl f +option (see below). +See +.Xr cvs 5 +for further information. +.Pp +.Nm +also supports +keyword substitution \(en +see the +.Xr rcs 1 +man page for more information. +.Pp +The following options are supported: +.Bl -tag -width Ds +.It Fl d Ar root +Use +.Ar root +as the path to the root directory of the CVS repository. +The value must specify an absolute path. +.It Fl e Ar editor +Use the program +.Ar editor +whenever editing log information. +This option overrides the environment variables CVSEDITOR, VISUAL, and EDITOR. +.It Fl f +Do not read the user's configuration file on startup. +.It Fl l +Suppress logging of history information. +.It Fl n +Dry-run mode. +Show which files will be used by the command issued +without really running it. +.It Fl Q +Be extra quiet. +Only error messages will be displayed. +.It Fl q +Be quiet about reporting. +.It Fl R +Permit checkout from a read-only repository. +Implies +.Fl l . +See also +.Ev CVSREADONLYFS , +below. +.It Fl r +Extract files in read-only mode. +.It Fl s Ar var Ns = Ns Ar val +Set the value of the internal variable +.Ar var +to the string +.Ar val . +.It Fl T Ar tmpdir +Set the value of the directory where temporary files are to be created. +The default is set to +.Pa /tmp . +This option overrides the +.Ev TMPDIR +environment variable. +.It Fl t +Trace program execution. +.It Fl V +Verbose mode. +All messages will be displayed. +This is the default. +.Fl V +and +.Fl Q +are mutually exclusive. +If both are specified, +.Fl Q +takes precedence. +.It Fl v +Display version information and exit. +.It Fl w +Extract new files in read-write mode. +Overrides the setting of the +.Ev CVSREAD +environment variable. +This is the default unless +.Ev CVSREAD +is set or the +.Fl r +option is specified. +.It Fl z Ar level +Specify the compression level to +.Xr gzip 1 +when transferring files. +The compression level ranges from 1 to 9, +with 1 being the fastest, +and 9 providing the best level of compression. +The default is 6. +.El +.Sh COMMANDS +.Nm +supports the following commands: +add, +admin, +annotate, +checkout, +commit, +diff, +edit, +editors, +export, +history, +import, +init, +kserver, +log, +rannotate, +rdiff, +release, +remove, +rlog, +rtag, +server, +status, +tag, +unedit, +update, +version, +watch, +watchers. +The commands are fully explained in this section. +.Pp +Files may be selected by +.Em revision +or, where no revision is specified, +the latest revision of the default branch is used. +Revisions are specified either by using the +.Fl r +option or +by appending the revision number to any option that supports it. +.Pp +.Nm +supports the notion of +.Em state . +The state is an arbitrary string of characters used to describe a file +(or a specific revision of a file). +States can be set or changed using the +.Fl s +option, for CVS tools which support it. +The state of a file/revision can be modified without having to +.Ic commit +a new file/revision. +The default state is +.Sq Exp +(Experimental). +Examples of states could be +.Sq Dev , +.Sq Reviewed , +or +.Sq Stab . +.Ss add +Before a file is known to +.Nm , +it must be added to the repository using this command. +Adding a file does not actually publish the contents of the +file: the +.Ic commit +command must also be used to publish it into the repository, +and thus let others access the file. +.Pp +Note: since directories have no versioning system, it is sufficient +to add them with the +.Ic add +command alone; the +.Ic commit +command is not necessary. +.Bd -literal -offset indent +usage: cvs add [-k mode] [-m msg] file ... +.Ed +.Pp +The +.Ic add +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl k Ar mode +Specify the keyword substitution mode. +.It Fl m Ar msg +Attach log message +.Ar msg . +By default, no log message is required. +.El +.Pp +Aliases: +.Ic ad , +.Ic new . +.Ss admin +The +.Ic admin +command is used to directly modify the RCS files. +.Bd -literal -offset indent +usage: cvs admin [-Iq] [-b branch] [-k mode] [-m rev:msg] + [-N tag[:rev]] [-n tag[:rev]] [-o rev] + [-s state[:rev]] [-t file | str] +.Ed +.Pp +The +.Ic admin +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl b Ar branch +Set the default branch to +.Ar branch . +.It Fl I +Command is interactive. +.It Fl k Ar mode +Specify the keyword substitution mode. +.It Fl m Ar rev : Ns Ar msg +Change the log message of a revision. +.It Xo Fl N +.Ar tag Ns Op : Ns Ar rev +.Xc +Same as +.Fl n , +but override tag if it already exists. +.It Xo Fl n +.Ar tag Ns Op : Ns Ar rev +.Xc +Associate the +.Ar tag +with the +.Ar rev +or the branch given as argument. +If the revision or the branch is not specified, the tag is deleted. +The +.Sq \&: +character means the association of the tag and the latest revision of +the default branch. +A branch number ending with the +.Sq \&. +character means the current latest revision in the branch. +This option is functionally the same as the +.Ic rtag +command, but it avoids the check of the tags done with the +.Pa CVSROOT/taginfo +file. +.It Fl o Ar rev +Delete one or more revisions. +The specifications of the values or revisions are as follows: +.Bl -tag -width Ds +.It rev +Specific revision. +.It rev1:rev2 +Delete all revisions of a branch between +.Ar rev1 +and +.Ar rev2 . +.It rev1::rev2 +Delete all revisions of a branch between +.Ar rev1 +and +.Ar rev2 +without deleting revisions +.Ar rev1 +and +.Ar rev2 . +.It :rev +Delete all revisions of the branch until revision +.Ar rev . +.It rev: +Delete all revisions of the branch from revision +.Ar rev +until the last revision of the branch. +.El +.It Fl q +Quiet mode. +.It Xo Fl s +.Ar state Ns Op : Ns Ar rev +.Xc +Change state of a revision. +.It Fl t Ar file \*(Ba Ar str +Change the descriptive text. +The descriptive text is taken from the +.Ar file +specified as argument or from the string +.Ar str +given as argument if it is preceded by the +.Sq - +character. +If no argument is used, the descriptive text is taken from standard input. +.El +.Pp +Aliases: +.Ic adm , +.Ic rcs . +.Ss annotate +For each line of any files specified, show information about its +last revision. +The information given is the last revision when a modification occurred, +the author's name, and the date of the revision. +.Bd -literal -offset indent +usage: cvs annotate [-flR] [-D date | -r rev] [file ...] +.Ed +.Pp +The +.Ic annotate +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl D Ar date +Show the annotations as of the latest revision no later than +.Ar date . +.It Fl f +Force the use of the head revision if the specified +tag or date is not found. +This can be used in combination with +.Fl D +or +.Fl r +to ensure that there is some output from the +.Ic annotate +command, even if only to show Revision 1.1 of the file. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Show annotations as of revision +.Ar rev +(can be a revision number or a tag). +.El +.Pp +Aliases: +.Ic ann , +.Ic blame . +.Ss checkout +The +.Ic checkout +command is used to create a local copy of one or more modules present on the +target CVS repository. +.Bd -literal -offset indent +usage: cvs checkout [-AcflNnPpRs] [-d dir] [-j rev] [-k mode] + -D date | -r rev module ... +.Ed +.Pp +The +.Ic checkout +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl A +Reset any sticky tags, dates, or keyword substitution modes that +have been set on the tree. +.It Fl c +Display the list of available modules. +.It Fl D Ar date +Check out as of the latest revision no later than +.Ar date +(implies +.Fl P ) +(is sticky). +.It Fl d Ar dir +Check out in directory +.Ar dir +instead of the directory bearing the same name as the +.Ar module . +.It Fl f +Force the use of the head revision if the specified +tag or date is not found. +.It Fl j Ar rev +Merge in changes made between current revision and +.Ar rev . +If two +.Fl j +options are specified, only merge the differences between the two +revisions of the branch. +This allows successive merges without having to resolve +already resolved conflicts again. +.It Fl k Ar mode +Specify the keyword substitution mode (is sticky). +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl N +If used in conjunction with the +.Fl d +option, files are placed in local directory +.Ar module , +located in directory +.Ar dir . +.It Fl n +Do not execute programs listed in the +.Pa CVSROOT/modules +file. +.It Fl P +Prune empty directories. +.It Fl p +Check out files to standard output (avoids stickiness). +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Check out from a particular revision or branch (implies +.Fl P ) +(is sticky). +.It Fl s +Like +.Fl c , +but include module status. +.El +.Pp +Aliases: +.Ic co , +.Ic get . +.Ss commit +The +.Ic commit +command is used to send local changes back to the server and update the +repository's information to reflect the changes. +.Bd -literal -offset indent +usage: cvs commit [-flnR] [-F logfile | -m msg] [-r rev] [file ...] +.Ed +.Pp +The +.Ic commit +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl F Ar logfile +Specify a +.Ar file +which contains the log message. +.It Fl f +Force a file to be committed, even though it is unchanged. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl m Ar msg +Specify a log message on the command line (suppresses the editor invocation). +.It Fl n +Do not execute programs listed in the +.Pa CVSROOT/modules +file. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Commit to a particular symbolic or numerical revision. +.El +.Pp +Aliases: +.Ic ci , +.Ic com . +.Ss diff +The +.Ic diff +command is very similar to the +.Xr diff 1 +program, except that the differential comparisons that it generates are +between local or remote revisions of files stored in the CVS repository. +.Bd -literal -offset indent +usage: cvs diff [-abcdilNnpRuw] + [[-D date1 | -r rev1] [-D date2 | -r rev2]] + [-k mode] [file ...] +.Ed +.Pp +The +.Ic diff +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl a +Treat all files as ASCII text. +See +.Xr diff 1 +for more information. +.It Fl b +Causes trailing blanks (spaces and tabs) to be ignored, and other +strings of blanks to compare equal. +.It Fl c +Produces a diff with three lines of context. +See +.Xr diff 1 +for more information. +.It Xo Fl D Ar date1 +.Op Fl D Ar date2 +.Xc +Differences between the revision at +.Ar date1 +and the working copy or +.Ar date1 +and +.Ar date2 +(if specified). +.It Fl d +Try very hard to produce a diff as small as possible. +See +.Xr diff 1 +for more information. +.It Fl i +Ignore the case of letters. +For example, +.Sq A +will compare equal to +.Sq a . +.It Fl k Ar mode +Specify the keyword substitution mode. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl N +Include added or removed files. +.It Fl n +Produces a diff in the same format as that used by +.Xr rcsdiff 1 , +with a count of changed lines on each insert or delete command. +.It Fl p +With unified and context diffs, show with each change the first +40 characters of the last line before the context beginning with +a letter, an underscore or a dollar sign. +See +.Xr diff 1 +for more information. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Xo Fl r Ar rev1 +.Op Fl r Ar rev2 +.Xc +Differences between revision +.Ar rev1 +and the working copy or +.Ar rev1 +and +.Ar rev2 +(if specified). +.It Fl t +Will expand tabs in output lines. +Normal or +.Fl c +output adds character(s) to the front of each line which may screw up +the indentation of the original source lines and make the output listing +difficult to interpret. +This option will preserve the original source's indentation. +.It Fl u +Produces a unified diff with three lines of context. +See +.Xr diff 1 +for more information. +.It Fl w +Is similar to +.Fl b +but causes whitespace (blanks and tabs) to be totally ignored. +For example, +.Dq if (\ \&a == b \&) +will compare equal to +.Dq if(a==b) . +.El +.Pp +Aliases: +.Ic di , +.Ic dif . +.Ss edit +The +.Ic edit +command is used to make a file that is being watched +(and therefore read-only) +readable and writable and to inform others that it is in the +process of being changed. +Notifications terminate when the +.Ic commit +command is issued. +Editing rights on the file can be given up using the +.Ic unedit +command, which terminates the temporary notifications. +.Bd -literal -offset indent +usage: cvs edit [-lR] [-a action] [file ...] +.Ed +.Pp +The +.Ic edit +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl a Ar action +Specify the temporary notification wanted: +.Pp +.Bl -tag -width Ds -compact +.It Cm commit +Another user has committed changes to the file. +.It Cm edit +Another user has issued the +.Ic edit +command on the file. +.It Cm unedit +Another user has issued the +.Ic unedit +command on the file. +.It Cm all +All of the above. +.It Cm none +None of the above. +.El +.Pp +The +.Fl a +flag may appear more than once, or not at all. +If omitted, the action defaults to +.Cm all . +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Ss editors +The +.Ic editors +command lists the users with edition rights on a file. +For that, pseudo-lock mode must be enabled (see the +.Ic watch +command). +The email address of the user editing the file, the timestamp +when the edition first started, the host from where the edition +has been requested and the path to the edited file are listed. +.Bd -literal -offset indent +usage: cvs editors [-lR] [file ...] +.Ed +.Pp +The +.Ic editors +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Ss export +The +.Ic export +command extracts a copy of +.Ar module +without including the directories used for management by +.Nm . +This eases production of a software release. +A date or a revision must be specified for the command to be valid, +which ensures that later extractions can be reproduced with the same +options as the release. +.Pp +The checked out module's files will be placed in a directory +bearing the same name as the checked out module, by default. +.Bd -literal -offset indent +usage: cvs export [-flNnR] [-d dir] [-k mode] + -D date | -r rev module ... +.Ed +.Pp +The +.Ic export +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl D Ar date +Export as of the latest revision no later than +.Ar date . +.It Fl d Ar dir +Export in directory +.Ar dir +instead of the directory bearing the same name as the +.Ar module . +.It Fl f +Force the use of the head revision if the specified +tag or date is not found. +This can be used in combination with +.Fl D +or +.Fl r +to ensure that the +.Ic export +command is valid. +.It Fl k Ar mode +Specify the keyword substitution mode: the +.Fl k Ar v +option is often used to avoid substitution of keywords during +a release cycle. +However, be aware that it does not handle an export containing +binary files correctly. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl N +If used in conjunction with the +.Fl d +option, files are placed in local directory +.Ar module , +located in directory +.Ar dir . +.It Fl n +Do not execute programs listed in the +.Pa CVSROOT/modules +file. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Export from a particular symbolic or numerical revision. +.El +.Pp +Aliases: +.Ic ex , +.Ic exp . +.Ss history +The +.Ic history +command is used to display the history of actions done in the +base repository. +This functionality is only available if the +.Pa CVSROOT/history +file has been created. +Only the +.Ic checkout , +.Ic commit , +.Ic export , +.Ic release , +.Ic rtag , +and +.Ic update +commands are logged into this file. +.Bd -literal -offset indent +usage: cvs history [-aceloTw] [-b str] [-D date] [-f file] + [-m module] [-n module] [-p path] [-r rev] + [-t tag] [-u user] [-x ACEFGMORTUW] [-z tz] + [file ...] +.Ed +.Pp +The +.Ic history +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl a +Display records for all users. +By default, only records from the user issuing the +.Ic history +command are displayed. +.It Fl b Ar str +Display everything back to a record containing the string +.Ar str +in either the module name, the file name, or the repository path. +.It Fl c +Display the archived files +.Pf ( Ic commit +command). +.It Fl D Ar date +Report no later than +.Ar date . +.It Fl e +Select all records (same as +.Fl x +with all types). +.It Fl f Ar file +Display records related to +.Ar file . +.It Fl l +Show last checkouts of modules with the +.Ic checkout +command. +.It Fl m Ar module +Look for the +.Ar module +(can be used several times). +.It Fl n Ar module +Search into the +.Ar module . +.It Fl o +Report on modules checked out by users. +.It Fl p Ar path +Display records from the base repository being in the directory +specified by the +.Ar path . +.It Fl r Ar rev +Report for a particular revision (checks in the RCS file). +.It Fl T +Report on all tags. +.It Fl t Ar tag +Report since tag record placed in the +.Pa CVSROOT/history +file by any user. +.It Fl u Ar user +Report for a specified +.Ar user . +Can be used several times to match many users. +.It Fl w +Check that records match the current working directory. +.It Fl x Ar ACEFGMORTUW +Extract by a specific record type specified by a single letter. +They can be used in combination. +The available types are as follows: +.Bl -tag -width Ds +.It A +A file has been added with the +.Ic add +command. +.It C +A merge has been done, but unresolved conflicts still remain. +.It E +Export. +.It F +Release. +.It G +A merge has been done without conflict. +.It M +A file has been modified (using the +.Ic commit +command). +.It O +Checkout. +.It R +A file has been removed with the +.Ic remove +command. +.It T +Rtag. +.It U +Normal update. +.It W +The file has been deleted from the directory because it does not +exist anymore in the base repository. +.El +.It Fl z Ar tz +Display records with the time synchronized with timezone +.Ar tz . +.El +.Pp +All records have the following five first columns: +.Pp +.Bl -dash -compact +.It +The record type (the +.Fl x +option). +.It +The date of the action. +.It +The time of the action. +.It +The time zone. +.It +The user who made the action. +.El +.Pp +The other columns vary depending on the command issued: +.Pp +For records coming from the +.Ic rtag +command, the additional columns are as follows: +.Bd -literal -offset indent + [:] {} +.Ed +.Pp +For records coming from the +.Ic checkout +and +.Ic export +commands, the additional columns are as follows: +.Bd -literal -offset indent + == +.Ed +.Pp +For records coming from the +.Ic release +command, the additional columns are as follows: +.Bd -literal -offset indent +== +.Ed +.Pp +For records coming from the +.Ic commit +and +.Ic update +commands, the additional columns are as follows: +.Bd -literal -offset indent + == +.Ed +.Pp +Aliases: +.Ic hi , +.Ic his . +.Ss import +Import sources into CVS using vendor branches. +.Pp +At least three arguments are required: +.Ar module +specifies the location of the sources to be imported; +.Ar vendortag +is a tag for the entire branch; +.Ar releasetag +is used to identify the files created with +.Ic cvs import . +.Bd -literal -offset indent +usage: cvs import [-d] [-b branch] [-I ign] [-k mode] [-m msg] + [-W spec] module vendortag releasetag +.Ed +.Pp +The +.Ic import +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl b Ar branch +Specify the first-level branch number. +.It Fl d +Use the file's last modification time as the timestamp for the +initial revisions. +.It Fl I Ar ign +Ignore files specified by +.Ar ign . +This option can be used several times on the command line. +To see all files, use the +.Fl I Ar !\& +specification. +.It Fl k Ar mode +Specify the keyword substitution mode (is sticky). +.It Fl m Ar msg +Specify the log message to send. +.It Fl W Ar spec +Wrappers specification line. +.El +.Pp +Aliases: +.Ic im , +.Ic imp . +.Ss init +Create a CVS repository if it doesn't exist. +.Ss kserver +Start a Kerberos authentication server. +.Ss log +The +.Ic log +command displays information on a +.Ar file +such as its different revisions, description, different tags, +as well as the comments, dates, and authors of these revisions. +By default, the +.Ic log +command displays all the available information; the options are only +used to restrict the displayed information. +.Bd -literal -offset indent +usage: cvs log [-bhlNRt] [-d dates] [-r revs] [-s state] + [-w users] [file ...] +.Ed +.Pp +The +.Ic log +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl b +List revisions of the default branch only. +.It Fl d Ar dates +Specify revisions with dates matching the specification. +The specification might be as follows: +.Bl -tag -width Ds +.It date1date1 +Select all revisions between +.Ar date1 +and +.Ar date2 . +.It +Select all revisions before +.Ar date . +.It >date or date< +Select all revisions after +.Ar date . +.It date +Select the latest revision before or equal to +.Ar date . +.El +.Pp +The +.Sq \*(Gt +and +.Sq \*(Lt +characters can be followed by the +.Sq = +character to imply an inclusive specification. +Several specifications can be used by separating them with the +.Sq \&; +character. +.It Fl h +Print header only. +.It Fl l +Limit the scope of the search to the local directory only. +.It Fl N +Do not list tags. +.It Fl R +Print name of RCS file only. +.It Fl r Ar revs +Specify revision(s) to list: +.Bl -tag -width Ds +.It rev1,rev2,... +A list of revisions is specified by separating names or numbers +of revisions by the +.Sq \&, +character. +.It rev1:rev2 +List all revisions between +.Ar rev1 +and +.Ar rev2 +(they must be on the same branch). +.It :rev +List all revisions since the beginning of the branch until +.Ar rev +included. +.It rev: +List all revisions of the branch beginning with +.Ar rev . +.It branch +List all revisions of a branch. +.It branch. +List the latest revision of the branch +.Ar branch . +.It branch1:branch2 +List all revisions of branches between +.Ar branch1 +and +.Ar branch2 . +.El +.Pp +Without argument, the +.Fl r +option means the latest revision of the default branch. +.It Fl s Ar state +List revisions of the specified +.Ar state +only. +Several states can be listed by separating them with the +.Sq \&, +character. +.It Fl t +Print header and description only. +.It Fl w Ar users +Do not list revisions made by specified +.Ar users . +Usernames should be separated by the +.Sq \&, +character. +.El +.Pp +Aliases: +.Ic lo . +.Ss rannotate +For each line of any files specified, show information about its +last revision. +The information given is the last revision when a modification occurred, +the author's name, and the date of the revision. +This command does not need a local checkout of the repository +to work. +.Bd -literal -offset indent +usage: cvs rannotate [-flR] [-D date | -r rev] module ... +.Ed +.Pp +The +.Ic rannotate +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl D Ar date +Show the annotations as of the latest revision no later than +.Ar date . +.It Fl f +Force the use of the head revision if the specified +tag or date is not found. +This can be used in combination with +.Fl D +or +.Fl r +to ensure that there is some output from the +.Ic rannotate +command, even if only to show Revision 1.1 of the file. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Show annotations as of revision +.Ar rev +(can be a revision number or a tag). +.El +.Pp +Aliases: +.Ic rann , +.Ic ra . +.Ss rdiff +The +.Ic rdiff +command lists differences between two revisions in a +.Xr patch 1 +compatible format. +This command does not need a local checkout of the repository +to work. +.Bd -literal -offset indent +usage: cvs rdiff [-flR] [-c | -u] [-s | -t] [-V ver] + -D date | -r rev [-D date2 | -r rev2] + module ... +.Ed +.Pp +The +.Ic rdiff +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl c +Produces a diff with three lines of context. +See +.Xr diff 1 +for more information. +This is the default. +.It Xo Fl D Ar date +.Op Fl D Ar date2 +.Xc +Differences between the revision at +.Ar date +and the working copy or +.Ar date +and +.Ar date2 +(if specified). +.It Fl f +Force the use of the head revision if the specified +date or revision is not found. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Xo Fl r Ar rev +.Op Fl r Ar rev2 +.Xc +Differences between revision +.Ar rev +and the working copy or +.Ar rev +and +.Ar rev2 +(if specified). +.It Fl s +Create a summary change instead of a whole patch. +.It Fl t +Lists differences between the last two revisions of each file. +.It Fl u +Produces a diff in unidiff format. +.It Fl V Ar ver +Use the RCS version +.Ar ver +for keyword substitution. +.El +.Pp +Aliases: +.Ic pa , +.Ic patch . +.Ss release +The +.Ic release +command indicates to +.Nm +that the working copy of a module is no longer in use and checks +that non archived modifications in the base repository do exist. +This command is not mandatory. +Local directories could always be removed without using it, but +in this case the handling of history information will no longer be +correct (see the +.Ic history +command). +.Bd -literal -offset indent +usage: cvs release [-d] dir ... +.Ed +.Pp +The +.Ic release +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl d Ar dir +Remove the directory +.Ar dir . +Be aware that this option silently removes any directories that have +been added to the local working copy without using the +.Ic add +command. +.El +.Pp +For each file not being synchronized with the base repository, +a single letter prefix is given to specify the state of the file. +The possible prefixes are as follows: +.Bl -tag -width Ds +.It \&? +The file is unknown to +.Nm +and is not in the list of files to ignore. +Any new directories which have not been added with the +.Ic add +command are silently ignored as well as their content. +.It A +The file has been added with the +.Ic add +command, but has not been committed to the repository with the +.Ic commit +command. +.It M +The file has been locally modified; a more recent version might +exist in the base repository. +.It R +The file has been removed with the +.Ic remove +command, but has not been committed to the repository with the +.Ic commit +command. +.It U +A more recent version of the file does exist but it is not +locally up to date. +.El +.Pp +Aliases: +.Ic re , +.Ic rel . +.Ss remove +The +.Ic remove +command is used to inform +.Nm +that +.Ar file +is scheduled to be removed from the repository. +Files are not actually removed from the repository until the +.Ic commit +command has been run subsequently. +.Pp +There is no way to remove a directory with the +.Ic remove +command. +.Nm +will only remove a directory if it is empty and if the +.Ic checkout +or +.Ic update +commands are run with the +.Fl P +option. +(Note that the +.Ic export +command always removes empty directories.) +.Bd -literal -offset indent +usage: cvs remove [-flR] [file ...] +.Ed +.Pp +The +.Ic remove +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl f +Force local file removal. +If this flag is not used, the file must be locally removed beforehand for +the command to be valid. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Pp +Aliases: +.Ic rm , +.Ic delete . +.Ss rlog +The +.Ic rlog +command displays information on a +.Ar file +such as its different revisions, description, different tags, +as well as the comments, dates, and authors of these revisions. +By default, the +.Ic rlog +command displays all the available information; the options are only +used to restrict the displayed information. +This command does not need a local checkout of the repository +to work. +.Bd -literal -offset indent +usage: cvs rlog [-bhlNRt] [-d dates] [-r revs] [-s state] + [-w users] module ... +.Ed +.Pp +The +.Ic rlog +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl b +List revisions of the default branch only. +.It Fl d Ar dates +Specify revisions with dates matching the specification. +The specification might be as follows: +.Bl -tag -width Ds +.It date1date1 +Select all revisions between +.Ar date1 +and +.Ar date2 . +.It +Select all revisions before +.Ar date . +.It >date or date< +Select all revisions after +.Ar date . +.It date +Select the latest revision before or equal to +.Ar date . +.El +.Pp +The +.Sq \*(Gt +and +.Sq \*(Lt +characters can be followed by the +.Sq = +character to imply an inclusive specification. +Several specifications can be used by separating them with the +.Sq \&; +character. +.It Fl h +Print header only. +.It Fl l +Limit the scope of the search to the local directory only. +.It Fl N +Do not list tags. +.It Fl R +Print name of RCS file only. +.It Fl r Ar revs +Specify revision(s) to list: +.Bl -tag -width Ds +.It rev1,rev2,... +A list of revisions is specified by separating names or numbers +of revisions by the +.Sq \&, +character. +.It rev1:rev2 +List all revisions between +.Ar rev1 +and +.Ar rev2 +(they must be on the same branch). +.It :rev +List all revisions since the beginning of the branch until +.Ar rev +included. +.It rev: +List all revisions of the branch beginning with +.Ar rev . +.It branch +List all revisions of a branch. +.It branch. +List the latest revision of the branch +.Ar branch . +.It branch1:branch2 +List all revisions of branches between +.Ar branch1 +and +.Ar branch2 . +.El +.Pp +Without argument, the +.Fl r +option means the latest revision of the default branch. +.It Fl s Ar state +List revisions of the specified +.Ar state +only. +Several states can be listed by separating them with the +.Sq \&, +character. +.It Fl t +Print header and description only. +.It Fl w Ar users +Do not list revisions made by specified +.Ar users . +Usernames should be separated by the +.Sq \&, +character. +.El +.Pp +Aliases: +.Ic rlo . +.Ss rtag +The +.Ic rtag +command adds a symbolic tag to one or more modules. +It is often used to create a new branch using the +.Fl b +option. +.Bd -literal -offset indent +usage: cvs rtag [-abdFflnR] [-D date | -r rev] + symbolic_tag module ... +.Ed +.Pp +The +.Ic rtag +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl a +Clear tag from files already removed with the +.Ic remove +command. +.It Fl b +Create a branch. +.It Fl D Ar date +Tag the most recent revision before +.Ar date . +.It Fl d +Delete tag. +.It Fl F +Move tag if it already exists. +If this option is not used and a tag is used a second time, +.Nm +will not execute the action. +.It Fl f +Force the use of the head revision if the specified +revision or date is not found. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl n +Do not execute programs listed in the +.Pa CVSROOT/modules +file. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Tag at revision +.Ar rev . +.El +.Pp +Aliases: +.Ic rt , +.Ic rfreeze . +.Ss server +Server mode. +.Ss status +The +.Ic status +command is used to display the state of checked out files. +.Bd -literal -offset indent +usage: cvs status [-lRv] [file ...] +.Ed +.Pp +The +.Ic status +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl v +Display symbolic tags for +.Ar file . +.Pp +The state may be one of the following: +.Bl -tag -width Ds +.It Cm Locally Added +The file has been added with the +.Ic add +command, but has not been committed to the repository with the +.Ic commit +command. +.It Cm Locally Modified +The file is up to date, but has been locally modified. +.It Cm Locally Removed +The file has been removed with the +.Ic remove +command, but has not been committed to the repository with the +.Ic commit +command. +.It Cm Needs Checkout +The file has not been modified; a new version is available. +.It Cm Needs Merge +The file has been modified and a newer version is available. +.It Cm Needs Patch +Same as +.Ic Needs Checkout +but, in client-server mode, only the differences are sent to save +network resources. +.It Cm Unresolved Conflict +A merge has been done, but unresolved conflicts still remain. +.It Cm Up-to-date +The file is up to date. +.El +.El +.Pp +Aliases: +.Ic st , +.Ic stat . +.Ss tag +The +.Ic tag +command adds a symbolic tag to a checked out version of one or more files. +.Bd -literal -offset indent +usage: cvs tag [-bcdFflR] [-D date | -r rev] [symbolic_tag] + [file ...] +.Ed +.Pp +The +.Ic tag +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl b +Create a branch. +.It Fl c +Check that working files are not modified. +.It Fl D Ar date +Tag the most recent revision before +.Ar date . +.It Fl d +Delete tag. +.It Fl F +Move tag if it already exists. +If this option is not used and a tag is used a second time, +.Nm +will not execute the action. +.It Fl f +Force the use of the head revision if the specified +revision or date is not found. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Tag at revision +.Ar rev . +.El +.Pp +Aliases: +.Ic ta , +.Ic freeze . +.Ss unedit +The +.Ic unedit +command is used to give up an edition on a file and thus cancel +the wanted temporary notifications. +If the file has been modified since the +.Ic edit +command has been issued, +.Nm +will ask if it should go back to the previous version, and lose the +modifications done on the file, or stay in edition mode on it. +.Bd -literal -offset indent +usage: cvs unedit [-lR] [file ...] +.Ed +.Pp +The +.Ic unedit +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Ss update +The +.Ic update +command is used to merge any of the changes that have occurred on the remote +repository into the local one where the command was run. +.Bd -literal -offset indent +usage: cvs update [-ACdflPpR] [-D date | -r rev] [-I ign] + [-j rev] [-k mode] [-W spec] [file ...] +.Ed +.Pp +The +.Ic update +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl A +Reset any sticky tags, dates, or keyword substitution modes that +have been set on the tree. +.It Fl C +Overwrite locally modified files with clean repository copies. +.It Fl D Ar date +Update as of the latest revision no later than +.Ar date +(is sticky). +.It Fl d +Create any new directories. +Without this option, +.Nm +does not create any new files sitting in these new directories +added in the base repository since the last update of the working +copy, or since the last update with the +.Fl d +option. +.It Fl f +Force the use of the head revision if the specified +tag or date is not found. +.It Fl I Ar ign +Ignore files specified by +.Ar ign . +This option can be used several times on the command line. +To see all files, use the +.Fl I Ar !\& +specification. +.It Fl j Ar rev +Merge in changes made between current revision and +.Ar rev . +If two +.Fl j +options are specified, only merge the differences between the two +revisions of the branch. +This allows successive merges without having to resolve +already resolved conflicts again. +.It Fl k Ar mode +Specify the keyword substitution mode (is sticky). +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl P +Prune any directories that have become empty as a result of the update. +.It Fl p +Send the result of the update to standard output (avoids stickiness). +.It Fl R +Enable recursive behaviour. +This is the default. +.It Fl r Ar rev +Update from a particular revision or branch (is sticky). +.It Fl W Ar spec +Wrappers specification line. +.El +.Pp +By default, the +.Ic update +command does not create new directories; the +.Fl d +option must be used for that. +.Pp +For each file updated, a single letter prefix is given to +specify the state of the file. +The possible prefixes are as follows: +.Bl -tag -width Ds +.It \&? +The file is unknown to +.Nm . +.It A +The file has been added with the +.Ic add +command, but has not been committed to the repository with the +.Ic commit +command. +.It C +A merge, with a more recent version of the file, has been done, +but unresolved conflicts still remain. +.It M +The file has been locally modified; if a more recent version +is available, the merge has been done without conflict. +.It P +The same as +.Sq U , +but, in client-server mode, only differences are sent to save network +resources. +.It R +The file has been removed with the +.Ic remove +command, but has not been committed to the repository with the +.Ic commit +command. +.It U +The file is up to date. +.El +.Pp +Aliases: +.Ic up , +.Ic upd . +.Ss version +Causes +.Nm +to print its version information. +If this command is issued within a local copy of a remote repository or +if either the +.Ev CVSROOT +environment variable or the +.Fl d +flag specify a remote repository, +.Nm +will also connect to the server and ask it to print its version information. +.Pp +Aliases: +.Ic ve , +.Ic ver . +.Ss watch +The +.Ic watch +command switches a file from normal mode to +pseudo-lock mode as well as handling the notifications associated +with it. +Pseudo-lock mode means knowing who is editing a file: +for that, +.Nm +extracts the file in read-only mode. +Users must use the +.Ic edit +command to get the editing rights on the file. +.Pp +One of the following arguments to the +.Ic watch +command is mandatory: on, off, add, or remove. +.Ar on +switches the file into pseudo-lock mode; +.Ar off +switches it back to normal mode; +.Ar add +adds notifications for specific actions on the file; +.Ar remove +removes those notifications. +.Pp +The notifications are permanent. +They remain in place until the +.Ic watch remove +command is issued while the temporary notifications are +made available with the +.Ic edit +command. +.Bd -literal -offset indent +usage: cvs watch on | off | add | remove [-lR] [-a action] + [file ...] +.Ed +.Pp +The +.Ic watch +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl a Ar action +Specify the permanent notification wanted for +.Ar add | remove : +.Pp +.Bl -tag -width Ds -compact +.It Cm commit +Another user has committed changes to the file. +.It Cm edit +Another user is editing the file. +.It Cm unedit +Another user has finished editing the file. +.It Cm all +All of the above. +.It Cm none +No notification. +.El +.Pp +If no specification is requested using the +.Ar add +or +.Ar remove +arguments, it implies the +.Fl a Ar all +option. +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Ss watchers +The +.Ic watchers +command lists the users who asked for notifications as well as the +notification details. +The possible notifications are as follows: +.Bl -tag -width Ds +.It Cm commit +Permanent watch of a commit of a new version of a file. +.It Cm edit +Permanent watch of the start of file edition. +.It Cm tcommit +Temporary watch of a commit of new version of a file. +.It Cm tedit +Temporary watch of the start of file edition. +.It Cm tunedit +Temporary watch of the end of file edition. +.It Cm unedit +Permanent watch of the end of file edition. +.El +.Pp +The temporary watches are set using the +.Ic edit +command, until the +.Ic commit +or +.Ic unedit +command is issued on a file. +.Bd -literal -offset indent +usage: cvs watchers [-lR] [file ...] +.Ed +.Pp +The +.Ic watchers +command takes the following options: +.Bl -tag -width Ds -offset 3n +.It Fl l +Limit the scope of the search to the local directory +only and disable recursive behaviour. +.It Fl R +Enable recursive behaviour. +This is the default. +.El +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev CVS_CLIENT_LOG +This variable enables logging of all communications between the client and +server when running in non-local mode. +If set, this environment variable must contain a base path from which two +paths will be generated by appending ".in" to the value for the server's +input and ".out" for the server's output. +.Pp +The path can contain the following substitutes: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It %c +the command being run +.It %d +the date +.It %p +the process ID +.It %u +the username of the person running it +.El +.Pp +The substitutes are only supported by OpenCVS. +.It Ev CVS_RSH +Name of the program to use when connecting to the server through a remote +shell. +The default is to use the +.Xr ssh 1 +program. +.It Ev CVS_SERVER +If set, gives the name of the program to invoke as a +.Nm +server when using remote shell. +The default is to use `cvs'. +.It Ev CVSEDITOR +Name of the editor to use when editing commit messages. +Checked before +.Ev EDITOR +and +.Ev VISUAL . +.It Ev CVSREAD +If set, +.Nm +extracts files in read-only mode. +.It Ev CVSREADONLYFS +Permit checkout from a read-only repository. +Implies +.Fl l . +See also +.Fl R , +above. +.It Ev CVSROOT +When set, this variable should contain the string pointing to the root +directory of the CVS repository. +The contents of this variable are ignored when the +.Fl d +option is given or if `Root' files exist in the checked-out copy. +.It Ev EDITOR +Name of the editor to use when editing commit messages. +This is traditionally a line-oriented editor, +such as +.Xr ex 1 . +.It Ev HOME +Directory where the +.Pa .cvsignore +and +.Pa .cvsrc +files are searched for. +.It Ev TMPDIR +When set, this variable specifies the directory where temporary files +are to be created. +The default is set to +.Pa /tmp . +.It Ev VISUAL +Name of the editor to use when editing commit messages. +This is traditionally a screen-oriented editor, +such as +.Xr vi 1 . +.El +.Sh EXIT STATUS +.Ex -std cvs +.Sh SEE ALSO +.Xr diff 1 , +.Xr gzip 1 , +.Xr patch 1 , +.Xr rcs 1 , +.Xr cvs 5 , +.Xr cvsintro 7 +.Sh STANDARDS +The flag +.Op Fl x +has no effect and is provided +for compatibility only. +.Sh HISTORY +The OpenCVS project is a BSD-licensed rewrite of the original +Concurrent Versioning System written by Jean-Francois Brousseau. +The original CVS code was written in large parts by Dick Grune, +Brian Berliner and Jeff Polk. +.Sh AUTHORS +.An Jean-Francois Brousseau +.An Vincent Labrecque +.An Joris Vink +.An Xavier Santolaria +.Sh CAVEATS +This CVS implementation does not fully conform to the GNU CVS version. +In some cases, this was done explicitly because GNU CVS has inconsistencies +or ambiguous behaviour. +Some things have also been left out or modified to enhance the overall +security of the system. +.Pp +Among other things, support for the pserver connection mechanism has been +dropped because of security issues with the authentication mechanism. diff --git a/man/test_files/mdoc/dc.1 b/man/test_files/mdoc/dc.1 new file mode 100644 index 00000000..ab7d7c6f --- /dev/null +++ b/man/test_files/mdoc/dc.1 @@ -0,0 +1,550 @@ +.\" $OpenBSD: dc.1,v 1.35 2021/03/08 02:47:27 jsg Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, +.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)dc.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: March 8 2021 $ +.Dt DC 1 +.Os +.Sh NAME +.Nm dc +.Nd desk calculator +.Sh SYNOPSIS +.Nm +.Op Fl x +.Op Fl e Ar expression +.Op Ar file +.Sh DESCRIPTION +.Nm +is an arbitrary precision arithmetic package. +The overall structure of +.Nm +is +a stacking (reverse Polish) calculator i.e.\& +numbers are stored on a stack. +Adding a number pushes it onto the stack. +Arithmetic operations pop arguments off the stack +and push the results. +See also the +.Xr bc 1 +utility, which is a preprocessor for +.Nm +providing infix notation and a C-like syntax +which implements functions and reasonable control +structures for programs. +The options are as follows: +.Bl -tag -width Ds +.It Fl e Ar expression +Evaluate +.Ar expression . +If multiple +.Fl e +options are specified, they will be processed in the order given. +.It Fl x +Enable extended register mode. +This mode is used by +.Xr bc 1 +to allow more than 256 registers. +See +.Sx Registers +for a more detailed description. +.El +.Pp +If neither +.Ar expression +nor +.Ar file +are specified on the command line, +.Nm +reads from the standard input. +Otherwise +.Ar expression +and +.Ar file +are processed and +.Nm +exits. +.Pp +Ordinarily, +.Nm +operates on decimal integers, +but one may specify an input base, output base, +and a number of fractional digits (scale) to be maintained. +Whitespace is ignored, except where it signals the end of a number, +end of a line or when a register name is expected. +The following constructions are recognized: +.Bl -tag -width "number" +.It Va number +The value of the number is pushed on the stack. +A number is an unbroken string of the digits 0\-9 and letters A\-F. +It may be preceded by an underscore +.Pq Sq _ +to input a negative number. +A number may contain a single decimal point. +A number may also contain the characters A\-F, with the values 10\-15. +.It Cm "+ - / * % ~ ^" +The +top two values on the stack are added +(+), +subtracted +(\-), +multiplied (*), +divided (/), +remaindered (%), +divided and remaindered (~), +or exponentiated (^). +The two entries are popped off the stack; +the result is pushed on the stack in their place. +Any fractional part of an exponent is ignored. +.Pp +For addition and subtraction, the scale of the result is the maximum +of scales of the operands. +For division the scale of the result is defined +by the scale set by the +.Ic k +operation. +For multiplication, the scale is defined by the expression +.Sy min(a+b,max(a,b,scale)) , +where +.Sy a +and +.Sy b +are the scales of the operands, and +.Sy scale +is the scale defined by the +.Ic k +operation. +For exponentiation with a non-negative exponent, the scale of the result is +.Sy min(a*b,max(scale,a)) , +where +.Sy a +is the scale of the base, and +.Sy b +is the +.Em value +of the exponent. +If the exponent is negative, the scale of the result is the scale +defined by the +.Ic k +operation. +.Pp +In the case of the division and modulus operator (~), +the resultant quotient is pushed first followed by the remainder. +This is a shorthand for the sequence: +.Bd -literal -offset indent -compact +x y / x y % +.Ed +The division and modulus operator is a non-portable extension. +.It Ic a +Pop the top value from the stack. +If that value is a number, compute the integer part of the number modulo 256. +If the result is zero, push an empty string. +Otherwise push a one character string by interpreting the computed value +as an +.Tn ASCII +character. +.Pp +If the top value is a string, push a string containing the first character +of the original string. +If the original string is empty, an empty string is pushed back. +The +.Ic a +operator is a non-portable extension. +.It Ic c +All values on the stack are popped. +.It Ic d +The top value on the stack is duplicated. +.It Ic e +Equivalent to +.Ic p , +except that the output is written to the standard error stream. +This is a non-portable extension. +.It Ic f +All values on the stack are printed, separated by newlines. +.It Ic G +The top two numbers are popped from the stack and compared. +A one is pushed if the top of the stack is equal to the second number +on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic I +Pushes the input base on the top of the stack. +.It Ic i +The top value on the stack is popped and used as the +base for further input. +The initial input base is 10. +.It Ic J +Pop the top value from the stack. +The recursion level is popped by that value and, following that, +the input is skipped until the first occurrence of the +.Ic M +operator. +The +.Ic J +operator is a non-portable extension, used by the +.Xr bc 1 +command. +.It Ic K +The current scale factor is pushed onto the stack. +.It Ic k +The top of the stack is popped, and that value is used as +a non-negative scale factor: +the appropriate number of places +are printed on output, +and maintained during multiplication, division, and exponentiation. +The interaction of scale factor, +input base, and output base will be reasonable if all are changed +together. +.It Ic L Ns Ar x +Register +.Ar x +is treated as a stack and its top value is popped onto the main stack. +.It Ic l Ns Ar x +The +value in register +.Ar x +is pushed on the stack. +The register +.Ar x +is not altered. +Initially, all registers contain the value zero. +.It Ic M +Mark used by the +.Ic J +operator. +The +.Ic M +operator is a non-portable extension, used by the +.Xr bc 1 +command. +.It Ic N +The top of the stack is replaced by one if the top of the stack +is equal to zero. +If the top of the stack is unequal to zero, it is replaced by zero. +This is a non-portable extension. +.It Ic n +The top value on the stack is popped and printed without a newline. +This is a non-portable extension. +.It Ic O +Pushes the output base on the top of the stack. +.It Ic o +The top value on the stack is popped and used as the +base for further output. +The initial output base is 10. +.It Ic P +The top of the stack is popped. +If the top of the stack is a string, it is printed without a trailing newline. +If the top of the stack is a number, it is interpreted as a +base 256 number, and each digit of this base 256 number is printed as +an +.Tn ASCII +character, without a trailing newline. +.It Ic p +The top value on the stack is printed with a trailing newline. +The top value remains unchanged. +.It Ic Q +The top value on the stack is popped and the string execution level is popped +by that value. +.It Ic q +Exits the program. +If executing a string, the recursion level is +popped by two. +.It Ic R +The top of the stack is removed (popped). +This is a non-portable extension. +.It Ic r +The top two values on the stack are reversed (swapped). +This is a non-portable extension. +.It Ic S Ns Ar x +Register +.Ar x +is treated as a stack. +The top value of the main stack is popped and pushed on it. +.It Ic s Ns Ar x +The +top of the stack is popped and stored into +a register named +.Ar x . +.It Ic v +Replaces the top element on the stack by its square root. +The scale of the result is the maximum of the scale of the argument +and the current value of scale. +.It Ic X +Replaces the number on the top of the stack with its scale factor. +If the top of the stack is a string, replace it with the integer 0. +.It Ic x +Treats the top element of the stack as a character string +and executes it as a string of +.Nm +commands. +.It Ic Z +Replaces the number on the top of the stack with its length. +The length of a string is its number of characters. +The length of a number is its number of digits, not counting the minus sign +and decimal point. +The length of a zero value is its scale. +.It Ic z +The stack level is pushed onto the stack. +.It Cm \&[ Ns ... Ns Cm \&] +Puts the bracketed +.Tn ASCII +string onto the top of the stack. +If the string includes brackets, these must be properly balanced. +The backslash character +.Pq Sq \e +may be used as an escape character, making it +possible to include unbalanced brackets in strings. +To include a backslash in a string, use a double backslash. +.It Xo +.Cm < Ns Va x +.Cm > Ns Va x +.Cm = Ns Va x +.Cm !< Ns Va x +.Cm !> Ns Va x +.Cm != Ns Va x +.Xc +The top two elements of the stack are popped and compared. +Register +.Ar x +is executed if they obey the stated +relation. +.It Xo +.Cm < Ns Va x Ns e Ns Va y +.Cm > Ns Va x Ns e Ns Va y +.Cm = Ns Va x Ns e Ns Va y +.Cm !< Ns Va x Ns e Ns Va y +.Cm !> Ns Va x Ns e Ns Va y +.Cm != Ns Va x Ns e Ns Va y +.Xc +These operations are variants of the comparison operations above. +The first register name is followed by the letter +.Sq e +and another register name. +Register +.Ar x +will be executed if the relation is true, and register +.Ar y +will be executed if the relation is false. +This is a non-portable extension. +.It Ic \&( +The top two numbers are popped from the stack and compared. +A one is pushed if the top of the stack is less than the second number +on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic { +The top two numbers are popped from the stack and compared. +A one is pushed if the top of stack is less than or equal to the +second number on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic \&? +A line of input is taken from the input source (usually the terminal) +and executed. +.It Ic \&: Ns Ar r +Pop two values from the stack. +The second value on the stack is stored into the array +.Ar r +indexed by the top of stack. +.It Ic \&; Ns Ar r +Pop a value from the stack. +The value is used as an index into register +.Ar r . +The value in this register is pushed onto the stack. +.Pp +Array elements initially have the value zero. +Each level of a stacked register has its own array associated with +it. +The command sequence +.Bd -literal -offset indent +[first] 0:a [dummy] Sa [second] 0:a 0;a p La 0;a p +.Ed +.Pp +will print +.Bd -literal -offset indent +second +first +.Ed +.Pp +since the string +.Ql second +is written in an array that is later popped, to reveal the array that +stored +.Ql first . +.It Ic # +Skip the rest of the line. +This is a non-portable extension. +.El +.Ss Registers +Registers have a single character name +.Ar x , +where +.Ar x +may be any character, including space, tab or any other special character. +If extended register mode is enabled using the +.Fl x +option and the register identifier +.Ar x +has the value 255, the next two characters are interpreted as a +two-byte register index. +The set of standard single character registers and the set of extended +registers do not overlap. +Extended register mode is a non-portable extension. +.Sh EXAMPLES +An example which prints the first ten values of +.Ic n! : +.Bd -literal -offset indent +[la1+dsa*pla10>y]sy +0sa1 +lyx +.Ed +.Pp +Independent of the current input base, the command +.Bd -literal -offset indent +Ai +.Ed +.Pp +will reset the input base to decimal 10. +.Sh DIAGNOSTICS +.Bl -diag +.It %c (0%o) is unimplemented +an undefined operation was called. +.It stack empty +for not enough elements on the stack to do what was asked. +.It stack register '%c' (0%o) is empty +for an +.Ar L +operation from a stack register that is empty. +.It Runtime warning: non-zero scale in exponent +for a fractional part of an exponent that is being ignored. +.It divide by zero +for trying to divide by zero. +.It remainder by zero +for trying to take a remainder by zero. +.It square root of negative number +for trying to take the square root of a negative number. +.It index too big +for an array index that is larger than 2048. +.It negative index +for a negative array index. +.It "input base must be a number between 2 and 16" +for trying to set an illegal input base. +.It output base must be a number greater than 1 +for trying to set an illegal output base. +.It scale must be a nonnegative number +for trying to set a negative or zero scale. +.It scale too large +for trying to set a scale that is too large. +A scale must be representable as a 32-bit unsigned number. +.It Q command argument exceeded string execution depth +for trying to pop the recursion level more than the current +recursion level. +.It Q command requires a number >= 1 +for trying to pop an illegal number of recursion levels. +.It recursion too deep +for too many levels of nested execution. +.Pp +The recursion level is increased by one if the +.Ar x +or +.Ar ?\& +operation or one of the compare operations resulting in the execution +of register is executed. +As an exception, the recursion level is not increased if the operation +is executed as the last command of a string. +For example, the commands +.Bd -literal -offset indent +[lax]sa +1 lax +.Ed +.Pp +will execute an endless loop, while the commands +.Bd -literal -offset indent +[laxp]sa +1 lax +.Ed +.Pp +will terminate because of a too deep recursion level. +.It J command argument exceeded string execution depth +for trying to pop the recursion level more than the current +recursion level. +.It mark not found +for a failed scan for an occurrence of the +.Ic M +operator. +.El +.Sh SEE ALSO +.Xr bc 1 +.Rs +.\" 4.4BSD USD:5 +.%A R. H. Morris +.%A L. L. Cherry +.%T DC \(em An Interactive Desk Calculator +.Re +.Sh STANDARDS +The arithmetic operations of the +.Nm +utility are expected to conform to the definition listed in the +.Xr bc 1 +section of the +.St -p1003.2 +specification. +.Sh HISTORY +The +.Nm +command appeared in +.At v1 . +A complete rewrite of the +.Nm +command using the +.Xr BN_new 3 +big number routines first appeared in +.Ox 3.5 . +.Sh AUTHORS +.An -nosplit +The original version of the +.Nm +command was written by +.An Robert Morris +and +.An Lorinda Cherry . +The current version of the +.Nm +utility was written by +.An Otto Moerbeek . +.Sh CAVEATS +While fractional input in base 10 is always exact, +other bases may suffer from unintuitive rounding. +To avoid surprising results, plain integer division can be used +instead of the corresponding floating point notation. diff --git a/man/test_files/mdoc/diff.1 b/man/test_files/mdoc/diff.1 new file mode 100644 index 00000000..c0e2d76e --- /dev/null +++ b/man/test_files/mdoc/diff.1 @@ -0,0 +1,475 @@ +.\" $OpenBSD: diff.1,v 1.52 2024/12/03 07:09:14 jmc Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)diff.1 8.1 (Berkeley) 6/30/93 +.\" +.Dd $Mdocdate: December 3 2024 $ +.Dt DIFF 1 +.Os +.Sh NAME +.Nm diff +.Nd differential file and directory comparator +.Sh SYNOPSIS +.Nm diff +.Op Fl abdipTtw +.Oo +.Fl c | e | f | +.Fl n | q | u +.Oc +.Op Fl I Ar pattern +.Op Fl L Ar label +.Ar file1 file2 +.Nm diff +.Op Fl abdipTtw +.Op Fl I Ar pattern +.Op Fl L Ar label +.Fl C Ar number +.Ar file1 file2 +.Nm diff +.Op Fl abditw +.Op Fl I Ar pattern +.Fl D Ar string +.Ar file1 file2 +.Nm diff +.Op Fl abdipTtw +.Op Fl I Ar pattern +.Op Fl L Ar label +.Fl U Ar number +.Ar file1 file2 +.Nm diff +.Op Fl abdiNPprsTtw +.Oo +.Fl c | e | f | +.Fl n | q | u +.Oc +.Op Fl I Ar pattern +.Bk -words +.Op Fl L Ar label +.Op Fl S Ar name +.Op Fl X Ar file +.Op Fl x Ar pattern +.Ek +.Ar dir1 dir2 +.Sh DESCRIPTION +The +.Nm +utility compares the contents of +.Ar file1 +and +.Ar file2 +and writes to the standard output the list of changes necessary to +convert one file into the other. +No output is produced if the files are identical. +.Pp +Output options (mutually exclusive): +.Bl -tag -width Ds +.It Fl C Ar number +Like +.Fl c +but produces a diff with +.Ar number +lines of context. +.It Fl c +Produces a diff with 3 lines of context. +With +.Fl c +the output format is modified slightly: +the output begins with identification of the files involved and +their creation dates and then each change is separated +by a line with fifteen +.Li * Ns 's . +The lines removed from +.Ar file1 +are marked with +.Sq \-\ \& ; +those added to +.Ar file2 +are marked +.Sq +\ \& . +Lines which are changed from one file to the other are marked in +both files with +.Sq !\ \& . +Changes which lie within 3 lines of each other are grouped together on +output. +.It Fl D Ar string +Creates a merged version of +.Ar file1 +and +.Ar file2 +on the standard output, with C preprocessor controls included so that +a compilation of the result without defining +.Ar string +is equivalent to compiling +.Ar file1 , +while defining +.Ar string +will yield +.Ar file2 . +.It Fl e +Produces output in a form suitable as input for the editor utility, +.Xr ed 1 , +which can then be used to convert file1 into file2. +.Pp +Extra commands are added to the output when comparing directories with +.Fl e , +so that the result is a +.Xr sh 1 +script for converting text files which are common to the two directories +from their state in +.Ar dir1 +to their state in +.Ar dir2 . +.It Fl f +Identical output to that of the +.Fl e +flag, but in reverse order. +It cannot be digested by +.Xr ed 1 . +.It Fl n +Produces a script similar to that of +.Fl e , +but in the opposite order and with a count of changed lines on each +insert or delete command. +This is the form used by +.Xr rcsdiff 1 . +.It Fl q +Just print a line when the files differ. +Does not output a list of changes. +.It Fl U Ar number +Like +.Fl u +but produces a diff with +.Ar number +lines of context. +.It Fl u +Produces a +.Em unified +diff with 3 lines of context. +A unified diff is similar to the context diff produced by the +.Fl c +option. +However, unlike with +.Fl c , +all lines to be changed (added and/or removed) are present in +a single section. +.El +.Pp +Comparison options: +.Bl -tag -width Ds +.It Fl a +Treat all files as ASCII text. +Normally +.Nm +will simply print +.Dq Binary files ... differ +if files contain binary characters. +Use of this option forces +.Nm +to produce a diff. +.It Fl b +Causes trailing blanks (spaces and tabs) to be ignored, and other +strings of blanks to compare equal. +.It Fl d +Try very hard to produce a diff as small as possible. +This may consume a lot of processing power and memory when processing +large files with many changes. +.It Fl I Ar pattern +Ignores changes, insertions, and deletions whose lines match the +extended regular expression +.Ar pattern . +Multiple +.Fl I +patterns may be specified. +All lines in the change must match some pattern for the change to be +ignored. +See +.Xr re_format 7 +for more information on regular expression patterns. +.It Fl i +Ignores the case of letters. +E.g., +.Dq A +will compare equal to +.Dq a . +.It Fl L Ar label +Print +.Ar label +instead of the first (and second, if this option is specified twice) +file name and time in the context or unified diff header. +.It Fl p +With unified and context diffs, show with each change +the first 40 characters of the last line before the context beginning +with a letter, an underscore or a dollar sign. +For C source code following standard layout conventions, this will +show the prototype of the function the change applies to. +.It Fl T +Print a tab rather than a space before the rest of the line for the +normal, context or unified output formats. +This makes the alignment of tabs in the line consistent. +.It Fl t +Will expand tabs in output lines. +Normal or +.Fl c +output adds character(s) to the front of each line which may screw up +the indentation of the original source lines and make the output listing +difficult to interpret. +This option will preserve the original source's indentation. +.It Fl w +Is similar to +.Fl b +but causes whitespace (blanks and tabs) to be totally ignored. +E.g., +.Dq if (\ \&a == b \&) +will compare equal to +.Dq if(a==b) . +.El +.Pp +Directory comparison options: +.Bl -tag -width Ds +.It Fl N +If a file is found in only one directory, act as if it was found in the +other directory too but was of zero size. +.It Fl P +If a file is found only in +.Ar dir2 , +act as if it was found in +.Ar dir1 +too but was of zero size. +.It Fl r +Causes application of +.Nm +recursively to common subdirectories encountered. +.It Fl S Ar name +Re-starts a directory +.Nm +in the middle, beginning with file +.Ar name . +.It Fl s +Causes +.Nm +to report files which are the same, which are otherwise not mentioned. +.It Fl X Ar file +Exclude files and subdirectories from comparison whose basenames match +lines in +.Ar file . +Multiple +.Fl X +options may be specified. +.It Fl x Ar pattern +Exclude files and subdirectories from comparison whose basenames match +.Ar pattern . +Patterns are matched using shell-style globbing as described in +.Xr glob 7 . +Multiple +.Fl x +options may be specified. +.El +.Pp +If both arguments are directories, +.Nm +sorts the contents of the directories by name, and then runs the +regular file +.Nm +algorithm, producing a change list, +on text files which are different. +Binary files which differ, +common subdirectories, and files which appear in only one directory +are described as such. +In directory mode only regular files and directories are compared. +If a non-regular file such as a device special file or FIFO +is encountered, a diagnostic message is printed. +.Pp +If only one of +.Ar file1 +and +.Ar file2 +is a directory, +.Nm +is applied to the non-directory file and the file contained in +the directory file with a filename that is the same as the +last component of the non-directory file. +.Pp +If either +.Ar file1 +or +.Ar file2 +is +.Sq - , +the standard input is +used in its place. +.Ss Output Style +The default (without +.Fl e , +.Fl c , +or +.Fl n +.\" -C +options) +output contains lines of these forms, where +.Va XX , YY , ZZ , QQ +are line numbers respective of file order. +.Pp +.Bl -tag -width "XX,YYcZZ,QQ" -compact +.It Li XX Ns Ic a Ns Li YY +At (the end of) line +.Va XX +of +.Ar file1 , +append the contents +of line +.Va YY +of +.Ar file2 +to make them equal. +.It Li XX Ns Ic a Ns Li YY,ZZ +Same as above, but append the range of lines, +.Va YY +through +.Va ZZ +of +.Ar file2 +to line +.Va XX +of file1. +.It Li XX Ns Ic d Ns Li YY +At line +.Va XX +delete +the line. +The value +.Va YY +tells to which line the change would bring +.Ar file1 +in line with +.Ar file2 . +.It Li XX,YY Ns Ic d Ns Li ZZ +Delete the range of lines +.Va XX +through +.Va YY +in +.Ar file1 . +.It Li XX Ns Ic c Ns Li YY +Change the line +.Va XX +in +.Ar file1 +to the line +.Va YY +in +.Ar file2 . +.It Li XX,YY Ns Ic c Ns Li ZZ +Replace the range of specified lines with the line +.Va ZZ . +.It Li XX,YY Ns Ic c Ns Li ZZ,QQ +Replace the range +.Va XX , Ns Va YY +from +.Ar file1 +with the range +.Va ZZ , Ns Va QQ +from +.Ar file2 . +.El +.Pp +These lines resemble +.Xr ed 1 +subcommands to convert +.Ar file1 +into +.Ar file2 . +The line numbers before the action letters pertain to +.Ar file1 ; +those after pertain to +.Ar file2 . +Thus, by exchanging +.Ic a +for +.Ic d +and reading the line in reverse order, one can also +determine how to convert +.Ar file2 +into +.Ar file1 . +As in +.Xr ed 1 , +identical +pairs (where num1 = num2) are abbreviated as a single +number. +.Sh FILES +.Bl -tag -width /tmp/diff.XXXXXXXX -compact +.It Pa /tmp/diff. Ns Ar XXXXXXXX +Temporary file used when comparing a device or the standard input. +Note that the temporary file is unlinked as soon as it is created +so it will not show up in a directory listing. +.El +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It 0 +No differences were found. +.It 1 +Differences were found. +.It >1 +An error occurred. +.El +.Sh SEE ALSO +.Xr cmp 1 , +.Xr comm 1 , +.Xr diff3 1 , +.Xr ed 1 , +.Xr patch 1 , +.Xr sdiff 1 +.Rs +.%A James W. Hunt +.%A M. Douglas McIlroy +.%T "An Algorithm for Differential File Comparison" +.%I AT&T Bell Laboratories +.%J Computing Science Technical Report +.%N 41 +.%D June 1976 +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl aDdIiLNnPpqSsTtwXx +are extensions to that specification. +.Sh HISTORY +A +.Nm +command appeared in +.At v5 . diff --git a/man/test_files/mdoc/dup.2 b/man/test_files/mdoc/dup.2 new file mode 100644 index 00000000..948f1082 --- /dev/null +++ b/man/test_files/mdoc/dup.2 @@ -0,0 +1,214 @@ +.\" $OpenBSD: dup.2,v 1.20 2018/06/25 16:06:27 visa Exp $ +.\" $NetBSD: dup.2,v 1.4 1995/02/27 12:32:21 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dup.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: June 25 2018 $ +.Dt DUP 2 +.Os +.Sh NAME +.Nm dup , +.Nm dup2 , +.Nm dup3 +.Nd duplicate an existing file descriptor +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn dup "int oldd" +.Ft int +.Fn dup2 "int oldd" "int newd" +.In fcntl.h +.In unistd.h +.Ft int +.Fn dup3 "int oldd" "int newd" "int flags" +.Sh DESCRIPTION +.Fn dup +duplicates an existing object descriptor and returns its value to +the calling process +.Fa ( newd += +.Fn dup oldd ) . +The argument +.Fa oldd +is a small non-negative integer index in the per-process descriptor table. +The value must be less than the size of the table, which is returned by +.Xr getdtablesize 3 . +The new descriptor returned by the call is the lowest numbered descriptor +currently not in use by the process. +.Pp +The object referenced by the descriptor does not distinguish between +.Fa oldd +and +.Fa newd +in any way. +Thus if +.Fa newd +and +.Fa oldd +are duplicate references to an open +file, +.Xr read 2 , +.Xr write 2 +and +.Xr lseek 2 +calls all move a single pointer into the file, +and append mode, non-blocking I/O and asynchronous I/O options +are shared between the references. +If a separate pointer into the file is desired, a different +object reference to the file must be obtained by issuing an +additional +.Xr open 2 +call. +The close-on-exec flag on the new file descriptor is unset. +.Pp +In +.Fn dup2 , +the value of the new descriptor +.Fa newd +is specified. +If this descriptor is already in use, it is first deallocated as if a +.Xr close 2 +call had been done first. +When +.Fa newd +equals +.Fa oldd , +.Fn dup2 +just returns without affecting the close-on-exec flag. +.Pp +In +.Fn dup3 , +both the value of the new descriptor and the close-on-exec flag on +the new file descriptor are specified: +.Fa newd +specifies the value and the +.Dv O_CLOEXEC +bit in +.Fa flags +specifies the close-on-exec flag. +Unlike +.Fn dup2 , +if +.Fa oldd +and +.Fa newd +are equal then +.Fn dup3 +fails. +Otherwise, if +.Fa flags +is zero then +.Fn dup3 +is identical to a call to +.Fn dup2 . +.Sh RETURN VALUES +Upon successful completion, the value of the new descriptor is returned. +The value \-1 is returned if an error occurs in either call. +The external variable +.Va errno +indicates the cause of the error. +.Sh ERRORS +.Fn dup +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa oldd +is not a valid active descriptor. +.It Bq Er EMFILE +Too many descriptors are active. +.El +.Pp +.Fn dup2 +and +.Fn dup3 +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa oldd +is not a valid active descriptor or +.Fa newd +is negative or greater than or equal to the process's +.Dv RLIMIT_NOFILE +limit. +.It Bq Er EBUSY +A race condition with +.Xr accept 2 +or +.Xr open 2 +has been detected. +.It Bq Er EINTR +An interrupt was received. +.It Bq Er EIO +An I/O error occurred while writing to the file system. +.El +.Pp +In addition, +.Fn dup3 +will return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa oldd +is equal to +.Fa newd +or +.Fa flags +is invalid. +.El +.Sh SEE ALSO +.Xr accept 2 , +.Xr close 2 , +.Xr fcntl 2 , +.Xr getrlimit 2 , +.Xr open 2 , +.Xr pipe 2 , +.Xr socket 2 , +.Xr socketpair 2 , +.Xr getdtablesize 3 +.Sh STANDARDS +.Fn dup +and +.Fn dup2 +conform to +.St -p1003.1-2008 . +The +.Fn dup3 +function is expected to conform to a future revision of that standard. +.Sh HISTORY +The +.Fn dup +system call first appeared in +.At v3 , +.Fn dup2 +in +.At v7 , +and +.Fn dup3 +in +.Ox 5.7 . diff --git a/man/test_files/mdoc/execve.2 b/man/test_files/mdoc/execve.2 new file mode 100644 index 00000000..0d5c1b03 --- /dev/null +++ b/man/test_files/mdoc/execve.2 @@ -0,0 +1,348 @@ +.\" $OpenBSD: execve.2,v 1.58 2022/10/13 21:37:05 jmc Exp $ +.\" $NetBSD: execve.2,v 1.9 1995/02/27 12:32:25 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)execve.2 8.3 (Berkeley) 1/24/94 +.\" +.Dd $Mdocdate: October 13 2022 $ +.Dt EXECVE 2 +.Os +.Sh NAME +.Nm execve +.Nd execute a file +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn execve "const char *path" "char *const argv[]" "char *const envp[]" +.Sh DESCRIPTION +.Fn execve +transforms the calling process into a new process. +The new process is constructed from an ordinary file, +whose name is pointed to by +.Fa path , +called the +.Em new process file . +This file is either an executable object file, +or a file of data for an interpreter. +An executable object file consists of an identifying header, +followed by pages of data representing the initial program (text) +and initialized data pages. +Additional pages may be specified by the header to be initialized +with zero data; see +.Xr elf 5 . +.Pp +An interpreter file begins with a line of the form: +.Pp +.D1 #! Ar interpreter Op Ar arg +.Pp +When an interpreter file is passed to +.Fn execve , +the system instead calls +.Fn execve +with the specified +.Ar interpreter . +If the optional +.Ar arg +is specified, it becomes the first argument to the +.Ar interpreter , +and the original +.Fa path +becomes the second argument; +otherwise, +.Fa path +becomes the first argument. +The original arguments are shifted over to become the subsequent arguments. +The zeroth argument, normally the name of the file being executed, is left +unchanged. +.Pp +The argument +.Fa argv +is a pointer to a null-terminated array of +character pointers to NUL-terminated character strings. +These strings construct the argument list to be made available to the new +process. +At least one non-null argument must be present in the array; +by custom, the first element should be +the name of the executed program (for example, the last component of +.Fa path ) . +.Pp +The argument +.Fa envp +is also a pointer to a null-terminated array of +character pointers to NUL-terminated strings. +A pointer to this array is normally stored in the global variable +.Va environ . +These strings pass information to the +new process that is not directly an argument to the command (see +.Xr environ 7 ) . +.Pp +File descriptors open in the calling process image remain open in +the new process image, except for those for which the close-on-exec +flag is set (see +.Xr close 2 +and +.Xr fcntl 2 ) . +Descriptors that remain open are unaffected by +.Fn execve . +In the case of a new setuid or setgid executable being executed, if +file descriptors 0, 1, or 2 (representing stdin, stdout, and stderr) +are currently unallocated, these descriptors will be opened to point to +some system file like +.Pa /dev/null . +The intent is to ensure these descriptors are not unallocated, since +many libraries make assumptions about the use of these 3 file descriptors. +.Pp +Signals set to be ignored in the calling process, +with the exception of +.Dv SIGCHLD , +are set to be ignored in +the +new process. +Other signals +are set to default action in the new process image. +Blocked signals remain blocked regardless of changes to the signal action. +The signal stack is reset to be undefined (see +.Xr sigaction 2 +for more information). +.Pp +If the set-user-ID mode bit of the new process image file is set +(see +.Xr chmod 2 ) , +the effective user ID of the new process image is set to the owner ID +of the new process image file. +If the set-group-ID mode bit of the new process image file is set, +the effective group ID of the new process image is set to the group ID +of the new process image file. +(The effective group ID is the first element of the group list.) +The real user ID, real group ID and +other group IDs of the new process image remain the same as the calling +process image. +After any set-user-ID and set-group-ID processing, +the effective user ID is recorded as the saved set-user-ID, +and the effective group ID is recorded as the saved set-group-ID. +These values may be used in changing the effective IDs later (see +.Xr setuid 2 ) . +The set-user-ID and set-group-ID bits have no effect if the +new process image file is located on a file system mounted with +the nosuid flag. +The process will be started without the new permissions. +.Pp +The new process also inherits the following attributes from +the calling process: +.Pp +.Bl -tag -width controlling_terminal -offset indent -compact +.It process ID +see +.Xr getpid 2 +.It parent process ID +see +.Xr getppid 2 +.It process group ID +see +.Xr getpgrp 2 +.It session ID +see +.Xr getsid 2 +.It access groups +see +.Xr getgroups 2 +.It working directory +see +.Xr chdir 2 +.It root directory +see +.Xr chroot 2 +.It controlling terminal +see +.Xr termios 4 +.It resource usages +see +.Xr getrusage 2 +.It interval timers +see +.Xr getitimer 2 +(unless process image file is setuid or setgid, +in which case all timers are disabled) +.It resource limits +see +.Xr getrlimit 2 +.It file mode mask +see +.Xr umask 2 +.It signal mask +see +.Xr sigaction 2 , +.Xr sigprocmask 2 +.El +.Pp +When a program is executed as a result of an +.Fn execve +call, it is entered as follows: +.Pp +.Dl main(int argc, char **argv, char **envp) +.Pp +where +.Fa argc +is the number of elements in +.Fa argv +(the +.Dq arg count ) +and +.Fa argv +points to the array of character pointers +to the arguments themselves. +.Sh RETURN VALUES +As the +.Fn execve +function overlays the current process image +with a new process image, the successful call +has no process to return to. +If +.Fn execve +does return to the calling process, an error has occurred; the +return value will be \-1 and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn execve +will fail and return to the calling process if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The new process file does not exist. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er EACCES +The new process file is not an ordinary file. +.It Bq Er EACCES +The new process file mode denies execute permission. +.It Bq Er EACCES +The new process file is on a filesystem mounted with execution +disabled +.Pf ( Dv MNT_NOEXEC +in +.In sys/mount.h ) . +.It Bq Er EACCES +The new process file is marked with +.Xr ld 1 +.Fl z Cm wxneeded +to perform W^X violating operations, but it is located on a file +system not allowing such operations, being mounted without the +.Xr mount 8 +.Fl o Cm wxallowed +flag. +.It Bq Er EACCES +The parent used +.Xr pledge 2 +to declare an +.Va execpromise , +and that is not permitted for setuid or setgid images. +.It Bq Er ENOEXEC +The new process file has the appropriate access +permission, but has an invalid magic number in its header. +.It Bq Er ETXTBSY +The new process file is a pure procedure (shared text) +file that is currently open for writing by some process. +.It Bq Er ENOMEM +The new process requires more virtual memory than +is allowed by the imposed maximum +.Pq Xr getrlimit 2 . +.It Bq Er E2BIG +The number of bytes in the new process's argument list +is larger than the system-imposed limit. +The limit in the system as released is 524288 bytes +.Pq Dv ARG_MAX . +.It Bq Er EFAULT +The new process file is not as long as indicated by +the size values in its header. +.It Bq Er EFAULT +.Fa path , +.Fa argv , +or +.Fa envp +point +to an illegal address. +.It Bq Er EINVAL +.Fa argv +did not contain at least one element. +.It Bq Er EIO +An I/O error occurred while reading from the file system. +.It Bq Er ENFILE +During startup of an +.Ar interpreter , +the system file table was found to be full. +.El +.Sh SEE ALSO +.Xr _exit 2 , +.Xr fork 2 , +.Xr execl 3 , +.Xr exit 3 , +.Xr elf 5 , +.Xr environ 7 +.Sh STANDARDS +The +.Fn execve +function is expected to conform to +.St -p1003.1-2008 . +.Sh HISTORY +The predecessor of these functions, the former +.Fn exec +system call, first appeared in +.At v1 . +The +.Fn execve +function first appeared in +.At v7 . +.Sh CAVEATS +If a program is +.Em setuid +to a non-superuser, but is executed when the real +.Em uid +is +.Dq root , +then the process has some of the powers of a superuser as well. +.Pp +.St -p1003.1-2008 +permits +.Nm +to leave +.Dv SIGCHLD +as ignored in the new process; portable programs cannot rely on +.Nm +resetting it to the default disposition. diff --git a/man/test_files/mdoc/fgen.1 b/man/test_files/mdoc/fgen.1 new file mode 100644 index 00000000..6ef12f85 --- /dev/null +++ b/man/test_files/mdoc/fgen.1 @@ -0,0 +1,71 @@ +.\" $OpenBSD: fgen.1,v 1.7 2023/01/04 14:58:04 jsg Exp $ +.\" $NetBSD: fgen.1,v 1.6 2001/06/13 10:46:05 wiz Exp $ +.\" +.\" Copyright (c) 1998 Eduardo Horvath, All Rights Reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: January 4 2023 $ +.Dt FGEN 1 +.Os +.Sh NAME +.Nm fgen +.Nd IEEE 1275 Open Firmware FCode Tokenizer +.Sh SYNOPSIS +.Nm +.Op Fl d Ar level +.Op Fl o Ar outfile +.Ar infile +.Sh DESCRIPTION +Reads Forth source and generates tokenized FCode object file. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d Ar level +Sets the debug +.Ar level . +When the debug level is greater than zero, additional debugging messages +are printed to standard output. +Different levels of verbosity are available if compiled with DEBUG. +.It Fl o Ar outfile +Write output to +.Ar outfile +instead of standard output. +.El +.Sh AUTHORS +Written by +.An Eduardo E. Horvath Aq Mt eeh@one-o.com +.Sh BUGS +String escape sequences are not recognized so things such as +.Pp +.Li \&" foo \&"\&(01 02\&) \&"n \&" +.Pp +will result in the string +.Pp +.Dq foo \&"\&(01 02\&) \&"n . +.Pp +Hexadecimal numbers with dots in them such as +.Li 100.0000 +are not parsed. +.Pp +Permissions on the output file are often incorrect. +.Pp +Output to the standard output device can cause problems. diff --git a/man/test_files/mdoc/file.1 b/man/test_files/mdoc/file.1 new file mode 100644 index 00000000..53bfaa10 --- /dev/null +++ b/man/test_files/mdoc/file.1 @@ -0,0 +1,130 @@ +.\" $OpenBSD: file.1,v 1.44 2015/12/24 11:45:34 jca Exp $ +.\" $FreeBSD: src/usr.bin/file/file.1,v 1.16 2000/03/01 12:19:39 sheldonh Exp $ +.\" +.\" Copyright (c) 2015 Nicholas Marriott +.\" Copyright (c) Ian F. Darwin 1986-1995. +.\" Software written by Ian F. Darwin and others; +.\" maintained 1995-present by Christos Zoulas and others. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice immediately at the beginning of the file, without modification, +.\" this list of conditions, and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR +.\" ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd $Mdocdate: December 24 2015 $ +.Dt FILE 1 +.Os +.Sh NAME +.Nm file +.Nd determine file type +.Sh SYNOPSIS +.Nm +.Op Fl bchiLsW +.Ar +.Sh DESCRIPTION +The +.Nm +utility tests each argument and attempts to determine its type. +Three sets of tests are performed: +.Bl -enum -offset Ds +.It +Filesystem tests, for example if a file is empty, or a special file such as a +socket or named pipe (FIFO). +.It +.Dq Magic +tests for data in particular fixed formats. +These are loaded from the +.Pa /etc/magic +file (or +.Pa ~/.magic +instead if it exists and +.Nm +is not running as root). +The file format is described in +.Xr magic 5 . +.It +Tests for text files such as plain ASCII or C programming language files. +.El +.Pp +The first test which succeeds causes the file type to be printed. +The type will often contain one of the words +.Em text +(contains only printing characters and is probably safe to read on an ASCII +terminal), +.Em executable +(the file contains a compiled executable program) +or +.Em data +meaning anything else. +.Pp +If +.Ar file +is a single dash +.Pq Sq - , +.Nm +reads from the standard input. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b , -brief +Does not prepend filenames to output lines. +.It Fl c +Prints a summary of the parsed magic file; usually used for debugging. +.It Fl h +Causes symlinks not to be followed. +This is the default. +.It Fl i , -mime , -mime-type +Outputs MIME type strings rather than the more +traditional human-readable ones. +Thus it may say +.Dq text/plain +rather than +.Dq ASCII text . +.It Fl L , -dereference +Causes symlinks to be followed. +.It Fl s +Attempts to read block and character device files, not just regular files. +.It Fl W +Displays warnings when parsing the magic file or applying its tests. +Usually used for debugging. +.El +.Sh FILES +.Bl -tag -width /etc/magic -compact +.It Pa /etc/magic +default magic file +.El +.Sh EXIT STATUS +.Ex -std file +.Sh SEE ALSO +.Xr magic 5 +.Sh AUTHORS +.An -nosplit +.Nm +commands have appeared in many previous versions of +.Ux . +This version was written by +.An Nicholas Marriott +for +.Ox 5.8 +to replace the previous version originally written by +.An Ian Darwin . +.Pp +There is a large number of contributors to the magic files; many are listed in +the source files. diff --git a/man/test_files/mdoc/flex.1 b/man/test_files/mdoc/flex.1 new file mode 100644 index 00000000..fead20ef --- /dev/null +++ b/man/test_files/mdoc/flex.1 @@ -0,0 +1,4440 @@ +.\" $OpenBSD: flex.1,v 1.46 2024/11/09 18:06:00 op Exp $ +.\" +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Vern Paxson. +.\" +.\" The United States Government has rights in this work pursuant +.\" to contract no. DE-AC03-76SF00098 between the United States +.\" Department of Energy and the University of California. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE. +.\" +.Dd $Mdocdate: November 9 2024 $ +.Dt FLEX 1 +.Os +.Sh NAME +.Nm flex , +.Nm flex++ , +.Nm lex +.Nd fast lexical analyzer generator +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl 78BbdFfhIiLlnpsTtVvw+? +.Op Fl C Ns Op Cm aeFfmr +.Op Fl Fl help +.Op Fl Fl version +.Op Fl o Ns Ar output +.Op Fl P Ns Ar prefix +.Op Fl S Ns Ar skeleton +.Op Ar +.Ek +.Sh DESCRIPTION +.Nm +is a tool for generating +.Em scanners : +programs which recognize lexical patterns in text. +.Nm +reads the given input files, or its standard input if no file names are given, +for a description of a scanner to generate. +The description is in the form of pairs of regular expressions and C code, +called +.Em rules . +.Nm +generates as output a C source file, +.Pa lex.yy.c , +which defines a routine +.Fn yylex . +This file is compiled and linked with the +.Fl lfl +library to produce an executable. +When the executable is run, it analyzes its input for occurrences +of the regular expressions. +Whenever it finds one, it executes the corresponding C code. +.Pp +.Nm lex +is a synonym for +.Nm flex . +.Nm flex++ +is a synonym for +.Nm +.Fl + . +.Pp +The manual includes both tutorial and reference sections: +.Bl -ohang +.It Sy Some Simple Examples +.It Sy Format of the Input File +.It Sy Patterns +The extended regular expressions used by +.Nm . +.It Sy How the Input is Matched +The rules for determining what has been matched. +.It Sy Actions +How to specify what to do when a pattern is matched. +.It Sy The Generated Scanner +Details regarding the scanner that +.Nm +produces; +how to control the input source. +.It Sy Start Conditions +Introducing context into scanners, and managing +.Qq mini-scanners . +.It Sy Multiple Input Buffers +How to manipulate multiple input sources; +how to scan from strings instead of files. +.It Sy End-of-File Rules +Special rules for matching the end of the input. +.It Sy Miscellaneous Macros +A summary of macros available to the actions. +.It Sy Values Available to the User +A summary of values available to the actions. +.It Sy Interfacing with Yacc +Connecting flex scanners together with +.Xr yacc 1 +parsers. +.It Sy Options +.Nm +command-line options, and the +.Dq %option +directive. +.It Sy Performance Considerations +How to make scanners go as fast as possible. +.It Sy Generating C++ Scanners +The +.Pq experimental +facility for generating C++ scanner classes. +.It Sy Incompatibilities with Lex and POSIX +How +.Nm +differs from +.At +.Nm lex +and the +.Tn POSIX +.Nm lex +standard. +.It Sy Files +Files used by +.Nm . +.It Sy Diagnostics +Those error messages produced by +.Nm +.Pq or scanners it generates +whose meanings might not be apparent. +.It Sy See Also +Other documentation, related tools. +.It Sy Authors +Includes contact information. +.It Sy Bugs +Known problems with +.Nm . +.El +.Sh SOME SIMPLE EXAMPLES +First some simple examples to get the flavor of how one uses +.Nm . +The following +.Nm +input specifies a scanner which whenever it encounters the string +.Qq username +will replace it with the user's login name: +.Bd -literal -offset indent +%% +username printf("%s", getlogin()); +.Ed +.Pp +By default, any text not matched by a +.Nm +scanner is copied to the output, so the net effect of this scanner is +to copy its input file to its output with each occurrence of +.Qq username +expanded. +In this input, there is just one rule. +.Qq username +is the +.Em pattern +and the +.Qq printf +is the +.Em action . +The +.Qq %% +marks the beginning of the rules. +.Pp +Here's another simple example: +.Bd -literal -offset indent +%{ +int num_lines = 0, num_chars = 0; +%} + +%% +\en ++num_lines; ++num_chars; +\&. ++num_chars; + +%% +main() +{ + yylex(); + printf("# of lines = %d, # of chars = %d\en", + num_lines, num_chars); +} +.Ed +.Pp +This scanner counts the number of characters and the number +of lines in its input +(it produces no output other than the final report on the counts). +The first line declares two globals, +.Qq num_lines +and +.Qq num_chars , +which are accessible both inside +.Fn yylex +and in the +.Fn main +routine declared after the second +.Qq %% . +There are two rules, one which matches a newline +.Pq \&"\en\&" +and increments both the line count and the character count, +and one which matches any character other than a newline +(indicated by the +.Qq \&. +regular expression). +.Pp +A somewhat more complicated example: +.Bd -literal -offset indent +/* scanner for a toy Pascal-like language */ + +DIGIT [0-9] +ID [a-z][a-z0-9]* + +%% + +{DIGIT}+ { + printf("An integer: %s\en", yytext); +} + +{DIGIT}+"."{DIGIT}* { + printf("A float: %s\en", yytext); +} + +if|then|begin|end|procedure|function { + printf("A keyword: %s\en", yytext); +} + +{ID} printf("An identifier: %s\en", yytext); + +"+"|"-"|"*"|"/" printf("An operator: %s\en", yytext); + +"{"[^}\en]*"}" /* eat up one-line comments */ + +[ \et\en]+ /* eat up whitespace */ + +\&. printf("Unrecognized character: %s\en", yytext); + +%% + +int +main(int argc, char *argv[]) +{ + ++argv; --argc; /* skip over program name */ + if (argc > 0) + yyin = fopen(argv[0], "r"); + else + yyin = stdin; + + yylex(); +} +.Ed +.Pp +This is the beginnings of a simple scanner for a language like Pascal. +It identifies different types of +.Em tokens +and reports on what it has seen. +.Pp +The details of this example will be explained in the following sections. +.Sh FORMAT OF THE INPUT FILE +The +.Nm +input file consists of three sections, separated by a line with just +.Qq %% +in it: +.Bd -unfilled -offset indent +definitions +%% +rules +%% +user code +.Ed +.Pp +The +.Em definitions +section contains declarations of simple +.Em name +definitions to simplify the scanner specification, and declarations of +.Em start conditions , +which are explained in a later section. +.Pp +Name definitions have the form: +.Pp +.D1 name definition +.Pp +The +.Qq name +is a word beginning with a letter or an underscore +.Pq Sq _ +followed by zero or more letters, digits, +.Sq _ , +or +.Sq - +.Pq dash . +The definition is taken to begin at the first non-whitespace character +following the name and continuing to the end of the line. +The definition can subsequently be referred to using +.Qq {name} , +which will expand to +.Qq (definition) . +For example: +.Bd -literal -offset indent +DIGIT [0-9] +ID [a-z][a-z0-9]* +.Ed +.Pp +This defines +.Qq DIGIT +to be a regular expression which matches a single digit, and +.Qq ID +to be a regular expression which matches a letter +followed by zero-or-more letters-or-digits. +A subsequent reference to +.Pp +.Dl {DIGIT}+"."{DIGIT}* +.Pp +is identical to +.Pp +.Dl ([0-9])+"."([0-9])* +.Pp +and matches one-or-more digits followed by a +.Sq .\& +followed by zero-or-more digits. +.Pp +The +.Em rules +section of the +.Nm +input contains a series of rules of the form: +.Pp +.Dl pattern action +.Pp +The pattern must be unindented and the action must begin +on the same line. +.Pp +See below for a further description of patterns and actions. +.Pp +Finally, the user code section is simply copied to +.Pa lex.yy.c +verbatim. +It is used for companion routines which call or are called by the scanner. +The presence of this section is optional; +if it is missing, the second +.Qq %% +in the input file may be skipped too. +.Pp +In the definitions and rules sections, any indented text or text enclosed in +.Sq %{ +and +.Sq %} +is copied verbatim to the output +.Pq with the %{}'s removed . +The %{}'s must appear unindented on lines by themselves. +.Pp +In the rules section, +any indented or %{} text appearing before the first rule may be used to +declare variables which are local to the scanning routine and +.Pq after the declarations +code which is to be executed whenever the scanning routine is entered. +Other indented or %{} text in the rule section is still copied to the output, +but its meaning is not well-defined and it may well cause compile-time +errors (this feature is present for +.Tn POSIX +compliance; see below for other such features). +.Pp +In the definitions section +.Pq but not in the rules section , +an unindented comment +(i.e., a line beginning with +.Qq /* ) +is also copied verbatim to the output up to the next +.Qq */ . +.Sh PATTERNS +The patterns in the input are written using an extended set of regular +expressions. +These are: +.Bl -tag -width "XXXXXXXX" +.It x +Match the character +.Sq x . +.It .\& +Any character +.Pq byte +except newline. +.It [xyz] +A +.Qq character class ; +in this case, the pattern matches either an +.Sq x , +a +.Sq y , +or a +.Sq z . +.It [abj-oZ] +A +.Qq character class +with a range in it; matches an +.Sq a , +a +.Sq b , +any letter from +.Sq j +through +.Sq o , +or a +.Sq Z . +.It [^A-Z] +A +.Qq negated character class , +i.e., any character but those in the class. +In this case, any character EXCEPT an uppercase letter. +.It [^A-Z\en] +Any character EXCEPT an uppercase letter or a newline. +.It r* +Zero or more r's, where +.Sq r +is any regular expression. +.It r+ +One or more r's. +.It r? +Zero or one r's (that is, +.Qq an optional r ) . +.It r{2,5} +Anywhere from two to five r's. +.It r{2,} +Two or more r's. +.It r{4} +Exactly 4 r's. +.It {name} +The expansion of the +.Qq name +definition +.Pq see above . +.It \&"[xyz]\e\&"foo\&" +The literal string: [xyz]"foo. +.It \eX +If +.Sq X +is an +.Sq a , +.Sq b , +.Sq f , +.Sq n , +.Sq r , +.Sq t , +or +.Sq v , +then the ANSI-C interpretation of +.Sq \eX . +Otherwise, a literal +.Sq X +(used to escape operators such as +.Sq * ) . +.It \e0 +A NUL character +.Pq ASCII code 0 . +.It \e123 +The character with octal value 123. +.It \ex2a +The character with hexadecimal value 2a. +.It (r) +Match an +.Sq r ; +parentheses are used to override precedence +.Pq see below . +.It rs +The regular expression +.Sq r +followed by the regular expression +.Sq s ; +called +.Qq concatenation . +.It r|s +Either an +.Sq r +or an +.Sq s . +.It r/s +An +.Sq r , +but only if it is followed by an +.Sq s . +The text matched by +.Sq s +is included when determining whether this rule is the +.Qq longest match , +but is then returned to the input before the action is executed. +So the action only sees the text matched by +.Sq r . +This type of pattern is called +.Qq trailing context . +(There are some combinations of r/s that +.Nm +cannot match correctly; see notes in the +.Sx BUGS +section below regarding +.Qq dangerous trailing context . ) +.It ^r +An +.Sq r , +but only at the beginning of a line +(i.e., just starting to scan, or right after a newline has been scanned). +.It r$ +An +.Sq r , +but only at the end of a line +.Pq i.e., just before a newline . +Equivalent to +.Qq r/\en . +.Pp +Note that +.Nm flex Ns 's +notion of +.Qq newline +is exactly whatever the C compiler used to compile +.Nm +interprets +.Sq \en +as. +.\" In particular, on some DOS systems you must either filter out \er's in the +.\" input yourself, or explicitly use r/\er\en for +.\" .Qq r$ . +.It r +An +.Sq r , +but only in start condition +.Sq s +.Pq see below for discussion of start conditions . +.It r +The same, but in any of start conditions s1, s2, or s3. +.It <*>r +An +.Sq r +in any start condition, even an exclusive one. +.It <> +An end-of-file. +.It <> +An end-of-file when in start condition s1 or s2. +.El +.Pp +Note that inside of a character class, all regular expression operators +lose their special meaning except escape +.Pq Sq \e +and the character class operators, +.Sq - , +.Sq ]\& , +and, at the beginning of the class, +.Sq ^ . +.Pp +The regular expressions listed above are grouped according to +precedence, from highest precedence at the top to lowest at the bottom. +Those grouped together have equal precedence. +For example, +.Pp +.D1 foo|bar* +.Pp +is the same as +.Pp +.D1 (foo)|(ba(r*)) +.Pp +since the +.Sq * +operator has higher precedence than concatenation, +and concatenation higher than alternation +.Pq Sq |\& . +This pattern therefore matches +.Em either +the string +.Qq foo +.Em or +the string +.Qq ba +followed by zero-or-more r's. +To match +.Qq foo +or zero-or-more "bar"'s, +use: +.Pp +.D1 foo|(bar)* +.Pp +and to match zero-or-more "foo"'s-or-"bar"'s: +.Pp +.D1 (foo|bar)* +.Pp +In addition to characters and ranges of characters, character classes +can also contain character class +.Em expressions . +These are expressions enclosed inside +.Sq [: +and +.Sq :] +delimiters (which themselves must appear between the +.Sq \&[ +and +.Sq ]\& +of the +character class; other elements may occur inside the character class, too). +The valid expressions are: +.Bd -unfilled -offset indent +[:alnum:] [:alpha:] [:blank:] +[:cntrl:] [:digit:] [:graph:] +[:lower:] [:print:] [:punct:] +[:space:] [:upper:] [:xdigit:] +.Ed +.Pp +These expressions all designate a set of characters equivalent to +the corresponding standard C +.Fn isXXX +function. +For example, [:alnum:] designates those characters for which +.Xr isalnum 3 +returns true \- i.e., any alphabetic or numeric. +Some systems don't provide +.Xr isblank 3 , +so +.Nm +defines [:blank:] as a blank or a tab. +.Pp +For example, the following character classes are all equivalent: +.Bd -unfilled -offset indent +[[:alnum:]] +[[:alpha:][:digit:]] +[[:alpha:]0-9] +[a-zA-Z0-9] +.Ed +.Pp +If the scanner is case-insensitive (the +.Fl i +flag), then [:upper:] and [:lower:] are equivalent to [:alpha:]. +.Pp +Some notes on patterns: +.Bl -dash +.It +A negated character class such as the example +.Qq [^A-Z] +above will match a newline unless "\en" +.Pq or an equivalent escape sequence +is one of the characters explicitly present in the negated character class +(e.g., +.Qq [^A-Z\en] ) . +This is unlike how many other regular expression tools treat negated character +classes, but unfortunately the inconsistency is historically entrenched. +Matching newlines means that a pattern like +.Qq [^"]* +can match the entire input unless there's another quote in the input. +.It +A rule can have at most one instance of trailing context +(the +.Sq / +operator or the +.Sq $ +operator). +The start condition, +.Sq ^ , +and +.Qq <> +patterns can only occur at the beginning of a pattern and, as well as with +.Sq / +and +.Sq $ , +cannot be grouped inside parentheses. +A +.Sq ^ +which does not occur at the beginning of a rule or a +.Sq $ +which does not occur at the end of a rule loses its special properties +and is treated as a normal character. +.It +The following are illegal: +.Bd -unfilled -offset indent +foo/bar$ +foobar +.Ed +.Pp +Note that the first of these, can be written +.Qq foo/bar\en . +.It +The following will result in +.Sq $ +or +.Sq ^ +being treated as a normal character: +.Bd -unfilled -offset indent +foo|(bar$) +foo|^bar +.Ed +.Pp +If what's wanted is a +.Qq foo +or a bar-followed-by-a-newline, the following could be used +(the special +.Sq |\& +action is explained below): +.Bd -unfilled -offset indent +foo | +bar$ /* action goes here */ +.Ed +.Pp +A similar trick will work for matching a foo or a +bar-at-the-beginning-of-a-line. +.El +.Sh HOW THE INPUT IS MATCHED +When the generated scanner is run, +it analyzes its input looking for strings which match any of its patterns. +If it finds more than one match, +it takes the one matching the most text +(for trailing context rules, this includes the length of the trailing part, +even though it will then be returned to the input). +If it finds two or more matches of the same length, +the rule listed first in the +.Nm +input file is chosen. +.Pp +Once the match is determined, the text corresponding to the match +(called the +.Em token ) +is made available in the global character pointer +.Fa yytext , +and its length in the global integer +.Fa yyleng . +The +.Em action +corresponding to the matched pattern is then executed +.Pq a more detailed description of actions follows , +and then the remaining input is scanned for another match. +.Pp +If no match is found, then the default rule is executed: +the next character in the input is considered matched and +copied to the standard output. +Thus, the simplest legal +.Nm +input is: +.Pp +.D1 %% +.Pp +which generates a scanner that simply copies its input +.Pq one character at a time +to its output. +.Pp +Note that +.Fa yytext +can be defined in two different ways: +either as a character pointer or as a character array. +Which definition +.Nm +uses can be controlled by including one of the special directives +.Dq %pointer +or +.Dq %array +in the first +.Pq definitions +section of flex input. +The default is +.Dq %pointer , +unless the +.Fl l +.Nm lex +compatibility option is used, in which case +.Fa yytext +will be an array. +The advantage of using +.Dq %pointer +is substantially faster scanning and no buffer overflow when matching +very large tokens +.Pq unless not enough dynamic memory is available . +The disadvantage is that actions are restricted in how they can modify +.Fa yytext +.Pq see the next section , +and calls to the +.Fn unput +function destroy the present contents of +.Fa yytext , +which can be a considerable porting headache when moving between different +.Nm lex +versions. +.Pp +The advantage of +.Dq %array +is that +.Fa yytext +can be modified as much as wanted, and calls to +.Fn unput +do not destroy +.Fa yytext +.Pq see below . +Furthermore, existing +.Nm lex +programs sometimes access +.Fa yytext +externally using declarations of the form: +.Pp +.D1 extern char yytext[]; +.Pp +This definition is erroneous when used with +.Dq %pointer , +but correct for +.Dq %array . +.Pp +.Dq %array +defines +.Fa yytext +to be an array of +.Dv YYLMAX +characters, which defaults to a fairly large value. +The size can be changed by simply #define'ing +.Dv YYLMAX +to a different value in the first section of +.Nm +input. +As mentioned above, with +.Dq %pointer +yytext grows dynamically to accommodate large tokens. +While this means a +.Dq %pointer +scanner can accommodate very large tokens +.Pq such as matching entire blocks of comments , +bear in mind that each time the scanner must resize +.Fa yytext +it also must rescan the entire token from the beginning, so matching such +tokens can prove slow. +.Fa yytext +presently does not dynamically grow if a call to +.Fn unput +results in too much text being pushed back; instead, a run-time error results. +.Pp +Also note that +.Dq %array +cannot be used with C++ scanner classes +.Pq the c++ option; see below . +.Sh ACTIONS +Each pattern in a rule has a corresponding action, +which can be any arbitrary C statement. +The pattern ends at the first non-escaped whitespace character; +the remainder of the line is its action. +If the action is empty, +then when the pattern is matched the input token is simply discarded. +For example, here is the specification for a program +which deletes all occurrences of +.Qq zap me +from its input: +.Bd -literal -offset indent +%% +"zap me" +.Ed +.Pp +(It will copy all other characters in the input to the output since +they will be matched by the default rule.) +.Pp +Here is a program which compresses multiple blanks and tabs down to +a single blank, and throws away whitespace found at the end of a line: +.Bd -literal -offset indent +%% +[ \et]+ putchar(' '); +[ \et]+$ /* ignore this token */ +.Ed +.Pp +If the action contains a +.Sq { , +then the action spans till the balancing +.Sq } +is found, and the action may cross multiple lines. +.Nm +knows about C strings and comments and won't be fooled by braces found +within them, but also allows actions to begin with +.Sq %{ +and will consider the action to be all the text up to the next +.Sq %} +.Pq regardless of ordinary braces inside the action . +.Pp +An action consisting solely of a vertical bar +.Pq Sq |\& +means +.Qq same as the action for the next rule . +See below for an illustration. +.Pp +Actions can include arbitrary C code, +including return statements to return a value to whatever routine called +.Fn yylex . +Each time +.Fn yylex +is called, it continues processing tokens from where it last left off +until it either reaches the end of the file or executes a return. +.Pp +Actions are free to modify +.Fa yytext +except for lengthening it +(adding characters to its end \- these will overwrite later characters in the +input stream). +This, however, does not apply when using +.Dq %array +.Pq see above ; +in that case, +.Fa yytext +may be freely modified in any way. +.Pp +Actions are free to modify +.Fa yyleng +except they should not do so if the action also includes use of +.Fn yymore +.Pq see below . +.Pp +There are a number of special directives which can be included within +an action: +.Bl -tag -width Ds +.It ECHO +Copies +.Fa yytext +to the scanner's output. +.It BEGIN +Followed by the name of a start condition, places the scanner in the +corresponding start condition +.Pq see below . +.It REJECT +Directs the scanner to proceed on to the +.Qq second best +rule which matched the input +.Pq or a prefix of the input . +The rule is chosen as described above in +.Sx HOW THE INPUT IS MATCHED , +and +.Fa yytext +and +.Fa yyleng +set up appropriately. +It may either be one which matched as much text +as the originally chosen rule but came later in the +.Nm +input file, or one which matched less text. +For example, the following will both count the +words in the input and call the routine +.Fn special +whenever +.Qq frob +is seen: +.Bd -literal -offset indent +int word_count = 0; +%% + +frob special(); REJECT; +[^ \et\en]+ ++word_count; +.Ed +.Pp +Without the +.Em REJECT , +any "frob"'s in the input would not be counted as words, +since the scanner normally executes only one action per token. +Multiple +.Em REJECT Ns 's +are allowed, +each one finding the next best choice to the currently active rule. +For example, when the following scanner scans the token +.Qq abcd , +it will write +.Qq abcdabcaba +to the output: +.Bd -literal -offset indent +%% +a | +ab | +abc | +abcd ECHO; REJECT; +\&.|\en /* eat up any unmatched character */ +.Ed +.Pp +(The first three rules share the fourth's action since they use +the special +.Sq |\& +action.) +.Em REJECT +is a particularly expensive feature in terms of scanner performance; +if it is used in any of the scanner's actions it will slow down +all of the scanner's matching. +Furthermore, +.Em REJECT +cannot be used with the +.Fl Cf +or +.Fl CF +options +.Pq see below . +.Pp +Note also that unlike the other special actions, +.Em REJECT +is a +.Em branch ; +code immediately following it in the action will not be executed. +.It yymore() +Tells the scanner that the next time it matches a rule, the corresponding +token should be appended onto the current value of +.Fa yytext +rather than replacing it. +For example, given the input +.Qq mega-kludge +the following will write +.Qq mega-mega-kludge +to the output: +.Bd -literal -offset indent +%% +mega- ECHO; yymore(); +kludge ECHO; +.Ed +.Pp +First +.Qq mega- +is matched and echoed to the output. +Then +.Qq kludge +is matched, but the previous +.Qq mega- +is still hanging around at the beginning of +.Fa yytext +so the +.Em ECHO +for the +.Qq kludge +rule will actually write +.Qq mega-kludge . +.Pp +Two notes regarding use of +.Fn yymore : +First, +.Fn yymore +depends on the value of +.Fa yyleng +correctly reflecting the size of the current token, so +.Fa yyleng +must not be modified when using +.Fn yymore . +Second, the presence of +.Fn yymore +in the scanner's action entails a minor performance penalty in the +scanner's matching speed. +.It yyless(n) +Returns all but the first +.Ar n +characters of the current token back to the input stream, where they +will be rescanned when the scanner looks for the next match. +.Fa yytext +and +.Fa yyleng +are adjusted appropriately (e.g., +.Fa yyleng +will now be equal to +.Ar n ) . +For example, on the input +.Qq foobar +the following will write out +.Qq foobarbar : +.Bd -literal -offset indent +%% +foobar ECHO; yyless(3); +[a-z]+ ECHO; +.Ed +.Pp +An argument of 0 to +.Fa yyless +will cause the entire current input string to be scanned again. +Unless how the scanner will subsequently process its input has been changed +(using +.Em BEGIN , +for example), +this will result in an endless loop. +.Pp +Note that +.Fa yyless +is a macro and can only be used in the +.Nm +input file, not from other source files. +.It unput(c) +Puts the character +.Ar c +back into the input stream. +It will be the next character scanned. +The following action will take the current token and cause it +to be rescanned enclosed in parentheses. +.Bd -literal -offset indent +{ + int i; + char *yycopy; + + /* Copy yytext because unput() trashes yytext */ + if ((yycopy = strdup(yytext)) == NULL) + err(1, NULL); + unput(')'); + for (i = yyleng - 1; i >= 0; --i) + unput(yycopy[i]); + unput('('); + free(yycopy); +} +.Ed +.Pp +Note that since each +.Fn unput +puts the given character back at the beginning of the input stream, +pushing back strings must be done back-to-front. +.Pp +An important potential problem when using +.Fn unput +is that if using +.Dq %pointer +.Pq the default , +a call to +.Fn unput +destroys the contents of +.Fa yytext , +starting with its rightmost character and devouring one character to +the left with each call. +If the value of +.Fa yytext +should be preserved after a call to +.Fn unput +.Pq as in the above example , +it must either first be copied elsewhere, or the scanner must be built using +.Dq %array +instead (see +.Sx HOW THE INPUT IS MATCHED ) . +.Pp +Finally, note that EOF cannot be put back +to attempt to mark the input stream with an end-of-file. +.It input() +Reads the next character from the input stream. +For example, the following is one way to eat up C comments: +.Bd -literal -offset indent +%% +"/*" { + int c; + + for (;;) { + while ((c = input()) != '*' && c != EOF) + ; /* eat up text of comment */ + + if (c == '*') { + while ((c = input()) == '*') + ; + if (c == '/') + break; /* found the end */ + } + + if (c == EOF) { + errx(1, "EOF in comment"); + break; + } + } +} +.Ed +.Pp +(Note that if the scanner is compiled using C++, then +.Fn input +is instead referred to as +.Fn yyinput , +in order to avoid a name clash with the C++ stream by the name of input.) +.It YY_FLUSH_BUFFER +Flushes the scanner's internal buffer +so that the next time the scanner attempts to match a token, +it will first refill the buffer using +.Dv YY_INPUT +(see +.Sx THE GENERATED SCANNER , +below). +This action is a special case of the more general +.Fn yy_flush_buffer +function, described below in the section +.Sx MULTIPLE INPUT BUFFERS . +.It yyterminate() +Can be used in lieu of a return statement in an action. +It terminates the scanner and returns a 0 to the scanner's caller, indicating +.Qq all done . +By default, +.Fn yyterminate +is also called when an end-of-file is encountered. +It is a macro and may be redefined. +.El +.Sh THE GENERATED SCANNER +The output of +.Nm +is the file +.Pa lex.yy.c , +which contains the scanning routine +.Fn yylex , +a number of tables used by it for matching tokens, +and a number of auxiliary routines and macros. +By default, +.Fn yylex +is declared as follows: +.Bd -unfilled -offset indent +int yylex() +{ + ... various definitions and the actions in here ... +} +.Ed +.Pp +(If the environment supports function prototypes, then it will +be "int yylex(void)".) +This definition may be changed by defining the +.Dv YY_DECL +macro. +For example: +.Bd -literal -offset indent +#define YY_DECL float lexscan(a, b) float a, b; +.Ed +.Pp +would give the scanning routine the name +.Em lexscan , +returning a float, and taking two floats as arguments. +Note that if arguments are given to the scanning routine using a +K&R-style/non-prototyped function declaration, +the definition must be terminated with a semi-colon +.Pq Sq ;\& . +.Pp +Whenever +.Fn yylex +is called, it scans tokens from the global input file +.Pa yyin +.Pq which defaults to stdin . +It continues until it either reaches an end-of-file +.Pq at which point it returns the value 0 +or one of its actions executes a +.Em return +statement. +.Pp +If the scanner reaches an end-of-file, subsequent calls are undefined +unless either +.Em yyin +is pointed at a new input file +.Pq in which case scanning continues from that file , +or +.Fn yyrestart +is called. +.Fn yyrestart +takes one argument, a +.Fa FILE * +pointer (which can be nil, if +.Dv YY_INPUT +has been set up to scan from a source other than +.Em yyin ) , +and initializes +.Em yyin +for scanning from that file. +Essentially there is no difference between just assigning +.Em yyin +to a new input file or using +.Fn yyrestart +to do so; the latter is available for compatibility with previous versions of +.Nm , +and because it can be used to switch input files in the middle of scanning. +It can also be used to throw away the current input buffer, +by calling it with an argument of +.Em yyin ; +but better is to use +.Dv YY_FLUSH_BUFFER +.Pq see above . +Note that +.Fn yyrestart +does not reset the start condition to +.Em INITIAL +(see +.Sx START CONDITIONS , +below). +.Pp +If +.Fn yylex +stops scanning due to executing a +.Em return +statement in one of the actions, the scanner may then be called again and it +will resume scanning where it left off. +.Pp +By default +.Pq and for purposes of efficiency , +the scanner uses block-reads rather than simple +.Xr getc 3 +calls to read characters from +.Em yyin . +The nature of how it gets its input can be controlled by defining the +.Dv YY_INPUT +macro. +.Dv YY_INPUT Ns 's +calling sequence is +.Qq YY_INPUT(buf,result,max_size) . +Its action is to place up to +.Dv max_size +characters in the character array +.Em buf +and return in the integer variable +.Em result +either the number of characters read or the constant +.Dv YY_NULL +(0 on +.Ux +systems) +to indicate +.Dv EOF . +The default +.Dv YY_INPUT +reads from the global file-pointer +.Qq yyin . +.Pp +A sample definition of +.Dv YY_INPUT +.Pq in the definitions section of the input file : +.Bd -unfilled -offset indent +%{ +#define YY_INPUT(buf,result,max_size) \e +{ \e + int c = getchar(); \e + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \e +} +%} +.Ed +.Pp +This definition will change the input processing to occur +one character at a time. +.Pp +When the scanner receives an end-of-file indication from +.Dv YY_INPUT , +it then checks the +.Fn yywrap +function. +If +.Fn yywrap +returns false +.Pq zero , +then it is assumed that the function has gone ahead and set up +.Em yyin +to point to another input file, and scanning continues. +If it returns true +.Pq non-zero , +then the scanner terminates, returning 0 to its caller. +Note that in either case, the start condition remains unchanged; +it does not revert to +.Em INITIAL . +.Pp +If you do not supply your own version of +.Fn yywrap , +then you must either use +.Dq %option noyywrap +(in which case the scanner behaves as though +.Fn yywrap +returned 1), or you must link with +.Fl lfl +to obtain the default version of the routine, which always returns 1. +.Pp +Three routines are available for scanning from in-memory buffers rather +than files: +.Fn yy_scan_string , +.Fn yy_scan_bytes , +and +.Fn yy_scan_buffer . +See the discussion of them below in the section +.Sx MULTIPLE INPUT BUFFERS . +.Pp +The scanner writes its +.Em ECHO +output to the +.Em yyout +global +.Pq default, stdout , +which may be redefined by the user simply by assigning it to some other +.Va FILE +pointer. +.Sh START CONDITIONS +.Nm +provides a mechanism for conditionally activating rules. +Any rule whose pattern is prefixed with +.Qq Aq sc +will only be active when the scanner is in the start condition named +.Qq sc . +For example, +.Bd -literal -offset indent +[^"]* { /* eat up the string body ... */ + ... +} +.Ed +.Pp +will be active only when the scanner is in the +.Qq STRING +start condition, and +.Bd -literal -offset indent +\e. { /* handle an escape ... */ + ... +} +.Ed +.Pp +will be active only when the current start condition is either +.Qq INITIAL , +.Qq STRING , +or +.Qq QUOTE . +.Pp +Start conditions are declared in the definitions +.Pq first +section of the input using unindented lines beginning with either +.Sq %s +or +.Sq %x +followed by a list of names. +The former declares +.Em inclusive +start conditions, the latter +.Em exclusive +start conditions. +A start condition is activated using the +.Em BEGIN +action. +Until the next +.Em BEGIN +action is executed, rules with the given start condition will be active and +rules with other start conditions will be inactive. +If the start condition is inclusive, +then rules with no start conditions at all will also be active. +If it is exclusive, +then only rules qualified with the start condition will be active. +A set of rules contingent on the same exclusive start condition +describe a scanner which is independent of any of the other rules in the +.Nm +input. +Because of this, exclusive start conditions make it easy to specify +.Qq mini-scanners +which scan portions of the input that are syntactically different +from the rest +.Pq e.g., comments . +.Pp +If the distinction between inclusive and exclusive start conditions +is still a little vague, here's a simple example illustrating the +connection between the two. +The set of rules: +.Bd -literal -offset indent +%s example +%% + +foo do_something(); + +bar something_else(); +.Ed +.Pp +is equivalent to +.Bd -literal -offset indent +%x example +%% + +foo do_something(); + +bar something_else(); +.Ed +.Pp +Without the +.Aq INITIAL,example +qualifier, the +.Dq bar +pattern in the second example wouldn't be active +.Pq i.e., couldn't match +when in start condition +.Dq example . +If we just used +.Aq example +to qualify +.Dq bar , +though, then it would only be active in +.Dq example +and not in +.Em INITIAL , +while in the first example it's active in both, +because in the first example the +.Dq example +start condition is an inclusive +.Pq Sq %s +start condition. +.Pp +Also note that the special start-condition specifier +.Sq Aq * +matches every start condition. +Thus, the above example could also have been written: +.Bd -literal -offset indent +%x example +%% + +foo do_something(); + +<*>bar something_else(); +.Ed +.Pp +The default rule (to +.Em ECHO +any unmatched character) remains active in start conditions. +It is equivalent to: +.Bd -literal -offset indent +<*>.|\en ECHO; +.Ed +.Pp +.Dq BEGIN(0) +returns to the original state where only the rules with +no start conditions are active. +This state can also be referred to as the start-condition +.Em INITIAL , +so +.Dq BEGIN(INITIAL) +is equivalent to +.Dq BEGIN(0) . +(The parentheses around the start condition name are not required but +are considered good style.) +.Pp +.Em BEGIN +actions can also be given as indented code at the beginning +of the rules section. +For example, the following will cause the scanner to enter the +.Qq SPECIAL +start condition whenever +.Fn yylex +is called and the global variable +.Fa enter_special +is true: +.Bd -literal -offset indent +int enter_special; + +%x SPECIAL +%% + if (enter_special) + BEGIN(SPECIAL); + +blahblahblah +\&...more rules follow... +.Ed +.Pp +To illustrate the uses of start conditions, +here is a scanner which provides two different interpretations +of a string like +.Qq 123.456 . +By default it will treat it as three tokens: the integer +.Qq 123 , +a dot +.Pq Sq .\& , +and the integer +.Qq 456 . +But if the string is preceded earlier in the line by the string +.Qq expect-floats +it will treat it as a single token, the floating-point number 123.456: +.Bd -literal -offset indent +%{ +#include +%} +%s expect + +%% +expect-floats BEGIN(expect); + +[0-9]+"."[0-9]+ { + printf("found a float, = %s\en", yytext); +} +\en { + /* + * That's the end of the line, so + * we need another "expect-number" + * before we'll recognize any more + * numbers. + */ + BEGIN(INITIAL); +} + +[0-9]+ { + printf("found an integer, = %s\en", yytext); +} + +"." printf("found a dot\en"); +.Ed +.Pp +Here is a scanner which recognizes +.Pq and discards +C comments while maintaining a count of the current input line: +.Bd -literal -offset indent +%x comment +%% +int line_num = 1; + +"/*" BEGIN(comment); + +[^*\en]* /* eat anything that's not a '*' */ +"*"+[^*/\en]* /* eat up '*'s not followed by '/'s */ +\en ++line_num; +"*"+"/" BEGIN(INITIAL); +.Ed +.Pp +This scanner goes to a bit of trouble to match as much +text as possible with each rule. +In general, when attempting to write a high-speed scanner +try to match as much as possible in each rule, as it's a big win. +.Pp +Note that start-condition names are really integer values and +can be stored as such. +Thus, the above could be extended in the following fashion: +.Bd -literal -offset indent +%x comment foo +%% +int line_num = 1; +int comment_caller; + +"/*" { + comment_caller = INITIAL; + BEGIN(comment); +} + +\&... + +"/*" { + comment_caller = foo; + BEGIN(comment); +} + +[^*\en]* /* eat anything that's not a '*' */ +"*"+[^*/\en]* /* eat up '*'s not followed by '/'s */ +\en ++line_num; +"*"+"/" BEGIN(comment_caller); +.Ed +.Pp +Furthermore, the current start condition can be accessed by using +the integer-valued +.Dv YY_START +macro. +For example, the above assignments to +.Em comment_caller +could instead be written +.Pp +.Dl comment_caller = YY_START; +.Pp +Flex provides +.Dv YYSTATE +as an alias for +.Dv YY_START +(since that is what's used by +.At +.Nm lex ) . +.Pp +Note that start conditions do not have their own name-space; +%s's and %x's declare names in the same fashion as #define's. +.Pp +Finally, here's an example of how to match C-style quoted strings using +exclusive start conditions, including expanded escape sequences +(but not including checking for a string that's too long): +.Bd -literal -offset indent +%x str + +%% +#define MAX_STR_CONST 1024 +char string_buf[MAX_STR_CONST]; +char *string_buf_ptr; + +\e" string_buf_ptr = string_buf; BEGIN(str); + +\e" { /* saw closing quote - all done */ + BEGIN(INITIAL); + *string_buf_ptr = '\e0'; + /* + * return string constant token type and + * value to parser + */ +} + +\en { + /* error - unterminated string constant */ + /* generate error message */ +} + +\e\e[0-7]{1,3} { + /* octal escape sequence */ + int result; + + (void) sscanf(yytext + 1, "%o", &result); + + if (result > 0xff) { + /* error, constant is out-of-bounds */ + } else + *string_buf_ptr++ = result; +} + +\e\e[0-9]+ { + /* + * generate error - bad escape sequence; something + * like '\e48' or '\e0777777' + */ +} + +\e\en *string_buf_ptr++ = '\en'; +\e\et *string_buf_ptr++ = '\et'; +\e\er *string_buf_ptr++ = '\er'; +\e\eb *string_buf_ptr++ = '\eb'; +\e\ef *string_buf_ptr++ = '\ef'; + +\e\e(.|\en) *string_buf_ptr++ = yytext[1]; + +[^\e\e\en\e"]+ { + char *yptr = yytext; + + while (*yptr) + *string_buf_ptr++ = *yptr++; +} +.Ed +.Pp +Often, such as in some of the examples above, +a whole bunch of rules are all preceded by the same start condition(s). +.Nm +makes this a little easier and cleaner by introducing a notion of +start condition +.Em scope . +A start condition scope is begun with: +.Pp +.Dl { +.Pp +where +.Dq SCs +is a list of one or more start conditions. +Inside the start condition scope, every rule automatically has the prefix +.Aq SCs +applied to it, until a +.Sq } +which matches the initial +.Sq { . +So, for example, +.Bd -literal -offset indent +{ + "\e\en" return '\en'; + "\e\er" return '\er'; + "\e\ef" return '\ef'; + "\e\e0" return '\e0'; +} +.Ed +.Pp +is equivalent to: +.Bd -literal -offset indent +"\e\en" return '\en'; +"\e\er" return '\er'; +"\e\ef" return '\ef'; +"\e\e0" return '\e0'; +.Ed +.Pp +Start condition scopes may be nested. +.Pp +Three routines are available for manipulating stacks of start conditions: +.Bl -tag -width Ds +.It void yy_push_state(int new_state) +Pushes the current start condition onto the top of the start condition +stack and switches to +.Fa new_state +as though +.Dq BEGIN new_state +had been used +.Pq recall that start condition names are also integers . +.It void yy_pop_state() +Pops the top of the stack and switches to it via +.Em BEGIN . +.It int yy_top_state() +Returns the top of the stack without altering the stack's contents. +.El +.Pp +The start condition stack grows dynamically and so has no built-in +size limitation. +If memory is exhausted, program execution aborts. +.Pp +To use start condition stacks, scanners must include a +.Dq %option stack +directive (see +.Sx OPTIONS +below). +.Sh MULTIPLE INPUT BUFFERS +Some scanners +(such as those which support +.Qq include +files) +require reading from several input streams. +As +.Nm +scanners do a large amount of buffering, one cannot control +where the next input will be read from by simply writing a +.Dv YY_INPUT +which is sensitive to the scanning context. +.Dv YY_INPUT +is only called when the scanner reaches the end of its buffer, which +may be a long time after scanning a statement such as an +.Qq include +which requires switching the input source. +.Pp +To negotiate these sorts of problems, +.Nm +provides a mechanism for creating and switching between multiple +input buffers. +An input buffer is created by using: +.Pp +.D1 YY_BUFFER_STATE yy_create_buffer(FILE *file, int size) +.Pp +which takes a +.Fa FILE +pointer and a +.Fa size +and creates a buffer associated with the given file and large enough to hold +.Fa size +characters (when in doubt, use +.Dv YY_BUF_SIZE +for the size). +It returns a +.Dv YY_BUFFER_STATE +handle, which may then be passed to other routines +.Pq see below . +The +.Dv YY_BUFFER_STATE +type is a pointer to an opaque +.Dq struct yy_buffer_state +structure, so +.Dv YY_BUFFER_STATE +variables may be safely initialized to +.Dq ((YY_BUFFER_STATE) 0) +if desired, and the opaque structure can also be referred to in order to +correctly declare input buffers in source files other than that of scanners. +Note that the +.Fa FILE +pointer in the call to +.Fn yy_create_buffer +is only used as the value of +.Fa yyin +seen by +.Dv YY_INPUT ; +if +.Dv YY_INPUT +is redefined so that it no longer uses +.Fa yyin , +then a nil +.Fa FILE +pointer can safely be passed to +.Fn yy_create_buffer . +To select a particular buffer to scan: +.Pp +.D1 void yy_switch_to_buffer(YY_BUFFER_STATE new_buffer) +.Pp +It switches the scanner's input buffer so subsequent tokens will +come from +.Fa new_buffer . +Note that +.Fn yy_switch_to_buffer +may be used by +.Fn yywrap +to set things up for continued scanning, +instead of opening a new file and pointing +.Fa yyin +at it. +Note also that switching input sources via either +.Fn yy_switch_to_buffer +or +.Fn yywrap +does not change the start condition. +.Pp +.D1 void yy_delete_buffer(YY_BUFFER_STATE buffer) +.Pp +is used to reclaim the storage associated with a buffer. +.Pf ( Fa buffer +can be nil, in which case the routine does nothing.) +To clear the current contents of a buffer: +.Pp +.D1 void yy_flush_buffer(YY_BUFFER_STATE buffer) +.Pp +This function discards the buffer's contents, +so the next time the scanner attempts to match a token from the buffer, +it will first fill the buffer anew using +.Dv YY_INPUT . +.Pp +.Fn yy_new_buffer +is an alias for +.Fn yy_create_buffer , +provided for compatibility with the C++ use of +.Em new +and +.Em delete +for creating and destroying dynamic objects. +.Pp +Finally, the +.Dv YY_CURRENT_BUFFER +macro returns a +.Dv YY_BUFFER_STATE +handle to the current buffer. +.Pp +Here is an example of using these features for writing a scanner +which expands include files (the +.Aq Aq EOF +feature is discussed below): +.Bd -literal -offset indent +/* + * the "incl" state is used for picking up the name + * of an include file + */ +%x incl + +%{ +#define MAX_INCLUDE_DEPTH 10 +YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; +int include_stack_ptr = 0; +%} + +%% +include BEGIN(incl); + +[a-z]+ ECHO; +[^a-z\en]*\en? ECHO; + +[ \et]* /* eat the whitespace */ +[^ \et\en]+ { /* got the include file name */ + if (include_stack_ptr >= MAX_INCLUDE_DEPTH) + errx(1, "Includes nested too deeply"); + + include_stack[include_stack_ptr++] = + YY_CURRENT_BUFFER; + + yyin = fopen(yytext, "r"); + + if (yyin == NULL) + err(1, NULL); + + yy_switch_to_buffer( + yy_create_buffer(yyin, YY_BUF_SIZE)); + + BEGIN(INITIAL); +} + +<> { + if (--include_stack_ptr < 0) + yyterminate(); + else { + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer( + include_stack[include_stack_ptr]); + } +} +.Ed +.Pp +Three routines are available for setting up input buffers for +scanning in-memory strings instead of files. +All of them create a new input buffer for scanning the string, +and return a corresponding +.Dv YY_BUFFER_STATE +handle (which should be deleted afterwards using +.Fn yy_delete_buffer ) . +They also switch to the new buffer using +.Fn yy_switch_to_buffer , +so the next call to +.Fn yylex +will start scanning the string. +.Bl -tag -width Ds +.It yy_scan_string(const char *str) +Scans a NUL-terminated string. +.It yy_scan_bytes(const char *bytes, int len) +Scans +.Fa len +bytes +.Pq including possibly NUL's +starting at location +.Fa bytes . +.El +.Pp +Note that both of these functions create and scan a copy +of the string or bytes. +(This may be desirable, since +.Fn yylex +modifies the contents of the buffer it is scanning.) +The copy can be avoided by using: +.Bl -tag -width Ds +.It yy_scan_buffer(char *base, yy_size_t size) +Which scans the buffer starting at +.Fa base , +consisting of +.Fa size +bytes, the last two bytes of which must be +.Dv YY_END_OF_BUFFER_CHAR +.Pq ASCII NUL . +These last two bytes are not scanned; thus, scanning consists of +base[0] through base[size-2], inclusive. +.Pp +If +.Fa base +is not set up in this manner +(i.e., forget the final two +.Dv YY_END_OF_BUFFER_CHAR +bytes), then +.Fn yy_scan_buffer +returns a nil pointer instead of creating a new input buffer. +.Pp +The type +.Fa yy_size_t +is an integral type which can be cast to an integer expression +reflecting the size of the buffer. +.El +.Sh END-OF-FILE RULES +The special rule +.Qq Aq Aq EOF +indicates actions which are to be taken when an end-of-file is encountered and +.Fn yywrap +returns non-zero +.Pq i.e., indicates no further files to process . +The action must finish by doing one of four things: +.Bl -dash +.It +Assigning +.Em yyin +to a new input file +(in previous versions of +.Nm , +after doing the assignment, it was necessary to call the special action +.Dv YY_NEW_FILE ; +this is no longer necessary). +.It +Executing a +.Em return +statement. +.It +Executing the special +.Fn yyterminate +action. +.It +Switching to a new buffer using +.Fn yy_switch_to_buffer +as shown in the example above. +.El +.Pp +.Aq Aq EOF +rules may not be used with other patterns; +they may only be qualified with a list of start conditions. +If an unqualified +.Aq Aq EOF +rule is given, it applies to all start conditions which do not already have +.Aq Aq EOF +actions. +To specify an +.Aq Aq EOF +rule for only the initial start condition, use +.Pp +.Dl <> +.Pp +These rules are useful for catching things like unclosed comments. +An example: +.Bd -literal -offset indent +%x quote +%% + +\&...other rules for dealing with quotes... + +<> { + error("unterminated quote"); + yyterminate(); +} +<> { + if (*++filelist) + yyin = fopen(*filelist, "r"); + else + yyterminate(); +} +.Ed +.Sh MISCELLANEOUS MACROS +The macro +.Dv YY_USER_ACTION +can be defined to provide an action +which is always executed prior to the matched rule's action. +For example, +it could be #define'd to call a routine to convert yytext to lower-case. +When +.Dv YY_USER_ACTION +is invoked, the variable +.Fa yy_act +gives the number of the matched rule +.Pq rules are numbered starting with 1 . +For example, to profile how often each rule is matched, +the following would do the trick: +.Pp +.Dl #define YY_USER_ACTION ++ctr[yy_act] +.Pp +where +.Fa ctr +is an array to hold the counts for the different rules. +Note that the macro +.Dv YY_NUM_RULES +gives the total number of rules +(including the default rule, even if +.Fl s +is used), +so a correct declaration for +.Fa ctr +is: +.Pp +.Dl int ctr[YY_NUM_RULES]; +.Pp +The macro +.Dv YY_USER_INIT +may be defined to provide an action which is always executed before +the first scan +.Pq and before the scanner's internal initializations are done . +For example, it could be used to call a routine to read +in a data table or open a logging file. +.Pp +The macro +.Dv yy_set_interactive(is_interactive) +can be used to control whether the current buffer is considered +.Em interactive . +An interactive buffer is processed more slowly, +but must be used when the scanner's input source is indeed +interactive to avoid problems due to waiting to fill buffers +(see the discussion of the +.Fl I +flag below). +A non-zero value in the macro invocation marks the buffer as interactive, +a zero value as non-interactive. +Note that use of this macro overrides +.Dq %option always-interactive +or +.Dq %option never-interactive +(see +.Sx OPTIONS +below). +.Fn yy_set_interactive +must be invoked prior to beginning to scan the buffer that is +.Pq or is not +to be considered interactive. +.Pp +The macro +.Dv yy_set_bol(at_bol) +can be used to control whether the current buffer's scanning +context for the next token match is done as though at the +beginning of a line. +A non-zero macro argument makes rules anchored with +.Sq ^ +active, while a zero argument makes +.Sq ^ +rules inactive. +.Pp +The macro +.Dv YY_AT_BOL +returns true if the next token scanned from the current buffer will have +.Sq ^ +rules active, false otherwise. +.Pp +In the generated scanner, the actions are all gathered in one large +switch statement and separated using +.Dv YY_BREAK , +which may be redefined. +By default, it is simply a +.Qq break , +to separate each rule's action from the following rules. +Redefining +.Dv YY_BREAK +allows, for example, C++ users to +.Dq #define YY_BREAK +to do nothing +(while being very careful that every rule ends with a +.Qq break +or a +.Qq return ! ) +to avoid suffering from unreachable statement warnings where because a rule's +action ends with +.Dq return , +the +.Dv YY_BREAK +is inaccessible. +.Sh VALUES AVAILABLE TO THE USER +This section summarizes the various values available to the user +in the rule actions. +.Bl -tag -width Ds +.It char *yytext +Holds the text of the current token. +It may be modified but not lengthened +.Pq characters cannot be appended to the end . +.Pp +If the special directive +.Dq %array +appears in the first section of the scanner description, then +.Fa yytext +is instead declared +.Dq char yytext[YYLMAX] , +where +.Dv YYLMAX +is a macro definition that can be redefined in the first section +to change the default value +.Pq generally 8KB . +Using +.Dq %array +results in somewhat slower scanners, but the value of +.Fa yytext +becomes immune to calls to +.Fn input +and +.Fn unput , +which potentially destroy its value when +.Fa yytext +is a character pointer. +The opposite of +.Dq %array +is +.Dq %pointer , +which is the default. +.Pp +.Dq %array +cannot be used when generating C++ scanner classes +(the +.Fl + +flag). +.It int yyleng +Holds the length of the current token. +.It FILE *yyin +Is the file which by default +.Nm +reads from. +It may be redefined, but doing so only makes sense before +scanning begins or after an +.Dv EOF +has been encountered. +Changing it in the midst of scanning will have unexpected results since +.Nm +buffers its input; use +.Fn yyrestart +instead. +Once scanning terminates because an end-of-file +has been seen, +.Fa yyin +can be assigned as the new input file +and the scanner can be called again to continue scanning. +.It void yyrestart(FILE *new_file) +May be called to point +.Fa yyin +at the new input file. +The switch-over to the new file is immediate +.Pq any previously buffered-up input is lost . +Note that calling +.Fn yyrestart +with +.Fa yyin +as an argument thus throws away the current input buffer and continues +scanning the same input file. +.It FILE *yyout +Is the file to which +.Em ECHO +actions are done. +It can be reassigned by the user. +.It YY_CURRENT_BUFFER +Returns a +.Dv YY_BUFFER_STATE +handle to the current buffer. +.It YY_START +Returns an integer value corresponding to the current start condition. +This value can subsequently be used with +.Em BEGIN +to return to that start condition. +.El +.Sh INTERFACING WITH YACC +One of the main uses of +.Nm +is as a companion to the +.Xr yacc 1 +parser-generator. +yacc parsers expect to call a routine named +.Fn yylex +to find the next input token. +The routine is supposed to return the type of the next token +as well as putting any associated value in the global +.Fa yylval , +which is defined externally, +and can be a union or any other complex data structure. +To use +.Nm +with yacc, one specifies the +.Fl d +option to yacc to instruct it to generate the file +.Pa y.tab.h +containing definitions of all the +.Dq %tokens +appearing in the yacc input. +This file is then included in the +.Nm +scanner. +For example, part of the scanner might look like: +.Bd -literal -offset indent +%{ +#include "y.tab.h" +%} + +%% + +if return TOK_IF; +then return TOK_THEN; +begin return TOK_BEGIN; +end return TOK_END; +.Ed +.Sh OPTIONS +.Nm +has the following options: +.Bl -tag -width Ds +.It Fl 7 +Instructs +.Nm +to generate a 7-bit scanner, i.e., one which can only recognize 7-bit +characters in its input. +The advantage of using +.Fl 7 +is that the scanner's tables can be up to half the size of those generated +using the +.Fl 8 +option +.Pq see below . +The disadvantage is that such scanners often hang +or crash if their input contains an 8-bit character. +.Pp +Note, however, that unless generating a scanner using the +.Fl Cf +or +.Fl CF +table compression options, use of +.Fl 7 +will save only a small amount of table space, +and make the scanner considerably less portable. +.Nm flex Ns 's +default behavior is to generate an 8-bit scanner unless +.Fl Cf +or +.Fl CF +is specified, in which case +.Nm +defaults to generating 7-bit scanners unless it was +configured to generate 8-bit scanners +(as will often be the case with non-USA sites). +It is possible tell whether +.Nm +generated a 7-bit or an 8-bit scanner by inspecting the flag summary in the +.Fl v +output as described below. +.Pp +Note that if +.Fl Cfe +or +.Fl CFe +are used +(the table compression options, but also using equivalence classes as +discussed below), +.Nm +still defaults to generating an 8-bit scanner, +since usually with these compression options full 8-bit tables +are not much more expensive than 7-bit tables. +.It Fl 8 +Instructs +.Nm +to generate an 8-bit scanner, i.e., one which can recognize 8-bit +characters. +This flag is only needed for scanners generated using +.Fl Cf +or +.Fl CF , +as otherwise +.Nm +defaults to generating an 8-bit scanner anyway. +.Pp +See the discussion of +.Fl 7 +above for +.Nm flex Ns 's +default behavior and the tradeoffs between 7-bit and 8-bit scanners. +.It Fl B +Instructs +.Nm +to generate a +.Em batch +scanner, the opposite of +.Em interactive +scanners generated by +.Fl I +.Pq see below . +In general, +.Fl B +is used when the scanner will never be used interactively, +and you want to squeeze a little more performance out of it. +If the aim is instead to squeeze out a lot more performance, +use the +.Fl Cf +or +.Fl CF +options +.Pq discussed below , +which turn on +.Fl B +automatically anyway. +.It Fl b +Generate backing-up information to +.Pa lex.backup . +This is a list of scanner states which require backing up +and the input characters on which they do so. +By adding rules one can remove backing-up states. +If all backing-up states are eliminated and +.Fl Cf +or +.Fl CF +is used, the generated scanner will run faster (see the +.Fl p +flag). +Only users who wish to squeeze every last cycle out of their +scanners need worry about this option. +(See the section on +.Sx PERFORMANCE CONSIDERATIONS +below.) +.It Fl C Ns Op Cm aeFfmr +Controls the degree of table compression and, more generally, trade-offs +between small scanners and fast scanners. +.Bl -tag -width Ds +.It Fl Ca +Instructs +.Nm +to trade off larger tables in the generated scanner for faster performance +because the elements of the tables are better aligned for memory access +and computation. +On some +.Tn RISC +architectures, fetching and manipulating longwords is more efficient +than with smaller-sized units such as shortwords. +This option can double the size of the tables used by the scanner. +.It Fl Ce +Directs +.Nm +to construct +.Em equivalence classes , +i.e., sets of characters which have identical lexical properties +(for example, if the only appearance of digits in the +.Nm +input is in the character class +.Qq [0-9] +then the digits +.Sq 0 , +.Sq 1 , +.Sq ... , +.Sq 9 +will all be put in the same equivalence class). +Equivalence classes usually give dramatic reductions in the final +table/object file sizes +.Pq typically a factor of 2\-5 +and are pretty cheap performance-wise +.Pq one array look-up per character scanned . +.It Fl CF +Specifies that the alternate fast scanner representation +(described below under the +.Fl F +option) +should be used. +This option cannot be used with +.Fl + . +.It Fl Cf +Specifies that the +.Em full +scanner tables should be generated \- +.Nm +should not compress the tables by taking advantage of +similar transition functions for different states. +.It Fl \&Cm +Directs +.Nm +to construct +.Em meta-equivalence classes , +which are sets of equivalence classes +(or characters, if equivalence classes are not being used) +that are commonly used together. +Meta-equivalence classes are often a big win when using compressed tables, +but they have a moderate performance impact +(one or two +.Qq if +tests and one array look-up per character scanned). +.It Fl Cr +Causes the generated scanner to +.Em bypass +use of the standard I/O library +.Pq stdio +for input. +Instead of calling +.Xr fread 3 +or +.Xr getc 3 , +the scanner will use the +.Xr read 2 +system call, +resulting in a performance gain which varies from system to system, +but in general is probably negligible unless +.Fl Cf +or +.Fl CF +are being used. +Using +.Fl Cr +can cause strange behavior if, for example, reading from +.Fa yyin +using stdio prior to calling the scanner +(because the scanner will miss whatever text previous reads left +in the stdio input buffer). +.Pp +.Fl Cr +has no effect if +.Dv YY_INPUT +is defined +(see +.Sx THE GENERATED SCANNER +above). +.El +.Pp +A lone +.Fl C +specifies that the scanner tables should be compressed but neither +equivalence classes nor meta-equivalence classes should be used. +.Pp +The options +.Fl Cf +or +.Fl CF +and +.Fl \&Cm +do not make sense together \- there is no opportunity for meta-equivalence +classes if the table is not being compressed. +Otherwise the options may be freely mixed, and are cumulative. +.Pp +The default setting is +.Fl Cem +which specifies that +.Nm +should generate equivalence classes and meta-equivalence classes. +This setting provides the highest degree of table compression. +It is possible to trade off faster-executing scanners at the cost of +larger tables with the following generally being true: +.Bd -unfilled -offset indent +slowest & smallest + -Cem + -Cm + -Ce + -C + -C{f,F}e + -C{f,F} + -C{f,F}a +fastest & largest +.Ed +.Pp +Note that scanners with the smallest tables are usually generated and +compiled the quickest, +so during development the default is usually best, +maximal compression. +.Pp +.Fl Cfe +is often a good compromise between speed and size for production scanners. +.It Fl d +Makes the generated scanner run in debug mode. +Whenever a pattern is recognized and the global +.Fa yy_flex_debug +is non-zero +.Pq which is the default , +the scanner will write to stderr a line of the form: +.Pp +.D1 --accepting rule at line 53 ("the matched text") +.Pp +The line number refers to the location of the rule in the file +defining the scanner +(i.e., the file that was fed to +.Nm ) . +Messages are also generated when the scanner backs up, +accepts the default rule, +reaches the end of its input buffer +(or encounters a NUL; +at this point, the two look the same as far as the scanner's concerned), +or reaches an end-of-file. +.It Fl F +Specifies that the fast scanner table representation should be used +.Pq and stdio bypassed . +This representation is about as fast as the full table representation +.Pq Fl f , +and for some sets of patterns will be considerably smaller +.Pq and for others, larger . +In general, if the pattern set contains both +.Qq keywords +and a catch-all, +.Qq identifier +rule, such as in the set: +.Bd -unfilled -offset indent +"case" return TOK_CASE; +"switch" return TOK_SWITCH; +\&... +"default" return TOK_DEFAULT; +[a-z]+ return TOK_ID; +.Ed +.Pp +then it's better to use the full table representation. +If only the +.Qq identifier +rule is present and a hash table or some such is used to detect the keywords, +it's better to use +.Fl F . +.Pp +This option is equivalent to +.Fl CFr +.Pq see above . +It cannot be used with +.Fl + . +.It Fl f +Specifies +.Em fast scanner . +No table compression is done and stdio is bypassed. +The result is large but fast. +This option is equivalent to +.Fl Cfr +.Pq see above . +.It Fl h +Generates a help summary of +.Nm flex Ns 's +options to stdout and then exits. +.Fl ?\& +and +.Fl Fl help +are synonyms for +.Fl h . +.It Fl I +Instructs +.Nm +to generate an +.Em interactive +scanner. +An interactive scanner is one that only looks ahead to decide +what token has been matched if it absolutely must. +It turns out that always looking one extra character ahead, +even if the scanner has already seen enough text +to disambiguate the current token, is a bit faster than +only looking ahead when necessary. +But scanners that always look ahead give dreadful interactive performance; +for example, when a user types a newline, +it is not recognized as a newline token until they enter +.Em another +token, which often means typing in another whole line. +.Pp +.Nm +scanners default to +.Em interactive +unless +.Fl Cf +or +.Fl CF +table-compression options are specified +.Pq see above . +That's because if high-performance is most important, +one of these options should be used, +so if they weren't, +.Nm +assumes it is preferable to trade off a bit of run-time performance for +intuitive interactive behavior. +Note also that +.Fl I +cannot be used in conjunction with +.Fl Cf +or +.Fl CF . +Thus, this option is not really needed; it is on by default for all those +cases in which it is allowed. +.Pp +A scanner can be forced to not be interactive by using +.Fl B +.Pq see above . +.It Fl i +Instructs +.Nm +to generate a case-insensitive scanner. +The case of letters given in the +.Nm +input patterns will be ignored, +and tokens in the input will be matched regardless of case. +The matched text given in +.Fa yytext +will have the preserved case +.Pq i.e., it will not be folded . +.It Fl L +Instructs +.Nm +not to generate +.Dq #line +directives. +Without this option, +.Nm +peppers the generated scanner with #line directives so error messages +in the actions will be correctly located with respect to either the original +.Nm +input file +(if the errors are due to code in the input file), +or +.Pa lex.yy.c +(if the errors are +.Nm flex Ns 's +fault \- these sorts of errors should be reported to the email address +given below). +.It Fl l +Turns on maximum compatibility with the original +.At +.Nm lex +implementation. +Note that this does not mean full compatibility. +Use of this option costs a considerable amount of performance, +and it cannot be used with the +.Fl + , f , F , Cf , +or +.Fl CF +options. +For details on the compatibilities it provides, see the section +.Sx INCOMPATIBILITIES WITH LEX AND POSIX +below. +This option also results in the name +.Dv YY_FLEX_LEX_COMPAT +being #define'd in the generated scanner. +.It Fl n +Another do-nothing, deprecated option included only for +.Tn POSIX +compliance. +.It Fl o Ns Ar output +Directs +.Nm +to write the scanner to the file +.Ar output +instead of +.Pa lex.yy.c . +If +.Fl o +is combined with the +.Fl t +option, then the scanner is written to stdout but its +.Dq #line +directives +(see the +.Fl L +option above) +refer to the file +.Ar output . +.It Fl P Ns Ar prefix +Changes the default +.Qq yy +prefix used by +.Nm +for all globally visible variable and function names to instead be +.Ar prefix . +For example, +.Fl P Ns Ar foo +changes the name of +.Fa yytext +to +.Fa footext . +It also changes the name of the default output file from +.Pa lex.yy.c +to +.Pa lex.foo.c . +Here are all of the names affected: +.Bd -unfilled -offset indent +yy_create_buffer +yy_delete_buffer +yy_flex_debug +yy_init_buffer +yy_flush_buffer +yy_load_buffer_state +yy_switch_to_buffer +yyin +yyleng +yylex +yylineno +yyout +yyrestart +yytext +yywrap +.Ed +.Pp +(If using a C++ scanner, then only +.Fa yywrap +and +.Fa yyFlexLexer +are affected.) +Within the scanner itself, it is still possible to refer to the global variables +and functions using either version of their name; but externally, they +have the modified name. +.Pp +This option allows multiple +.Nm +programs to be easily linked together into the same executable. +Note, though, that using this option also renames +.Fn yywrap , +so now either an +.Pq appropriately named +version of the routine for the scanner must be supplied, or +.Dq %option noyywrap +must be used, as linking with +.Fl lfl +no longer provides one by default. +.It Fl p +Generates a performance report to stderr. +The report consists of comments regarding features of the +.Nm +input file which will cause a serious loss of performance in the resulting +scanner. +If the flag is specified twice, +comments regarding features that lead to minor performance losses +will also be reported> +.Pp +Note that the use of +.Em REJECT , +.Dq %option yylineno , +and variable trailing context +(see the +.Sx BUGS +section below) +entails a substantial performance penalty; use of +.Fn yymore , +the +.Sq ^ +operator, and the +.Fl I +flag entail minor performance penalties. +.It Fl S Ns Ar skeleton +Overrides the default skeleton file from which +.Nm +constructs its scanners. +This option is needed only for +.Nm +maintenance or development. +.It Fl s +Causes the default rule +.Pq that unmatched scanner input is echoed to stdout +to be suppressed. +If the scanner encounters input that does not +match any of its rules, it aborts with an error. +This option is useful for finding holes in a scanner's rule set. +.It Fl T +Makes +.Nm +run in +.Em trace +mode. +It will generate a lot of messages to stderr concerning +the form of the input and the resultant non-deterministic and deterministic +finite automata. +This option is mostly for use in maintaining +.Nm . +.It Fl t +Instructs +.Nm +to write the scanner it generates to standard output instead of +.Pa lex.yy.c . +.It Fl V +Prints the version number to stdout and exits. +.Fl Fl version +is a synonym for +.Fl V . +.It Fl v +Specifies that +.Nm +should write to stderr +a summary of statistics regarding the scanner it generates. +Most of the statistics are meaningless to the casual +.Nm +user, but the first line identifies the version of +.Nm +(same as reported by +.Fl V ) , +and the next line the flags used when generating the scanner, +including those that are on by default. +.It Fl w +Suppresses warning messages. +.It Fl + +Specifies that +.Nm +should generate a C++ scanner class. +See the section on +.Sx GENERATING C++ SCANNERS +below for details. +.El +.Pp +.Nm +also provides a mechanism for controlling options within the +scanner specification itself, rather than from the +.Nm +command line. +This is done by including +.Dq %option +directives in the first section of the scanner specification. +Multiple options can be specified with a single +.Dq %option +directive, and multiple directives in the first section of the +.Nm +input file. +.Pp +Most options are given simply as names, optionally preceded by the word +.Qq no +.Pq with no intervening whitespace +to negate their meaning. +A number are equivalent to +.Nm +flags or their negation: +.Bd -unfilled -offset indent +7bit -7 option +8bit -8 option +align -Ca option +backup -b option +batch -B option +c++ -+ option + +caseful or +case-sensitive opposite of -i (default) + +case-insensitive or +caseless -i option + +debug -d option +default opposite of -s option +ecs -Ce option +fast -F option +full -f option +interactive -I option +lex-compat -l option +meta-ecs -Cm option +perf-report -p option +read -Cr option +stdout -t option +verbose -v option +warn opposite of -w option + (use "%option nowarn" for -w) + +array equivalent to "%array" +pointer equivalent to "%pointer" (default) +.Ed +.Pp +Some %option's provide features otherwise not available: +.Bl -tag -width Ds +.It always-interactive +Instructs +.Nm +to generate a scanner which always considers its input +.Qq interactive . +Normally, on each new input file the scanner calls +.Fn isatty +in an attempt to determine whether the scanner's input source is interactive +and thus should be read a character at a time. +When this option is used, however, no such call is made. +.It main +Directs +.Nm +to provide a default +.Fn main +program for the scanner, which simply calls +.Fn yylex . +This option implies +.Dq noyywrap +.Pq see below . +.It never-interactive +Instructs +.Nm +to generate a scanner which never considers its input +.Qq interactive +(again, no call made to +.Fn isatty ) . +This is the opposite of +.Dq always-interactive . +.It stack +Enables the use of start condition stacks +(see +.Sx START CONDITIONS +above). +.It stdinit +If set (i.e., +.Dq %option stdinit ) , +initializes +.Fa yyin +and +.Fa yyout +to stdin and stdout, instead of the default of +.Dq nil . +Some existing +.Nm lex +programs depend on this behavior, even though it is not compliant with ANSI C, +which does not require stdin and stdout to be compile-time constant. +.It yylineno +Directs +.Nm +to generate a scanner that maintains the number of the current line +read from its input in the global variable +.Fa yylineno . +This option is implied by +.Dq %option lex-compat . +.It yywrap +If unset (i.e., +.Dq %option noyywrap ) , +makes the scanner not call +.Fn yywrap +upon an end-of-file, but simply assume that there are no more files to scan +(until the user points +.Fa yyin +at a new file and calls +.Fn yylex +again). +.El +.Pp +.Nm +scans rule actions to determine whether the +.Em REJECT +or +.Fn yymore +features are being used. +The +.Dq reject +and +.Dq yymore +options are available to override its decision as to whether to use the +options, either by setting them (e.g., +.Dq %option reject ) +to indicate the feature is indeed used, +or unsetting them to indicate it actually is not used +(e.g., +.Dq %option noyymore ) . +.Pp +Three options take string-delimited values, offset with +.Sq = : +.Pp +.D1 %option outfile="ABC" +.Pp +is equivalent to +.Fl o Ns Ar ABC , +and +.Pp +.D1 %option prefix="XYZ" +.Pp +is equivalent to +.Fl P Ns Ar XYZ . +Finally, +.Pp +.D1 %option yyclass="foo" +.Pp +only applies when generating a C++ scanner +.Pf ( Fl + +option). +It informs +.Nm +that +.Dq foo +has been derived as a subclass of yyFlexLexer, so +.Nm +will place actions in the member function +.Dq foo::yylex() +instead of +.Dq yyFlexLexer::yylex() . +It also generates a +.Dq yyFlexLexer::yylex() +member function that emits a run-time error (by invoking +.Dq yyFlexLexer::LexerError() ) +if called. +See +.Sx GENERATING C++ SCANNERS , +below, for additional information. +.Pp +A number of options are available for +lint +purists who want to suppress the appearance of unneeded routines +in the generated scanner. +Each of the following, if unset +(e.g., +.Dq %option nounput ) , +results in the corresponding routine not appearing in the generated scanner: +.Bd -unfilled -offset indent +input, unput +yy_push_state, yy_pop_state, yy_top_state +yy_scan_buffer, yy_scan_bytes, yy_scan_string +.Ed +.Pp +(though +.Fn yy_push_state +and friends won't appear anyway unless +.Dq %option stack +is being used). +.Sh PERFORMANCE CONSIDERATIONS +The main design goal of +.Nm +is that it generate high-performance scanners. +It has been optimized for dealing well with large sets of rules. +Aside from the effects on scanner speed of the table compression +.Fl C +options outlined above, +there are a number of options/actions which degrade performance. +These are, from most expensive to least: +.Bd -unfilled -offset indent +REJECT +%option yylineno +arbitrary trailing context + +pattern sets that require backing up +%array +%option interactive +%option always-interactive + +\&'^' beginning-of-line operator +yymore() +.Ed +.Pp +with the first three all being quite expensive +and the last two being quite cheap. +Note also that +.Fn unput +is implemented as a routine call that potentially does quite a bit of work, +while +.Fn yyless +is a quite-cheap macro; so if just putting back some excess text, +use +.Fn yyless . +.Pp +.Em REJECT +should be avoided at all costs when performance is important. +It is a particularly expensive option. +.Pp +Getting rid of backing up is messy and often may be an enormous +amount of work for a complicated scanner. +In principal, one begins by using the +.Fl b +flag to generate a +.Pa lex.backup +file. +For example, on the input +.Bd -literal -offset indent +%% +foo return TOK_KEYWORD; +foobar return TOK_KEYWORD; +.Ed +.Pp +the file looks like: +.Bd -literal -offset indent +State #6 is non-accepting - + associated rule line numbers: + 2 3 + out-transitions: [ o ] + jam-transitions: EOF [ \e001-n p-\e177 ] + +State #8 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ a ] + jam-transitions: EOF [ \e001-` b-\e177 ] + +State #9 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ r ] + jam-transitions: EOF [ \e001-q s-\e177 ] + +Compressed tables always back up. +.Ed +.Pp +The first few lines tell us that there's a scanner state in +which it can make a transition on an +.Sq o +but not on any other character, +and that in that state the currently scanned text does not match any rule. +The state occurs when trying to match the rules found +at lines 2 and 3 in the input file. +If the scanner is in that state and then reads something other than an +.Sq o , +it will have to back up to find a rule which is matched. +With a bit of headscratching one can see that this must be the +state it's in when it has seen +.Sq fo . +When this has happened, if anything other than another +.Sq o +is seen, the scanner will have to back up to simply match the +.Sq f +.Pq by the default rule . +.Pp +The comment regarding State #8 indicates there's a problem when +.Qq foob +has been scanned. +Indeed, on any character other than an +.Sq a , +the scanner will have to back up to accept +.Qq foo . +Similarly, the comment for State #9 concerns when +.Qq fooba +has been scanned and an +.Sq r +does not follow. +.Pp +The final comment reminds us that there's no point going to +all the trouble of removing backing up from the rules unless we're using +.Fl Cf +or +.Fl CF , +since there's no performance gain doing so with compressed scanners. +.Pp +The way to remove the backing up is to add +.Qq error +rules: +.Bd -literal -offset indent +%% +foo return TOK_KEYWORD; +foobar return TOK_KEYWORD; + +fooba | +foob | +fo { + /* false alarm, not really a keyword */ + return TOK_ID; +} +.Ed +.Pp +Eliminating backing up among a list of keywords can also be done using a +.Qq catch-all +rule: +.Bd -literal -offset indent +%% +foo return TOK_KEYWORD; +foobar return TOK_KEYWORD; + +[a-z]+ return TOK_ID; +.Ed +.Pp +This is usually the best solution when appropriate. +.Pp +Backing up messages tend to cascade. +With a complicated set of rules it's not uncommon to get hundreds of messages. +If one can decipher them, though, +it often only takes a dozen or so rules to eliminate the backing up +(though it's easy to make a mistake and have an error rule accidentally match +a valid token; a possible future +.Nm +feature will be to automatically add rules to eliminate backing up). +.Pp +It's important to keep in mind that the benefits of eliminating +backing up are gained only if +.Em every +instance of backing up is eliminated. +Leaving just one gains nothing. +.Pp +.Em Variable +trailing context +(where both the leading and trailing parts do not have a fixed length) +entails almost the same performance loss as +.Em REJECT +.Pq i.e., substantial . +So when possible a rule like: +.Bd -literal -offset indent +%% +mouse|rat/(cat|dog) run(); +.Ed +.Pp +is better written: +.Bd -literal -offset indent +%% +mouse/cat|dog run(); +rat/cat|dog run(); +.Ed +.Pp +or as +.Bd -literal -offset indent +%% +mouse|rat/cat run(); +mouse|rat/dog run(); +.Ed +.Pp +Note that here the special +.Sq |\& +action does not provide any savings, and can even make things worse (see +.Sx BUGS +below). +.Pp +Another area where the user can increase a scanner's performance +.Pq and one that's easier to implement +arises from the fact that the longer the tokens matched, +the faster the scanner will run. +This is because with long tokens the processing of most input +characters takes place in the +.Pq short +inner scanning loop, and does not often have to go through the additional work +of setting up the scanning environment (e.g., +.Fa yytext ) +for the action. +Recall the scanner for C comments: +.Bd -literal -offset indent +%x comment +%% +int line_num = 1; + +"/*" BEGIN(comment); + +[^*\en]* +"*"+[^*/\en]* +\en ++line_num; +"*"+"/" BEGIN(INITIAL); +.Ed +.Pp +This could be sped up by writing it as: +.Bd -literal -offset indent +%x comment +%% +int line_num = 1; + +"/*" BEGIN(comment); + +[^*\en]* +[^*\en]*\en ++line_num; +"*"+[^*/\en]* +"*"+[^*/\en]*\en ++line_num; +"*"+"/" BEGIN(INITIAL); +.Ed +.Pp +Now instead of each newline requiring the processing of another action, +recognizing the newlines is +.Qq distributed +over the other rules to keep the matched text as long as possible. +Note that adding rules does +.Em not +slow down the scanner! +The speed of the scanner is independent of the number of rules or +(modulo the considerations given at the beginning of this section) +how complicated the rules are with regard to operators such as +.Sq * +and +.Sq |\& . +.Pp +A final example in speeding up a scanner: +scan through a file containing identifiers and keywords, one per line +and with no other extraneous characters, and recognize all the keywords. +A natural first approach is: +.Bd -literal -offset indent +%% +asm | +auto | +break | +\&... etc ... +volatile | +while /* it's a keyword */ + +\&.|\en /* it's not a keyword */ +.Ed +.Pp +To eliminate the back-tracking, introduce a catch-all rule: +.Bd -literal -offset indent +%% +asm | +auto | +break | +\&... etc ... +volatile | +while /* it's a keyword */ + +[a-z]+ | +\&.|\en /* it's not a keyword */ +.Ed +.Pp +Now, if it's guaranteed that there's exactly one word per line, +then we can reduce the total number of matches by a half by +merging in the recognition of newlines with that of the other tokens: +.Bd -literal -offset indent +%% +asm\en | +auto\en | +break\en | +\&... etc ... +volatile\en | +while\en /* it's a keyword */ + +[a-z]+\en | +\&.|\en /* it's not a keyword */ +.Ed +.Pp +One has to be careful here, +as we have now reintroduced backing up into the scanner. +In particular, while we know that there will never be any characters +in the input stream other than letters or newlines, +.Nm +can't figure this out, and it will plan for possibly needing to back up +when it has scanned a token like +.Qq auto +and then the next character is something other than a newline or a letter. +Previously it would then just match the +.Qq auto +rule and be done, but now it has no +.Qq auto +rule, only an +.Qq auto\en +rule. +To eliminate the possibility of backing up, +we could either duplicate all rules but without final newlines or, +since we never expect to encounter such an input and therefore don't +how it's classified, we can introduce one more catch-all rule, +this one which doesn't include a newline: +.Bd -literal -offset indent +%% +asm\en | +auto\en | +break\en | +\&... etc ... +volatile\en | +while\en /* it's a keyword */ + +[a-z]+\en | +[a-z]+ | +\&.|\en /* it's not a keyword */ +.Ed +.Pp +Compiled with +.Fl Cf , +this is about as fast as one can get a +.Nm +scanner to go for this particular problem. +.Pp +A final note: +.Nm +is slow when matching NUL's, +particularly when a token contains multiple NUL's. +It's best to write rules which match short +amounts of text if it's anticipated that the text will often include NUL's. +.Pp +Another final note regarding performance: as mentioned above in the section +.Sx HOW THE INPUT IS MATCHED , +dynamically resizing +.Fa yytext +to accommodate huge tokens is a slow process because it presently requires that +the +.Pq huge +token be rescanned from the beginning. +Thus if performance is vital, it is better to attempt to match +.Qq large +quantities of text but not +.Qq huge +quantities, where the cutoff between the two is at about 8K characters/token. +.Sh GENERATING C++ SCANNERS +.Nm +provides two different ways to generate scanners for use with C++. +The first way is to simply compile a scanner generated by +.Nm +using a C++ compiler instead of a C compiler. +This should not generate any compilation errors +(please report any found to the email address given in the +.Sx AUTHORS +section below). +C++ code can then be used in rule actions instead of C code. +Note that the default input source for scanners remains +.Fa yyin , +and default echoing is still done to +.Fa yyout . +Both of these remain +.Fa FILE * +variables and not C++ streams. +.Pp +.Nm +can also be used to generate a C++ scanner class, using the +.Fl + +option (or, equivalently, +.Dq %option c++ ) , +which is automatically specified if the name of the flex executable ends in a +.Sq + , +such as +.Nm flex++ . +When using this option, +.Nm +defaults to generating the scanner to the file +.Pa lex.yy.cc +instead of +.Pa lex.yy.c . +The generated scanner includes the header file +.In g++/FlexLexer.h , +which defines the interface to two C++ classes. +.Pp +The first class, +.Em FlexLexer , +provides an abstract base class defining the general scanner class interface. +It provides the following member functions: +.Bl -tag -width Ds +.It const char* YYText() +Returns the text of the most recently matched token, the equivalent of +.Fa yytext . +.It int YYLeng() +Returns the length of the most recently matched token, the equivalent of +.Fa yyleng . +.It int lineno() const +Returns the current input line number +(see +.Dq %option yylineno ) , +or 1 if +.Dq %option yylineno +was not used. +.It void set_debug(int flag) +Sets the debugging flag for the scanner, equivalent to assigning to +.Fa yy_flex_debug +(see the +.Sx OPTIONS +section above). +Note that the scanner must be built using +.Dq %option debug +to include debugging information in it. +.It int debug() const +Returns the current setting of the debugging flag. +.El +.Pp +Also provided are member functions equivalent to +.Fn yy_switch_to_buffer , +.Fn yy_create_buffer +(though the first argument is an +.Fa std::istream* +object pointer and not a +.Fa FILE* ) , +.Fn yy_flush_buffer , +.Fn yy_delete_buffer , +and +.Fn yyrestart +(again, the first argument is an +.Fa std::istream* +object pointer). +.Pp +The second class defined in +.In g++/FlexLexer.h +is +.Fa yyFlexLexer , +which is derived from +.Fa FlexLexer . +It defines the following additional member functions: +.Bl -tag -width Ds +.It "yyFlexLexer(std::istream* arg_yyin = 0, std::ostream* arg_yyout = 0)" +Constructs a +.Fa yyFlexLexer +object using the given streams for input and output. +If not specified, the streams default to +.Fa cin +and +.Fa cout , +respectively. +.It virtual int yylex() +Performs the same role as +.Fn yylex +does for ordinary flex scanners: it scans the input stream, consuming +tokens, until a rule's action returns a value. +If subclass +.Sq S +is derived from +.Fa yyFlexLexer , +in order to access the member functions and variables of +.Sq S +inside +.Fn yylex , +use +.Dq %option yyclass="S" +to inform +.Nm +that the +.Sq S +subclass will be used instead of +.Fa yyFlexLexer . +In this case, rather than generating +.Dq yyFlexLexer::yylex() , +.Nm +generates +.Dq S::yylex() +(and also generates a dummy +.Dq yyFlexLexer::yylex() +that calls +.Dq yyFlexLexer::LexerError() +if called). +.It "virtual void switch_streams(std::istream* new_in = 0, std::ostream* new_out = 0)" +Reassigns +.Fa yyin +to +.Fa new_in +.Pq if non-nil +and +.Fa yyout +to +.Fa new_out +.Pq ditto , +deleting the previous input buffer if +.Fa yyin +is reassigned. +.It int yylex(std::istream* new_in, std::ostream* new_out = 0) +First switches the input streams via +.Dq switch_streams(new_in, new_out) +and then returns the value of +.Fn yylex . +.El +.Pp +In addition, +.Fa yyFlexLexer +defines the following protected virtual functions which can be redefined +in derived classes to tailor the scanner: +.Bl -tag -width Ds +.It virtual int LexerInput(char* buf, int max_size) +Reads up to +.Fa max_size +characters into +.Fa buf +and returns the number of characters read. +To indicate end-of-input, return 0 characters. +Note that +.Qq interactive +scanners (see the +.Fl B +and +.Fl I +flags) define the macro +.Dv YY_INTERACTIVE . +If +.Fn LexerInput +has been redefined, and it's necessary to take different actions depending on +whether or not the scanner might be scanning an interactive input source, +it's possible to test for the presence of this name via +.Dq #ifdef . +.It virtual void LexerOutput(const char* buf, int size) +Writes out +.Fa size +characters from the buffer +.Fa buf , +which, while NUL-terminated, may also contain +.Qq internal +NUL's if the scanner's rules can match text with NUL's in them. +.It virtual void LexerError(const char* msg) +Reports a fatal error message. +The default version of this function writes the message to the stream +.Fa cerr +and exits. +.El +.Pp +Note that a +.Fa yyFlexLexer +object contains its entire scanning state. +Thus such objects can be used to create reentrant scanners. +Multiple instances of the same +.Fa yyFlexLexer +class can be instantiated, and multiple C++ scanner classes can be combined +in the same program using the +.Fl P +option discussed above. +.Pp +Finally, note that the +.Dq %array +feature is not available to C++ scanner classes; +.Dq %pointer +must be used +.Pq the default . +.Pp +Here is an example of a simple C++ scanner: +.Bd -literal -offset indent +// An example of using the flex C++ scanner class. + +%{ +#include +int mylineno = 0; +%} + +string \e"[^\en"]+\e" + +ws [ \et]+ + +alpha [A-Za-z] +dig [0-9] +name ({alpha}|{dig}|\e$)({alpha}|{dig}|[_.\e-/$])* +num1 [-+]?{dig}+\e.?([eE][-+]?{dig}+)? +num2 [-+]?{dig}*\e.{dig}+([eE][-+]?{dig}+)? +number {num1}|{num2} + +%% + +{ws} /* skip blanks and tabs */ + +"/*" { + int c; + + while ((c = yyinput()) != 0) { + if(c == '\en') + ++mylineno; + else if(c == '*') { + if ((c = yyinput()) == '/') + break; + else + unput(c); + } + } +} + +{number} cout << "number " << YYText() << '\en'; + +\en mylineno++; + +{name} cout << "name " << YYText() << '\en'; + +{string} cout << "string " << YYText() << '\en'; + +%% + +int main(int /* argc */, char** /* argv */) +{ + FlexLexer* lexer = new yyFlexLexer; + while(lexer->yylex() != 0) + ; + return 0; +} +.Ed +.Pp +To create multiple +.Pq different +lexer classes, use the +.Fl P +flag +(or the +.Dq prefix= +option) +to rename each +.Fa yyFlexLexer +to some other +.Fa xxFlexLexer . +.In g++/FlexLexer.h +can then be included in other sources once per lexer class, first renaming +.Fa yyFlexLexer +as follows: +.Bd -literal -offset indent +#undef yyFlexLexer +#define yyFlexLexer xxFlexLexer +#include + +#undef yyFlexLexer +#define yyFlexLexer zzFlexLexer +#include +.Ed +.Pp +If, for example, +.Dq %option prefix="xx" +is used for one scanner and +.Dq %option prefix="zz" +is used for the other. +.Pp +.Sy IMPORTANT : +the present form of the scanning class is experimental +and may change considerably between major releases. +.Sh INCOMPATIBILITIES WITH LEX AND POSIX +.Nm +is a rewrite of the +.At +.Nm lex +tool +(the two implementations do not share any code, though), +with some extensions and incompatibilities, both of which are of concern +to those who wish to write scanners acceptable to either implementation. +.Nm +is fully compliant with the +.Tn POSIX +.Nm lex +specification, except that when using +.Dq %pointer +.Pq the default , +a call to +.Fn unput +destroys the contents of +.Fa yytext , +which is counter to the +.Tn POSIX +specification. +.Pp +In this section we discuss all of the known areas of incompatibility between +.Nm , +.At +.Nm lex , +and the +.Tn POSIX +specification. +.Pp +.Nm flex Ns 's +.Fl l +option turns on maximum compatibility with the original +.At +.Nm lex +implementation, at the cost of a major loss in the generated scanner's +performance. +We note below which incompatibilities can be overcome using the +.Fl l +option. +.Pp +.Nm +is fully compatible with +.Nm lex +with the following exceptions: +.Bl -dash +.It +The undocumented +.Nm lex +scanner internal variable +.Fa yylineno +is not supported unless +.Fl l +or +.Dq %option yylineno +is used. +.Pp +.Fa yylineno +should be maintained on a per-buffer basis, rather than a per-scanner +.Pq single global variable +basis. +.Pp +.Fa yylineno +is not part of the +.Tn POSIX +specification. +.It +The +.Fn input +routine is not redefinable, though it may be called to read characters +following whatever has been matched by a rule. +If +.Fn input +encounters an end-of-file, the normal +.Fn yywrap +processing is done. +A +.Dq real +end-of-file is returned by +.Fn input +as +.Dv EOF . +.Pp +Input is instead controlled by defining the +.Dv YY_INPUT +macro. +.Pp +The +.Nm +restriction that +.Fn input +cannot be redefined is in accordance with the +.Tn POSIX +specification, which simply does not specify any way of controlling the +scanner's input other than by making an initial assignment to +.Fa yyin . +.It +The +.Fn unput +routine is not redefinable. +This restriction is in accordance with +.Tn POSIX . +.It +.Nm +scanners are not as reentrant as +.Nm lex +scanners. +In particular, if a scanner is interactive and +an interrupt handler long-jumps out of the scanner, +and the scanner is subsequently called again, +the following error message may be displayed: +.Pp +.D1 fatal flex scanner internal error--end of buffer missed +.Pp +To reenter the scanner, first use +.Pp +.Dl yyrestart(yyin); +.Pp +Note that this call will throw away any buffered input; +usually this isn't a problem with an interactive scanner. +.Pp +Also note that flex C++ scanner classes are reentrant, +so if using C++ is an option , they should be used instead. +See +.Sx GENERATING C++ SCANNERS +above for details. +.It +.Fn output +is not supported. +Output from the +.Em ECHO +macro is done to the file-pointer +.Fa yyout +.Pq default stdout . +.Pp +.Fn output +is not part of the +.Tn POSIX +specification. +.It +.Nm lex +does not support exclusive start conditions +.Pq %x , +though they are in the +.Tn POSIX +specification. +.It +When definitions are expanded, +.Nm +encloses them in parentheses. +With +.Nm lex , +the following: +.Bd -literal -offset indent +NAME [A-Z][A-Z0-9]* +%% +foo{NAME}? printf("Found it\en"); +%% +.Ed +.Pp +will not match the string +.Qq foo +because when the macro is expanded the rule is equivalent to +.Qq foo[A-Z][A-Z0-9]*? +and the precedence is such that the +.Sq ?\& +is associated with +.Qq [A-Z0-9]* . +With +.Nm , +the rule will be expanded to +.Qq foo([A-Z][A-Z0-9]*)? +and so the string +.Qq foo +will match. +.Pp +Note that if the definition begins with +.Sq ^ +or ends with +.Sq $ +then it is not expanded with parentheses, to allow these operators to appear in +definitions without losing their special meanings. +But the +.Sq Aq s , +.Sq / , +and +.Aq Aq EOF +operators cannot be used in a +.Nm +definition. +.Pp +Using +.Fl l +results in the +.Nm lex +behavior of no parentheses around the definition. +.Pp +The +.Tn POSIX +specification is that the definition be enclosed in parentheses. +.It +Some implementations of +.Nm lex +allow a rule's action to begin on a separate line, +if the rule's pattern has trailing whitespace: +.Bd -literal -offset indent +%% +foo|bar + { foobar_action(); } +.Ed +.Pp +.Nm +does not support this feature. +.It +The +.Nm lex +.Sq %r +.Pq generate a Ratfor scanner +option is not supported. +It is not part of the +.Tn POSIX +specification. +.It +After a call to +.Fn unput , +.Fa yytext +is undefined until the next token is matched, +unless the scanner was built using +.Dq %array . +This is not the case with +.Nm lex +or the +.Tn POSIX +specification. +The +.Fl l +option does away with this incompatibility. +.It +The precedence of the +.Sq {} +.Pq numeric range +operator is different. +.Nm lex +interprets +.Qq abc{1,3} +as match one, two, or three occurrences of +.Sq abc , +whereas +.Nm +interprets it as match +.Sq ab +followed by one, two, or three occurrences of +.Sq c . +The latter is in agreement with the +.Tn POSIX +specification. +.It +The precedence of the +.Sq ^ +operator is different. +.Nm lex +interprets +.Qq ^foo|bar +as match either +.Sq foo +at the beginning of a line, or +.Sq bar +anywhere, whereas +.Nm +interprets it as match either +.Sq foo +or +.Sq bar +if they come at the beginning of a line. +The latter is in agreement with the +.Tn POSIX +specification. +.It +The special table-size declarations such as +.Sq %a +supported by +.Nm lex +are not required by +.Nm +scanners; +.Nm +ignores them. +.It +The name +.Dv FLEX_SCANNER +is #define'd so scanners may be written for use with either +.Nm +or +.Nm lex . +Scanners also include +.Dv YY_FLEX_MAJOR_VERSION +and +.Dv YY_FLEX_MINOR_VERSION +indicating which version of +.Nm +generated the scanner +(for example, for the 2.5 release, these defines would be 2 and 5, +respectively). +.El +.Pp +The following +.Nm +features are not included in +.Nm lex +or the +.Tn POSIX +specification: +.Bd -unfilled -offset indent +C++ scanners +%option +start condition scopes +start condition stacks +interactive/non-interactive scanners +yy_scan_string() and friends +yyterminate() +yy_set_interactive() +yy_set_bol() +YY_AT_BOL() +<> +<*> +YY_DECL +YY_START +YY_USER_ACTION +YY_USER_INIT +#line directives +%{}'s around actions +multiple actions on a line +.Ed +.Pp +plus almost all of the +.Nm +flags. +The last feature in the list refers to the fact that with +.Nm +multiple actions can be placed on the same line, +separated with semi-colons, while with +.Nm lex , +the following +.Pp +.Dl foo handle_foo(); ++num_foos_seen; +.Pp +is +.Pq rather surprisingly +truncated to +.Pp +.Dl foo handle_foo(); +.Pp +.Nm +does not truncate the action. +Actions that are not enclosed in braces +are simply terminated at the end of the line. +.Sh FILES +.Bl -tag -width "" +.It Pa flex.skl +Skeleton scanner. +This file is only used when building flex, not when +.Nm +executes. +.It Pa lex.backup +Backing-up information for the +.Fl b +flag (called +.Pa lex.bck +on some systems). +.It Pa lex.yy.c +Generated scanner +(called +.Pa lexyy.c +on some systems). +.It Pa lex.yy.cc +Generated C++ scanner class, when using +.Fl + . +.It In g++/FlexLexer.h +Header file defining the C++ scanner base class, +.Fa FlexLexer , +and its derived class, +.Fa yyFlexLexer . +.It Pa /usr/lib/libl.* +.Nm +libraries. +The +.Pa /usr/lib/libfl.*\& +libraries are links to these. +Scanners must be linked using either +.Fl \&ll +or +.Fl lfl . +.El +.Sh EXIT STATUS +.Ex -std flex +.Sh DIAGNOSTICS +.Bl -diag +.It warning, rule cannot be matched +Indicates that the given rule cannot be matched because it follows other rules +that will always match the same text as it. +For example, in the following +.Dq foo +cannot be matched because it comes after an identifier +.Qq catch-all +rule: +.Bd -literal -offset indent +[a-z]+ got_identifier(); +foo got_foo(); +.Ed +.Pp +Using +.Em REJECT +in a scanner suppresses this warning. +.It "warning, \-s option given but default rule can be matched" +Means that it is possible +.Pq perhaps only in a particular start condition +that the default rule +.Pq match any single character +is the only one that will match a particular input. +Since +.Fl s +was given, presumably this is not intended. +.It reject_used_but_not_detected undefined +.It yymore_used_but_not_detected undefined +These errors can occur at compile time. +They indicate that the scanner uses +.Em REJECT +or +.Fn yymore +but that +.Nm +failed to notice the fact, meaning that +.Nm +scanned the first two sections looking for occurrences of these actions +and failed to find any, but somehow they snuck in +.Pq via an #include file, for example . +Use +.Dq %option reject +or +.Dq %option yymore +to indicate to +.Nm +that these features are really needed. +.It flex scanner jammed +A scanner compiled with +.Fl s +has encountered an input string which wasn't matched by any of its rules. +This error can also occur due to internal problems. +.It token too large, exceeds YYLMAX +The scanner uses +.Dq %array +and one of its rules matched a string longer than the +.Dv YYLMAX +constant +.Pq 8K bytes by default . +The value can be increased by #define'ing +.Dv YYLMAX +in the definitions section of +.Nm +input. +.It "scanner requires \-8 flag to use the character 'x'" +The scanner specification includes recognizing the 8-bit character +.Sq x +and the +.Fl 8 +flag was not specified, and defaulted to 7-bit because the +.Fl Cf +or +.Fl CF +table compression options were used. +See the discussion of the +.Fl 7 +flag for details. +.It flex scanner push-back overflow +unput() was used to push back so much text that the scanner's buffer +could not hold both the pushed-back text and the current token in +.Fa yytext . +Ideally the scanner should dynamically resize the buffer in this case, +but at present it does not. +.It "input buffer overflow, can't enlarge buffer because scanner uses REJECT" +The scanner was working on matching an extremely large token and needed +to expand the input buffer. +This doesn't work with scanners that use +.Em REJECT . +.It "fatal flex scanner internal error--end of buffer missed" +This can occur in a scanner which is reentered after a long-jump +has jumped out +.Pq or over +the scanner's activation frame. +Before reentering the scanner, use: +.Pp +.Dl yyrestart(yyin); +.Pp +or, as noted above, switch to using the C++ scanner class. +.It "too many start conditions in <> construct!" +More start conditions than exist were listed in a <> construct +(so at least one of them must have been listed twice). +.El +.Sh SEE ALSO +.Xr awk 1 , +.Xr sed 1 , +.Xr yacc 1 +.Rs +.\" 4.4BSD PSD:16 +.%A M. E. Lesk +.%T Lex \(em Lexical Analyzer Generator +.%I AT&T Bell Laboratories +.%R Computing Science Technical Report +.%N 39 +.%D October 1975 +.Re +.Rs +.%A John Levine +.%A Tony Mason +.%A Doug Brown +.%B Lex & Yacc +.%I O'Reilly and Associates +.%N 2nd edition +.Re +.Rs +.%A Alfred Aho +.%A Ravi Sethi +.%A Jeffrey Ullman +.%B Compilers: Principles, Techniques and Tools +.%I Addison-Wesley +.%D 1986 +.%O "Describes the pattern-matching techniques used by flex (deterministic finite automata)" +.Re +.Sh STANDARDS +The +.Nm lex +utility is compliant with the +.St -p1003.1-2008 +specification, +though its presence is optional. +.Pp +The flags +.Op Fl 78BbCdFfhIiLloPpSsTVw+? , +.Op Fl -help , +and +.Op Fl -version +are extensions to that specification. +.Pp +See also the +.Sx INCOMPATIBILITIES WITH LEX AND POSIX +section, above. +.Sh AUTHORS +Vern Paxson, with the help of many ideas and much inspiration from +Van Jacobson. +Original version by Jef Poskanzer. +The fast table representation is a partial implementation of a design done by +Van Jacobson. +The implementation was done by Kevin Gong and Vern Paxson. +.Pp +Thanks to the many +.Nm +beta-testers, feedbackers, and contributors, especially Francois Pinard, +Casey Leedom, +Robert Abramovitz, +Stan Adermann, Terry Allen, David Barker-Plummer, John Basrai, +Neal Becker, Nelson H.F. Beebe, +.Mt benson@odi.com , +Karl Berry, Peter A. Bigot, Simon Blanchard, +Keith Bostic, Frederic Brehm, Ian Brockbank, Kin Cho, Nick Christopher, +Brian Clapper, J.T. Conklin, +Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David +Daniels, Chris G. Demetriou, Theo de Raadt, +Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin, +Chris Faylor, Chris Flatters, Jon Forrest, Jeffrey Friedl, +Joe Gayda, Kaveh R. Ghazi, Wolfgang Glunz, +Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel, +Jan Hajic, Charles Hemphill, NORO Hideo, +Jarkko Hietaniemi, Scott Hofmann, +Jeff Honig, Dana Hudes, Eric Hughes, John Interrante, +Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones, +Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane, +Amir Katz, +.Mt ken@ken.hilco.com , +Kevin B. Kenny, +Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht, +Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle, +David Loffredo, Mike Long, +Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall, +Bengt Martensson, Chris Metcalf, +Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum, +G.T. Nicol, Landon Noll, James Nordby, Marc Nozell, +Richard Ohnemus, Karsten Pahnke, +Sven Panne, Roland Pesch, Walter Pelissero, Gaumond Pierre, +Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha, +Frederic Raimbault, Pat Rankin, Rick Richardson, +Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini, +Andreas Scherer, Darrell Schiebel, Raf Schietekat, +Doug Schmidt, Philippe Schnoebelen, Andreas Schwab, +Larry Schwimmer, Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist, +Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor, +Chris Thewalt, Richard M. Timoney, Jodi Tsai, +Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, +Ken Yap, Ron Zellar, Nathan Zelle, David Zuhn, +and those whose names have slipped my marginal mail-archiving skills +but whose contributions are appreciated all the +same. +.Pp +Thanks to Keith Bostic, Jon Forrest, Noah Friedman, +John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T. +Nicol, Francois Pinard, Rich Salz, and Richard Stallman for help with various +distribution headaches. +.Pp +Thanks to Esmond Pitt and Earle Horton for 8-bit character support; +to Benson Margulies and Fred Burke for C++ support; +to Kent Williams and Tom Epperly for C++ class support; +to Ove Ewerlid for support of NUL's; +and to Eric Hughes for support of multiple buffers. +.Pp +This work was primarily done when I was with the Real Time Systems Group +at the Lawrence Berkeley Laboratory in Berkeley, CA. +Many thanks to all there for the support I received. +.Pp +Send comments to +.Aq Mt vern@ee.lbl.gov . +.Sh BUGS +Some trailing context patterns cannot be properly matched and generate +warning messages +.Pq "dangerous trailing context" . +These are patterns where the ending of the first part of the rule +matches the beginning of the second part, such as +.Qq zx*/xy* , +where the +.Sq x* +matches the +.Sq x +at the beginning of the trailing context. +(Note that the POSIX draft states that the text matched by such patterns +is undefined.) +.Pp +For some trailing context rules, parts which are actually fixed-length are +not recognized as such, leading to the above mentioned performance loss. +In particular, parts using +.Sq |\& +or +.Sq {n} +(such as +.Qq foo{3} ) +are always considered variable-length. +.Pp +Combining trailing context with the special +.Sq |\& +action can result in fixed trailing context being turned into +the more expensive variable trailing context. +For example, in the following: +.Bd -literal -offset indent +%% +abc | +xyz/def +.Ed +.Pp +Use of +.Fn unput +invalidates yytext and yyleng, unless the +.Dq %array +directive +or the +.Fl l +option has been used. +.Pp +Pattern-matching of NUL's is substantially slower than matching other +characters. +.Pp +Dynamic resizing of the input buffer is slow, as it entails rescanning +all the text matched so far by the current +.Pq generally huge +token. +.Pp +Due to both buffering of input and read-ahead, +it is not possible to intermix calls to +.In stdio.h +routines, such as, for example, +.Fn getchar , +with +.Nm +rules and expect it to work. +Call +.Fn input +instead. +.Pp +The total table entries listed by the +.Fl v +flag excludes the number of table entries needed to determine +what rule has been matched. +The number of entries is equal to the number of DFA states +if the scanner does not use +.Em REJECT , +and somewhat greater than the number of states if it does. +.Pp +.Em REJECT +cannot be used with the +.Fl f +or +.Fl F +options. +.Pp +The +.Nm +internal algorithms need documentation. diff --git a/man/test_files/mdoc/flock.2 b/man/test_files/mdoc/flock.2 new file mode 100644 index 00000000..82483b12 --- /dev/null +++ b/man/test_files/mdoc/flock.2 @@ -0,0 +1,151 @@ +.\" $OpenBSD: flock.2,v 1.21 2019/06/25 19:28:31 millert Exp $ +.\" $NetBSD: flock.2,v 1.5 1995/02/27 12:32:32 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)flock.2 8.2 (Berkeley) 12/11/93 +.\" +.Dd $Mdocdate: June 25 2019 $ +.Dt FLOCK 2 +.Os +.Sh NAME +.Nm flock +.Nd apply or remove an advisory lock on an open file +.Sh SYNOPSIS +.In fcntl.h +.Ft int +.Fn flock "int fd" "int operation" +.Sh DESCRIPTION +.Fn flock +applies or removes an +.Em advisory +lock on the file associated with the file descriptor +.Fa fd . +The +.Fa operation +argument is one of: +.Pp +.Bl -tag -width LOCK_SH -offset indent -compact +.It Dv LOCK_SH +Apply a shared lock. +.It Dv LOCK_EX +Apply an exclusive lock. +.It Dv LOCK_UN +Remove an existing lock. +.El +.Pp +.Dv LOCK_SH +and +.Dv LOCK_EX +may be combined with the optional +.Dv LOCK_NB +for nonblocking mode. +.Pp +Advisory locks allow cooperating processes to perform +consistent operations on files, but do not guarantee +consistency (i.e., processes may still access files +without using advisory locks possibly resulting in +inconsistencies). +.Pp +The locking mechanism allows two types of locks: +.Em shared +locks and +.Em exclusive +locks. +At any time multiple shared locks may be applied to a file, +but at no time are multiple exclusive, or both shared and exclusive, +locks allowed simultaneously on a file. +.Pp +A shared lock may be +.Em upgraded +to an exclusive lock, and vice versa, simply by specifying +the appropriate lock type; this results in the previous +lock being released and the new lock applied (possibly +after other processes have gained and released the lock). +.Pp +Requesting a lock on an object that is already locked normally causes +the caller to be blocked until the lock may be acquired. +If +.Fa operation +is the bitwise OR of +.Dv LOCK_NB +and +.Dv LOCK_SH +or +.Dv LOCK_EX , +then this will not happen; instead the call will fail and the error +.Er EWOULDBLOCK +will be returned. +.Sh NOTES +Locks are on files, not file descriptors. +That is, file descriptors duplicated through +.Xr dup 2 +or +.Xr fork 2 +do not result in multiple instances of a lock, but rather multiple +references to a single lock. +If a process holding a lock on a file forks and the child explicitly +unlocks the file, the parent will lose its lock. +.Pp +Processes blocked awaiting a lock may be awakened by signals. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn flock +call fails if: +.Bl -tag -width Er +.It Bq Er EWOULDBLOCK +The file is locked and the +.Dv LOCK_NB +option was specified. +.It Bq Er EBADF +The argument +.Fa fd +is an invalid descriptor. +.It Bq Er EINVAL +The argument +.Fa operation +has an invalid value. +.It Bq Er EOPNOTSUPP +The argument +.Fa fd +refers to a file that does not support locking. +.El +.Sh SEE ALSO +.Xr close 2 , +.Xr dup 2 , +.Xr execve 2 , +.Xr fcntl 2 , +.Xr fork 2 , +.Xr open 2 +.Sh HISTORY +The +.Fn flock +system call first appeared in +.Bx 4.1c . diff --git a/man/test_files/mdoc/fork.2 b/man/test_files/mdoc/fork.2 new file mode 100644 index 00000000..cd1cedd2 --- /dev/null +++ b/man/test_files/mdoc/fork.2 @@ -0,0 +1,145 @@ +.\" $OpenBSD: fork.2,v 1.18 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: fork.2,v 1.6 1995/02/27 12:32:36 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fork.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt FORK 2 +.Os +.Sh NAME +.Nm fork +.Nd create a new process +.Sh SYNOPSIS +.In unistd.h +.Ft pid_t +.Fn fork void +.Sh DESCRIPTION +.Fn fork +causes creation of a new process. +The new process (child process) is an exact copy of the +calling process (parent process) except for the following: +.Bl -bullet -offset indent +.It +The child process has a unique process ID, +which also does not match any existing process group ID. +.It +The child process has a different parent +process ID (i.e., the process ID of the parent process). +.It +The child process has a single thread. +.It +The child process has its own copy of the parent's descriptors. +These descriptors reference the same underlying objects, so that, +for instance, file pointers in file objects are shared between +the child and the parent, so that an +.Xr lseek 2 +on a descriptor in the child process can affect a subsequent +.Xr read 2 +or +.Xr write 2 +by the parent. +This descriptor copying is also used by the shell to +establish standard input and output for newly created processes +as well as to set up pipes. +.It +The child process has no +.Xr fcntl 2 Ns -style +file locks. +.It +The child process' resource utilizations +are set to 0; see +.Xr getrusage 2 . +.It +All interval timers are cleared; see +.Xr setitimer 2 . +.It +The child process' semaphore undo values are set to 0; see +.Xr semop 2 . +.It +The child process' pending signals set is empty. +.It +The child process has no memory locks; see +.Xr mlock 2 +and +.Xr mlockall 2 . +.El +.Pp +In general, the child process should call +.Xr _exit 2 +rather than +.Xr exit 3 . +Otherwise, any stdio buffers that exist both in the parent and child +will be flushed twice. +Similarly, +.Xr _exit 2 +should be used to prevent +.Xr atexit 3 +routines from being called twice (once in the parent and once in the child). +.Sh RETURN VALUES +Upon successful completion, +.Fn fork +returns a value +of 0 to the child process and returns the process ID of the child +process to the parent process. +Otherwise, a value of \-1 is returned to the parent process, +no child process is created, and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn fork +will fail and no child process will be created if: +.Bl -tag -width [EAGAIN] +.It Bq Er EAGAIN +The system-imposed limits on the total +number of processes or total number of threads +under execution would be exceeded. +These limits are configuration dependent. +.It Bq Er EAGAIN +The limit +.Dv RLIMIT_NPROC +on the total number of processes under execution by the user ID +would be exceeded. +.It Bq Er ENOMEM +There is insufficient swap space for the new process. +.El +.Sh SEE ALSO +.Xr execve 2 , +.Xr getrusage 2 , +.Xr wait 2 +.Sh STANDARDS +The +.Fn fork +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn fork +system call first appeared in +.At v1 . diff --git a/man/test_files/mdoc/fsync.2 b/man/test_files/mdoc/fsync.2 new file mode 100644 index 00000000..348c2625 --- /dev/null +++ b/man/test_files/mdoc/fsync.2 @@ -0,0 +1,121 @@ +.\" $OpenBSD: fsync.2,v 1.15 2019/04/18 23:51:13 tedu Exp $ +.\" $NetBSD: fsync.2,v 1.4 1995/02/27 12:32:38 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fsync.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: April 18 2019 $ +.Dt FSYNC 2 +.Os +.Sh NAME +.Nm fsync , +.Nm fdatasync +.Nd synchronize a file's in-core state with that on disk +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn fsync "int fd" +.Ft int +.Fn fdatasync "int fd" +.Sh DESCRIPTION +The +.Fn fsync +function causes all modified data and attributes of +.Fa fd +to be moved to a permanent storage device. +This normally results in all in-core modified copies +of buffers for the associated file to be written to a disk. +.Pp +The +.Fn fdatasync +function is similar to +.Fn fsync +except that it only guarantees modified data +.Pq and metadata necessary to read that data +is committed to storage. +Other file modifications may be left unsynchronized. +.Pp +.Fn fsync +and +.Fn fdatasync +should be used by programs that require a file to be in a known state, +for example, in building a simple transaction facility. +.Pp +If +.Fn fsync +or +.Fn fdatasync +fail with +.Er EIO , +the state of the on-disk data may have been only partially written. +To guard against potential inconsistency, future calls will continue failing +until all references to the file are closed. +.Sh RETURN VALUES +.Rv -std fsync fdatasync +.Sh ERRORS +The +.Fn fsync +and +.Fn fdatasync +functions fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa fd +is not a valid descriptor. +.It Bq Er EINVAL +.Fa fd +does not refer to a file which can be synchronized. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Sh SEE ALSO +.Xr sync 2 , +.Xr sync 8 +.Sh STANDARDS +The +.Fn fsync +and +.Fn fdatasync +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn fsync +system call first appeared in +.Bx 4.1c , +and the +.Fn fdatasync +function has been available since +.Ox 5.4 . +.Sh BUGS +The +.Fn fdatasync +function is currently a wrapper around +.Fn fsync , +so it synchronizes more state than necessary. diff --git a/man/test_files/mdoc/futex.2 b/man/test_files/mdoc/futex.2 new file mode 100644 index 00000000..89c6d4a3 --- /dev/null +++ b/man/test_files/mdoc/futex.2 @@ -0,0 +1,153 @@ +.\" $OpenBSD: futex.2,v 1.7 2023/11/09 09:13:32 jasper Exp $ +.\" +.\" Copyright (c) 2017 Martin Pieuchot +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: November 9 2023 $ +.Dt FUTEX 2 +.Os +.Sh NAME +.Nm futex +.Nd fast userspace locking primitive +.Sh SYNOPSIS +.In sys/time.h +.In sys/futex.h +.Ft int +.Fo futex +.Fa "volatile uint32_t *uaddr" +.Fa "int op" +.Fa "int val" +.Fa "const struct timespec *timeout" +.Fa "volatile uint32_t *uaddr2" +.Fc +.Sh DESCRIPTION +The +.Fn futex +syscall provides sleep and wakeup primitives related to a particular address. +.Pp +Three +.Fa op +operations are currently supported: +.Bl -tag -width FUTEX_REQUEUE -offset indent +.It Dv FUTEX_WAIT +If +.Fa val +is equal to +.Pf * Fa uaddr , +the calling thread is blocked on the +.Dq wait channel +identified by +.Fa uaddr +until +.Fa timeout +expires or until another thread issues a +.Dv FUTEX_WAKE +or +.Dv FUTEX_REQUEUE +operation with the same +.Fa uaddr +address. +.Fa uaddr2 +is ignored. +.It Dv FUTEX_WAKE +Unblocks +.Fa val +threads sleeping on the +wait channel identified by +.Fa uaddr . +.Fa timeout +and +.Fa uaddr2 +are ignored. +.It Dv FUTEX_REQUEUE +Similar to +.Dv FUTEX_WAKE +but also requeue remaining threads from the wait channel +.Fa uaddr +to +.Fa uaddr2 . +In this case, pass +.Fa "uint32_t val2" +as the fourth argument instead of +.Fa timeout . +At most that number of threads is requeued. +.El +.Sh RETURN VALUES +For +.Dv FUTEX_WAKE +and +.Dv FUTEX_REQUEUE , +.Fn futex +returns the number of woken threads. +.Pp +For +.Dv FUTEX_WAIT , +.Fn futex +returns zero if woken by a matching +.Dv FUTEX_WAKE +or +.Dv FUTEX_REQUEUE +call. +Otherwise, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn futex +will fail if: +.Bl -tag -width Er +.It Bq Er ENOSYS +The +.Fa op +argument is invalid. +.It Bq Er EFAULT +The userspace address +.Fa uaddr +is invalid. +.It Bq Er EAGAIN +The value pointed to by +.Fa uaddr +is not the same as the expected value +.Fa val . +.It Bq Er EINVAL +The +.Fa timeout +specified a second value less than zero, +or a nanosecond value less than zero or greater than or equal to 1000 million. +.It Bq Er ETIMEDOUT +The +.Fa timeout +expired before the thread was woken up. +.It Bq Er EINTR +A signal arrived. +.It Bq Er ECANCELED +A signal arrived and +.Fa SA_RESTART +was set. +.El +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr pthread_cond_wait 3 , +.Xr pthread_mutex_lock 3 , +.Xr tsleep 9 +.Rs +.%A Ulrich Drepper +.%T Futexes Are Tricky +.%U https://www.akkadia.org/drepper/futex.pdf +.%D November 5, 2011 +.Re +.Sh HISTORY +The +.Fn futex +syscall first appeared in Linux 2.5.7 and was added to +.Ox 6.2 . diff --git a/man/test_files/mdoc/getdents.2 b/man/test_files/mdoc/getdents.2 new file mode 100644 index 00000000..133ab9d3 --- /dev/null +++ b/man/test_files/mdoc/getdents.2 @@ -0,0 +1,195 @@ +.\" $OpenBSD: getdents.2,v 1.4 2022/08/04 06:20:24 jsg Exp $ +.\" $NetBSD: getdirentries.2,v 1.7 1995/10/12 15:40:50 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getdirentries.2 8.1 (Berkeley) 6/9/93 +.\" +.Dd $Mdocdate: August 4 2022 $ +.Dt GETDENTS 2 +.Os +.Sh NAME +.Nm getdents +.Nd get directory entries in a filesystem independent format +.Sh SYNOPSIS +.In dirent.h +.Ft int +.Fn getdents "int fd" "void *buf" "size_t nbytes" +.Sh DESCRIPTION +.Fn getdents +reads directory entries from the directory +referenced by the file descriptor +.Fa fd +into the buffer pointed to by +.Fa buf , +in a filesystem independent format. +Up to +.Fa nbytes +of data will be transferred. +.Fa nbytes +must be greater than or equal to the +block size associated with the file (see +.Xr stat 2 ) . +Some filesystems may not support +.Fn getdents +with buffers smaller than this size. +.Pp +The data in the buffer is a series of +.Em dirent +structures each containing at least the following entries: +.Bd -literal -offset indent +ino_t d_fileno; +off_t d_off; +u_int16_t d_reclen; +u_int8_t d_type; +u_int8_t d_namlen; +char d_name[MAXNAMLEN + 1]; /* see below */ +.Ed +.Pp +The +.Fa d_fileno +entry is a number which is unique for each distinct file in the filesystem. +Files that are linked by hard links (see +.Xr link 2 ) +have the same +.Fa d_fileno . +The +.Fa d_off +entry is the file offset of the next entry. +The +.Fa d_reclen +entry is the length, in bytes, of the directory record. +.Pp +The +.Fa d_type +is the type of file, where the following are possible types: +.Dv DT_UNKNOWN , +.Dv DT_FIFO , +.Dv DT_CHR , +.Dv DT_DIR , +.Dv DT_BLK , +.Dv DT_REG , +.Dv DT_LNK , +and +.Dv DT_SOCK . +.Pp +The +.Fa d_namlen +entry specifies the length of the file name excluding the NUL byte. +Thus the actual size of +.Fa d_name +may vary from 1 to +.Dv MAXNAMLEN +\&+ 1. +.Pp +The +.Fa d_name +entry contains a NUL-terminated file name. +.Pp +Entries may be separated by extra space. +The +.Fa d_reclen +entry may be used as an offset from the start of a +.Fa dirent +structure to the next structure, if any. +.Pp +Invalid entries with +.Fa d_fileno +set to 0 may be returned among regular entries. +.Pp +The actual number of bytes transferred is returned. +The current position pointer associated with +.Fa fd +is set to point to the next block of entries. +The pointer may not advance by the number of bytes returned by +.Fn getdents . +.Pp +The current position pointer may be set and retrieved by +.Xr lseek 2 . +The current position pointer should only be set to a value returned by +.Xr lseek 2 , +the value of +.Fa d_off +from an entry, +or zero. +.Sh RETURN VALUES +If successful, the number of bytes actually transferred is returned. +A value of zero is returned when +the end of the directory has been reached. +Otherwise, \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn getdents +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa fd +is not a valid file descriptor open for reading. +.It Bq Er EFAULT +Part of +.Fa buf +points outside the process's allocated address space. +.It Bq Er EINVAL +The file referenced by +.Fa fd +is not a directory, or +.Fa nbytes +is too small for returning a directory entry or block of entries, +or the current position pointer is invalid. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Sh SEE ALSO +.Xr lseek 2 , +.Xr open 2 , +.Xr opendir 3 , +.Xr dirent 5 +.Sh STANDARDS +The +.Fn getdents +call is not a portable interface and should not be used directly by +applications. +Use +.Xr readdir 3 +instead. +.Sh HISTORY +The +.Fn getdirentries +function first appeared in +.Bx 4.3 Reno . +In +.Ox 5.5 +the +.Fa d_off +entry was added to +.Vt struct dirent +and +.Fn getdirentries +was replaced with +.Fn getdents . diff --git a/man/test_files/mdoc/getfh.2 b/man/test_files/mdoc/getfh.2 new file mode 100644 index 00000000..814f040f --- /dev/null +++ b/man/test_files/mdoc/getfh.2 @@ -0,0 +1,102 @@ +.\" $OpenBSD: getfh.2,v 1.20 2022/07/30 07:19:30 jsg Exp $ +.\" $NetBSD: getfh.2,v 1.7 1995/10/12 15:40:53 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getfh.2 8.1 (Berkeley) 6/9/93 +.\" +.Dd $Mdocdate: July 30 2022 $ +.Dt GETFH 2 +.Os +.Sh NAME +.Nm getfh +.Nd get file handle +.Sh SYNOPSIS +.In sys/types.h +.In sys/mount.h +.Ft int +.Fn getfh "const char *path" "fhandle_t *fhp" +.Sh DESCRIPTION +.Fn getfh +returns a file handle for the specified file or directory +.Fa path +in the file handle pointed to by +.Fa fhp . +This system call is restricted to the superuser. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn getfh +fails if one or more of the following are true: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix of +.Fa path +is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The file referred to by +.Fa path +does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix of +.Fa path . +.It Bq Er ELOOP +Too many symbolic links were encountered in translating +.Fa path . +.It Bq Er EPERM +The effective user ID is not the superuser. +.It Bq Er EFAULT +.Fa fhp +or +.Fa path +points to an invalid address. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.It Bq Er EINVAL +A portion of +.Fa path +refers to a remote file system. +.It Bq Er EOPNOTSUPP +A portion of +.Fa path +refers to a remote file system. +.El +.Sh SEE ALSO +.Xr fhstat 2 +.Sh HISTORY +The +.Fn getfh +function first appeared in +.Bx 4.3 Reno . diff --git a/man/test_files/mdoc/getgroups.2 b/man/test_files/mdoc/getgroups.2 new file mode 100644 index 00000000..c290ccee --- /dev/null +++ b/man/test_files/mdoc/getgroups.2 @@ -0,0 +1,99 @@ +.\" $OpenBSD: getgroups.2,v 1.15 2019/07/08 18:48:30 anton Exp $ +.\" $NetBSD: getgroups.2,v 1.8 1995/02/27 12:32:57 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getgroups.2 8.2 (Berkeley) 4/16/94 +.\" +.Dd $Mdocdate: July 8 2019 $ +.Dt GETGROUPS 2 +.Os +.Sh NAME +.Nm getgroups +.Nd get group access list +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn getgroups "int gidsetlen" "gid_t *gidset" +.Sh DESCRIPTION +.Fn getgroups +gets the current group access list of the current user process +and stores it in the array +.Fa gidset . +The parameter +.Fa gidsetlen +indicates the number of entries that may be placed in +.Fa gidset . +.Fn getgroups +returns the actual number of groups returned in +.Fa gidset . +No more than +.Dv NGROUPS_MAX +will ever +be returned. +If +.Fa gidsetlen +is 0, +.Fn getgroups +returns the number of groups without modifying the +.Fa gidset +array. +.Sh RETURN VALUES +A successful call returns the number of groups in the group set. +A value of \-1 indicates that an error occurred, and the error +code is stored in the global variable +.Va errno . +.Sh ERRORS +The possible errors for +.Fn getgroups +are: +.Bl -tag -width Er +.It Bq Er EINVAL +The argument +.Fa gidsetlen +is smaller than the number of groups in the group set. +.It Bq Er EFAULT +The argument +.Fa gidset +specifies an invalid address. +.El +.Sh SEE ALSO +.Xr getgid 2 , +.Xr setgid 2 , +.Xr setgroups 2 , +.Xr initgroups 3 +.Sh STANDARDS +The +.Fn getgroups +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getgroups +system call first appeared in +.Bx 4.1c . diff --git a/man/test_files/mdoc/getitimer.2 b/man/test_files/mdoc/getitimer.2 new file mode 100644 index 00000000..1e1d25dd --- /dev/null +++ b/man/test_files/mdoc/getitimer.2 @@ -0,0 +1,176 @@ +.\" $OpenBSD: getitimer.2,v 1.33 2019/06/24 21:20:12 schwarze Exp $ +.\" $NetBSD: getitimer.2,v 1.6 1995/10/12 15:40:54 jtc Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getitimer.2 8.2 (Berkeley) 12/11/93 +.\" +.Dd $Mdocdate: June 24 2019 $ +.Dt GETITIMER 2 +.Os +.Sh NAME +.Nm getitimer , +.Nm setitimer +.Nd get/set value of interval timer +.Sh SYNOPSIS +.In sys/time.h +.Pp +.Fd #define ITIMER_REAL 0 +.Fd #define ITIMER_VIRTUAL 1 +.Fd #define ITIMER_PROF 2 +.Ft int +.Fn getitimer "int which" "struct itimerval *value" +.Ft int +.Fn setitimer "int which" "const struct itimerval *value" "struct itimerval *ovalue" +.Sh DESCRIPTION +The system provides each process with three interval timers, +defined in +.In sys/time.h . +The +.Fn getitimer +call returns the current value for the timer specified in +.Fa which +in the structure at +.Fa value . +The +.Fn setitimer +call sets a timer to the specified +.Fa value +(returning the previous value of the timer if +.Fa ovalue +is non-null). +.Pp +A timer value is defined by the +.Fa itimerval +structure: +.Bd -literal -offset indent +struct itimerval { + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; +.Ed +.Pp +If +.Fa it_value +is non-zero, it indicates the time to the next timer expiration. +If +.Fa it_interval +is non-zero, it specifies a value to be used in reloading +.Fa it_value +when the timer expires. +Setting +.Fa it_value +to 0 disables a timer. +Setting +.Fa it_interval +to 0 causes a timer to be disabled after its next expiration (assuming +.Fa it_value +is non-zero). +.Pp +Time values smaller than the resolution of the +system clock are rounded up to this resolution +(typically 10 milliseconds). +.Pp +The +.Dv ITIMER_REAL +timer decrements in real time. +A +.Dv SIGALRM +signal is +delivered when this timer expires. +.Pp +The +.Dv ITIMER_VIRTUAL +timer decrements in process virtual time. +It runs only when the process is executing. +A +.Dv SIGVTALRM +signal is delivered when it expires. +.Pp +The +.Dv ITIMER_PROF +timer decrements both in process virtual time and +when the system is running on behalf of the process. +It is designed to be used by interpreters in statistically profiling +the execution of interpreted programs. +Each time the +.Dv ITIMER_PROF +timer expires, the +.Dv SIGPROF +signal is delivered. +Because this signal may interrupt in-progress +system calls, programs using this timer must be prepared to +restart interrupted system calls. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn getitimer +and +.Fn setitimer +will fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +The +.Fa value +parameter specified a bad address. +.It Bq Er EINVAL +An unrecognized value for +.Fa which +was specified. +.El +.Pp +In addition, +.Fn setitimer +may return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa value +or +.Fa ovalue +specified a time that was too large to be handled. +.El +.Sh SEE ALSO +.Xr clock_gettime 2 , +.Xr gettimeofday 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr sigaction 2 +.Sh STANDARDS +The +.Fn getitimer +and +.Fn setitimer +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getitimer +and +.Fn setitimer +system calls first appeared in +.Bx 4.1c . diff --git a/man/test_files/mdoc/getpeername.2 b/man/test_files/mdoc/getpeername.2 new file mode 100644 index 00000000..f507d168 --- /dev/null +++ b/man/test_files/mdoc/getpeername.2 @@ -0,0 +1,143 @@ +.\" $OpenBSD: getpeername.2,v 1.27 2022/09/11 06:38:11 jmc Exp $ +.\" $NetBSD: getpeername.2,v 1.6 1995/10/12 15:40:56 jtc Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getpeername.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 11 2022 $ +.Dt GETPEERNAME 2 +.Os +.Sh NAME +.Nm getpeername +.Nd get name of connected peer +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn getpeername "int s" "struct sockaddr *name" "socklen_t *namelen" +.Sh DESCRIPTION +.Fn getpeername +returns the address information of the peer connected to socket +.Fa s . +One common use occurs when a process inherits an open socket, such as +TCP servers forked from +.Xr inetd 8 . +In this scenario, +.Fn getpeername +is used to determine the connecting client's IP address. +.Pp +.Fn getpeername +takes three parameters: +.Pp +.Fa s +contains the file descriptor of the socket whose peer should be looked up. +.Pp +.Fa name +points to a +.Vt sockaddr +structure that will hold the address information for the connected peer. +Normal use requires one to use a structure +specific to the protocol family in use, such as +.Vt sockaddr_in +(IPv4) or +.Vt sockaddr_in6 +(IPv6), cast to a (struct sockaddr *). +.Pp +For greater portability, especially with the newer protocol families, the new +.Vt struct sockaddr_storage +should be used. +.Vt sockaddr_storage +is large enough to hold any of the other sockaddr_* variants. +On return, it can be cast to the correct sockaddr type, +based on the protocol family contained in its ss_family field. +.Pp +.Fa namelen +indicates the amount of space pointed to by +.Fa name , +in bytes. +.Pp +If address information for the local end of the socket is required, the +.Xr getsockname 2 +function should be used instead. +.Pp +If +.Fa name +does not point to enough space to hold the entire socket address, the +result will be truncated to +.Fa namelen +bytes. +.Sh RETURN VALUES +If the call succeeds, a 0 is returned and +.Fa namelen +is set to the actual size of the socket address returned in +.Fa name . +Otherwise, +.Va errno +is set and a value of \-1 is returned. +.Sh ERRORS +On failure, +.Va errno +is set to one of the following: +.Bl -tag -width Er +.It Bq Er EBADF +The argument +.Fa s +is not a valid descriptor. +.It Bq Er ENOTSOCK +The argument +.Fa s +is a file, not a socket. +.It Bq Er ENOTCONN +The socket is not connected. +.It Bq Er ENOBUFS +Insufficient resources were available in the system +to perform the operation. +.It Bq Er EFAULT +The +.Fa name +or +.Fa namelen +parameter points to memory not in a valid part of the +process address space. +.El +.Sh SEE ALSO +.Xr accept 2 , +.Xr bind 2 , +.Xr getsockname 2 , +.Xr socket 2 , +.Xr getpeereid 3 +.Sh STANDARDS +The +.Fn getpeername +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getpeername +function call appeared in +.Bx 4.2 . diff --git a/man/test_files/mdoc/getpriority.2 b/man/test_files/mdoc/getpriority.2 new file mode 100644 index 00000000..426dd80c --- /dev/null +++ b/man/test_files/mdoc/getpriority.2 @@ -0,0 +1,158 @@ +.\" $OpenBSD: getpriority.2,v 1.15 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: getpriority.2,v 1.4 1995/02/27 12:33:15 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getpriority.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt GETPRIORITY 2 +.Os +.Sh NAME +.Nm getpriority , +.Nm setpriority +.Nd get/set process scheduling priority +.Sh SYNOPSIS +.In sys/resource.h +.Ft int +.Fn getpriority "int which" "id_t who" +.Ft int +.Fn setpriority "int which" "id_t who" "int prio" +.Sh DESCRIPTION +The scheduling priority of the process, process group, or user, +as indicated by +.Fa which +and +.Fa who +is obtained with the +.Fn getpriority +call and set with the +.Fn setpriority +call. +.Fa which +is one of +.Dv PRIO_PROCESS , +.Dv PRIO_PGRP , +or +.Dv PRIO_USER , +and +.Fa who +is interpreted relative to +.Fa which +(a process identifier for +.Dv PRIO_PROCESS , +process group identifier for +.Dv PRIO_PGRP , +and a user ID for +.Dv PRIO_USER ) . +A zero value of +.Fa who +denotes the current process, process group, or user. +.Fa prio +is a value in the range \-20 to 20. +The default priority is 0; lower priorities cause more favorable scheduling. +.Pp +The +.Fn getpriority +call returns the highest priority (lowest numerical value) +enjoyed by any of the specified processes. +The +.Fn setpriority +call sets the priorities of all of the specified processes +to the specified value. +Priority values outside the range \-20 to 20 are truncated to the +appropriate limit. +Only the superuser may lower priorities. +.Sh RETURN VALUES +Since +.Fn getpriority +can legitimately return the value \-1, it is necessary +to clear the external variable +.Va errno +prior to the +call, then check it afterward to determine +if a \-1 is an error or a legitimate value. +The +.Fn setpriority +call returns 0 if there is no error, or +\-1 if there is. +.Sh ERRORS +.Fn getpriority +and +.Fn setpriority +will fail if: +.Bl -tag -width Er +.It Bq Er ESRCH +No process was located using the +.Fa which +and +.Fa who +values specified. +.It Bq Er EINVAL +.Fa which +was not one of +.Dv PRIO_PROCESS , +.Dv PRIO_PGRP , +or +.Dv PRIO_USER . +.El +.Pp +In addition, +.Fn setpriority +will fail if: +.Bl -tag -width Er +.It Bq Er EPERM +A process was located, but neither its effective nor real user +ID matched the effective user ID of the caller. +.It Bq Er EACCES +A non-superuser attempted to lower a process priority. +.El +.Sh SEE ALSO +.Xr nice 1 , +.Xr fork 2 , +.Xr renice 8 +.Sh STANDARDS +The +.Fn getpriority +and +.Fn setpriority +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The predecessor of these functions, the former +.Fn nice +system call, appeared in +.At v3 +and was removed in +.Bx 4.3 Reno . +The +.Fn getpriority +and +.Fn setpriority +system calls appeared in +.Bx 4.1c . diff --git a/man/test_files/mdoc/getrtable.2 b/man/test_files/mdoc/getrtable.2 new file mode 100644 index 00000000..12b2dbd9 --- /dev/null +++ b/man/test_files/mdoc/getrtable.2 @@ -0,0 +1,66 @@ +.\" $OpenBSD: getrtable.2,v 1.5 2023/02/22 06:31:51 guenther Exp $ +.\" +.\" Copyright (c) 2009 Reyk Floeter +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: February 22 2023 $ +.Dt GETRTABLE 2 +.Os +.Sh NAME +.Nm getrtable , +.Nm setrtable +.Nd get or set the default routing table of the current process +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.Ft int +.Fn getrtable "void" +.Ft int +.Fn setrtable "int rtableid" +.Sh DESCRIPTION +.Fn getrtable +and +.Fn setrtable +manipulate the routing table and routing domain associated with the current +process. +.Pp +Only the superuser is allowed to change the process routing table if +it is already set to a non-zero value. +.Sh RETURN VALUES +.Fn getrtable +returns the routing table of the current process. +Upon successful completion, +.Fn setrtable +returns 0 if the call succeeds, \-1 if it fails. +.Sh ERRORS +The call succeeds unless: +.Bl -tag -width Er +.It Bq Er EINVAL +The value of the +.Fa rtableid +argument is not a valid routing table. +.It Bq Er EPERM +The user is not the superuser and the routing table of the +calling process is already set to a non-zero value. +.El +.Sh SEE ALSO +.Xr getsockopt 2 , +.Xr route 8 +.Sh HISTORY +The +.Fn getrtable +and +.Fn setrtable +system calls appeared in +.Ox 4.8 . diff --git a/man/test_files/mdoc/getrusage.2 b/man/test_files/mdoc/getrusage.2 new file mode 100644 index 00000000..35eccf70 --- /dev/null +++ b/man/test_files/mdoc/getrusage.2 @@ -0,0 +1,192 @@ +.\" $OpenBSD: getrusage.2,v 1.18 2024/07/17 13:29:05 claudio Exp $ +.\" +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getrusage.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: July 17 2024 $ +.Dt GETRUSAGE 2 +.Os +.Sh NAME +.Nm getrusage +.Nd get information about resource utilization +.Sh SYNOPSIS +.In sys/resource.h +.Ft int +.Fn getrusage "int who" "struct rusage *rusage" +.Sh DESCRIPTION +.Fn getrusage +returns resource usage information for argument +.Fa who , +which can be one of the following: +.Bl -tag -width RUSAGE_CHILDREN -offset indent +.It Dv RUSAGE_SELF +Resources used by the current process. +.It Dv RUSAGE_CHILDREN +Resources used by all the terminated children of the current process which +were waited upon. +If the child is never waited for, the resource information for the child +process is discarded. +.It Dv RUSAGE_THREAD +Resources used by the current thread. +.El +.Pp +The buffer to which +.Fa rusage +points will be filled in with +the following structure: +.Bd -literal +struct rusage { + struct timeval ru_utime; /* user time used */ + struct timeval ru_stime; /* system time used */ + long ru_maxrss; /* max resident set size */ + long ru_ixrss; /* integral shared text memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary context switches */ +}; +.Ed +.Pp +The fields are interpreted as follows: +.Bl -tag -width ru_minfltaa +.It Fa ru_utime +the total amount of time spent executing in user mode. +.It Fa ru_stime +the total amount of time spent in the system executing on behalf +of the process(es). +.It Fa ru_maxrss +the maximum resident set size utilized (in kilobytes). +.It Fa ru_ixrss +an +.Dq integral +value indicating the amount of memory used +by the text segment +that was also shared among other processes. +This value is expressed in units of kilobytes * ticks-of-execution. +.It Fa ru_idrss +an integral value of the amount of unshared memory residing in the +data segment of a process (expressed in units of +kilobytes * ticks-of-execution). +.It Fa ru_isrss +an integral value of the amount of unshared memory residing in the +stack segment of a process (expressed in units of +kilobytes * ticks-of-execution). +.It Fa ru_minflt +the number of page faults serviced without any I/O activity; here +I/O activity is avoided by +.Dq reclaiming +a page frame from +the list of pages awaiting reallocation. +.It Fa ru_majflt +the number of page faults serviced that required I/O activity. +.It Fa ru_nswap +the number of times a process was +.Dq swapped +out of main memory. +.It Fa ru_inblock +the number of times the file system had to perform input. +.It Fa ru_oublock +the number of times the file system had to perform output. +.It Fa ru_msgsnd +the number of IPC messages sent. +.It Fa ru_msgrcv +the number of IPC messages received. +.It Fa ru_nsignals +the number of signals delivered. +.It Fa ru_nvcsw +the number of times a context switch resulted due to a process +voluntarily giving up the processor before its time slice was +completed (usually to await availability of a resource). +.It Fa ru_nivcsw +the number of times a context switch resulted due to a higher +priority process becoming runnable or because the current process +exceeded its time slice. +.El +.Sh NOTES +The numbers +.Fa ru_inblock +and +.Fa ru_oublock +account only for real +I/O; data supplied by the caching mechanism is charged only +to the first process to read or write the data. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn getrusage +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa who +parameter is not a valid value. +.It Bq Er EFAULT +The address specified by the +.Fa rusage +parameter is not in a valid part of the process address space. +.El +.Sh SEE ALSO +.Xr clock_gettime 2 , +.Xr gettimeofday 2 , +.Xr wait 2 +.Sh STANDARDS +The +.Fn getrusage +function conforms to +.St -p1003.1-2008 . +.Pp +The +.Dv RUSAGE_THREAD +flag is an extension to that specification. +.Sh HISTORY +A predecessor to +.Fn getrusage , +.Fn times , +first appeared in +.At v3 . +The +.Fn getrusage +system call first appeared in +.Bx 4.1c . +.Pp +The +.Dv RUSAGE_THREAD +flag has been available since +.Ox 4.8 . +.Sh BUGS +There is no way to obtain information about a child process +that has not yet terminated or has not been waited for by the parent. diff --git a/man/test_files/mdoc/getsid.2 b/man/test_files/mdoc/getsid.2 new file mode 100644 index 00000000..049c6ed9 --- /dev/null +++ b/man/test_files/mdoc/getsid.2 @@ -0,0 +1,83 @@ +.\" $OpenBSD: getsid.2,v 1.12 2015/09/10 17:55:21 schwarze Exp $ +.\" +.\" Copyright (c) 1997 Peter Wemm +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt GETSID 2 +.Os +.Sh NAME +.Nm getsid +.Nd get process session +.Sh SYNOPSIS +.In unistd.h +.Ft pid_t +.Fn getsid "pid_t pid" +.Sh DESCRIPTION +The session ID of the process identified by +.Fa pid +is returned by +.Fn getsid . +If +.Fa pid +is zero, +.Fn getsid +returns the session ID of the current process. +.Sh RETURN VALUES +Upon successful completion, the function +.Fn getsid +returns the session ID of +the specified process; otherwise, it returns a value of \-1 and +sets +.Va errno +to indicate an error. +.Sh ERRORS +.Fn getsid +will succeed unless: +.Bl -tag -width Er +.It Bq Er EPERM +The current process and the process +.Fa pid +are not in the same session. +.It Bq Er ESRCH +There is no process with a process ID equal to +.Fa pid . +.El +.Sh SEE ALSO +.Xr getpgid 2 , +.Xr getpgrp 2 , +.Xr setpgid 2 , +.Xr setsid 2 , +.Xr termios 4 +.Sh STANDARDS +.Fn getsid +conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getsid +function call is derived from its usage in +.At V , +and is mandated by +.St -xpg4 . diff --git a/man/test_files/mdoc/getsockname.2 b/man/test_files/mdoc/getsockname.2 new file mode 100644 index 00000000..bd681a44 --- /dev/null +++ b/man/test_files/mdoc/getsockname.2 @@ -0,0 +1,162 @@ +.\" $OpenBSD: getsockname.2,v 1.32 2022/09/11 06:38:11 jmc Exp $ +.\" $NetBSD: getsockname.2,v 1.6 1995/10/12 15:41:00 jtc Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getsockname.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 11 2022 $ +.Dt GETSOCKNAME 2 +.Os +.Sh NAME +.Nm getsockname +.Nd get socket name +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn getsockname "int s" "struct sockaddr *name" "socklen_t *namelen" +.Sh DESCRIPTION +.Fn getsockname +returns the locally bound address information for a specified socket. +.Pp +Common uses of this function are as follows: +.Bl -bullet +.It +When +.Xr bind 2 +is called with a port number of 0 (indicating the kernel should pick +an ephemeral port), +.Fn getsockname +is used to retrieve the kernel-assigned port number. +.It +When a process calls +.Xr bind 2 +on a wildcard IP address, +.Fn getsockname +is used to retrieve the local IP address for the connection. +.It +When a function wishes to know the address family of a socket, +.Fn getsockname +can be used. +.El +.Pp +.Fn getsockname +takes three parameters: +.Pp +.Fa s +contains the file descriptor for the socket to be looked up. +.Pp +.Fa name +points to a +.Vt sockaddr +structure which will hold the resulting address information. +Normal use requires one to use a structure +specific to the protocol family in use, such as +.Vt sockaddr_in +(IPv4) or +.Vt sockaddr_in6 +(IPv6), cast to a (struct sockaddr *). +.Pp +For greater portability (such as newer protocol families) the new +structure sockaddr_storage exists. +.Vt sockaddr_storage +is large enough to hold any of the other sockaddr_* variants. +On return, it should be cast to the correct sockaddr type, +according to the current protocol family. +.Pp +.Fa namelen +indicates the amount of space pointed to by +.Fa name , +in bytes. +Upon return, +.Fa namelen +is set to the actual size of the returned address information. +.Pp +If the address of the destination socket for a given socket connection is +needed, the +.Xr getpeername 2 +function should be used instead. +.Pp +If +.Fa name +does not point to enough space to hold the entire socket address, the +result will be truncated to +.Fa namelen +bytes. +.Sh RETURN VALUES +On success, +.Fn getsockname +returns a 0, and +.Fa namelen +is set to the actual size of the socket address returned in +.Fa name . +Otherwise, +.Va errno +is set, and a value of \-1 is returned. +.Sh ERRORS +If +.Fn getsockname +fails, +.Va errno +is set to one of the following: +.Bl -tag -width Er +.It Bq Er EBADF +The argument +.Fa s +is not a valid descriptor. +.It Bq Er ENOTSOCK +The argument +.Fa s +is a file, not a socket. +.It Bq Er ENOBUFS +Insufficient resources were available in the system +to perform the operation. +.It Bq Er EFAULT +The +.Fa name +or +.Fa namelen +parameter points to memory not in a valid part of the +process address space. +.El +.Sh SEE ALSO +.Xr accept 2 , +.Xr bind 2 , +.Xr getpeername 2 , +.Xr socket 2 , +.Xr getpeereid 3 +.Sh STANDARDS +The +.Fn getsockname +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getsockname +function call appeared in +.Bx 4.2 . diff --git a/man/test_files/mdoc/getsockopt.2 b/man/test_files/mdoc/getsockopt.2 new file mode 100644 index 00000000..eb9c89e4 --- /dev/null +++ b/man/test_files/mdoc/getsockopt.2 @@ -0,0 +1,541 @@ +.\" $OpenBSD: getsockopt.2,v 1.62 2024/04/02 14:23:15 claudio Exp $ +.\" $NetBSD: getsockopt.2,v 1.7 1995/02/27 12:33:29 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getsockopt.2 8.3 (Berkeley) 4/19/94 +.\" +.Dd $Mdocdate: April 2 2024 $ +.Dt GETSOCKOPT 2 +.Os +.Sh NAME +.Nm getsockopt , +.Nm setsockopt +.Nd get or set options on sockets +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn getsockopt "int s" "int level" "int optname" "void *optval" "socklen_t *optlen" +.Ft int +.Fn setsockopt "int s" "int level" "int optname" "const void *optval" "socklen_t optlen" +.Sh DESCRIPTION +.Fn getsockopt +and +.Fn setsockopt +manipulate the +.Em options +associated with a socket. +Options may exist at multiple protocol levels; +they are always present at the uppermost +.Dq socket +level. +.Pp +When manipulating socket options, the level at which the +option resides and the name of the option must be specified. +To manipulate options at the socket level, +.Fa level +is specified as +.Dv SOL_SOCKET . +To manipulate options at any other level the protocol number of the +appropriate protocol controlling the option is supplied. +For example, to indicate that an option is to be interpreted by the +TCP protocol, +.Fa level +should be set to the protocol number of TCP; see +.Xr getprotoent 3 . +.Pp +The parameters +.Fa optval +and +.Fa optlen +are used to access option values for +.Fn setsockopt . +For +.Fn getsockopt +they identify a buffer in which the value for the +requested option(s) are to be returned. +For +.Fn getsockopt , +.Fa optlen +is a value-result parameter, initially containing the +size of the buffer pointed to by +.Fa optval , +and modified on return to indicate the actual size of the value returned. +If no option value is to be supplied or returned, +.Fa optval +may be +.Dv NULL . +.Pp +.Fa optname +and any specified options are passed uninterpreted to the appropriate +protocol module for interpretation. +The include file +.In sys/socket.h +contains definitions for socket level options, described below. +Options at other protocol levels vary in format and name; +consult the appropriate entries in section 4 of the manual. +.Pp +Most socket-level options utilize an +.Vt int +parameter for +.Fa optval . +For +.Fn setsockopt , +the parameter should be non-zero to enable a boolean option, +or zero if the option is to be disabled. +.Dv SO_LINGER +uses a +.Vt struct linger +parameter, defined in +.In sys/socket.h , +which specifies the desired state of the option and the +linger interval (see below). +.Dv SO_SNDTIMEO +and +.Dv SO_RCVTIMEO +use a +.Vt struct timeval +parameter, defined in +.In sys/time.h . +.Pp +The following options are recognized at the socket level. +Except as noted, each may be examined with +.Fn getsockopt +and set with +.Fn setsockopt . +.Pp +.Bl -tag -width SO_OOBINLINE -offset indent -compact +.It Dv SO_DEBUG +enables recording of debugging information +.It Dv SO_REUSEADDR +enables local address reuse +.It Dv SO_REUSEPORT +enables duplicate address and port bindings +.It Dv SO_KEEPALIVE +enables keep connections alive +.It Dv SO_DONTROUTE +enables routing bypass; not supported +.It Dv SO_LINGER +linger on close if data present +.It Dv SO_BROADCAST +enables permission to transmit broadcast messages +.It Dv SO_OOBINLINE +enables reception of out-of-band data in band +.It Dv SO_BINDANY +enables binding to any address +.It Dv SO_SNDBUF +set buffer size for output +.It Dv SO_RCVBUF +set buffer size for input +.It Dv SO_SNDLOWAT +set minimum count for output +.It Dv SO_RCVLOWAT +set minimum count for input +.It Dv SO_SNDTIMEO +set timeout value for output +.It Dv SO_RCVTIMEO +set timeout value for input +.It Dv SO_TIMESTAMP +enables reception of a timestamp with datagrams +.It Dv SO_RTABLE +set the routing table used for route lookups +.It Dv SO_SPLICE +splice two sockets together or get data length +.It Dv SO_ZEROIZE +clear all memory containing user supplied data +.It Dv SO_TYPE +get the type of the socket (get only) +.It Dv SO_ERROR +get and clear error on the socket (get only) +.It Dv SO_DOMAIN +get the domain of the socket (get only) +.It Dv SO_PROTOCOL +get the protocol of the socket (get only) +.It Dv SO_ACCEPTCONN +get listening status of the socket (get only) +.It Dv SO_PEERCRED +get the credentials from other side of connection (get only) +.El +.Pp +.Dv SO_DEBUG +enables debugging in the underlying protocol modules. +Transliterate the protocol trace with +.Xr trpt 8 . +.Dv SO_REUSEADDR +indicates that the rules used in validating addresses supplied in a +.Xr bind 2 +call should allow reuse of local addresses +by callers with the same user ID (or the superuser). +.Dv SO_REUSEPORT +allows completely duplicate bindings by multiple processes if they all set +.Dv SO_REUSEPORT +before binding the port. +This option permits multiple instances of a program to each +receive UDP/IP multicast or broadcast datagrams destined for the bound port. +.Dv SO_KEEPALIVE +enables the periodic transmission of messages on a connected socket. +Should the connected party fail to respond to these messages, the connection +is considered broken and processes using the socket are notified via a +.Dv SIGPIPE +signal when attempting to send data. +.Pp +.Dv SO_LINGER +controls the action taken when unsent messages +are queued on socket and a +.Xr close 2 +is performed. +If the socket promises reliable delivery of data and +.Dv SO_LINGER +is set, the system will block the process on the +.Xr close 2 +attempt until it is able to transmit the data or until it decides it +is unable to deliver the information (a timeout period measured in seconds, +termed the linger interval, is specified in the +.Fn setsockopt +call when +.Dv SO_LINGER +is requested). +If +.Dv SO_LINGER +is disabled and a +.Xr close 2 +is issued, the system will process the close in a manner that allows +the process to continue as quickly as possible. +.Pp +The option +.Dv SO_BROADCAST +requests permission to send broadcast datagrams +on the socket. +Broadcast was a privileged operation in earlier versions of the system. +With protocols that support out-of-band data, the +.Dv SO_OOBINLINE +option requests that out-of-band data be placed in the normal data input +queue as received; it will then be accessible with +.Xr recv 2 +or +.Xr read 2 +calls without the +.Dv MSG_OOB +flag. +Some protocols always behave as if this option is set. +.Pp +.Dv SO_BINDANY +allows the socket to be bound to addresses +which are not local to the machine, so it +can be used to make a transparent proxy. +Note that this option is limited to the superuser. +In order to receive packets for these addresses, +.Dv SO_BINDANY +needs to be combined with matching outgoing +.Xr pf 4 +rules with the +.Ar divert-reply +parameter. +For example, with the following rule the socket receives packets +for 192.168.0.10 even if it is not a local address: +.Pp +.Dl pass out inet from 192.168.0.10 divert-reply +.Pp +.Dv SO_SNDBUF +and +.Dv SO_RCVBUF +are options to adjust the normal +buffer sizes allocated for output and input buffers, respectively. +The buffer size may be increased for high-volume connections, +or may be decreased to limit the possible backlog of incoming data. +The system places an absolute limit on these values. +.Pp +.Dv SO_SNDLOWAT +is an option to set the minimum count for output operations. +Most output operations process all of the data supplied +by the call, delivering data to the protocol for transmission +and blocking as necessary for flow control. +Nonblocking output operations will process as much data as permitted +subject to flow control without blocking, but will process no data +if flow control does not allow the smaller of the low water mark value +or the entire request to be processed. +A +.Xr select 2 +or +.Xr poll 2 +operation testing the ability to write to a socket will return true +only if the low water mark amount could be processed. +The default value for +.Dv SO_SNDLOWAT +is set to a convenient size for network efficiency, often 1024. +.Dv SO_RCVLOWAT +is an option to set the minimum count for input operations. +In general, receive calls will block until any (non-zero) amount of data +is received, then return with the smaller of the amount available or the amount +requested. +The default value for +.Dv SO_RCVLOWAT +is 1. +If +.Dv SO_RCVLOWAT +is set to a larger value, blocking receive calls normally +wait until they have received the smaller of the low water mark value +or the requested amount. +Receive calls may still return less than the low water mark if an error +occurs, a signal is caught, or the type of data next in the receive queue +is different than that returned. +.Pp +.Dv SO_SNDTIMEO +is an option to set a timeout value for output operations. +It accepts a +.Vt struct timeval +parameter with the number of seconds and microseconds +used to limit waits for output operations to complete. +If a send operation has blocked for this much time, +it returns with a partial count or with the error +.Er EWOULDBLOCK +if no data was sent. +In the current implementation, this timer is restarted each time additional +data are delivered to the protocol, +implying that the limit applies to output portions ranging in size +from the low water mark to the high water mark for output. +.Dv SO_RCVTIMEO +is an option to set a timeout value for input operations. +It accepts a +.Vt struct timeval +parameter with the number of seconds and microseconds +used to limit waits for input operations to complete. +In the current implementation, this timer is restarted each time additional +data are received by the protocol, +and thus the limit is in effect an inactivity timer. +If a receive operation has been blocked for this much time without +receiving additional data, it returns with a short count +or with the error +.Er EWOULDBLOCK +if no data were received. +.Pp +If the +.Dv SO_TIMESTAMP +option is enabled on a +.Dv SOCK_DGRAM +socket, the +.Xr recvmsg 2 +call will return a timestamp corresponding to when the datagram was +received. +The msg_control field in the msghdr structure points to a buffer +that contains a cmsghdr structure followed by a struct timeval. +The cmsghdr fields have the following values: +.Bd -literal -offset indent +cmsg_len = CMSG_LEN(sizeof(struct timeval)) +cmsg_level = SOL_SOCKET +cmsg_type = SCM_TIMESTAMP +.Ed +.Pp +The +.Dv SO_RTABLE +option gets or sets the routing table which will be used by the socket +for address lookups. +If a protocol family of the socket doesn't support switching routing tables, +the +.Er ENOPROTOOPT +error is returned. +Only the superuser is allowed to change the routing table if it is already +set to a non-zero value. +A socket's chosen routing table is initialized from the process's configuration, +previously selected using +.Xr setrtable 2 . +.Pp +.Dv SO_SPLICE +can splice together two TCP or UDP sockets for unidirectional +zero-copy data transfers. +Splice also the other way around to get bidirectional data flow. +Both sockets must be of the same type. +In the first form, +.Fn setsockopt +is called with the source socket +.Fa s +and the drain socket's +.Vt int +file descriptor as +.Fa optval . +In the second form, +.Fa optval +is a +.Vt struct splice +with the drain socket in +.Va sp_fd , +a positive maximum number of bytes or 0 in +.Va sp_max +and an idle timeout +.Va sp_idle +in the form of a +.Vt struct timeval . +If \-1 is given as drain socket, the source socket +.Fa s +gets unspliced. +Otherwise the spliced data transfer continues within the kernel +until the optional maximum is reached, one of the connections +terminates, idle timeout expires or an error occurs. +A successful +.Xr select 2 , +.Xr poll 2 , +or +.Xr kqueue 2 +operation testing the ability to read from the source socket indicates +that the splicing has terminated. +When one of the sockets gets closed, splicing ends. +The error status can be examined with +.Dv SO_ERROR +at the source socket. +The +.Er ELOOP +error is set if userland created a loop by splicing sockets connected +to localhost. +The +.Er ETIMEDOUT +error is set if there was no data transferred between two sockets +during the +.Va sp_idle +period of time. +The +.Er EFBIG +error is set after exactly +.Va sp_max +bytes have been transferred. +Note that if a maximum is given, it is only guaranteed that no more +bytes are transferred. +A short splice can happen, but then a second call to splice will +transfer the remaining data immediately. +The +.Dv SO_SPLICE +option with +.Fn getsockopt +and an +.Vt off_t +value as +.Fa optval +can be used to retrieve the number of bytes transferred so far from the +source socket +.Fa s . +A successful new splice resets this number. +.Pp +Userland may write sensitive data into a socket. +If +.Dv SO_ZEROIZE +is set, overwrite kernel memory after sending data. +.Pp +Finally, +.Dv SO_TYPE , +.Dv SO_DOMAIN , +.Dv SO_PROTOCOL , +.Dv SO_ERROR , +.Dv SO_ACCEPTCONN , +and +.Dv SO_PEERCRED +are options used only with +.Fn getsockopt . +.Dv SO_TYPE +returns the type of the socket, such as +.Dv SOCK_STREAM ; +it is useful for servers that inherit sockets on startup. +.Dv SO_DOMAIN +returns the domain of the socket, such as +.Dv AF_INET . +.Dv SO_PROTOCOL +returns the protocol of the socket such as +.Dv IPPROTO_TCP . +.Dv SO_ERROR +returns any pending error on the socket and clears the error status. +It may be used to check for asynchronous errors on connected +datagram sockets or for other asynchronous errors. +.Dv SO_ACCEPTCONN +returns whether the socket is currently accepting connections, that is, +whether or not +.Xr listen 2 +was called. +.Dv SO_PEERCRED +fetches the +.Va struct sockpeercred +credentials from the other side of the connection +(currently only possible on +.Dv AF_UNIX +sockets). +These credentials are from the time that +.Xr bind 2 , +.Xr connect 2 +or +.Xr socketpair 2 +were called. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The call succeeds unless: +.Bl -tag -width Er +.It Bq Er EBADF +The argument +.Fa s +is not a valid descriptor. +.It Bq Er ENOTSOCK +The argument +.Fa s +is a file, not a socket. +.It Bq Er ENOPROTOOPT +The option is unknown at the level indicated. +.It Bq Er EOPNOTSUPP +The option is unsupported. +.It Bq Er EFAULT +The address pointed to by +.Fa optval +is not in a valid part of the process address space. +For +.Fn getsockopt , +this error may also be returned if +.Fa optlen +is not in a valid part of the process address space. +.El +.Sh SEE ALSO +.Xr connect 2 , +.Xr getrtable 2 , +.Xr ioctl 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr socket 2 , +.Xr getprotoent 3 , +.Xr divert 4 , +.Xr pf.conf 5 , +.Xr protocols 5 , +.Xr sosplice 9 +.Sh STANDARDS +The +.Fn getsockopt +and +.Fn setsockopt +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn getsockopt +system call appeared in +.Bx 4.1c . +.Sh BUGS +Several of the socket options should be handled at lower levels of the system. diff --git a/man/test_files/mdoc/gettimeofday.2 b/man/test_files/mdoc/gettimeofday.2 new file mode 100644 index 00000000..24a4ca49 --- /dev/null +++ b/man/test_files/mdoc/gettimeofday.2 @@ -0,0 +1,205 @@ +.\" $OpenBSD: gettimeofday.2,v 1.33 2022/03/31 17:27:16 naddy Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)gettimeofday.2 8.2 (Berkeley) 5/26/95 +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt GETTIMEOFDAY 2 +.Os +.Sh NAME +.Nm gettimeofday , +.Nm settimeofday +.Nd get or set the time of day +.Sh SYNOPSIS +.In sys/time.h +.Ft int +.Fn gettimeofday "struct timeval *now" "struct timezone *tz" +.Ft int +.Fn settimeofday "const struct timeval *now" "const struct timezone *tz" +.Sh DESCRIPTION +The +.Fn gettimeofday +function writes the absolute value of the system's Coordinated Universal Time +.Pq UTC +clock to +.Fa now +unless +.Fa now +is +.Dv NULL . +.Pp +The UTC clock's absolute value is the time elapsed since +Jan 1 1970 00:00:00 +0000 +.Pq the Epoch . +The clock normally advances continuously, +though it may jump discontinuously if a process calls +.Fn settimeofday +or +.Xr clock_settime 2 . +For this reason, +.Fn gettimeofday +is not generally suitable for measuring elapsed time. +Whenever possible, +use +.Xr clock_gettime 2 +to measure elapsed time with one of the system's monotonic clocks instead. +.Pp +The +.Fn settimeofday +function sets the system's UTC clock to the absolute value +.Fa now +unless +.Fa now +is +.Dv NULL . +Only the superuser may set the clock. +If the system +.Xr securelevel 7 +is 2 or greater, the clock may only be advanced. +This limitation prevents a malicious superuser +from setting arbitrary timestamps on files. +Setting the clock cancels any ongoing +.Xr adjtime 2 +adjustment. +.Pp +The structure pointed to by +.Fa now +is defined in +.In sys/time.h +as: +.Bd -literal +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* and microseconds */ +}; +.Ed +.Pp +The +.Fa tz +argument is historical: +the system no longer maintains timezone information in the kernel. +The +.Fa tz +argument should always be +.Dv NULL . +.Fn gettimeofday +zeroes +.Fa tz +if it is not +.Dv NULL . +.Fn settimeofday +ignores the contents of +.Fa tz +if it is not +.Dv NULL . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn gettimeofday +and +.Fn settimeofday +will fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +.Fa now +or +.Fa tz +are not +.Dv NULL +and reference invalid memory. +.El +.Pp +.Fn settimeofday +will also fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa now +specifies a microsecond value less than zero or greater than or equal to +one million. +.It Bq Er EPERM +The caller is not the superuser. +.It Bq Er EPERM +The system +.Xr securelevel 7 +is 2 or greater and +.Fa now +specifies a time in the past. +.El +.Sh SEE ALSO +.Xr date 1 , +.Xr adjtime 2 , +.Xr clock_gettime 2 , +.Xr getitimer 2 , +.Xr ctime 3 , +.Xr time 3 , +.Xr timeradd 3 +.Sh STANDARDS +The +.Fn gettimeofday +function conforms to +.St -p1003.1-2008 . +.Pp +The +.Fn settimeofday +function is non-standard, +though many systems offer it. +.Sh HISTORY +As predecessors of these functions, +former system calls +.Fn time +and +.Fn stime +first appeared in +.At v1 , +and +.Fn ftime +first appeared in +.At v7 . +The +.Fn gettimeofday +and +.Fn settimeofday +system calls first appeared in +.Bx 4.1c . +.Sh CAVEATS +Setting the time with +.Fn settimeofday +is dangerous; if possible use +.Xr adjtime 2 +instead. +Many daemon programming techniques utilize time-delta techniques +using the results from +.Fn gettimeofday +instead of from +.Xr clock_gettime 2 +on the +.Dv CLOCK_MONOTONIC +clock. +Time jumps can cause these programs to malfunction in unexpected ways. +If the time must be set, consider rebooting the machine for safety. diff --git a/man/test_files/mdoc/grep.1 b/man/test_files/mdoc/grep.1 new file mode 100644 index 00000000..3fe35fb7 --- /dev/null +++ b/man/test_files/mdoc/grep.1 @@ -0,0 +1,395 @@ +.\" $OpenBSD: grep.1,v 1.53 2023/11/15 00:50:43 millert Exp $ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)grep.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd $Mdocdate: November 15 2023 $ +.Dt GREP 1 +.Os +.Sh NAME +.Nm grep , egrep , fgrep , +.Nm zgrep , zegrep , zfgrep +.Nd file pattern searcher +.Sh SYNOPSIS +.Nm grep +.Bk -words +.Op Fl abcEFGHhIiLlnoqRsUVvwxZ +.Op Fl A Ar num +.Op Fl B Ar num +.Op Fl C Ns Op Ar num +.Op Fl e Ar pattern +.Op Fl f Ar file +.Op Fl m Ar num +.Op Fl -binary-files Ns = Ns Ar value +.Op Fl -context Ns Op = Ns Ar num +.Op Fl -label Ns = Ns Ar name +.Op Fl -line-buffered +.Op Fl -null +.Op Ar pattern +.Op Ar +.Ek +.Sh DESCRIPTION +The +.Nm grep +utility searches any given input files, +selecting lines that match one or more patterns. +By default, a pattern matches an input line if the regular expression +(RE) in the pattern matches the input line +without its trailing newline. +An empty expression matches every line. +Each input line that matches at least one of the patterns is written +to the standard output. +If no file arguments are specified, the standard input is used. +.Pp +.Nm grep +is used for simple patterns and +basic regular expressions +.Pq BREs ; +.Nm egrep +can handle extended regular expressions +.Pq EREs . +See +.Xr re_format 7 +for more information on regular expressions. +.Nm fgrep +is quicker than both +.Nm grep +and +.Nm egrep , +but can only handle fixed patterns +(i.e. it does not interpret regular expressions). +Patterns may consist of one or more lines, +allowing any of the pattern lines to match a portion of the input. +.Pp +.Nm zgrep , +.Nm zegrep , +and +.Nm zfgrep +act like +.Nm grep , +.Nm egrep , +and +.Nm fgrep , +respectively, but accept input files compressed with the +.Xr compress 1 +or +.Xr gzip 1 +compression utilities. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl A Ar num +Print +.Ar num +lines of trailing context after each match. +See also the +.Fl B +and +.Fl C +options. +.It Fl a +Treat all files as ASCII text. +Normally +.Nm +will simply print +.Dq Binary file ... matches +if files contain binary characters. +Use of this option forces +.Nm +to output lines matching the specified pattern. +.It Fl B Ar num +Print +.Ar num +lines of leading context before each match. +See also the +.Fl A +and +.Fl C +options. +.It Fl b +Each output line is preceded by its position (in bytes) in the file. +If option +.Fl o +is also specified, the position of the matched pattern is displayed. +.It Fl C Ns Oo Ar num Oc , Fl -context Ns Op = Ns Ar num +Print +.Ar num +lines of leading and trailing context surrounding each match. +The default is 2 and is equivalent to +.Fl A +.Ar 2 +.Fl B +.Ar 2 . +Note: +no whitespace may be given between the option and its argument. +.It Fl c +Only a count of selected lines is written to standard output. +.It Fl E +Interpret +.Ar pattern +as an extended regular expression +(i.e. force +.Nm grep +to behave as +.Nm egrep ) . +.It Fl e Ar pattern +Specify a pattern used during the search of the input: +an input line is selected if it matches any of the specified patterns. +This option is most useful when multiple +.Fl e +options are used to specify multiple patterns, +or when a pattern begins with a dash +.Pq Sq - . +.It Fl F +Interpret +.Ar pattern +as a set of fixed strings +(i.e. force +.Nm grep +to behave as +.Nm fgrep ) . +.It Fl f Ar file +Read one or more newline separated patterns from +.Ar file . +Empty pattern lines match every input line. +Newlines are not considered part of a pattern. +If +.Ar file +is empty, nothing is matched. +.It Fl G +Interpret +.Ar pattern +as a basic regular expression +(i.e. force +.Nm grep +to behave as traditional +.Nm grep ) . +.It Fl H +Always print filename headers +.Pq i.e. filenames +with output lines. +.It Fl h +Never print filename headers +.Pq i.e. filenames +with output lines. +.It Fl I +Ignore binary files. +.It Fl i +Perform case insensitive matching. +By default, +.Nm grep +is case sensitive. +.It Fl L +Only the names of files not containing selected lines are written to +standard output. +Pathnames are listed once per file searched. +If the standard input is searched, the string +.Dq (standard input) +is written. +.It Fl l +Only the names of files containing selected lines are written to +standard output. +.Nm grep +will only search a file until a match has been found, +making searches potentially less expensive. +Pathnames are listed once per file searched. +If the standard input is searched, the string +.Dq (standard input) +is written. +.It Fl m Ar num +Stop after finding at least one match on +.Ar num +different lines. +.It Fl n +Each output line is preceded by its relative line number in the file, +starting at line 1. +The line number counter is reset for each file processed. +This option is ignored if +.Fl c , +.Fl L , +.Fl l , +or +.Fl q +is +specified. +.It Fl o +Print each match, but only the match, not the entire line. +.It Fl q +Quiet mode: +suppress normal output. +.Nm grep +will only search a file until a match has been found, +making searches potentially less expensive. +.It Fl R +Recursively search subdirectories listed. +If no +.Ar file +is given, +.Nm +searches the current working directory. +.It Fl s +Silent mode. +Nonexistent and unreadable files are ignored +(i.e. their error messages are suppressed). +.It Fl U +Search binary files, but do not attempt to print them. +.It Fl V +Display version information. +All other options are ignored. +.It Fl v +Selected lines are those +.Em not +matching any of the specified patterns. +.It Fl w +The expression is searched for as a word (as if surrounded by +.Sq [[:<:]] +and +.Sq [[:>:]] ; +see +.Xr re_format 7 ) . +.It Fl x +Only input lines selected against an entire fixed string or regular +expression are considered to be matching lines. +.It Fl Z +Force +.Nm grep +to behave as +.Nm zgrep . +.It Fl -binary-files Ns = Ns Ar value +Controls searching and printing of binary files. +Options are +.Ar binary , +the default: search binary files but do not print them; +.Ar without-match : +do not search binary files; +and +.Ar text : +treat all files as text. +.It Fl -label Ns = Ns Ar name +Print +.Ar name +instead of the filename before lines. +.It Fl -line-buffered +Force output to be line buffered. +By default, output is line buffered when standard output is a terminal +and block buffered otherwise. +.It Fl -null +Output a zero byte instead of the character that normally follows a +file name. +This option makes the output unambiguous, even in the presence of file +names containing unusual characters like newlines. +This is similar to the +.Fl print0 +primary in +.Xr find 1 . +.El +.Sh EXIT STATUS +The +.Nm grep +utility exits with one of the following values: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Li 0 +One or more lines were selected. +.It Li 1 +No lines were selected. +.It Li >1 +An error occurred. +.El +.Sh EXAMPLES +To find all occurrences of the word +.Sq patricia +in a file: +.Pp +.Dl $ grep 'patricia' myfile +.Pp +To find all occurrences of the pattern +.Ql .Pp +at the beginning of a line: +.Pp +.Dl $ grep '^\e.Pp' myfile +.Pp +The apostrophes ensure the entire expression is evaluated by +.Nm grep +instead of by the user's shell. +The caret +.Ql ^ +matches the null string at the beginning of a line, +and the +.Ql \e +escapes the +.Ql \&. , +which would otherwise match any character. +.Pp +To find all lines in a file which do not contain the words +.Sq foo +or +.Sq bar : +.Pp +.Dl $ grep -v -e 'foo' -e 'bar' myfile +.Pp +A simple example of an extended regular expression: +.Pp +.Dl $ egrep '19|20|25' calendar +.Pp +Peruses the file +.Sq calendar +looking for either 19, 20, or 25. +.Sh SEE ALSO +.Xr ed 1 , +.Xr ex 1 , +.Xr gzip 1 , +.Xr sed 1 , +.Xr re_format 7 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl AaBbCGHhILmoRUVwZ +are extensions to that specification, and the behaviour of the +.Fl f +flag when used with an empty pattern file is left undefined. +.Pp +All long options are provided for compatibility with +GNU versions of this utility. +.Pp +Historic versions of the +.Nm grep +utility also supported the flags +.Op Fl ruy . +This implementation supports those options; +however, their use is strongly discouraged. +.Sh HISTORY +The +.Nm grep +command first appeared in +.At v4 . diff --git a/man/test_files/mdoc/id.1 b/man/test_files/mdoc/id.1 new file mode 100644 index 00000000..6da665d4 --- /dev/null +++ b/man/test_files/mdoc/id.1 @@ -0,0 +1,164 @@ +.\" $OpenBSD: id.1,v 1.21 2022/07/25 02:25:55 jsg Exp $ +.\" $NetBSD: id.1,v 1.5 1995/09/28 08:05:40 perry Exp $ +.\" +.\" Copyright (c) 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)id.1 8.2 (Berkeley) 5/5/94 +.\" +.Dd $Mdocdate: July 25 2022 $ +.Dt ID 1 +.Os +.Sh NAME +.Nm id +.Nd return user identity +.Sh SYNOPSIS +.Nm id +.Op Ar user +.Nm id +.Fl c +.Op Ar user +.Nm id +.Fl G Op Fl n +.Op Ar user +.Nm id +.Fl g Op Fl nr +.Op Ar user +.Nm id +.Fl p +.Op Ar user +.Nm id +.Fl R +.Nm id +.Fl u Op Fl nr +.Op Ar user +.Sh DESCRIPTION +The +.Nm +utility displays the user and group names and numeric IDs, of the +calling process, to the standard output. +If the real and effective IDs are different, both are displayed, +otherwise only the real ID is displayed. +.Pp +If a +.Ar user +(login name or user ID) +is specified, the user and group IDs of that user are displayed. +In this case, the real and effective IDs are assumed to be the same. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +Display the login class of the real user ID or the specified +.Ar user . +.It Fl G +Display the different group IDs (effective, real and supplementary) +as whitespace separated numbers, in no particular order. +.It Fl g +Display the effective group ID as a number. +.It Fl n +Display the name of the user or group ID for the +.Fl G , +.Fl g +and +.Fl u +options instead of the number. +If any of the ID numbers cannot be mapped into names, the number will be +displayed as usual. +.It Fl p +Make the output human-readable. +If the user name returned by +.Xr getlogin 2 +is different from the login name referenced by the user ID, the name +returned by +.Xr getlogin 2 +is displayed, preceded by the keyword +.Dq login . +The user ID as a name is displayed, preceded by the keyword +.Dq uid . +If the effective user ID is different from the real user ID, the real user +ID is displayed as a name, preceded by the keyword +.Dq euid . +If the effective group ID is different from the real group ID, the real group +ID is displayed as a name, preceded by the keyword +.Dq rgid . +The list of groups to which the user belongs is then displayed as names, +preceded by the keyword +.Dq groups . +If there is a login class specified for the user in the +.Xr passwd 5 +database, it is displayed, preceded by the keyword +.Dq class . +Each display is on a separate line. +.It Fl R +Display the routing table of the current process. +.It Fl r +Display the real ID for the +.Fl g +and +.Fl u +options instead of the effective ID. +.It Fl u +Display the effective user ID as a number. +.El +.Sh EXIT STATUS +.Ex -std id +.Sh SEE ALSO +.Xr who 1 , +.Xr login.conf 5 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl cpR +are extensions to that specification. +.Sh HISTORY +The +historic +.Xr groups 1 +command is equivalent to +.Ic id Fl Gn Op Ar user . +.Pp +The +historic +.Xr whoami 1 +command is equivalent to +.Ic id Fl un . +.Pp +The +.Nm +command first appeared in +.At III +and was reimplemented for +.Bx 4.3 Net/2 . diff --git a/man/test_files/mdoc/ioctl.2 b/man/test_files/mdoc/ioctl.2 new file mode 100644 index 00000000..b9a084c2 --- /dev/null +++ b/man/test_files/mdoc/ioctl.2 @@ -0,0 +1,171 @@ +.\" $OpenBSD: ioctl.2,v 1.20 2022/09/11 06:38:11 jmc Exp $ +.\" $NetBSD: ioctl.2,v 1.5 1995/02/27 12:33:47 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ioctl.2 8.2 (Berkeley) 12/11/93 +.\" +.Dd $Mdocdate: September 11 2022 $ +.Dt IOCTL 2 +.Os +.Sh NAME +.Nm ioctl +.Nd control device +.Sh SYNOPSIS +.In sys/ioctl.h +.Ft int +.Fn ioctl "int d" "unsigned long request" "..." +.Sh DESCRIPTION +The +.Fn ioctl +function manipulates the underlying device parameters of special files. +In particular, many operating +characteristics of character special files (e.g., terminals) +may be controlled with +.Fn ioctl +requests. +.Pp +The argument +.Fa d +must be an open file descriptor. +The third argument is called +.Fa arg +and contains additional information needed by this device +to perform the requested function. +.Fa arg +is either an +.Vt int +or a pointer to a device-specific data structure, depending upon +the given +.Fa request . +.Pp +An +.Nm +.Fa request +has encoded in it whether the argument is an +.Dq in +parameter +or +.Dq out +parameter, and the size of the third argument +.Pq Fa arg +in bytes. +Macros and defines used in specifying an ioctl +.Fa request +are located in the file +.In sys/ioctl.h . +.Sh GENERIC IOCTLS +Some ioctls are applicable to any file descriptor. +These include: +.Bl -tag -width "xxxxxx" +.It Dv FIOCLEX +Set close-on-exec flag. +The file will be closed when +.Xr execve 2 +is invoked. +.It Dv FIONCLEX +Clear close-on-exec flag. +The file will remain open across +.Xr execve 2 . +.El +.Pp +Some generic ioctls are not implemented for all types of file +descriptors. +These include: +.Bl -tag -width "xxxxxx" +.It Dv FIONREAD Fa "int *" +Get the number of bytes that are immediately available for reading. +.It Dv FIONBIO Fa "int *" +Set non-blocking I/O mode if the argument is non-zero. +In non-blocking mode, +.Xr read 2 +or +.Xr write 2 +calls return \-1 and set +.Va errno +to +.Er EAGAIN +immediately when no data is available. +.It Dv FIOASYNC Fa "int *" +Set asynchronous I/O mode if the argument is non-zero. +In asynchronous mode, the process or process group specified by +.Dv FIOSETOWN +will start receiving +.Dv SIGIO +signals when data is available. +The +.Dv SIGIO +signal will be delivered when data is available on the file +descriptor. +.It Dv FIOSETOWN, FIOGETOWN Fa "int *" +Set/get the process or the process group (if negative) that should receive +.Dv SIGIO +signals when data is available. +.El +.Sh RETURN VALUES +If an error has occurred, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn ioctl +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa d +is not a valid descriptor. +.It Bq Er ENOTTY +.Fa d +is not associated with a character +special device. +.It Bq Er ENOTTY +The specified request does not apply to the kind +of object that the descriptor +.Fa d +references. +.It Bq Er EINVAL +.Fa request +or +.Fa arg +is not valid. +.It Bq Er EFAULT +.Fa arg +points outside the process's allocated address space. +.El +.Sh SEE ALSO +.Xr cdio 1 , +.Xr chio 1 , +.Xr mt 1 , +.Xr execve 2 , +.Xr fcntl 2 , +.Xr intro 4 , +.Xr tty 4 +.Sh HISTORY +An +.Fn ioctl +function call appeared in +.At v7 . diff --git a/man/test_files/mdoc/ipcs.1 b/man/test_files/mdoc/ipcs.1 new file mode 100644 index 00000000..635d6828 --- /dev/null +++ b/man/test_files/mdoc/ipcs.1 @@ -0,0 +1,149 @@ +.\" $OpenBSD: ipcs.1,v 1.23 2014/03/22 08:02:03 jmc Exp $ +.\" +.\" Copyright (c) 1994 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 22 2014 $ +.Dt IPCS 1 +.Os +.Sh NAME +.Nm ipcs +.Nd report System V interprocess communication facilities status +.Sh SYNOPSIS +.Nm ipcs +.Op Fl abcMmopQqSsTt +.Op Fl C Ar core +.Op Fl N Ar system +.Sh DESCRIPTION +The +.Nm +program provides information on System V interprocess communication +(IPC) facilities on the system. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Show the maximum amount of information possible when +displaying active semaphores, message queues, +and shared memory segments. +(This is shorthand for specifying the +.Fl b , +.Fl c , +.Fl o , +.Fl p , +and +.Fl t +options.) +.It Fl b +Show the maximum allowed sizes for active semaphores, message queues, +and shared memory segments. +The +.Dq maximum allowed size +is the maximum number of bytes in a message on a message queue, +the size of a shared memory segment, +or the number of semaphores in a set of semaphores. +.It Fl C Ar core +Extract values associated with the name list from the specified +core instead of the running kernel. +.It Fl c +Show the creator's name and group for active semaphores, message queues, +and shared memory segments. +.It Fl M +Display system information about shared memory. +.It Fl m +Display information about active shared memory segments. +.It Fl N Ar system +Extract the name list from the specified system instead of the running kernel. +.It Fl o +Show outstanding usage for active message queues, +and shared memory segments. +The +.Dq outstanding usage +is the number of messages in a message queue, or the number +of processes attached to a shared memory segment. +.It Fl p +Show the process ID information for active semaphores, message queues, +and shared memory segments. +The +.Dq process ID information +is the last process to send a message to or receive a message from +a message queue, +the process that created a semaphore, or the last process to attach +or detach a shared memory segment. +.It Fl Q +Display system information about messages queues. +.It Fl q +Display information about active message queues. +.It Fl S +Display system information about semaphores. +.It Fl s +Display information about active semaphores. +.It Fl T +Display system information about shared memory, message queues and semaphores. +.It Fl t +Show access times for active semaphores, message queues, +and shared memory segments. +The access times is the time +of the last control operation on an IPC object, +the last send or receive of a message, +the last attach or detach of a shared memory segment, +or the last operation on a semaphore. +.El +.Pp +If none of the +.Fl M , +.Fl m , +.Fl Q , +.Fl q , +.Fl S , +or +.Fl s +options are specified, information about all active IPC facilities is +listed. +.Sh RESTRICTIONS +System data structures may change while +.Nm +is running; the output of +.Nm +is not guaranteed to be consistent. +.Sh EXIT STATUS +.Ex -std ipcs +.Sh SEE ALSO +.Xr ipcrm 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +X/Open System Interfaces option of the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl CMNQST +are extensions to that specification. +.Sh AUTHORS +.An Thorsten Lockert Aq Mt tholo@sigmasoft.com +.Sh BUGS +This manual page is woefully incomplete, because it does not +at all attempt to explain the information printed by +.Nm ipcs . diff --git a/man/test_files/mdoc/ktrace.2 b/man/test_files/mdoc/ktrace.2 new file mode 100644 index 00000000..75bd466e --- /dev/null +++ b/man/test_files/mdoc/ktrace.2 @@ -0,0 +1,232 @@ +.\" $OpenBSD: ktrace.2,v 1.43 2023/02/23 01:34:27 deraadt Exp $ +.\" $NetBSD: ktrace.2,v 1.2 1995/02/27 12:33:58 cgd Exp $ +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ktrace.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: February 23 2023 $ +.Dt KTRACE 2 +.Os +.Sh NAME +.Nm ktrace +.Nd process tracing +.Sh SYNOPSIS +.In sys/types.h +.In sys/ktrace.h +.Ft int +.Fn ktrace "const char *tracefile" "int ops" "int trpoints" "pid_t pid" +.Sh DESCRIPTION +The +.Fn ktrace +function enables or disables tracing of one or more processes. +Users may only trace their own processes. +Only the superuser can trace setuid or setgid programs. +This function is only available on kernels compiled with the +.Cm KTRACE +option. +.Pp +.Fa tracefile +gives the pathname of the file to be used for tracing. +The file must exist, be writable by the calling process, and +not be a symbolic link. +If tracing points are being disabled (see +.Dv KTROP_CLEAR +below), +.Fa tracefile +must be +.Dv NULL . +.Pp +Trace records are always appended to the file, ignoring the file offset, +so the caller will usually want to truncate the file before calling +these functions. +.Pp +The +.Fa ops +parameter specifies the requested ktrace operation. +The defined operations are: +.Pp +.Bl -tag -width KTRFLAG_DESCEND -offset indent -compact +.It Dv KTROP_SET +Enable trace points specified in +.Fa trpoints . +.It Dv KTROP_CLEAR +Disable trace points specified in +.Fa trpoints . +.It Dv KTROP_CLEARFILE +Stop all tracing to the trace file. +.It Dv KTRFLAG_DESCEND +The tracing change should apply to the +specified process and all its current children. +.El +.Pp +The +.Fa trpoints +parameter specifies the trace points of interest. +The defined trace points are: +.Pp +.Bl -tag -width KTRFAC_EXECARGS -offset indent -compact +.It Dv KTRFAC_SYSCALL +Trace system calls. +.It Dv KTRFAC_SYSRET +Trace return values from system calls. +.It Dv KTRFAC_NAMEI +Trace name lookup operations. +.It Dv KTRFAC_GENIO +Trace all I/O +(note that this option can generate much output). +.It Dv KTRFAC_PSIG +Trace posted signals. +.It Dv KTRFAC_STRUCT +Trace various structs. +.It Dv KTRFAC_USER +Trace user data coming from +.Xr utrace 2 +calls. +.It Dv KTRFAC_EXECARGS +Trace argument vector in +.Xr execve 2 +calls. +.It Dv KTRFAC_EXECENV +Trace environment vector in +.Xr execve 2 +calls. +.It Dv KTRFAC_PLEDGE +Trace violations of +.Xr pledge 2 +restrictions. +.It Dv KTRFAC_INHERIT +Inherit tracing to future children. +.El +.Pp +The +.Fa pid +parameter refers to a process ID. +If it is negative, +it refers to a process group ID. +.Pp +Each tracing event outputs a record composed of a generic header +followed by a trace point specific structure. +The generic header is: +.Bd -literal +struct ktr_header { + uint ktr_type; /* trace record type */ + pid_t ktr_pid; /* process id */ + pid_t ktr_tid; /* thread id */ + struct timespec ktr_time; /* timestamp */ + char ktr_comm[MAXCOMLEN+1]; /* command name */ + size_t ktr_len; /* length of buf */ +}; +.Ed +.Pp +The +.Fa ktr_len +field specifies the length of the +.Fa ktr_type +data that follows this header. +The +.Fa ktr_pid , ktr_tid , +and +.Fa ktr_comm +fields specify the process, thread, and command generating the record. +The +.Fa ktr_time +field gives the time (with nanosecond resolution) +that the record was generated. +.Pp +The generic header is followed by +.Fa ktr_len +bytes of a +.Fa ktr_type +record. +The type specific records are defined in the +.In sys/ktrace.h +include file. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn ktrace +will fail if: +.Bl -tag -width EINVALAA +.It Bq Er EINVAL +No trace points were selected. +.It Bq Er EPERM +The tracing process is not the superuser and either its effective +user ID does not match the real user ID of the receiving process, +its effective group ID does not match the real group ID of the +receiving process, +the receiving process is currently being traced by the superuser, +or the receiving process has changed its UIDs or GIDs. +When tracing multiple processes, +this error is returned if none of the targeted processes could be traced. +When clearing a trace file with +.Dv KTROP_CLEARFILE , +this error is returned if it could not stop tracing any of the processes +tracing to the file. +.It Bq Er ESRCH +No process can be found corresponding to that specified by +.Fa pid . +.It Bq Er EACCES +The named file is a device or FIFO. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Pp +Additionally, +.Fn ktrace +will fail if: +.Bl -tag -width ENAMETOOLONGAA +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named tracefile does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix or the +path refers to a symbolic link. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EFAULT +.Fa tracefile +points outside the process's allocated address space. +.El +.Sh SEE ALSO +.Xr kdump 1 , +.Xr ktrace 1 , +.Xr utrace 2 +.Sh HISTORY +A +.Fn ktrace +function call first appeared in +.Bx 4.3 Reno . diff --git a/man/test_files/mdoc/lpq.1 b/man/test_files/mdoc/lpq.1 new file mode 100644 index 00000000..bc207d86 --- /dev/null +++ b/man/test_files/mdoc/lpq.1 @@ -0,0 +1,141 @@ +.\" $OpenBSD: lpq.1,v 1.13 2020/04/23 21:28:10 jmc Exp $ +.\" $NetBSD: lpq.1,v 1.11 2002/01/19 03:23:11 wiz Exp $ +.\" +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lpq.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd $Mdocdate: April 23 2020 $ +.Dt LPQ 1 +.Os +.Sh NAME +.Nm lpq +.Nd spool queue examination program +.Sh SYNOPSIS +.Nm lpq +.Op Fl al +.Op Fl P Ns Ar printer +.Op Ar job# ... +.Op Ar user ... +.Sh DESCRIPTION +.Nm lpq +examines the spooling area used by +.Xr lpd 8 +for printing files on the line printer, and reports the status of the +specified jobs or all jobs associated with a user. +.Nm +invoked +without any arguments reports on any jobs currently in the queue. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Report on the local queues for all printers, +rather than just the specified printer. +.It Fl l +Information about each of the files comprising the job entry +is printed. +Normally, only as much information as will fit on one line is displayed. +.It Fl P Ns Ar printer +Specify a particular printer, otherwise the default +line printer is used (or the value of the +.Ev PRINTER +variable in the +environment). +All other arguments supplied are interpreted as user +names or job numbers to filter out only those jobs of interest. +.El +.Pp +For each job submitted (i.e., invocation of +.Xr lpr 1 ) +.Nm +reports the user's name, current rank in the queue, the +names of files comprising the job, the job identifier (a number which +may be supplied to +.Xr lprm 1 +for removing a specific job), and the total size in bytes. +Job ordering is dependent on +the algorithm used to scan the spooling directory and is supposed +to be +.Tn FIFO +(First In First Out). +File names comprising a job may be unavailable +(when +.Xr lpr 1 +is used as a sink in a pipeline) in which case the file is indicated as +.Dq (standard input) . +.Pp +If +.Nm +warns that there is no daemon present (i.e., due to some malfunction), the +.Xr lpc 8 +command can be used to restart the printer daemon. +.Sh ENVIRONMENT +If the following environment variables exist, they are used by +.Nm lpq : +.Bl -tag -width PRINTER +.It Ev COLUMNS +If set to a positive integer, +output is formatted to the given width in columns. +Otherwise, +.Nm +defaults to the terminal width, or 80 columns if the output is not a terminal. +.It Ev PRINTER +Specifies an alternate default printer. +.El +.Sh FILES +.Bl -tag -width "/var/spool/output/*/lock" -compact +.It Pa /etc/printcap +To determine printer characteristics. +.It Pa /var/spool/* +The spooling directory, as determined from printcap. +.It Pa /var/spool/output/*/cf* +Control files specifying jobs. +.It Pa /var/spool/output/*/lock +The lock file to obtain the currently active job. +.El +.Sh DIAGNOSTICS +Unable to open various files. +The lock file being malformed. +Garbage files when there is no daemon active, but files in the +spooling directory. +.Sh SEE ALSO +.Xr lpr 1 , +.Xr lprm 1 , +.Xr lpc 8 , +.Xr lpd 8 +.Sh HISTORY +.Nm lpq +appeared in +.Bx 3 . +.Sh BUGS +Due to the dynamic nature of the information in the spooling directory, +.Nm +may report unreliably. +Output formatting is sensitive to the line length of the terminal; +this can result in widely spaced columns. diff --git a/man/test_files/mdoc/mg.1 b/man/test_files/mdoc/mg.1 new file mode 100644 index 00000000..ab42a8f2 --- /dev/null +++ b/man/test_files/mdoc/mg.1 @@ -0,0 +1,1203 @@ +.\" $OpenBSD: mg.1,v 1.139 2024/07/10 05:19:02 jmc Exp $ +.\" This file is in the public domain. +.\" +.Dd $Mdocdate: July 10 2024 $ +.Dt MG 1 +.Os +.Sh NAME +.Nm mg +.Nd emacs-like text editor +.Sh SYNOPSIS +.Nm mg +.Op Fl nR +.Op Fl b Ar file +.Op Fl f Ar mode +.Op Fl u Ar file +.Op + Ns Ar number +.Op Ar +.Sh DESCRIPTION +.Nm +is intended to be a small, fast, and portable editor for +people who can't (or don't want to) run emacs for one +reason or another, or are not familiar with the +.Xr vi 1 +editor. +It is compatible with emacs because there shouldn't +be any reason to learn more editor types than emacs or +.Xr vi 1 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It + Ns Ar number +Go to the line specified by number (do not insert +a space between the +.Sq + +sign and the number). +If a negative number is specified, the line number counts +backwards from the end of the file i.e. +-1 will be the last +line of the file, +-2 will be second last, and so on. +.It Fl b Ar file +Turn on batch mode and execute the +.Nm +commands found in the specified +.Ar file +and then terminate. +.It Fl f Ar mode +Run the +.Ar mode +command for all buffers created from +arguments on the command line, including the +scratch buffer and all files. +.It Fl n +Turn off backup file generation. +.It Fl R +Files specified on the command line will be opened read-only. +.It Fl u Ar file +Use +.Ar file +as the startup file, instead of the default +.Pa ~/.mg . +.El +.Sh WINDOWS AND BUFFERS +When a file is loaded into +.Nm , +it is stored in a +.Em buffer . +This buffer may be displayed on the screen in more than one window. +At present, windows may only be split horizontally, so each window is +delineated by a modeline at the bottom. +If changes are made to a buffer, it will be reflected in all open windows. +.Pp +If a file is changed outside +.Nm +and its buffer is about to be changed, +.Nm +prompts if the change should go ahead (y), not go ahead (n) or if the buffer +should be reverted (r) to the latest file on disk. +.Pp +If a buffer name begins and ends with an asterisk, the buffer is considered +throwaway; i.e. the user will not be prompted to save changes when +the buffer is killed. +.Sh POINT AND MARK +The current cursor location in +.Nm +is called the +.Em point +(or +.Em dot ) . +It is possible to define a window-specific region of text by setting a second +location, called the +.Em mark . +The +.Em region +is the text between point and mark inclusive. +Deleting the character at the mark position leaves +the mark at the point of deletion. +.Pp +Note: The point and mark are window-specific in +.Nm , +not buffer-specific, as in other emacs flavours. +.Sh BACKUP FILES +Backup files have a +.Sq ~ +character appended to the file name and +are created in the current working directory by default. +Whether to create backup files or not can be toggled with the +.Ic make-backup-files +command. +The backup file location can either be in the current +working directory, or all backups can be moved to a +.Pa ~/.mg.d +directory where files retain their path name to retain uniqueness. +Use the +.Ic backup-to-home-directory +command to alternate between these two locations. +Further, if any application creates backup files in +.Pa /tmp , +these can be left with the +.Ic leave-tmpdir-backups +command. +.Sh TAGS +.Nm +supports tag files created by +.Xr ctags 1 , +allowing the user to quickly locate various object definitions. +Note though that emacs uses etags, not ctags. +.Sh CSCOPE +.Nm +supports navigating source code using cscope. +However, +.Nm +requires cscope and cscope-indexer executables to be present in +.Ev PATH +for it to work. +.Sh DEFAULT KEY BINDINGS +Normal editing commands are very similar to GNU Emacs. +In the following examples, C-x means Control-x, and M-x means Meta-x, +where the Meta key may be either a special key on the keyboard +or the ALT key; otherwise ESC followed by the key X works as well. +.Pp +.Bl -tag -width xxxxxxxxxxxx -offset indent -compact +.It C-SPC +set-mark-command +.It C-a +beginning-of-line +.It C-b +backward-char +.It C-c s c +cscope-find-functions-calling-this-function +.It C-c s d +cscope-find-global-definition +.It C-c s e +cscope-find-egrep-pattern +.It C-c s f +cscope-find-this-file +.It C-c s i +cscope-find-files-including-file +.It C-c s n +cscope-next-symbol +.It C-c s p +cscope-prev-symbol +.It C-c s s +cscope-find-this-symbol +.It C-c s t +cscope-find-this-text-string +.It C-d +delete-char +.It C-e +end-of-line +.It C-f +forward-char +.It C-g +keyboard-quit +.It C-h C-h +help-help +.It C-h a +apropos +.It C-h b +describe-bindings +.It C-h c +describe-key-briefly +.It C-j +newline-and-indent +.It C-k +kill-line +.It C-l +recenter +.It RET +newline +.It C-n +next-line +.It C-o +open-line +.It C-p +previous-line +.It C-q +quoted-insert +.It C-r +isearch-backward +.It C-s +isearch-forward +.It C-t +transpose-chars +.It C-u +universal-argument +.It C-v +scroll-up +.It C-w +kill-region +.It C-x C-b +list-buffers +.It C-x C-c +save-buffers-kill-emacs +.It C-x C-f +find-file +.It C-x C-j +dired-jump +.It C-x C-g +keyboard-quit +.It C-x C-l +downcase-region +.It C-x C-o +delete-blank-lines +.It C-x C-q +toggle-read-only +.It C-x C-r +find-file-read-only +.It C-x C-s +save-buffer +.It C-x C-u +upcase-region +.It C-x C-v +find-alternate-file +.It C-x C-w +write-file +.It C-x C-x +exchange-point-and-mark +.It C-x ( +start-kbd-macro +.It C-x \&) +end-kbd-macro +.It C-x 0 +delete-window +.It C-x 1 +delete-other-windows +.It C-x 2 +split-window-vertically +.It C-x 4 C-f +find-file-other-window +.It C-x 4 C-g +keyboard-quit +.It C-x 4 b +switch-to-buffer-other-window +.It C-x 4 f +find-file-other-window +.It C-x = +what-cursor-position +.It C-x ^ +enlarge-window +.It C-x ` +next-error +.It C-x b +switch-to-buffer +.It C-x d +dired +.It C-x e +call-last-kbd-macro +.It C-x f +set-fill-column +.It C-x g +goto-line +.It C-x h +mark-whole-buffer +.It C-x i +insert-file +.It C-x k +kill-buffer +.It C-x n +other-window +.It C-x o +other-window +.It C-x p +previous-window +.It C-x s +save-some-buffers +.It C-x u +undo +.It C-y +yank +.It C-z +suspend-emacs +.It M-C-v +scroll-other-window +.It M-SPC +just-one-space +.It M-! +shell-command +.It M-. +find-tag +.It M-* +pop-tag-mark +.It M-% +query-replace +.It M-< +beginning-of-buffer +.It M-> +end-of-buffer +.It M-\e +delete-horizontal-space +.It M-^ +join-line +.It M-b +backward-word +.It M-c +capitalize-word +.It M-d +kill-word +.It M-f +forward-word +.It M-h +mark-paragraph +.It M-l +downcase-word +.It M-m +back-to-indentation +.It M-q +fill-paragraph +.It M-r +search-backward +.It M-s +search-forward +.It M-t +transpose-words +.It M-u +upcase-word +.It M-v +scroll-down +.It M-w +copy-region-as-kill +.It M-x +execute-extended-command +.It M-z +zap-to-char +.It M-{ +backward-paragraph +.It M-| +shell-command-on-region +.It M-} +forward-paragraph +.It M-~ +not-modified +.It M-DEL +backward-kill-word +.It C-_ +undo +.It ) +blink-and-insert +.It DEL +delete-backward-char +.El +.Pp +For a complete description of +.Nm +commands, see +.Sx MG COMMANDS . +To see the active keybindings at any time, type +.Dq M-x describe-bindings . +.Sh MG COMMANDS +Commands are invoked by +.Dq M-x , +or by binding to a key. +Many commands take an optional numerical parameter, +.Va n . +This parameter is set either by +M- (where +.Va n +is the numerical argument) before the command, or by +one or more invocations of the universal argument, usually bound to C-u. +When invoked in this manner, the value of the numeric parameter to +be passed is displayed in the minibuffer before the M-x. +One common use of the parameter is in mode toggles (e.g.\& +make-backup-files). +If no parameter is supplied, the mode is toggled to its +alternate state. +If a positive parameter is supplied, the mode is forced to on. +Otherwise, it is forced to off. +.\" +.Bl -tag -width xxxxx +.It Ic apropos +Help Apropos. +Prompt the user for a string, open the *help* buffer, +and list all +.Nm +commands that contain that string. +.It Ic audible-bell +Toggle the audible system bell. +.It Ic auto-execute +Register an auto-execute hook; that is, specify a filename pattern +(conforming to the shell's filename globbing rules) and an associated +function to execute when a file matching the specified pattern +is read into a buffer. +.It Ic auto-fill-mode +Toggle auto-fill mode (sometimes called mail-mode) in the current buffer, +where text inserted past the fill column is automatically wrapped +to a new line. +Can be set globally with +.Ic set-default-mode . +.It Ic auto-indent-mode +Toggle indent mode in the current buffer, +where indentation is preserved after a newline. +Can be set globally with +.Ic set-default-mode . +.It Ic back-to-indentation +Move the dot to the first non-whitespace character on the current line. +.It Ic backup-to-home-directory +Save backup copies to a +.Pa ~/.mg.d +directory instead of working directory. +Requires +.Ic make-backup-files +to be on. +.It Ic backward-char +Move cursor backwards one character. +.It Ic backward-kill-word +Kill text backwards by +.Va n +words. +.It Ic backward-paragraph +Move cursor backwards +.Va n +paragraphs. +Paragraphs are delimited by or or . +.It Ic backward-word +Move cursor backwards by the specified number of words. +.It Ic beginning-of-buffer +Move cursor to the top of the buffer. +If set, keep mark's position, otherwise set at current position. +A numeric argument +.Va n +will move n/10th of the way from the top. +.It Ic beginning-of-line +Move cursor to the beginning of the line. +.It Ic blink-and-insert +Self-insert a character, then search backwards and blink its +matching delimiter. +For delimiters other than +parenthesis, brackets, and braces, the character itself +is used as its own match. +Can be used in the startup file with the +.Ic global-set-key +command. +.It Ic bsmap-mode +Toggle bsmap mode, where DEL and C-h are swapped. +.It Ic c-mode +Toggle a KNF-compliant mode for editing C program files. +.It Ic call-last-kbd-macro +Invoke the keyboard macro. +.It Ic capitalize-word +Capitalize +.Va n +words; i.e. convert the first character of the word to +upper case, and subsequent letters to lower case. +.It Ic cd +Change the global working directory. +See also +.Ic global-wd-mode . +.It Ic column-number-mode +Toggle whether the column number is displayed in the modeline. +.It Ic copy-region-as-kill +Copy all of the characters in the region to the kill buffer, +clearing the mark afterwards. +This is a bit like a +.Ic kill-region +followed by a +.Ic yank . +.It Ic count-matches +Count the number of lines matching the supplied regular expression. +.It Ic count-non-matches +Count the number of lines not matching the supplied regular expression. +.It Ic cscope-find-this-symbol +List the matches for the given symbol. +.It Ic cscope-find-global-definition +List global definitions for the given literal. +.It Ic cscope-find-called-functions +List functions called from the given function. +.It Ic cscope-find-functions-calling-this-function +List functions calling the given function. +.It Ic cscope-find-this-text-string +List locations matching the given text string. +.It Ic cscope-find-egrep-pattern +List locations matching the given extended regular expression pattern. +.It Ic cscope-find-this-file +List filenames matching the given filename. +.It Ic cscope-find-files-including-file +List files that #include the given filename. +.It Ic cscope-next-symbol +Navigate to the next match. +.It Ic cscope-prev-symbol +Navigate to the previous match. +.It Ic cscope-next-file +Navigate to the next file. +.It Ic cscope-prev-file +Navigate to the previous file. +.It Ic cscope-create-list-of-files-to-index +Create cscope's List and Index in the given directory. +.It Ic define-key +Prompts the user for a named keymap (mode), +a key, and an +.Nm +command, then creates a keybinding in the appropriate +map. +.It Ic delete-backward-char +Delete backwards +.Va n +characters. +Like +.Ic delete-char , +this actually does a kill if presented +with an argument. +.It Ic delete-blank-lines +Delete blank lines around dot. +If dot is sitting on a blank line, this command +deletes all the blank lines above and below the current line. +Otherwise, it deletes all of the blank lines after the current line. +.It Ic delete-char +Delete +.Va n +characters forward. +If any argument is present, it kills rather than deletes, +saving the result in the kill buffer. +.It Ic delete-horizontal-space +Delete any whitespace around the dot. +.It Ic delete-leading-space +Delete leading whitespace on the current line. +.It Ic delete-trailing-space +Delete trailing whitespace on the current line. +.It Ic delete-matching-lines +Delete all lines after dot that contain a string matching +the supplied regular expression. +.It Ic delete-non-matching-lines +Delete all lines after dot that don't contain a string matching +the supplied regular expression. +.It Ic delete-other-windows +Make the current window the only window visible on the screen. +.It Ic delete-window +Delete current window. +.It Ic describe-bindings +List all global and local keybindings, putting the result in +the *help* buffer. +.It Ic describe-key-briefly +Read a key from the keyboard, and look it up in the keymap. +Display the name of the function currently bound to the key. +.It Ic diff-buffer-with-file +View the differences between buffer and its associated file. +.It Ic digit-argument +Process a numerical argument for keyboard-invoked functions. +.It Ic dired-jump +Open a dired buffer containing the current buffer's directory location. +.It Ic downcase-region +Set all characters in the region to lower case. +.It Ic downcase-word +Set characters to lower case, starting at the dot, and ending +.Va n +words away. +.It Ic emacs-version +Return an +.Nm +version string. +.It Ic end-kbd-macro +Stop defining a keyboard macro. +.It Ic end-of-buffer +Move cursor to the end of the buffer. +If set, keep mark's position, otherwise set at current position. +A numeric argument +.Va n +will move n/10th of the way from the end. +.It Ic end-of-line +Move cursor to the end of the line. +.It Ic enlarge-window +Enlarge the current window by shrinking either the window above +or below it. +.It Ic eval-current-buffer +Evaluate the current buffer as a series of +.Nm +commands. +Useful for testing +.Nm +startup files. +.It Ic eval-expression +Get one line from the user, and run it. +Useful for testing expressions in +.Nm +startup files. +.It Ic exchange-point-and-mark +Swap the values of "dot" and "mark" in the current window. +Return an error if no mark is set. +.It Ic execute-extended-command +Invoke an extended command; i.e. M-x. +Call the message line routine to read in the command name and apply +autocompletion to it. +When it comes back, look the name up in the symbol table and run the +command if it is found, passing arguments as necessary. +Print an error if there is anything wrong. +.It Ic fill-paragraph +Justify a paragraph, wrapping text at the current fill column. +.It Ic find-file +Select a file for editing. +First check if the file can be found +in another buffer; if it is there, just switch to that buffer. +If the file cannot be found, create a new buffer, read in the +file from disk, and switch to the new buffer. +.It Ic find-file-read-only +Same as +.Ic find-file , +except the new buffer is set to read-only. +.It Ic find-alternate-file +Replace the current file with an alternate one. +Semantics for finding the replacement file are the same as +.Ic find-file , +except the current buffer is killed before the switch. +If the kill fails, or is aborted, revert to the original file. +.It Ic find-file-other-window +Opens the specified file in a second buffer. +Splits the current window if necessary. +.It Ic find-tag +Jump to definition of tag at dot. +.It Ic forward-char +Move cursor forwards (or backwards, if +.Va n +is negative) +.Va n +characters. +Returns an error if the end of buffer is reached. +.It Ic forward-paragraph +Move forward +.Va n +paragraphs. +Paragraphs are delimited by or or . +.It Ic forward-word +Move the cursor forward by the specified number of words. +.It Ic global-set-key +Bind a key in the global (fundamental) key map. +.It Ic global-unset-key +Unbind a key from the global (fundamental) key map; i.e. set it to 'rescan'. +.It Ic global-wd-mode +Toggle global working-directory mode. +When enabled, +.Nm +defaults to opening files (and executing commands like +.Ic compile +and +.Ic grep ) +relative to the global working directory. +When disabled, a working directory is set for each buffer. +.It Ic goto-line +Go to a specific line. +If an argument is present, then +it is the line number, else prompt for a line number to use. +.It Ic help-help +Prompts for one of (a)propos, (b)indings, des(c)ribe key briefly. +.It Ic insert +Insert a string, mainly for use from macros. +.It Ic insert-buffer +Insert the contents of another buffer at dot. +.It Ic insert-file +Insert a file into the current buffer at dot. +.It Ic insert-with-wrap +Insert the bound character with word wrap. +Check to see if we're past the fill column, and if so, +justify this line. +.It Ic isearch-backward +Use incremental searching, initially in the reverse direction. +isearch ignores any explicit arguments. +If invoked during macro definition or evaluation, the non-incremental +.Ic search-backward +is invoked instead. +.It Ic isearch-forward +Use incremental searching, initially in the forward direction. +isearch ignores any explicit arguments. +If invoked during macro definition or evaluation, the non-incremental +.Ic search-forward +is invoked instead. +.It Ic join-line +Join the current line to the previous. +If called with an argument, +join the next line to the current one. +.It Ic just-one-space +Delete any whitespace around dot, then insert a space. +.It Ic keyboard-quit +Abort the current action. +.It Ic kill-buffer +Dispose of a buffer, by name. +If the buffer name does not start and end with an asterisk, +prompt the user if the buffer +has been changed. +.It Ic kill-line +Kill line. +If called without an argument, it kills from dot to the end +of the line, unless it is at the end of the line, when it kills the +newline. +If called with an argument of 0, it kills from the start of the +line to dot. +If called with a positive argument, it kills from dot +forward over that number of newlines. +If called with a negative argument +it kills any text before dot on the current line, then it kills back +abs(n) lines. +.It Ic kill-paragraph +Delete +.Va n +paragraphs starting with the current one. +.It Ic kill-region +Kill the currently defined region. +.It Ic kill-word +Delete forward +.Va n +words. +.It Ic leave-tmpdir-backups +Modifies the behaviour of +.Ic backup-to-home-directory . +Backup files that would normally reside in +.Pa /tmp +are left there and not moved to the +.Pa ~/.mg.d +directory. +.It Ic line-number-mode +Toggle whether the line number is displayed in the modeline. +.It Ic list-buffers +Display the list of available buffers. +The first column in the output indicates which buffer is active with a '>' +character. +The second column indicates which buffers are modified. +The third column indicates which buffers are read-only. +The remaining columns are self-explanatory. +.It Ic load +Prompt the user for a filename, and then execute commands +from that file. +.It Ic local-set-key +Bind a key mapping in the local (topmost) mode. +.It Ic local-unset-key +Unbind a key mapping in the local (topmost) mode. +.It Ic make-backup-files +Toggle generation of backup files. +Enabled by default. +.It Ic make-directory +Prompt the user for a path or directory name which is then created. +.It Ic mark-paragraph +Mark +.Va n +paragraphs. +.It Ic mark-whole-buffer +Marks whole buffer as a region by putting dot at the beginning and mark +at the end of buffer. +.It Ic meta-key-mode +When disabled, the meta key can be used to insert extended-ascii (8-bit) +characters. +When enabled, the meta key acts as usual. +.It Ic negative-argument +Process a negative argument for keyboard-invoked functions. +.It Ic newline +Insert a newline into the current buffer. +.It Ic newline-and-indent +Insert a newline, then enough tabs and spaces to duplicate the indentation +of the previous line, respecting +.Ic no-tab-mode +and the buffer tab width. +.It Ic next-line +Move forward +.Va n +lines. +.It Ic no-tab-mode +Toggle notab mode. +In this mode, spaces are inserted rather than tabs. +Can be set globally with +.Ic set-default-mode . +.It Ic not-modified +Turn off the modified flag in the current buffer. +.It Ic open-line +Open up some blank space. +Essentially, insert +.Va n +newlines, then back up over them. +.It Ic other-window +The command to make the next (down the screen) window the current +window. +There are no real errors, although the command does nothing if +there is only 1 window on the screen. +.It Ic overwrite-mode +Toggle overwrite mode in the current buffer, +where typing overwrites existing characters rather than inserting them. +Can be set globally with +.Ic set-default-mode . +.It Ic prefix-region +Inserts a prefix string before each line of a region. +The prefix string is settable by using +.Ic set-prefix-string +or by invoking this command with a prefix argument. +.It Ic previous-line +Move backwards +.Va n +lines. +.It Ic previous-window +This command makes the previous (up the screen) window the +current window. +There are no errors, although the command does not do +a lot if there is only 1 window. +.It Ic pop-tag-mark +Return to position where find-tag was previously invoked. +.It Ic push-shell +Suspend +.Nm +and switch to alternate screen, if available. +.It Ic pwd +Display current (global) working directory in the status area. +.It Ic query-replace +Query Replace. +Search and replace strings selectively, prompting after each match. +.It Ic replace-regexp +Replace regular expression globally without individual prompting. +.It Ic replace-string +Replace string globally without individual prompting. +.It Ic query-replace-regexp +Replace strings selectively. +Does a search and replace operation using regular +expressions for both patterns. +.It Ic quoted-insert +Insert the next character verbatim into the current buffer; i.e. ignore +any function bound to that key. +.It Ic re-search-again +Perform a regular expression search again, using the same search +string and direction as the last search command. +.It Ic re-search-backward +Search backwards using a regular expression. +Get a search string from the user, and search, starting at dot +and proceeding toward the front of the buffer. +If found, dot is left +pointing at the first character of the pattern [the last character that +was matched]. +.It Ic re-search-forward +Search forward using a regular expression. +Get a search string from the user and search for it starting at dot. +If found, move dot to just after the matched characters. +display does all +the hard stuff. +If not found, it just prints a message. +.It Ic recenter +Reposition dot in the current window. +By default, the dot is centered. +If given a positive argument (n), the display is repositioned to line +n. +If +.Va n +is negative, it is that line from the bottom. +.It Ic redraw-display +Refresh the display. +Recomputes all window sizes in case something has changed. +.It Ic revert-buffer +Revert the current buffer to the latest file on disk. +.It Ic save-buffer +Save the contents of the current buffer if it has been changed, +optionally creating a backup copy. +.It Ic save-buffers-kill-emacs +Offer to save modified buffers and quit +.Nm . +.It Ic save-some-buffers +Look through the list of buffers, offering to save any buffer that +has been changed. +Buffers that are not associated with files (such +as *scratch*, *grep*, *compile*) are ignored. +.It Ic scroll-down +Scroll backwards +.Va n +pages. +A two-line overlap between pages is +assumed. +If given a repeat argument, scrolls back lines, not pages. +.It Ic scroll-one-line-down +Scroll the display down +.Va n +lines without changing the cursor position. +.It Ic scroll-one-line-up +Scroll the display +.Va n +lines up without moving the cursor position. +.It Ic scroll-other-window +Scroll the next window in the window list window forward +.Va n +pages. +.It Ic scroll-up +Scroll forward one page. +A two-line overlap between pages is +assumed. +If given a repeat argument, scrolls back lines, not pages. +.It Ic search-again +Search again, using the same search string and direction as the last +search command. +.It Ic search-backward +Reverse search. +Get a search string from the user, and search, starting +at dot and proceeding toward the front of the buffer. +If found, dot is +left pointing at the first character of the pattern (the last character +that was matched). +.It Ic search-forward +Search forward. +Get a search string from the user, and search for it +starting at dot. +If found, dot gets moved to just after the matched +characters, if not found, print a message. +.It Ic self-insert-command +Insert a character. +.It Ic sentence-end-double-space +Toggle double or single spaces for end of sentences. +Double is the default. +Currently only affects fill-paragraph. +.It Ic set-case-fold-search +Set case-fold searching, causing case not to matter +in regular expression searches. +This is the default. +.It Ic set-case-replace +Preserve the case of the replaced string. +This is the default. +.It Ic set-default-mode +Append the supplied mode to the list of default modes +used by subsequent buffer creation. +Built in modes include: fill, indent, notab and overwrite. +.It Ic set-fill-column +Prompt the user for a fill column. +Used by +.Ic auto-fill-mode . +.It Ic set-mark-command +Sets the mark in the current window to the current dot location. +.It Ic set-prefix-string +Sets the prefix string to be used by the +.Ic prefix-region +command. +.It Ic set-tab-width +Set the tab width for the current buffer, or the default for new buffers +if called with a prefix argument or from the startup file. +.It Ic shell-command +Execute external command from mini-buffer. +With a universal argument it inserts the command output into the current +buffer. +.It Ic shell-command-on-region +Provide the text in region to the shell command as input. +With a universal argument it replaces the region with the command +output. +.It Ic shrink-window +Shrink current window by one line. +The window immediately below is expanded to pick up the slack. +If only one window is present, this command has no effect. +.It Ic space-to-tabstop +Insert enough spaces to reach the next tab-stop position. +By default, tab-stops occur every 8 characters. +.It Ic split-window-vertically +Split the current window. +A window smaller than 3 lines cannot be split. +.It Ic start-kbd-macro +Start defining a keyboard macro. +Macro definition is ended by invoking end-kbd-macro. +.It Ic suspend-emacs +Suspend +.Nm +and switch back to alternate screen, if in use. +.It Ic switch-to-buffer +Prompt and switch to a new buffer in the current window. +.It Ic switch-to-buffer-other-window +Switch to buffer in another window. +.It Ic toggle-read-only +Toggle the read-only flag on the current buffer. +.It Ic toggle-read-only-all +Toggle the read-only flag on all non-ephemeral buffers. +A simple toggle that switches a global read-only flag either on +or off. +.It Ic transpose-chars +Transpose the two characters in front of and under dot, +then move forward one character. +Treat newline characters the same as any other. +.It Ic transpose-paragraphs +Transpose adjacent paragraphs. +If multiple iterations are requested, the current paragraph will +be moved +.Va n +paragraphs forward. +.It Ic transpose-words +Transpose adjacent words. +.It Ic undo +Undo the most recent action. +If invoked again without an intervening command, +move the undo pointer to the previous action and undo it. +.It Ic undo-boundary +Add an undo boundary. +This is not usually done interactively. +.It Ic undo-boundary-toggle +Toggle whether undo boundaries are generated. +Undo boundaries are often disabled before operations that should +be considered atomically undoable. +.It Ic undo-enable +Toggle whether undo information is kept. +.It Ic undo-list +Show the undo records for the current buffer in a new buffer. +.It Ic universal-argument +Repeat the next command 4 times. +Usually bound to C-u. +This command may be stacked; e.g.\& +C-u C-u C-f moves the cursor forward 16 characters. +.It Ic upcase-region +Upper case region. +Change all of the lower case characters in the region to +upper case. +.It Ic upcase-word +Move the cursor forward by the specified number of words. +As it moves, convert any characters to upper case. +.It Ic visible-bell +Toggle the visible bell. +If this toggle is on, the modeline will flash. +.It Ic visit-tags-table +Load tags file to be used for subsequent +.Ic find-tag . +.It Ic what-cursor-position +Display a bunch of useful information about the current location of +dot. +The character under the cursor (in octal), the current line, row, +and column, and approximate position of the cursor in the file (as a +percentage) is displayed. +The column position assumes an infinite +position display; it does not truncate just because the screen does. +.It Ic write-file +Ask for a file name and write the contents of the current buffer to +that file. +Update the remembered file name and clear the buffer +changed flag. +.It Ic yank +Yank text from +.Ic kill-buffer . +Unlike emacs, the +.Nm +kill buffer consists only +of the most recent kill. +It is not a ring. +.It Ic zap-to-char +Ask for a character and delete text from the current cursor position +until the next instance of that character, including it. +.It Ic zap-up-to-char +Like +.Ic zap-to-char +but doesn't delete the target character. +.El +.Sh MG DIRED KEY BINDINGS +Specific key bindings are available in dired mode. +.Pp +.Bl -tag -width xxxxxxxxxxxxxxxxxx -offset indent -compact +.It DEL +dired-unmark-backward +.It RET, e, f and C-m +dired-find-file +.It SPC, n +dired-next-line +.It ! +dired-shell-command +.It + +dired-create-directory +.It ^ +dired-up-directory +.It a +dired-find-alternate-file +.It c +dired-do-copy +.It d and C-d +dired-flag-file-deletion +.It g +dired-revert +.It j +dired-goto-file +.It o +dired-find-file-other-window +.It p +dired-previous-line +.It q +quit-window +.It r +dired-do-rename +.It u +dired-unmark +.It x +dired-do-flagged-delete +.It C-v +dired-scroll-down +.It M-v +dired-scroll-up +.El +.Sh MG DIRED COMMANDS +The following are a list of the commands specific to dired mode: +.Bl -tag -width Ds +.It Ic dired-create-directory +Create a directory. +.It Ic dired-do-copy +Copy the file listed on the current line of the dired buffer. +.It Ic dired-do-flagged-delete +Delete the files that have been flagged for deletion. +.It Ic dired-do-rename +Rename the file listed on the current line of the dired buffer. +.It Ic dired-find-alternate-file +Replace the current dired buffer with an alternate one as specified +by the position of the cursor in the dired buffer. +.It Ic dired-find-file +Open the file on the current line of the dired buffer. +If the cursor is on a directory, it will be opened in dired mode. +.It Ic dired-flag-file-deletion +Flag the file listed on the current line for deletion. +This is indicated in the buffer by putting a D at the left margin. +No files are actually deleted until the function +.Ic dired-do-flagged-delete +is executed. +.It Ic dired-find-file-other-window +Open the file on the current line of the dired buffer in a +different window. +.It Ic dired-goto-file +Move the cursor to a file name in the dired buffer. +.It Ic dired-next-line +Move the cursor to the next line. +.It Ic dired-other-window +This function works just like dired, except that it puts the +dired buffer in another window. +.It Ic dired-previous-line +Move the cursor to the previous line. +.It Ic dired-revert +Refresh the dired buffer while retaining any flags. +.It Ic dired-scroll-down +Scroll down the dired buffer. +.It Ic dired-scroll-up +Scroll up the dired buffer. +.It Ic dired-shell-command +Pipe the file under the current cursor position through a shell command. +.It Ic dired-unmark +Remove the deletion flag for the file on the current line. +.It Ic dired-unmark-backward +Remove the deletion flag from the file listed on the previous line +of the dired buffer, then move up to that line. +.It Ic dired-up-directory +Open a dired buffer in the parent directory. +.It Ic quit-window +Close the current dired buffer. +.El +.Sh CONFIGURATION FILES +There are two configuration files, +.Pa .mg +and +.Pa .mg-TERM . +Here, +.Ev TERM +represents the name of the terminal type; e.g. if the terminal type +is set to +.Dq vt100 , +.Nm +will use +.Pa .mg-vt100 +as a startup file. +The terminal type startup file is used first. +.Pp +The startup file format is a list of commands, one per line, as used for +interactive evaluation. +Strings that are normally entered by the user at any subsequent prompts +may be specified after the command name; e.g.: +.Bd -literal -offset indent +global-set-key ")" self-insert-command +global-set-key "\e^x\e^f" find-file +global-set-key "\ee[Z" backward-char +set-default-mode fill +set-fill-column 72 +auto-execute *.c c-mode +.Ed +.Pp +Comments can be added to the startup files by placing +.Sq ;\& +or +.Sq # +as the first character of a line. +.Sh FILES +.Bl -tag -width /usr/share/doc/mg/tutorial -compact +.It Pa ~/.mg +normal startup file +.It Pa ~/.mg-TERM +terminal-specific startup file +.It Pa ~/.mg.d +alternative backup file location +.It Pa /usr/share/doc/mg/tutorial +concise tutorial +.El +.Sh SEE ALSO +.Xr ctags 1 , +.Xr vi 1 +.Sh CAVEATS +Since it is written completely in C, there is currently no +language in which extensions can be written; +however, keys can be rebound and certain parameters can be changed +in startup files. +.Pp +In order to use 8-bit characters (such as German umlauts), the Meta key +needs to be disabled via the +.Ic meta-key-mode +command. +.Pp +Multi-byte character sets, such as UTF-8, are not supported. diff --git a/man/test_files/mdoc/minherit.2 b/man/test_files/mdoc/minherit.2 new file mode 100644 index 00000000..e814a0a7 --- /dev/null +++ b/man/test_files/mdoc/minherit.2 @@ -0,0 +1,108 @@ +.\" $OpenBSD: minherit.2,v 1.17 2024/01/21 17:46:03 deraadt Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)minherit.2 8.1 (Berkeley) 6/9/93 +.\" +.Dd $Mdocdate: January 21 2024 $ +.Dt MINHERIT 2 +.Os +.Sh NAME +.Nm minherit +.Nd control the inheritance of pages +.Sh SYNOPSIS +.In sys/mman.h +.Ft int +.Fn minherit "void *addr" "size_t len" "int inherit" +.Sh DESCRIPTION +The +.Fn minherit +system call +changes the specified pages to have the inheritance characteristic +.Fa inherit . +A page's inheritance characteristic controls how it will be mapped +in child processes as created by +.Xr fork 2 . +.Pp +The possible inheritance characteristics are: +.Pp +.Bl -tag -width MAP_INHERIT_SHARE -offset indent -compact +.It Dv MAP_INHERIT_NONE +Pages are not mapped in the child process. +.It Dv MAP_INHERIT_COPY +Private copy of pages are mapped in the child process. +.It Dv MAP_INHERIT_SHARE +Mapped pages are shared between the parent and child processes. +.It Dv MAP_INHERIT_ZERO +New anonymous pages (initialized to all zero bytes) +are mapped in the child process. +.El +.Pp +Not all implementations will guarantee that the inheritance characteristic +can be set on a page basis; +the granularity of changes may be as large as an entire region. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn minherit +system call will fail if: +.Bl -tag -width Er +.It Bq Er EPERM +The +.Fa addr +and +.Fa len +parameters specify a region that contains +at least one page which is immutable, or +.Dv MAP_INHERIT_ZERO +is being requested on a page without +.Dv PROT_WRITE +permission. +.It Bq Er EINVAL +The virtual address range specified by the +.Fa addr +and +.Fa len +arguments is not valid. +.It Bq Er EINVAL +The +.Fa inherit +argument is invalid. +.El +.Sh SEE ALSO +.Xr madvise 2 , +.Xr mimmutable 2 , +.Xr mprotect 2 , +.Xr msync 2 , +.Xr munmap 2 +.Sh HISTORY +The +.Fn minherit +function first appeared in +.Ox 2.0 . diff --git a/man/test_files/mdoc/mkdir.1 b/man/test_files/mdoc/mkdir.1 new file mode 100644 index 00000000..80519119 --- /dev/null +++ b/man/test_files/mdoc/mkdir.1 @@ -0,0 +1,121 @@ +.\" $OpenBSD: mkdir.1,v 1.27 2010/09/03 09:53:20 jmc Exp $ +.\" $NetBSD: mkdir.1,v 1.9 1995/07/25 19:37:13 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94 +.\" +.Dd $Mdocdate: September 3 2010 $ +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm mkdir +.Op Fl p +.Op Fl m Ar mode +.Ar directory ... +.Sh DESCRIPTION +The +.Nm +utility creates the directories named as operands, in the order specified, +using mode +.Li rwxrwxrwx (\&0777) +as modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl m Ar mode +Set the file permission bits of the newly created directory to +.Ar mode . +The mode argument can be in any of the formats specified to the +.Xr chmod 1 +utility. +If a symbolic mode is specified, the operators +.Ql + +and +.Ql - +are interpreted relative to an initial mode of +.Dq a=rwx . +.It Fl p +Create intermediate directories as required. +If this option is not specified, the full path prefix of each +operand must already exist. +Intermediate directories are created with permission bits of +.Li rwxrwxrwx (\&0777) +as modified by the current umask, plus write and search +permission for the owner. +Do not consider it an error if the +argument directory already exists. +.El +.Pp +The user must have write permission in the parent directory. +For an explanation of the directory hierarchy, +see +.Xr hier 7 . +.Sh EXIT STATUS +.Ex -std mkdir +.Sh EXAMPLES +Create a directory named +.Pa foobar : +.Pp +.Dl $ mkdir foobar +.Pp +Create a directory named +.Pa foobar +and set its file mode to 700: +.Pp +.Dl $ mkdir -m 700 foobar +.Pp +Create a directory named +.Pa cow/horse/monkey , +creating any non-existent intermediate directories as necessary: +.Pp +.Dl $ mkdir -p cow/horse/monkey +.Sh SEE ALSO +.Xr chmod 1 , +.Xr rmdir 1 , +.Xr mkdir 2 , +.Xr umask 2 , +.Xr hier 7 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/man/test_files/mdoc/mkfifo.2 b/man/test_files/mdoc/mkfifo.2 new file mode 100644 index 00000000..3ccc36d4 --- /dev/null +++ b/man/test_files/mdoc/mkfifo.2 @@ -0,0 +1,181 @@ +.\" $OpenBSD: mkfifo.2,v 1.15 2015/05/31 23:54:25 schwarze Exp $ +.\" $NetBSD: mkfifo.2,v 1.8 1995/02/27 12:34:27 cgd Exp $ +.\" +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkfifo.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: May 31 2015 $ +.Dt MKFIFO 2 +.Os +.Sh NAME +.Nm mkfifo , +.Nm mkfifoat +.Nd make a FIFO file +.Sh SYNOPSIS +.In sys/stat.h +.Ft int +.Fn mkfifo "const char *path" "mode_t mode" +.In sys/stat.h +.In fcntl.h +.Ft int +.Fn mkfifoat "int fd" "const char *path" "mode_t mode" +.Sh DESCRIPTION +.Fn mkfifo +creates a new FIFO file with name +.Fa path . +The access permissions are +specified by +.Fa mode +and restricted by the +.Xr umask 2 +of the calling process. +.Pp +The FIFO's owner ID is set to the process's effective user ID. +The FIFO's group ID is set to that of the parent directory in +which it is created. +.Pp +The +.Fn mkfifoat +function is equivalent to +.Fn mkfifo +except that where +.Fa path +specifies a relative path, +the newly created FIFO is created relative to +the directory associated with file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn mkfifoat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used +and the behavior is identical to a call to +.Fn mkfifo . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn mkfifo +and +.Fn mkfifoat +will fail and no FIFO will be created if: +.Bl -tag -width Er +.It Bq Er EOPNOTSUPP +The kernel has not been configured to support FIFOs. +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +A component of the path prefix does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EROFS +The named file resides on a read-only file system. +.It Bq Er EEXIST +The named file exists. +.It Bq Er ENOSPC +The directory in which the entry for the new FIFO is being placed +cannot be extended because there is no space left on the file +system containing the directory. +.It Bq Er ENOSPC +There are no free inodes on the file system on which the +FIFO is being created. +.It Bq Er EDQUOT +The directory in which the entry for the new FIFO +is being placed cannot be extended because the +user's quota of disk blocks on the file system +containing the directory has been exhausted. +.It Bq Er EDQUOT +The user's quota of inodes on the file system on +which the FIFO is being created has been exhausted. +.It Bq Er EIO +An I/O error occurred while making the directory entry or allocating +the inode. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.El +.Pp +Additionally, +.Fn mkfifoat +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is a valid file descriptor but it does not reference a directory. +.It Bq Er EACCES +The +.Fa path +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fd +file descriptor references. +.El +.Sh SEE ALSO +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 +.Sh STANDARDS +The +.Fn mkfifo +and +.Fn mkfifoat +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn mkfifoat +function appeared in +.Ox 5.0 . diff --git a/man/test_files/mdoc/mlockall.2 b/man/test_files/mdoc/mlockall.2 new file mode 100644 index 00000000..234ec5f3 --- /dev/null +++ b/man/test_files/mdoc/mlockall.2 @@ -0,0 +1,124 @@ +.\" $OpenBSD: mlockall.2,v 1.10 2019/01/11 18:46:30 deraadt Exp $ +.\" $NetBSD: mlockall.2,v 1.6 2000/06/26 17:00:02 kleink Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, +.\" NASA Ames Research Center. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: January 11 2019 $ +.Dt MLOCKALL 2 +.Os +.Sh NAME +.Nm mlockall , +.Nm munlockall +.Nd lock (unlock) the address space of a process +.Sh SYNOPSIS +.In sys/mman.h +.Ft int +.Fn mlockall "int flags" +.Ft int +.Fn munlockall "void" +.Sh DESCRIPTION +The +.Fn mlockall +system call locks into memory the physical pages associated with the +address space of a process until the address space is unlocked, the +process exits, or execs another program image. +.Pp +The following flags affect the behavior of +.Fn mlockall : +.Bl -tag -width MCL_CURRENT +.It Dv MCL_CURRENT +Lock all pages currently mapped into the process's address space. +.It Dv MCL_FUTURE +Lock all pages mapped into the process's address space in the future, +at the time the mapping is established. +Note that this may cause future mappings to fail if those mappings +cause resource limits to be exceeded. +.El +.Pp +Since physical memory is a potentially scarce resource, processes are +limited in how much they can lock down. +A single process can lock the minimum of a system-wide +.Dq wired pages +limit and the per-process +.Dv RLIMIT_MEMLOCK +resource limit. +.Pp +The +.Fn munlockall +call unlocks any locked memory regions in the process address space. +Any regions mapped after an +.Fn munlockall +call will not be locked. +.Sh RETURN VALUES +.Rv -std mlockall munlockall +.Sh ERRORS +.Fn mlockall +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa flags +argument is zero or includes unimplemented flags. +.It Bq Er ENOMEM +Locking all of the pages currently mapped would exceed either +the system or per-process +limit for locked memory. +.It Bq Er EAGAIN +Some or all of the memory mapped into the process's address space +could not be locked when the call was made. +.It Bq Er EPERM +The calling process does not have the appropriate privileges to perform +the requested operation. +.El +.Sh SEE ALSO +.Xr mlock 2 , +.Xr mmap 2 , +.Xr munmap 2 , +.Xr setrlimit 2 +.Sh STANDARDS +The +.Fn mlockall +and +.Fn munlockall +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn mlockall +and +.Fn munlockall +functions first appeared in +.Ox 2.9 . +.Sh BUGS +The per-process resource limit is a limit on the amount of virtual +memory locked, while the system-wide limit is for the number of locked +physical pages. +Hence a process with two distinct locked mappings of the same physical page +counts as 2 pages against the per-process limit and only as a single page +in the system limit. diff --git a/man/test_files/mdoc/mopa.out.1 b/man/test_files/mdoc/mopa.out.1 new file mode 100644 index 00000000..f8653725 --- /dev/null +++ b/man/test_files/mdoc/mopa.out.1 @@ -0,0 +1,49 @@ +.\" $OpenBSD: mopa.out.1,v 1.16 2017/07/06 16:50:58 schwarze Exp $ +.\" +.\" Copyright (c) 1996 Mats O Jansson. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: July 6 2017 $ +.Dt MOPA.OUT 1 +.Os +.Sh NAME +.Nm mopa.out +.Nd create MOP image from another executable format +.Sh SYNOPSIS +.Nm mopa.out +.Ar infile +.Ar outfile +.Sh DESCRIPTION +.Nm +is used to convert a file from another executable format to a MOP-image. +.Pp +Elf64 alpha images, as well as Elf32 and a.out VAX images, +are the only currently supported input formats. +.Sh SEE ALSO +.Xr mopchk 1 , +.Xr mopprobe 1 , +.Xr moptrace 1 , +.Xr elf 5 , +.Xr mopd 8 +.Sh AUTHORS +.An Lloyd Parkes +.An Jason R. Thorpe diff --git a/man/test_files/mdoc/moptrace.1 b/man/test_files/mdoc/moptrace.1 new file mode 100644 index 00000000..ce77ed69 --- /dev/null +++ b/man/test_files/mdoc/moptrace.1 @@ -0,0 +1,74 @@ +.\" $OpenBSD: moptrace.1,v 1.13 2017/07/06 16:50:58 schwarze Exp $ +.\" +.\" Copyright (c) 1993-95 Mats O Jansson. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: July 6 2017 $ +.Dt MOPTRACE 1 +.Os +.Sh NAME +.Nm moptrace +.Nd MOP Trace Utility +.Sh SYNOPSIS +.Nm moptrace +.Op Fl 3 | 4 +.Op Fl ad +.Ar interface +.Sh DESCRIPTION +.Nm +prints the contents of MOP packages on the Ethernet connected to +.Ar interface +or all known interfaces if +.Fl a +is given. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 3 +Ignore MOP V3 messages (Ethernet II). +.It Fl 4 +Ignore MOP V4 messages (Ethernet 802.3). +.It Fl a +Listen on all the Ethernets attached to the system. +If +.Fl a +is omitted, an interface must be specified. +.It Fl d +Run in debug mode, with all the output to stderr. +.El +.Sh SEE ALSO +.Xr mopa.out 1 , +.Xr mopchk 1 , +.Xr mopprobe 1 , +.Xr mopd 8 +.Rs +.%B DECnet Digital Network Architecture Phase IV +.%R Maintenance Operations Functional Specification V3.0.0 +.%N AA-X436A-TK +.Re +.Rs +.%B DECnet Digital Network Architecture +.%R Maintenance Operations Protocol Functional Specification V4.0.0 +.%N EK-DNA11-FS-001 +.Re +.Sh AUTHORS +.An Mats O Jansson Aq Mt moj@stacken.kth.se diff --git a/man/test_files/mdoc/msgrcv.2 b/man/test_files/mdoc/msgrcv.2 new file mode 100644 index 00000000..3474f967 --- /dev/null +++ b/man/test_files/mdoc/msgrcv.2 @@ -0,0 +1,207 @@ +.\" $OpenBSD: msgrcv.2,v 1.18 2019/07/18 13:45:03 schwarze Exp $ +.\" $NetBSD: msgrcv.2,v 1.2 1997/03/27 08:20:37 mikel Exp $ +.\" +.\" Copyright (c) 1995 Frank van der Linden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project +.\" by Frank van der Linden +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\"/ +.Dd $Mdocdate: July 18 2019 $ +.Dt MSGRCV 2 +.Os +.Sh NAME +.Nm msgrcv +.Nd receive a message from a message queue +.Sh SYNOPSIS +.In sys/msg.h +.Ft int +.Fn msgrcv "int msqid" "void *msgp" "size_t msgsz" "long msgtyp" "int msgflg" +.Sh DESCRIPTION +The +.Fn msgrcv +function receives a message from the message queue specified in +.Fa msqid , +and places it into the structure pointed to by +.Fa msgp . +This structure should consist of the following members: +.Bd -literal + long mtype; /* message type */ + char mtext[1]; /* body of message */ +.Ed +.Pp +.Fa mtype +is an integer greater than 0 that can be used for selecting messages, +.Fa mtext +is an array of bytes, with a size up to that of the system limit +.Pq Dv MSGMAX . +.Pp +The value of +.Fa msgtyp +has one of the following meanings: +.Bl -bullet +.It +.Fa msgtyp +is greater than 0. +The first message of type +.Fa msgtyp +will be received. +.It +.Fa msgtyp +is equal to 0. +The first message on the queue will be received. +.It +.Fa msgtyp +is less than 0. +The first message of the lowest message type that is +less than or equal to the absolute value of +.Fa msgtyp +will be received. +.El +.Pp +.Fa msgsz +specifies the maximum length of the requested message. +If the received message has a length greater than +.Fa msgsz +it will be silently truncated if the +.Dv MSG_NOERROR +flag is set in +.Fa msgflg , +otherwise an error will be returned. +.Pp +If no matching message is present on the message queue specified by +.Fa msqid , +the behavior of +.Fn msgrcv +depends on whether the +.Dv IPC_NOWAIT +flag is set in +.Fa msgflg +or not. +If +.Dv IPC_NOWAIT +is set, +.Fn msgrcv +will immediately return a value of \-1, and set +.Va errno +to +.Er EAGAIN . +If +.Dv IPC_NOWAIT +is not set, the calling process will be blocked +until: +.Bl -bullet +.It +A message of the requested type becomes available on the message queue. +.It +The message queue is removed, in which case \-1 will be returned, and +.Va errno +set to +.Er EIDRM . +.It +A signal is received and caught. +\-1 is returned, and +.Va errno +set to +.Er EINTR . +.El +.Pp +If a message is successfully received, the data structure associated with +.Fa msqid +is updated as follows: +.Bl -bullet +.It +.Fa msg_cbytes +is decremented by the size of the message. +.It +.Fa msg_lrpid +is set to the pid of the caller. +.It +.Fa msg_lrtime +is set to the current time. +.It +.Fa msg_qnum +is decremented by 1. +.El +.Sh RETURN VALUES +Upon successful completion, +.Fn msgrcv +returns the number of bytes received into the +.Fa mtext +field of the structure pointed to by +.Fa msgp . +Otherwise, \-1 is returned, and +.Va errno +set to indicate the error. +.Sh ERRORS +.Fn msgrcv +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa msqid +is not a valid message queue identifier. +.Pp +.Fa msgsz +is less than 0. +.It Bq Er E2BIG +A matching message was received, but its size was greater than +.Fa msgsz +and the +.Dv MSG_NOERROR +flag was not set in +.Fa msgflg . +.It Bq Er EACCES +The calling process does not have read access to the message queue. +.It Bq Er EFAULT +.Fa msgp +points to an invalid address. +.It Bq Er EINTR +The system call was interrupted by the delivery of a signal. +.It Bq Er ENOMSG +There is no message of the requested type available on the message queue, +and +.Dv IPC_NOWAIT +is set in +.Fa msgflg . +.It Bq Er EIDRM +The message queue was removed while +.Fn msgrcv +was waiting for a message of the requested type to become available on it. +.El +.Sh SEE ALSO +.Xr msgctl 2 , +.Xr msgget 2 , +.Xr msgsnd 2 +.Sh STANDARDS +The +.Fn msgrcv +function conforms to the X/Open System Interfaces option of +.St -p1003.1-2008 . +.Sh HISTORY +Message queues first appeared in +.At V.1 +and have been available since +.Nx 1.0 . diff --git a/man/test_files/mdoc/msgsnd.2 b/man/test_files/mdoc/msgsnd.2 new file mode 100644 index 00000000..54d710ab --- /dev/null +++ b/man/test_files/mdoc/msgsnd.2 @@ -0,0 +1,168 @@ +.\" $OpenBSD: msgsnd.2,v 1.21 2019/07/18 13:45:03 schwarze Exp $ +.\" $NetBSD: msgsnd.2,v 1.2 1997/03/27 08:20:36 mikel Exp $ +.\" +.\" Copyright (c) 1995 Frank van der Linden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project +.\" by Frank van der Linden +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\"/ +.Dd $Mdocdate: July 18 2019 $ +.Dt MSGSND 2 +.Os +.Sh NAME +.Nm msgsnd +.Nd send a message to a message queue +.Sh SYNOPSIS +.In sys/msg.h +.Ft int +.Fn msgsnd "int msqid" "const void *msgp" "size_t msgsz" "int msgflg" +.Sh DESCRIPTION +The +.Fn msgsnd +function sends a message to the message queue specified by +.Fa msqid . +.Fa msgp +points to a structure containing the message. +This structure should consist of the following members: +.Bd -literal -offset indent +long mtype; /* message type */ +char mtext[1]; /* body of message */ +.Ed +.Pp +.Fa mtype +is an integer greater than 0 that can be used for selecting messages (see +.Xr msgrcv 2 ) ; +.Fa mtext +is an array of +.Fa msgsz +bytes, with a size between 0 and that of the system limit +.Pq Dv MSGMAX . +.Pp +If the number of bytes already on the message queue plus +.Fa msgsz +is bigger than the maximum number of bytes on the message queue +.Po Fa msg_qbytes , + see +.Xr msgctl 2 +.Pc , +or the number of messages on all queues system-wide is already equal to +the system limit, +.Fa msgflg +determines the action of +.Fn msgsnd . +If +.Fa msgflg +has +.Dv IPC_NOWAIT +mask set in it, the call will return immediately. +If +.Fa msgflg +does not have +.Dv IPC_NOWAIT +set in it, the call will block until: +.Bl -bullet +.It +The condition which caused the call to block does no longer exist. +The message will be sent. +.It +The message queue is removed, in which case \-1 will be returned, and +.Va errno +is set to +.Er EIDRM . +.It +The caller catches a signal. +The call returns with +.Va errno +set to +.Er EINTR . +.El +.Pp +After a successful call, the data structure associated with the message +queue is updated in the following way: +.Bl -bullet +.It +.Fa msg_cbytes +is incremented by the size of the message. +.It +.Fa msg_qnum +is incremented by 1. +.It +.Fa msg_lspid +is set to the pid of the calling process. +.It +.Fa msg_stime +is set to the current time. +.El +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn msgsnd +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa msqid +is not a valid message queue identifier. +.Pp +.Fa mtype +is less than 1. +.Pp +.Fa msgsz +is greater than +.Fa msg_qbytes . +.It Bq Er EACCES +The calling process does not have write access to the message queue. +.It Bq Er EAGAIN +There was no space for this message either on the queue, or in the whole +system, and +.Dv IPC_NOWAIT +was set in +.Fa msgflg . +.It Bq Er EFAULT +.Fa msgp +points to an invalid address. +.It Bq Er EINTR +The system call was interrupted by the delivery of a signal. +.It Bq Er EIDRM +The message queue was removed while +.Fn msgsnd +was waiting for a resource to become available in order to deliver the +message. +.El +.Sh SEE ALSO +.Xr msgctl 2 , +.Xr msgget 2 , +.Xr msgrcv 2 +.Sh STANDARDS +The +.Fn msgsnd +function conforms to the X/Open System Interfaces option of +.St -p1003.1-2008 . +.Sh HISTORY +Message queues first appeared in +.At V.1 +and have been available since +.Nx 1.0 . diff --git a/man/test_files/mdoc/munmap.2 b/man/test_files/mdoc/munmap.2 new file mode 100644 index 00000000..23d96524 --- /dev/null +++ b/man/test_files/mdoc/munmap.2 @@ -0,0 +1,104 @@ +.\" $OpenBSD: munmap.2,v 1.21 2024/01/21 17:00:42 deraadt Exp $ +.\" $NetBSD: munmap.2,v 1.5 1995/02/27 12:35:03 cgd Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)munmap.2 8.2 (Berkeley) 4/15/94 +.\" +.Dd $Mdocdate: January 21 2024 $ +.Dt MUNMAP 2 +.Os +.Sh NAME +.Nm munmap +.Nd remove a mapping +.Sh SYNOPSIS +.In sys/mman.h +.Ft int +.Fn munmap "void *addr" "size_t len" +.Sh DESCRIPTION +The +.Fn munmap +system call +deletes the mappings for the specified address range, +and causes further references to addresses within the range +to generate invalid memory references. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn munmap +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa addr +and +.Fa len +parameters +specify a region that would extend beyond the end of the address space, +or some part of the region being unmapped is not part of the currently +valid address space. +.It Bq Er EPERM +The +.Fa addr +and +.Fa len +parameters +specify a region which contains at least one page marked immutable. +.El +.Sh SEE ALSO +.Xr madvise 2 , +.Xr mimmutable 2 , +.Xr mlock 2 , +.Xr mlockall 2 , +.Xr mmap 2 , +.Xr mprotect 2 , +.Xr msync 2 , +.Xr getpagesize 3 +.Sh STANDARDS +When +.Fa len +is non-zero, the +.Fn munmap +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn munmap +system call has been available since +.Bx 4.3 Net/2 . +.Sh CAVEATS +.St -p1003.1-2008 +specifies that +.Fn munmap +shall fail with +.Er EINVAL +if +.Fa len +is 0. +.Ox +performs no action in this case. diff --git a/man/test_files/mdoc/mv.1 b/man/test_files/mdoc/mv.1 new file mode 100644 index 00000000..eb5c40f5 --- /dev/null +++ b/man/test_files/mdoc/mv.1 @@ -0,0 +1,203 @@ +.\" $OpenBSD: mv.1,v 1.34 2018/11/14 15:53:31 tedu Exp $ +.\" $NetBSD: mv.1,v 1.8 1995/03/21 09:06:51 cgd Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mv.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd $Mdocdate: November 14 2018 $ +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm mv +.Op Fl fiv +.Ar source target +.Nm mv +.Op Fl fiv +.Ar source ... directory +.Sh DESCRIPTION +In its first form, the +.Nm +utility moves the file named by the +.Ar source +operand to the destination path named by the +.Ar target +operand. +This form is assumed when the last operand does not name an already +existing directory. +.Pp +In its second form, +.Nm +moves each file named by a +.Ar source +operand to the destination specified by the +.Ar directory +operand. +It is an error if the +.Ar directory +does not exist. +The destination path for each +.Ar source +operand is the pathname produced by the concatenation of the +.Ar directory +operand, a slash, and the final pathname component of the named file. +.Pp +In both forms, a +.Ar source +operand is skipped with an error message +when the respective destination path is a non-empty directory, +or when the source is a non-directory file but the destination path +is a directory, or vice versa. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +Do not prompt for confirmation before overwriting the destination +path. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Causes +.Nm +to write a prompt to standard error before moving a file that would +overwrite an existing file. +If the response from the standard input begins with the character +.Dq y , +the move is attempted. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl v +Display the source and destination after each move. +.El +.Pp +The +.Nm +utility moves symbolic links, not the files referenced by the links. +.Pp +If the destination path does not have a mode which permits writing, +.Nm +prompts the user for confirmation as specified for the +.Fl i +option. +.Pp +Should the +.Xr rename 2 +call fail because the source and destination are on different file systems, +.Nm +will imitate +.Xr cp 1 +and +.Xr rm 1 +to accomplish the move. +The effect is equivalent to: +.Bd -literal -offset indent +$ rm -df -- destination_path && \e + cp -PRp -- source destination_path && \e + rm -rf -- source +.Ed +.Sh EXIT STATUS +.Ex -std mv +.Sh EXAMPLES +Rename file +.Pa foo +to +.Pa bar , +overwriting +.Pa bar +if it already exists: +.Pp +.Dl $ mv -f foo bar +.Pp +Either of these commands will rename the file +.Pa -f +to +.Pa bar , +prompting for confirmation if +.Pa bar +already exists: +.Bd -literal -offset indent +$ mv -i -- -f bar +$ mv -i ./-f bar +.Ed +.Sh SEE ALSO +.Xr cp 1 , +.Xr rm 1 , +.Xr rename 2 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flag +.Op Fl v +is an extension to that specification. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . +.Sh CAVEATS +In the second synopsis form, incompatible file types in +.Ar source +and +.Ar directory +cause partial moves. +For example, if +.Pa f +and +.Pa g +are non-directory files and +.Pa d +and +.Pa d/f +are directories, the command +.Pp +.Dl $ mv f g d +.Pp +will print an error message, leave +.Pa f +where it is, move +.Pa g +to +.Pa d/g +and return a non-zero exit status. diff --git a/man/test_files/mdoc/nl.1 b/man/test_files/mdoc/nl.1 new file mode 100644 index 00000000..c6bdef41 --- /dev/null +++ b/man/test_files/mdoc/nl.1 @@ -0,0 +1,231 @@ +.\" $OpenBSD: nl.1,v 1.10 2022/07/25 01:57:48 jsg Exp $ +.\" $NetBSD: nl.1,v 1.14 2013/09/09 09:02:25 wiz Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Klaus Klein. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: July 25 2022 $ +.Dt NL 1 +.Os +.Sh NAME +.Nm nl +.Nd line numbering filter +.Sh SYNOPSIS +.Nm +.Op Fl p +.Op Fl b Ar type +.Op Fl d Ar delim +.Op Fl f Ar type +.Op Fl h Ar type +.Op Fl i Ar incr +.Op Fl l Ar num +.Op Fl n Ar format +.Op Fl s Ar sep +.Op Fl v Ar startnum +.Op Fl w Ar width +.Op Ar file +.Sh DESCRIPTION +The +.Nm +utility reads lines from the named +.Ar file , +applies a configurable line numbering filter operation, +and writes the result to the standard output. +If +.Ar file +is a single dash +.Pq Sq \&- +or absent, +.Nm +reads from the standard input. +.Pp +The +.Nm +utility treats the text it reads in terms of logical pages. +Unless specified otherwise, line numbering is reset at the start of each +logical page. +A logical page consists of a header, a body and a footer section; empty +sections are valid. +Different line numbering options are independently available for header, +body and footer sections. +.Pp +The starts of logical page sections are signaled by input lines containing +nothing but one of the following sequences of delimiter characters: +.Bl -column "\e:\e:\e: " "header " -offset indent +.It Em "Line" Ta Em "Start of" +.It \e:\e:\e: header +.It \e:\e: body +.It \e: footer +.El +.Pp +If the input does not contain any logical page section signaling directives, +the text being read is assumed to consist of a single logical page body. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl b Ar type +Specify the logical page body lines to be numbered. +Recognized +.Ar type +arguments are: +.Bl -tag -width pstringXX +.It a +Number all lines. +.It t +Number only non-empty lines. +.It n +No line numbering. +.It p Ns Ar expr +Number only those lines that contain the basic regular expression specified +by +.Ar expr . +.El +.Pp +The default +.Ar type +for logical page body lines is t. +.It Fl d Ar delim +Specify the delimiter characters used to indicate the start of a logical +page section in the input file. +At most two characters may be specified; if only one character is specified, +the first character is replaced and the second character remains unchanged. +The default +.Ar delim +characters are +.Sq \e: . +.It Fl f Ar type +Specify the same as +.Fl b Ar type +except for logical page footer lines. +The default +.Ar type +for logical page footer lines is n. +.It Fl h Ar type +Specify the same as +.Fl b Ar type +except for logical page header lines. +The default +.Ar type +for logical page header lines is n. +.It Fl i Ar incr +Specify the increment value used to number logical page lines. +The default +.Ar incr +value is 1. +.It Fl l Ar num +If numbering of all lines is specified for the current logical section +using the corresponding +.Fl b +a, +.Fl f +a +or +.Fl h +a +option, +specify the number of adjacent blank lines to be considered as one. +For example, +.Fl l +2 results in only the second adjacent blank line being numbered. +The default +.Ar num +value is 1. +.It Fl n Ar format +Specify the line numbering output format. +Recognized +.Ar format +arguments are: +.Pp +.Bl -tag -width lnXX -compact -offset indent +.It ln +Left justified. +.It rn +Right justified, leading zeros suppressed. +.It rz +Right justified, leading zeros kept. +.El +.Pp +The default +.Ar format +is rn. +.It Fl p +Specify that line numbering should not be restarted at logical page delimiters. +.It Fl s Ar sep +Specify the characters used in separating the line number and the corresponding +text line. +The default +.Ar sep +setting is a single tab character. +.It Fl v Ar startnum +Specify the initial value used to number logical page lines; see also the +description of the +.Fl p +option. +The default +.Ar startnum +value is 1. +.It Fl w Ar width +Specify the number of characters to be occupied by the line number; +if the +.Ar width +is insufficient to hold the line number, it will be truncated to its +.Ar width +least significant digits. +The default +.Ar width +is 6. +.El +.Sh ENVIRONMENT +.Bl -tag -width LC_CTYPE +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 . +It decides which byte sequences form characters for the +.Fl d +option. +If unset or set to "C", "POSIX", or an unsupported value, +each byte is treated as a character. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr pr 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +X/Open System Interfaces option of the +.St -p1003.1-2008 +specification. +.Sh HISTORY +The +.Nm +utility first appeared in +.At III . +It was added to the +.Ox 5.5 +release. diff --git a/man/test_files/mdoc/nm.1 b/man/test_files/mdoc/nm.1 new file mode 100644 index 00000000..bbf80252 --- /dev/null +++ b/man/test_files/mdoc/nm.1 @@ -0,0 +1,166 @@ +.\" $OpenBSD: nm.1,v 1.31 2019/09/06 19:25:08 schwarze Exp $ +.\" $NetBSD: nm.1,v 1.3 1995/08/31 23:41:58 jtc Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nm.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: September 6 2019 $ +.Dt NM 1 +.Os +.Sh NAME +.Nm nm +.Nd display name list (symbol table) +.Sh SYNOPSIS +.Nm nm +.Op Fl AaCDegnoPprsuw +.Op Fl t Cm d Ns | Ns Cm o Ns | Ns Cm x +.Op Ar +.Sh DESCRIPTION +The symbol table (name list) of each object in +.Ar file(s) +is displayed. +If a library (archive) is given, +.Nm +displays a list for each +object archive member. +If +.Ar file +is not present, +.Nm +searches for the file +.Pa a.out +and displays its symbol table if it exists. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl A +Display the full path or library name of object on every line. +.It Fl a +Display symbol table entries inserted for use by debuggers. +.It Fl C +Decode low-level symbol names. +This involves removing extra underscores and making C++ function names readable. +.It Fl D +Display the dynamic symbol table instead of the normal symbol table. +.It Fl e +Output extended information, that is `w' for weak symbols, `f' for +function-like symbols, and `o' for object-like symbols. +.It Fl g +Restrict display to external (global) symbols. +.It Fl n +Present results in numerical order. +.It Fl o +Display the full path or library name of object on every line +.Pq this is similar to Fl A . +.It Fl P +Report information in POSIX format: full path or library name of object if +either +.Fl A +or +.Fl o +has been specified; symbol name; symbol type; +symbol value and size (unless the symbol is undefined). +The radix of symbol values and sizes defaults to decimal, and may be changed +with the +.Fl t +option. +.It Fl p +Do not sort at all. +.It Fl r +Reverse order sort. +.It Fl s +Show archive index. +.It Fl t Cm d Ns | Ns Cm o Ns | Ns Cm x +In POSIX format output, choose the numeric radix as follows: +.Pp +.Bl -tag -width 3n -compact -offset indent +.It Cm d +Decimal. +.It Cm o +Octal. +.It Cm x +Hexadecimal. +.El +.It Fl u +Display undefined symbols only. +.It Fl w +Warn about non-object archive members. +Normally, +.Nm nm +will silently ignore all archive members which are not +object files. +.El +.Pp +Each symbol name is preceded by its value (a blank field if the symbol +is undefined) and one of the following letters: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Fl +debugger symbol table entries (see the +.Fl a +option) +.It Li A +absolute +.It Li B +bss or tbss segment symbol +.It Li C +common symbol +.It Li D +data or tdata segment symbol +.It Li F +file name +.It Li R +read-only data segment symbol +.It Li T +text segment symbol +.It Li U +undefined +.It Li W +weak symbol +.El +.Pp +If the symbol is local (non-external), the type letter is in lower case. +The output is sorted alphabetically. +.Sh SEE ALSO +.Xr ar 1 , +.Xr size 1 , +.Xr ar 5 , +.Xr elf 5 +.Sh STANDARDS +The +.Nm +utility is part of the +.St -p1003.1-2008 +specification; +this implementation is largely incompatible with that standard. +.Sh HISTORY +An +.Nm nm +command appeared in +.At v1 . diff --git a/man/test_files/mdoc/open.2 b/man/test_files/mdoc/open.2 new file mode 100644 index 00000000..c8e056bb --- /dev/null +++ b/man/test_files/mdoc/open.2 @@ -0,0 +1,465 @@ +.\" $OpenBSD: open.2,v 1.51 2022/03/31 17:27:16 naddy Exp $ +.\" $NetBSD: open.2,v 1.8 1995/02/27 12:35:14 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)open.2 8.2 (Berkeley) 11/16/93 +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt OPEN 2 +.Os +.Sh NAME +.Nm open , +.Nm openat +.Nd open or create a file for reading or writing +.Sh SYNOPSIS +.In fcntl.h +.Ft int +.Fn open "const char *path" "int flags" ... +.Ft int +.Fn openat "int fd" "const char *path" "int flags" ... +.Sh DESCRIPTION +The file name specified by +.Fa path +is opened +for reading and/or writing as specified by the +argument +.Fa flags +and the file descriptor returned to the calling process. +The +.Fa flags +argument may indicate the file is to be +created if it does not exist (by specifying the +.Dv O_CREAT +flag), in which case the file is created with a mode +specified by an additional argument of type +.Vt mode_t +as described in +.Xr chmod 2 +and modified by the process' umask value (see +.Xr umask 2 ) . +.Pp +The +.Fa flags +specified are a bitwise OR of the following values. +Exactly one of the first three values (file access modes) must be specified: +.Pp +.Bl -tag -width O_DIRECTORY -offset indent -compact +.It Dv O_RDONLY +Open for reading only. +.It Dv O_WRONLY +Open for writing only. +.It Dv O_RDWR +Open for reading and writing. +.El +.Pp +Any combination of the following flags may additionally be used: +.Pp +.Bl -tag -width O_DIRECTORY -offset indent -compact +.It Dv O_NONBLOCK +Do not block on open or for data to become available. +.It Dv O_APPEND +Append on each write. +.It Dv O_CREAT +Create file if it does not exist. +An additional argument of type +.Vt mode_t +must be supplied to the call. +.It Dv O_TRUNC +Truncate size to 0. +.It Dv O_EXCL +Error if +.Dv O_CREAT +is set and file exists. +.It Dv O_SYNC +Perform synchronous I/O operations. +.It Dv O_SHLOCK +Atomically obtain a shared lock. +.It Dv O_EXLOCK +Atomically obtain an exclusive lock. +.It Dv O_NOFOLLOW +If last path element is a symlink, don't follow it. +.It Dv O_CLOEXEC +Set +.Dv FD_CLOEXEC +(the close-on-exec flag) +on the new file descriptor. +.It Dv O_DIRECTORY +Error if +.Fa path +does not name a directory. +.El +.Pp +Opening a file with +.Dv O_APPEND +set causes each write on the file +to be appended to the end. +If +.Dv O_TRUNC +and a writing mode are specified and the +file exists, the file is truncated to zero length. +If +.Dv O_EXCL +is set with +.Dv O_CREAT +and the file already +exists, +.Fn open +returns an error. +This may be used to implement a simple exclusive access locking mechanism. +If either of +.Dv O_EXCL +or +.Dv O_NOFOLLOW +are set and the last component of the pathname is +a symbolic link, +.Fn open +will fail even if the symbolic +link points to a non-existent name. +If the +.Dv O_NONBLOCK +flag is specified, do not wait for the device or file to be ready or +available. +If the +.Fn open +call would result +in the process being blocked for some reason (e.g., waiting for +carrier on a dialup line), +.Fn open +returns immediately. +This flag also has the effect of making all subsequent I/O on the open file +non-blocking. +If the +.Dv O_SYNC +flag is set, all I/O operations on the file will be done synchronously. +.Pp +A FIFO should either be opened with +.Dv O_RDONLY +or with +.Dv O_WRONLY . +The behavior for opening a FIFO with +.Dv O_RDWR +is undefined. +.Pp +When opening a file, a lock with +.Xr flock 2 +semantics can be obtained by setting +.Dv O_SHLOCK +for a shared lock, or +.Dv O_EXLOCK +for an exclusive lock. +If creating a file with +.Dv O_CREAT , +the request for the lock will never fail +(provided that the underlying filesystem supports locking). +.Pp +If +.Fn open +is successful, the file pointer used to mark the current position within +the file is set to the beginning of the file. +.Pp +When a new file is created, it is given the group of the directory +which contains it. +.Pp +The new descriptor is set to remain open across +.Xr execve 2 +system calls; see +.Xr close 2 +and +.Xr fcntl 2 . +.Pp +The system imposes a limit on the number of file descriptors +open simultaneously by one process. +.Xr getdtablesize 3 +returns the current system limit. +.Pp +The +.Fn openat +function is equivalent to +.Fn open +except that where +.Fa path +specifies a relative path, +the file to be opened is determined relative to +the directory associated with file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn openat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used +and the behavior is identical to a call to +.Fn open . +.Sh RETURN VALUES +If successful, +.Fn open +returns a non-negative integer, termed a file descriptor. +Otherwise, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +The +.Fn open +and +.Fn openat +functions will fail if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENOTDIR +.Dv O_DIRECTORY +is specified and +.Fa path +does not name a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +.Dv O_CREAT +is not set and the named file does not exist. +.It Bq Er ENOENT +A component of the pathname that must exist does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er EACCES +The required permissions (for reading and/or writing) +are denied for the given +.Fa flags . +.It Bq Er EACCES +.Dv O_CREAT +is specified, +the file does not exist, +and the directory in which it is to be created +does not permit writing. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname, +or the +.Dv O_NOFOLLOW +flag was specified and the target is a symbolic link. +.It Bq Er EISDIR +The named file is a directory, and the arguments specify +it is to be opened for writing. +.It Bq Er EINVAL +The +.Fa flags +specified for opening the file are not valid. +.It Bq Er EROFS +The named file resides on a read-only file system, +and the file is to be modified. +.It Bq Er EMFILE +The process has already reached its limit for open file descriptors. +.It Bq Er ENFILE +The system file table is full. +.It Bq Er ENXIO +The named file is a character special or block +special file, and the device associated with this special file +does not exist. +.It Bq Er ENXIO +The named file is a FIFO, the +.Dv O_NONBLOCK +and +.Dv O_WRONLY +flags are set, and no process has the file open for reading. +.It Bq Er EINTR +The +.Fn open +operation was interrupted by a signal. +.It Bq Er EOPNOTSUPP +.Dv O_SHLOCK +or +.Dv O_EXLOCK +is specified but the underlying filesystem does not support locking. +.It Bq Er EWOULDBLOCK +.Dv O_NONBLOCK +and one of +.Dv O_SHLOCK +or +.Dv O_EXLOCK +is specified and the file is already locked. +.It Bq Er ENOSPC +.Dv O_CREAT +is specified, +the file does not exist, +and the directory in which the entry for the new file is being placed +cannot be extended because there is no space left on the file +system containing the directory. +.It Bq Er ENOSPC +.Dv O_CREAT +is specified, +the file does not exist, +and there are no free inodes on the file system on which the +file is being created. +.It Bq Er EDQUOT +.Dv O_CREAT +is specified, +the file does not exist, +and the directory in which the entry for the new file +is being placed cannot be extended because the +user's quota of disk blocks on the file system +containing the directory has been exhausted. +.It Bq Er EDQUOT +.Dv O_CREAT +is specified, +the file does not exist, +and the user's quota of inodes on the file system on +which the file is being created has been exhausted. +.It Bq Er EIO +An I/O error occurred while making the directory entry or +allocating the inode for +.Dv O_CREAT . +.It Bq Er ETXTBSY +The file is a pure procedure (shared text) file that is being +executed and the +.Fn open +call requests write access. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EEXIST +.Dv O_CREAT +and +.Dv O_EXCL +were specified and the file exists. +.It Bq Er EPERM +The file named by +.Fa path +is flagged append-only but +.Dv O_APPEND +was not specified in +.Fa flags . +.It Bq Er EOPNOTSUPP +An attempt was made to open a socket (not currently implemented). +.It Bq Er EBUSY +An attempt was made to open a terminal device that requires exclusive +access and the specified device has already be opened. +.El +.Pp +Additionally, the +.Fn openat +function will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa path +argument specifies a relative path and the +.Fa fd +argument is a valid file descriptor but it does not reference a directory. +.It Bq Er EACCES +The +.Fa path +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fd +file descriptor references. +.El +.Sh SEE ALSO +.Xr chflags 2 , +.Xr chmod 2 , +.Xr close 2 , +.Xr dup 2 , +.Xr flock 2 , +.Xr lseek 2 , +.Xr read 2 , +.Xr umask 2 , +.Xr write 2 , +.Xr getdtablesize 3 +.Sh STANDARDS +The +.Fn open +and +.Fn openat +functions conform to +.St -p1003.1-2008 . +.Pp +.Dv POSIX +specifies three different flavors for synchronous I/O: +.Dv O_SYNC , +.Dv O_DSYNC , +and +.Dv O_RSYNC . +In +.Ox , +these are all equivalent. +.Pp +The +.Dv O_SHLOCK +and +.Dv O_EXLOCK +flags are non-standard extensions and should not be used if portability +is of concern. +.Sh HISTORY +An +.Fn open +system call first appeared in +.At v1 . +The +.Fa flags +argument has been supported since +.Bx 4.2 . +Before that, a dedicated +.Fn creat +system call had to be used to create new files; +it appeared in +.At v1 , +was deprecated in +.Bx 4.3 Reno , +and removed in +.Ox 5.0 . +.Pp +The +.Fn openat +system call has been available since +.Ox 5.0 . +.Sh CAVEATS +The +.Dv O_TRUNC +flag requires that one of +.Dv O_RDWR +or +.Dv O_WRONLY +also be specified, else +.Er EINVAL +is returned. diff --git a/man/test_files/mdoc/poll.2 b/man/test_files/mdoc/poll.2 new file mode 100644 index 00000000..1b77bd0b --- /dev/null +++ b/man/test_files/mdoc/poll.2 @@ -0,0 +1,364 @@ +.\" $OpenBSD: poll.2,v 1.41 2024/08/04 22:28:08 guenther Exp $ +.\" +.\" Copyright (c) 1994 Jason R. Thorpe +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Jason R. Thorpe. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" +.Dd $Mdocdate: August 4 2024 $ +.Dt POLL 2 +.Os +.Sh NAME +.Nm poll , +.Nm ppoll +.Nd synchronous I/O multiplexing +.Sh SYNOPSIS +.In poll.h +.Ft int +.Fn poll "struct pollfd *fds" "nfds_t nfds" "int timeout" +.Ft int +.Fn ppoll "struct pollfd *fds" "nfds_t nfds" "const struct timespec * restrict timeout" "const sigset_t * restrict mask" +.Sh DESCRIPTION +.Fn poll +provides a mechanism for multiplexing I/O across a set of file +descriptors. +It is similar in function to +.Xr select 2 . +Unlike +.Xr select 2 , +however, it is possible to only pass in data corresponding to the +file descriptors for which events are wanted. +This makes +.Fn poll +more efficient than +.Xr select 2 +in most cases. +.Pp +The arguments are as follows: +.Bl -tag -width timeout +.It Fa fds +Points to an array of +.Vt pollfd +structures, which are defined as: +.Bd -literal -offset indent +struct pollfd { + int fd; + short events; + short revents; +}; +.Ed +.Pp +The +.Fa fd +member is an open file descriptor. +If +.Fa fd +is -1, +the +.Vt pollfd +structure is considered unused, and +.Fa revents +will be cleared. +.Pp +The +.Fa events +and +.Fa revents +members are bitmasks of conditions to monitor and conditions found, +respectively. +.It Fa nfds +An unsigned integer specifying the number of +.Vt pollfd +structures in the array. +.It Fa timeout +Maximum interval to wait for the poll to complete, in milliseconds. +If this value is 0, +.Fn poll +will return immediately. +If this value is +.Dv INFTIM Pq -1 , +.Fn poll +will block indefinitely until a condition is found. +.El +.Pp +The calling process sets the +.Fa events +bitmask and +.Fn poll +sets the +.Fa revents +bitmask. +Each call to +.Fn poll +resets the +.Fa revents +bitmask for accuracy. +The condition flags in the bitmasks are defined as: +.Bl -tag -width POLLRDNORM +.It Dv POLLIN +Data other than high-priority data may be read without blocking. +.It Dv POLLRDNORM +Normal data may be read without blocking. +.It Dv POLLRDBAND +Priority data may be read without blocking. +.It Dv POLLNORM +Same as +.Dv POLLRDNORM . +This flag is provided for source code compatibility with older +programs and should not be used in new code. +.It Dv POLLPRI +High-priority data may be read without blocking. +.It Dv POLLOUT +Normal data may be written without blocking. +.It Dv POLLWRNORM +Same as +.Dv POLLOUT . +.It Dv POLLWRBAND +Priority data may be written. +.It Dv POLLERR +An error has occurred on the device or socket. +This flag is only valid in the +.Fa revents +bitmask; it is ignored in the +.Fa events +member. +.It Dv POLLHUP +The device or socket has been disconnected. +This event and +.Dv POLLOUT +are mutually-exclusive; a descriptor can never be writable if a hangup has +occurred. +However, this event and +.Dv POLLIN , +.Dv POLLRDNORM , +.Dv POLLRDBAND , +or +.Dv POLLPRI +are not mutually-exclusive. +This flag is only valid in the +.Fa revents +bitmask; it is ignored in the +.Fa events +member. +.It Dv POLLNVAL +The corresponding file descriptor is invalid. +This flag is only valid in the +.Fa revents +bitmask; it is ignored in the +.Fa events +member. +.El +.Pp +The significance and semantics of normal, priority, and high-priority +data are device-specific. +For example, on +.Ox , +the +.Dv POLLPRI +and +.Dv POLLRDBAND +flags may be used to detect when out-of-band socket data may be read +without blocking. +.Pp +The +.Fn ppoll +function is similar to +.Fn poll +except that it specifies the timeout using a timespec structure, +and a null pointer is used to specify an indefinite timeout +instead of +.Dv INFTIM . +Also, if +.Fa mask +is a non-null pointer, +.Fn ppoll +atomically sets the calling thread's signal mask to the signal set +pointed to by +.Fa mask +for the duration of the function call. +In this case, the original signal mask will be restored before +.Fn ppoll +returns. +.Sh RETURN VALUES +Upon error, +.Fn poll +and +.Fn ppoll +return \-1 and set the global variable +.Va errno +to indicate the error. +If the timeout interval was reached before any events occurred, +they return 0. +Otherwise, they return the number of +.Vt pollfd +structures for which +.Fa revents +is non-zero. +.Sh IDIOMS +Care must be taken when converting code from +.Xr select 2 +to +.Fn poll +as they have slightly different semantics. +The first semantic difference is that, unlike +.Xr select 2 , +.Fn poll +has a way of indicating that one or more file descriptors is invalid +by setting a flag in the +.Fa revents +field of corresponding entry of +.Fa fds , +whereas +.Xr select 2 +returns an error (-1) if any of the descriptors with bits set in +the +.Vt fd_set +are invalid. +The second difference is that on EOF there is no guarantee that +.Dv POLLIN +will be set in +.Fa revents , +the caller must also check for +.Dv POLLHUP . +This differs from +.Xr select 2 +where EOF is considered as a read event. +.Pp +Consider the following usage of +.Xr select 2 +that implements a read from the standard input with a +60 second time out: +.Bd -literal -offset indent +struct timeval timeout; +fd_set readfds; +char buf[BUFSIZ]; +int nready; + +timeout.tv_sec = 60; +timeout.tv_usec = 0; +FD_ZERO(&readfds); +FD_SET(STDIN_FILENO, &readfds); +nready = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &timeout); +if (nready == -1) + err(1, "select"); +if (nready == 0) + errx(1, "time out"); +if (FD_ISSET(STDIN_FILENO, &readfds)) { + if (read(STDIN_FILENO, buf, sizeof(buf)) == -1) + err(1, "read"); +} +.Ed +.Pp +This can be converted to +.Fn poll +as follows: +.Bd -literal -offset indent +struct pollfd pfd[1]; +char buf[BUFSIZ]; +int nready; + +pfd[0].fd = STDIN_FILENO; +pfd[0].events = POLLIN; +nready = poll(pfd, 1, 60 * 1000); +if (nready == -1) + err(1, "poll"); +if (nready == 0) + errx(1, "time out"); +if (pfd[0].revents & (POLLERR|POLLNVAL)) + errx(1, "bad fd %d", pfd[0].fd); +if (pfd[0].revents & (POLLIN|POLLHUP)) { + if (read(STDIN_FILENO, buf, sizeof(buf)) == -1) + err(1, "read"); +} +.Ed +.Sh ERRORS +.Fn poll +and +.Fn ppoll +will fail if: +.Bl -tag -width Er +.It Bq Er EAGAIN +The kernel failed to allocate memory for temporary data structures; +a later call may succeed. +.It Bq Er EFAULT +.Fa fds +points outside the process's allocated address space. +.It Bq Er EINTR +A signal was caught before any polled events occurred +and before the timeout elapsed. +.It Bq Er EINVAL +.Fa nfds +was greater than the number of available +file descriptors. +.It Bq Er EINVAL +The timeout passed was invalid. +.El +.Sh SEE ALSO +.Xr clock_gettime 2 , +.Xr getrlimit 2 , +.Xr read 2 , +.Xr select 2 , +.Xr write 2 +.Sh STANDARDS +The +.Fn poll +and +.Fn ppoll +functions conform to +.St -p1003.1-2024 . +.Sh HISTORY +A +.Fn poll +system call appeared in +.At V.3 . +The +.Fn ppoll +function appeared in +.Ox 5.4 . +.Sh CAVEATS +The +.Dv POLLWRBAND +flag is accepted but ignored by the kernel. +.Pp +Because +.Ox +does not implement STREAMS, +there is no distinction between some of the fields in the +.Fa events +and +.Fa revents +bitmasks. +As a result, the +.Dv POLLIN , +.Dv POLLNORM , +and +.Dv POLLRDNORM +flags are equivalent. +Similarly, the +.Dv POLLPRI +and +.Dv POLLRDBAND +flags are also equivalent. diff --git a/man/test_files/mdoc/profil.2 b/man/test_files/mdoc/profil.2 new file mode 100644 index 00000000..46dea31d --- /dev/null +++ b/man/test_files/mdoc/profil.2 @@ -0,0 +1,127 @@ +.\" $OpenBSD: profil.2,v 1.11 2022/12/29 05:00:12 jsg Exp $ +.\" $NetBSD: profil.2,v 1.3 1995/11/22 23:07:23 cgd Exp $ +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley of BSDI. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)profil.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: December 29 2022 $ +.Dt PROFIL 2 +.Os +.Sh NAME +.Nm profil +.Nd control process profiling +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn profil "char *samples" "size_t size" "u_long offset" "u_int scale" +.Sh DESCRIPTION +The +.Fn profil +function enables or disables program counter profiling of the current process. +If profiling is enabled, then at every clock tick, +the kernel updates an appropriate count in the +.Fa samples +buffer. +.Pp +The buffer +.Fa samples +contains +.Fa size +bytes and is divided into a series of 16-bit bins. +Each bin counts the number of times the program counter was in a particular +address range in the process when a clock tick occurred while profiling +was enabled. +For a given program counter address, the number of the corresponding bin +is given by the relation: +.Bd -literal -offset indent +[(pc - offset) / 2] * scale / 65536 +.Ed +.Pp +The +.Fa offset +parameter is the lowest address at which the kernel takes program +counter samples. +The +.Fa scale +parameter ranges from 1 to 65536 and can be used to change the +span of the bins. +A scale of 65536 maps each bin to 2 bytes of address range; +a scale of 32768 gives 4 bytes, 16384 gives 8 bytes and so on. +Intermediate values provide approximate intermediate ranges. +A +.Fa scale +value of 0 disables profiling. +.Sh RETURN VALUES +If the +.Fa scale +value is nonzero and the buffer +.Fa samples +contains an illegal address, +.Fn profil +returns \-1, profiling is terminated, and +.Va errno +is set appropriately. +Otherwise, +.Fn profil +returns 0. +.Sh FILES +.Bl -tag -width /usr/lib/gcrt0.o -compact +.It Pa /usr/lib/gcrt0.o +profiling C run-time startup file +.It Pa gmon.out +conventional name for profiling output file +.El +.Sh ERRORS +The following error may be reported: +.Bl -tag -width Er +.It Bq Er EFAULT +The buffer +.Fa samples +contains an invalid address. +.El +.Sh SEE ALSO +.Xr gprof 1 +.Sh HISTORY +The +.Fn profil +system call first appeared in +.At v5 . +.Sh BUGS +This routine should be named +.Fn profile . +.Pp +The +.Fa samples +argument should really be a vector of type +.Fa "unsigned short" . +.Pp +The format of the gmon.out file is undocumented. diff --git a/man/test_files/mdoc/quotactl.2 b/man/test_files/mdoc/quotactl.2 new file mode 100644 index 00000000..e5ddf698 --- /dev/null +++ b/man/test_files/mdoc/quotactl.2 @@ -0,0 +1,212 @@ +.\" $OpenBSD: quotactl.2,v 1.16 2022/09/11 06:38:11 jmc Exp $ +.\" $NetBSD: quotactl.2,v 1.8 1995/02/27 12:35:43 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Robert Elz at The University of Melbourne. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)quotactl.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 11 2022 $ +.Dt QUOTACTL 2 +.Os +.Sh NAME +.Nm quotactl +.Nd manipulate filesystem quotas +.Sh SYNOPSIS +.In ufs/ufs/quota.h +.In unistd.h +.Ft int +.Fn quotactl "const char *path" "int cmd" "int id" "char *addr" +.Sh DESCRIPTION +The +.Fn quotactl +call enables, disables and +manipulates filesystem quotas. +A quota control command +given by +.Fa cmd +operates on the given filename +.Fa path +for the given user +.Fa id . +The address of an optional command specific data structure, +.Fa addr , +may be given; its interpretation +is discussed below with each command. +.Pp +Currently quotas are supported only for the +.Dq ffs +filesystem. +For +.Dq ffs , +a command is composed of a primary command (see below) +and a command type used to interpret the +.Fa id . +Types are supported for interpretation of user identifiers +and group identifiers. +The +.Dq ffs +specific commands are: +.Bl -tag -width Q_QUOTAON +.It Dv Q_QUOTAON +Enable disk quotas for the filesystem specified by +.Fa path . +The command type specifies the type of the quotas being enabled. +The +.Fa addr +argument specifies a file from which to take the quotas. +The quota file must exist; +it is normally created with the +.Xr quotacheck 8 +program. +The +.Fa id +argument is unused. +Only the superuser may turn quotas on. +.It Dv Q_QUOTAOFF +Disable disk quotas for the filesystem specified by +.Fa path . +The command type specifies the type of the quotas being disabled. +The +.Fa addr +and +.Fa id +arguments are unused. +Only the superuser may turn quotas off. +.It Dv Q_GETQUOTA +Get disk quota limits and current usage for the user or group +(as determined by the command type) with identifier +.Fa id . +.Fa addr +is a pointer to a +.Vt struct dqblk +structure. +.It Dv Q_SETQUOTA +Set disk quota limits for the user or group +(as determined by the command type) with identifier +.Fa id . +.Fa addr +is a pointer to a +.Vt struct dqblk +structure. +The usage fields of +.Vt struct dqblk +structure are ignored. +This call is restricted to the superuser. +.It Dv Q_SETUSE +Set disk usage limits for the user or group +(as determined by the command type) with identifier +.Fa id . +.Fa addr +is a pointer to a +.Vt struct dqblk +structure. +Only the usage fields are used. +This call is restricted to the superuser. +.It Dv Q_SYNC +Update the on-disk copy of quota usages. +The command type specifies which type of quotas are to be updated. +The +.Fa id +and +.Fa addr +parameters are ignored. +.El +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +A +.Fn quotactl +call will fail if: +.Bl -tag -width Er +.It Bq Er EOPNOTSUPP +The kernel has not been compiled with the +.Dv QUOTA +option. +.It Bq Er EUSERS +The quota table cannot be expanded. +.It Bq Er EINVAL +.Fa cmd +or the command type is invalid. +.It Bq Er EACCES +In +.Dv Q_QUOTAON , +the quota file is not a plain file. +.It Bq Er EACCES +Search permission is denied for a component of a path prefix. +.It Bq Er ENOTDIR +A component of a path prefix was not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +A filename does not exist. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating a pathname. +.It Bq Er EROFS +In +.Dv Q_QUOTAON , +the quota file resides on a read-only filesystem. +.It Bq Er EIO +An I/O error occurred while reading from or writing +to a file containing quotas. +.It Bq Er EFAULT +An invalid +.Fa addr +was supplied; the associated structure could not be copied in or out +of the kernel. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.It Bq Er EPERM +The call was privileged and the caller was not the superuser. +.El +.Sh SEE ALSO +.Xr quota 1 , +.Xr fstab 5 , +.Xr edquota 8 , +.Xr quotacheck 8 , +.Xr quotaon 8 , +.Xr repquota 8 +.Sh HISTORY +The +.Fn quotactl +function call appeared in +.Bx 4.3 Reno . +.Sh BUGS +There should be some way to integrate this call with the resource +limit interface provided by +.Xr setrlimit 2 +and +.Xr getrlimit 2 . diff --git a/man/test_files/mdoc/rcs.1 b/man/test_files/mdoc/rcs.1 new file mode 100644 index 00000000..d88e0ce5 --- /dev/null +++ b/man/test_files/mdoc/rcs.1 @@ -0,0 +1,485 @@ +.\" $OpenBSD: rcs.1,v 1.62 2021/03/08 02:47:28 jsg Exp $ +.\" +.\" Copyright (c) 2005 Jean-Francois Brousseau +.\" Copyright (c) 2005 Xavier Santolaria +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 8 2021 $ +.Dt RCS 1 +.Os +.Sh NAME +.Nm rcs +.Nd RCS file management program +.Sh SYNOPSIS +.Nm +.Op Fl IiLqTUV +.Op Fl A Ns Ar oldfile +.Op Fl a Ns Ar users +.Op Fl b Ns Op Ar rev +.Op Fl c Ns Ar string +.Op Fl e Ns Op Ar users +.Op Fl k Ns Ar mode +.Op Fl l Ns Op Ar rev +.Op Fl m Ns Ar rev : Ns Ar msg +.Op Fl o Ns Ar rev +.Op Fl t Ns Op Ar str +.Op Fl u Ns Op Ar rev +.Op Fl x Ns Ar suffixes +.Ar +.Sh DESCRIPTION +Revision Control System (RCS) is a software tool which lets people +manage multiple revisions of text that is revised frequently, such as +source code or documentation. +.Pp +The +.Nm +program is used to create RCS files or manipulate the contents of existing +files. +A set of helper tools is also available: +specific revisions of files may be checked in or out, using +.Xr ci 1 +and +.Xr co 1 ; +differences between revisions viewed or merged, using +.Xr rcsdiff 1 +and +.Xr rcsmerge 1 ; +and information about RCS files and keyword strings displayed using +.Xr rlog 1 +and +.Xr ident 1 . +See the respective manual pages for more information +about these utilities. +.Pp +The following options are supported: +.Bl -tag -width "-e usersXX" +.It Fl A Ns Ar oldfile +Append the access list of +.Ar oldfile +to the access list of the RCS files. +.It Fl a Ns Ar users +Add the usernames specified in the comma-separated list +.Ar users +to the access list of the RCS files. +.It Fl b Ns Op Ar rev +Set the default branch (see below) to +.Ar rev . +If no argument is specified, +the default branch is set to the highest numbered branch. +.It Fl c Ns Ar string +Set comment leader to +.Ar string . +The comment leader specifies the comment character(s) for a file. +This option is useful for compatibility with older RCS implementations +only. +.It Fl e Ns Op Ar users +Remove the usernames specified in the comma-separated list +.Ar users +from the access list of the RCS files. +If +.Ar users +is not specified, all users are removed from the access list. +.It Fl I +Interactive mode. +.It Fl i +Create and initialize a new RCS file. +If the RCS file has no path prefix, try to first create it in the +.Pa ./RCS +subdirectory or, if that fails, in the current directory. +Files created this way contain no revision. +.It Fl k Ns Ar mode +Specify the keyword substitution mode (see below). +.It Fl L +Enable strict locking on the RCS files. +.It Fl l Ns Op Ar rev +Lock revision +.Ar rev +on the RCS files. +.It Fl m Ns Ar rev : Ns Ar msg +Replace revision +.Ar rev Ns 's +log message with +.Ar msg . +.It Fl o Ns Ar rev +Delete one or more revisions. +The specifications of the values or revisions are as follows: +.Bl -tag -width Ds +.It rev +Specific revision. +.It rev1:rev2 +Delete all revisions of a branch between +.Ar rev1 +and +.Ar rev2 . +.It rev1::rev2 +Delete all revisions of a branch between +.Ar rev1 +and +.Ar rev2 +without deleting revisions +.Ar rev1 +and +.Ar rev2 . +.It :rev +Delete all revisions of the branch until revision +.Ar rev . +.It rev: +Delete all revisions of the branch from revision +.Ar rev +until the last revision of the branch. +.El +.It Fl q +Be quiet about reporting. +.It Fl T +Preserve the modification time of RCS files. +.It Fl t Ns Op Ar str +Change the descriptive text. +The argument +.Ar str +is interpreted as the name of a file containing +the descriptive text or, +if prefixed with a +.Sq - , +the actual descriptive text itself. +If no argument is used, the descriptive text is taken from standard input +terminated by end-of-file or by a line containing the +.Sq \&. +character by itself. +.It Fl U +Disable strict locking on the RCS files. +.It Fl u Ns Op Ar rev +Unlock revision +.Ar rev +on the RCS files. +.It Fl V +Print the program's version string and exit. +.It Fl x Ns Ar suffixes +Specifies the suffixes for RCS files. +Suffixes should be separated by the +.Sq / +character. +.El +.Sh BRANCHES AND REVISIONS +Files may be selected by +.Em revision +or, where no revision is specified, +the latest revision of the default +.Em branch +is used. +Revisions are specified either by using the +.Fl r +option or +by appending the revision number to any option that supports it. +Branches are selected using the +.Fl b +option. +.Pp +A file's revision consists of two elements: +release number and level number. +For example, revision 2.3 of a file denotes release 2, level 3. +Levels may also be subdivided into sublevels: +this might happen, for example, +if a parallel development is forked from a lower level revision. +The primary levels and the sublevels belong to separate branches: +the primary levels belong to a branch called HEAD, +while sublevels belong to branches specified by revision. +.Pp +.Nm +also supports the notion of +.Em state . +The state is an arbitrary string of characters used to describe a file +(or a specific revision of a file). +States can be set or changed using the +.Fl s +option, for RCS tools which support it. +The state of a file/revision can be modified without having to check in +a new file/revision. +The default state is +.Sq Exp +(Experimental). +Examples of states could be +.Sq Dev , +.Sq Reviewed , +or +.Sq Stab . +.Pp +In order to make large groups of RCS files more manageable, +RCS tools have the ability to select files by their +.Em symbolic name . +Thus files can be selected by their symbolic name, +rather than numerical revision. +.Xr ci 1 +.Fl N +and +.Fl n +are used to set symbolic names for files. +.Pp +The following methods of file selection are therefore available: +revision number, state, and symbolic name. +For options which take as argument +.Ar rev +or +.Ar state , +any of these methods may be used. +Some examples: +.Bd -literal -offset indent +$ co -r"myproject" foo.c +$ rcs -m1.3:update foo.c +$ ci -s"Exp" bar.c +.Ed +.Sh KEYWORD SUBSTITUTION +As long as source files are edited inside a working directory, +their state can be determined using the +.Xr cvs 1 +.Ic status +or +.Ic log +commands, but as soon as files get exported from +a local working copy, it becomes harder to identify which +revisions they are. +.Pp +.Nm +and +.Xr cvs 1 +use a mechanism known as +.Sq keyword substitution +to help identify the files. +Embedded strings of the form $keyword$ and $keyword:...$ in a file +are replaced with strings of the form $keyword: value$ whenever +a new revision of the file is obtained. +The possible keywords are as follows: +.Bl -tag -width "XrevisionXX" -offset "XXX" +.It $\&Author$ +The name of the user who checked in the revision. +.It $\&Date$ +The date and hour (UTC) the revision was checked in. +.It $\&Header$ +Standard header containing the full pathname of the RCS +file, the revision number, the date (UTC), the author and the state. +.It $\&Id$ and $\&OpenBSD$ +The same content as $\&Header$ but without the path +of the RCS file. +.It $\&Log$ +The log message supplied during commit, preceded by a header +containing the RCS filename, the revision number, the +author, and the date (UTC). +.It $\&Mdocdate$ +Produce a date of the form month name, day number, and year, +suitable for the +.Xr mdoc 7 +.Dq \&Dd +macro. +.It $\&Name$ +The tag name used to check out the file. +.It $\&RCSfile$ +The name of the RCS file, but without a path. +.It $\&Revision$ +The revision number assigned to the revision. +.It $\&Source$ +The full pathname of the RCS file. +.It $\&State$ +The state assigned to the revision. +.El +.Pp +Keyword substitution has its disadvantages: sometimes the +literal text string $\&Author$ is wanted inside a file without +.Nm +or +.Xr cvs 1 +interpreting it as a keyword and expanding it. +The +.Fl k Ns Ar o +option can be used to turn off keyword substitution entirely though. +There is unfortunately no way to selectively turn off keyword substitution. +.Pp +Each file and working directory copy of a file have a stored +default substitution mode. +Substitution modes on files are set by the +.Fl k Ns Ar mode +option. +.Pp +The possible substitution modes are as follows: +.Bl -tag -width Ds -offset 3n +.It Fl k Ns Ar b +Like +.Fl k Ns Ar o , +but also avoids the conversion of line endings. +This option is used to handle binary files. +.It Fl k Ns Ar k +Does not substitute the keywords. +Useful with the +.Xr cvs 1 +.Ic diff +and +.Xr rcsdiff 1 +commands to avoid displaying the differences between keyword substitutions. +.It Fl k Ns Ar kv +The default behaviour. +Keywords are normally substituted i.e. $\&Revision$ becomes +$\&Revision: 1.1 $. +.It Fl k Ns Ar kvl +Like +.Fl k Ns Ar kv , +except that the locker's name is displayed along with the version +if the given revision is currently locked. +This option is normally not useful as +.Nm +and +.Xr cvs 1 +do not use file locking by default. +.It Fl k Ns Ar o +No substitutions are done. +This option is often used with the +.Xr cvs 1 +.Ic import +command to guarantee that files that already contain external keywords +do not get modified. +.It Fl k Ns Ar v +Substitute the value of keywords instead of keywords themselves +e.g. instead of $\&Revision$, only insert 1.1 and not $\&Revision: 1.1 $. +This option must be used with care, as it can only be used once. +It is often used with the +.Xr cvs 1 +.Ic export +command to freeze the values before releasing software. +.El +.Sh ENVIRONMENT +.Bl -tag -width RCSINIT +.It Ev RCSINIT +If set, this variable should contain a list of space-delimited options that +are prepended to the argument list. +.El +.Sh EXIT STATUS +.Ex -std rcs +.Sh EXAMPLES +One of the most common uses of +.Nm +is to track changes to a document containing source code. +.Pp +As an example, +we'll look at a user wishing to track source changes to a file +.Ar foo.c . +.Pp +If the +.Ar RCS +directory does not exist yet, create it as follows and invoke the +check-in command: +.Bd -literal -offset indent +$ mkdir RCS +$ ci foo.c +.Ed +.Pp +This command creates an RCS file +.Ar foo.c,v +in the +.Ar RCS +directory, stores +.Ar foo.c +into it as revision 1.1, and deletes +.Ar foo.c . +.Xr ci 1 +will prompt for a description of the file to be entered. +Whenever a newly created (or updated) file is checked-in, +.Xr ci 1 +will prompt for a log message to be entered which should summarize +the changes made to the file. +That log message will be added to the RCS file along with the new revision. +.Pp +The +.Xr co 1 +command can now be used to obtain a copy of the checked-in +.Ar foo.c,v +file: +.Pp +.Dl $ co foo.c +.Pp +This command checks the file out in unlocked mode. +If a user wants to have exclusive access to the file to make changes to it, +it needs to be checked out in locked mode using the +.Fl l +option of the +.Xr co 1 +command. +Only one concurrent locked checkout of a revision is permitted. +.Pp +Once changes have been made to the +.Pa foo.c +file, and before checking the file in, the +.Xr rcsdiff 1 +command can be used to view changes between the working file +and the most recently checked-in revision: +.Pp +.Dl $ rcsdiff -u foo.c +.Pp +The +.Fl u +option produces a unified diff. +See +.Xr diff 1 +for more information. +.Sh SEE ALSO +.Xr ci 1 , +.Xr co 1 , +.Xr cvs 1 , +.Xr ident 1 , +.Xr rcsclean 1 , +.Xr rcsdiff 1 , +.Xr rcsmerge 1 , +.Xr rlog 1 +.Rs +.\" 4.4BSD PSD:13 +.%A Tichy, Walter F. +.%T "RCS \(em a system for version control" +.%J "Software \(em Practice & Experience" +.%V 15:7 +.%D July, 1985 +.%P pp. 637-654 +.Re +.Sh STANDARDS +OpenRCS is compatible with +Walter Tichy's original RCS implementation. +.Pp +The flags +.Op Fl Mz +have no effect and are provided +for compatibility only. +.Sh HISTORY +The OpenRCS project is a BSD-licensed rewrite of the original +Revision Control System and first appeared in +.Ox 4.0 . +.Sh AUTHORS +.An -nosplit +OpenRCS was written by +.An Jean-Francois Brousseau , +.An Joris Vink , +.An Niall O'Higgins , +and +.An Xavier Santolaria . +.Pp +The original RCS code was written in large parts by +.An Walter F. Tichy +and +.An Paul Eggert . +.Sh CAVEATS +For historical reasons, +the RCS tools do not permit whitespace between options and their arguments. diff --git a/man/test_files/mdoc/rdist.1 b/man/test_files/mdoc/rdist.1 new file mode 100644 index 00000000..93539955 --- /dev/null +++ b/man/test_files/mdoc/rdist.1 @@ -0,0 +1,866 @@ +.\" $OpenBSD: rdist.1,v 1.51 2024/12/30 07:13:33 jmc Exp $ +.\" +.\" Copyright (c) 1983 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $From: rdist.man,v 6.34 1996/01/29 22:37:19 mcooper Exp $ +.\" @(#)rdist.1 6.6 (Berkeley) 5/13/86 +.\" +.Dd $Mdocdate: December 30 2024 $ +.Dt RDIST 1 +.Os +.Sh NAME +.Nm rdist +.Nd remote file distribution client program +.Sh SYNOPSIS +.Nm rdist +.Bk -words +.Op Fl DFnV +.Op Fl A Ar num +.Op Fl a Ar num +.Op Fl c Ar mini_distfile +.Op Fl d Ar var Ns = Ns Ar value +.Op Fl f Ar distfile +.Op Fl L Ar remote_logopts +.Op Fl l Ar local_logopts +.Op Fl M Ar maxproc +.Op Fl m Ar host +.Op Fl o Ar distopts +.Op Fl P Ar rsh-path +.Op Fl p Ar rdistd-path +.Op Fl t Ar timeout +.Op Ar name ... +.Ek +.Sh DESCRIPTION +.Nm +is a program to maintain identical copies of files over multiple hosts. +It preserves the owner, group, mode, and mtime of files if possible and +can update programs that are executing. +.Pp +.Nm +reads commands from +.Pa distfile +to direct the updating of files and/or directories. +If +.Pa distfile +is +.Sq - , +the standard input is used. +If no +.Fl f +option is present, the program looks first for +.Pa distfile , +then +.Pa Distfile , +to use as the input. +If no names are specified on the command line, +.Nm +will update all of the files and directories listed in +.Pa distfile . +If the file +.Pa /etc/Distfile +exists, +it will be run automatically by the clock daemon +.Xr cron 8 , +via the system script +.Xr daily 8 . +.Pp +If +.Ar name +is specified, +it is taken to be the name of a file to be updated +or the label of a command to execute. +If label and file names conflict, it is assumed to be a label. +These may be used together to update specific files using specific commands. +.Pp +.Nm +uses a remote shell command to access each target host. +By default, +.Xr ssh 1 +is used unless overridden by the +.Fl P +option or the +.Ev RSH +environment variable. +If the target host is the string +.Dq localhost +and the remote user name is the same as the local user name, +.Nm +will run the command: +.Bd -literal -offset indent +/bin/sh -c rdistd -S +.Ed +.Pp +Otherwise, +.Nm +will run the command: +.Bd -literal -offset indent +ssh -l rdistd -S +.Ed +.Pp +.Ar host +is the name of the target host; +.Ar login_name +is the name of the user to make the connection as. +.Pp +On each target host +.Nm +will attempt to run the command: +.Bd -literal -offset indent +rdistd -S +.Ed +.Pp +Or if the +.Fl p +option was specified, +.Nm +will attempt to run the command: +.Bd -literal -offset indent + -S +.Ed +.Pp +If no +.Fl p +option is specified, or +.Aq Ar rdistd path +is a simple filename, +.Xr rdistd 1 +or +.Aq Ar rdistd path +must be somewhere in the +.Ev PATH +of the user running +.Nm +on the remote (target) host. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl A Ar num +Set the minimum number of free files (inodes) on a filesystem that must exist +for +.Nm +to update or install a file. +.It Fl a Ar num +Set the minimum amount of free space (in bytes) on a filesystem that must exist +for +.Nm +to update or install a file. +.It Fl c Ar mini_distfile +Forces +.Nm +to interpret the remaining arguments as a small distfile. +The format is: +.Bd -literal -offset indent +$ rdist -c name ... [login@]host[:dest] +.Ed +.Pp +The equivalent distfile is as follows: +.Bd -literal -offset indent +( name ... ) -> [login@]host + install [dest] ; +.Ed +.It Fl D +Enable copious debugging messages. +.It Xo +.Fl d Ar var Ns = Ns Ar value +.Xc +Define +.Ar var +to have +.Ar value . +This +option is used to define or override variable definitions in +.Pa distfile . +.Ar value +can be the empty string, one name, or a list of names surrounded by +parentheses and separated by tabs and/or spaces. +.It Fl F +Do not fork any child +.Nm +processes. +All clients are updated sequentially. +.It Fl f Ar distfile +Set the name of the distfile to +.Ar distfile . +If +.Sq - +(dash) is used then read from standard input (stdin). +.It Fl L Ar remote_logopts +Set remote logging options. +See the section +.Sx MESSAGE LOGGING +for details on the syntax for +.Ar remote_logopts . +.It Fl l Ar local_logopts +Set local logging options. +See the section +.Sx MESSAGE LOGGING +for details on the syntax for +.Ar local_logopts . +.It Fl M Ar maxproc +Set the maximum number of simultaneously running child +.Nm +processes to +.Ar maxproc . +The default is 4. +.It Fl m Ar host +Limit which machines are to be updated. +Multiple +.Fl m +arguments can be given to limit updates to a subset of the hosts listed in +.Pa distfile . +.It Fl n +Print the commands without executing them. +This option is useful for debugging a distfile. +.It Fl o Ar distopts +Specify the dist options to enable. +.Ar distopts +is a comma separated list of options which are listed below. +The valid values for +.Ar distopts +are: +.Bl -tag -width Ds +.It Ic chknfs +Do not check or update files on the target host +that reside on NFS filesystems. +.It Ic chkreadonly +Enable a check on the target host +to see if a file resides on a read-only filesystem. +If a file does, then no checking or updating of the file is attempted. +.It Ic chksym +If the target on the remote host is a symbolic link, but is not on the +master host, the remote target will be left a symbolic link. +This behavior is generally considered a bug in the original version of +.Nm rdist , +but is present to allow compatibility with older versions. +.It Ic compare +Binary comparison. +Perform a binary comparison and update files if they differ rather than +comparing dates and sizes. +.It Ic defgroup Ns Op = Ns Ar groupname +If the group of a file to be transferred does not exist on the destination +host, use the specified group instead. +If +.Ar groupname +is not specified, the +.Em bin +group is used. +.It Ic defowner Ns Op = Ns Ar owner +If the owner of a file to be transferred does not exist on the destination +host, use the specified owner instead. +If +.Ar owner +is not specified, the user +.Em bin +is used. +.It Ic follow +Follow symbolic links. +Copy the file that the link points to rather than the link itself. +.It Ic history +When +.Ic savetargets +and +.Ic history +are both defined then the target file that is updated is first renamed from +.Pa file +to +.Pa file.NNN +where NNN increases for each generation update. +The first generation is 001, and the last is 999. +After 999 generations, the counter is reset and stuck to 001, +and 001 will get overwritten all the time. +This is undesirable behavior, so some other method needs to be devised +to clean up or limit the number of generations. +.It Ic ignlnks +Ignore unresolved links. +.Nm +will normally try to maintain the link structure of files being transferred +and warn the user if all the links cannot be found. +.It Ic nochkgroup +Do not check group ownership of files that already exist. +The file ownership is only set when the file is updated. +.It Ic nochkmode +Do not check file and directory permission modes. +The permission mode is only set when the file is updated. +.It Ic nochkowner +Do not check user ownership of files that already exist. +The file ownership is only set when the file is updated. +.It Ic nodescend +Do not descend into a directory. +Normally, +.Nm +will recursively check directories. +If this option is enabled, then any files listed in the file list in the +distfile that are directories are not recursively scanned. +Only the existence, ownership, and mode of the directory are checked. +.It Ic noexec +Automatically exclude executable binary files in +.Xr elf 5 +format from being checked or updated. +.It Ic numchkgroup +Use the numeric group ID (GID) to check group ownership instead of +the group name. +.It Ic numchkowner +Use the numeric user ID (UID) to check user ownership instead of +the user name. +.It Ic quiet +Quiet mode. +Files that are being modified are normally printed on standard output. +This option suppresses that. +.It Ic remove +Remove extraneous files. +If a directory is being updated, any files that exist on the remote host +that do not exist in the master directory are removed. +This is useful for maintaining truly identical copies of directories. +.It Ic savetargets +Save files that are updated instead of removing them. +Any target file that is updated is first renamed from +.Pa file +to +.Pa file.OLD . +.It Ic sparse +Enable checking for sparse files. +One of the most common types of sparse files are those produced by +.Xr dbopen 3 . +This option adds some additional processing overhead so it should +only be enabled for targets likely to contain sparse files. +.It Ic updateperm +Do not send the whole file when the size and the modification time match. +Instead, just update the ownership, group, and permissions as necessary. +.It Ic verify +Verify that the files are up to date on all the hosts. +Any files that are out of date will be displayed +but no files will be changed and no mail will be sent. +.It Ic whole +Whole mode. +The whole file name is appended to the destination directory name. +Normally, only the last component of a name is used when renaming files. +This will preserve the directory structure of the files being +copied instead of flattening the directory structure. +For example, rdisting a list of files such as +.Pa /p/dir1/f1 +and +.Pa /p/dir2/f2 +to +.Pa /tmp/dir +would create files +.Pa /tmp/dir/p/dir1/f1 +and +.Pa /tmp/dir/p/dir2/f2 +instead of +.Pa /tmp/dir/dir1/f1 +and +.Pa /tmp/dir/dir2/f2 . +.It Ic younger +Younger mode. +Files are normally updated if their +.Em mtime +and +.Em size +(see +.Xr stat 2 ) +disagree. +This option causes +.Nm +not to update files that are younger than the master copy. +This can be used to prevent newer copies on other hosts from being replaced. +A warning message is printed for files which are newer than the master copy. +.El +.It Fl P Ar rsh-path +Set the path to the remote shell command. +.Ar rsh-path +may be a colon separated list of possible pathnames, +in which case the first component of the path to exist is used. +.It Fl p Ar rdistd-path +Set the path where the rdistd server is searched for on the target host. +.It Fl t Ar timeout +Set the timeout period, +in seconds, +for waiting for responses from the remote +.Nm +server. +The default is 900 seconds. +.It Fl V +Print version information and exit. +.El +.Sh DISTFILES +The +.Pa distfile +contains a sequence of entries that specify the files +to be copied, the destination hosts, and what operations to perform +to do the updating. +Each entry has one of the following formats. +.Bd -literal -offset indent + = +[ label: ] -> +[ label: ] :: +.Ed +.Pp +The first format is used for defining variables. +The second format is used for distributing files to other hosts. +The third format is used for making lists of files that have been changed +since some given date. +The +.Ar source list +specifies a list of files and/or directories on the local host which are to +be used as the master copy for distribution. +The +.Ar destination list +is the list of hosts to which these files are to be copied. +Each file in the source list is added to a list of changes if the file +is out of date on the host which is being updated (second format) or +the file is newer than the +.Ar timestamp file +(third format). +.Pp +Newlines, tabs, and blanks are only used as separators and are +otherwise ignored. +Comments begin with +.Sq # +and end with a newline. +.Pp +Variables to be expanded begin with +.Sq $ +followed by one character or a name enclosed in curly braces +(see the examples at the end). +.Pp +Labels are optional. +They are used to identify a specific command to execute +(for example, allowing an update of a subset of a repository). +.Pp +The source and destination lists have the following format: +.Bd -literal -offset indent + +.Ed +or +.Bd -literal -compact -offset indent +`(' `)' +.Ed +.Pp +These simple lists can be modified by using one level of set addition, +subtraction, or intersection like this: +.Pp +.Dl list - list +or +.Dl list + list +or +.Dl list & list +.Pp +If additional modifications are needed (e.g.\& +.Do +all servers and client machines except for the OSF/1 machines +.Dc ) +then the list will have to be explicitly constructed in steps using +.Dq temporary +variables. +.Pp +The shell meta-characters +.Sq \&[ , +.Sq \&] , +.Sq \&{ , +.Sq \&} , +.Sq * , +and +.Sq \&? +are recognized and expanded (on the local host only) in the same way as +.Xr ksh 1 . +They can be escaped with a backslash. +The +.Sq ~ +character is also expanded in the same way as +.Xr ksh 1 +but is expanded separately on the local and destination hosts. +When the +.Fl o Ar whole +option is used with a file name that begins with +.Sq \&~ , +everything except the home directory is appended to the destination name. +File names which do not begin with +.Sq / +or +.Sq ~ +use the destination user's +home directory as the root directory for the rest of the file name. +.Pp +The command list consists of zero or more commands of the following +format: +.Bl -column "except_pat" "" "opt_dest_name" ";" -offset indent +.It install Ta Ta opt_dest_name Ta ; +.It notify Ta Ta "" Ta ; +.It except Ta Ta "" Ta ; +.It except_pat Ta Ta "" Ta ; +.It special Ta Ta string Ta ; +.It cmdspecial Ta Ta string Ta ; +.El +.Pp +The +.Cm install +command is used to copy out-of-date files and/or directories. +Each source file is copied to each host in the destination list. +Directories are recursively copied in the same way. +.Ar opt_dest_name +is an optional parameter to rename files. +If no +.Cm install +command appears in the command list or the destination name is not specified, +the source file name is used. +Directories in the path name will be created if they +do not exist on the remote host. +The +.Fl o Ar distopts +option as specified above has the same semantics as +on the command line except +.Ar distopts +only applies to the files in the source list. +The login name used on the destination host is the same as the local host +unless the destination name is of the format +.Dq login@host . +.Pp +The +.Cm notify +command is used to mail the list of files updated (and any errors +that may have occurred) to the listed names. +If no `@' appears in the name, the destination host is appended to +the name +(e.g. name1@host, name2@host, ...). +.Pp +The +.Cm except +command is used to update all of the files in the source list +.Sy except +for the files listed in +.Ar name list . +This is usually used to copy everything in a directory except certain files. +.Pp +The +.Cm except_pat +command is like the +.Cm except +command except that +.Ar pattern list +is a list of basic regular expressions +(see +.Xr re_format 7 +for details). +If one of the patterns matches some string within a file name, that file will +be ignored. +Note that since +.Sq \e +is a quote character, it must be doubled to become +part of the regular expression. +Variables are expanded in +.Ar pattern list +but not shell file pattern matching characters. +To include a +.Sq $ , +it must be escaped with +.Sq \e . +.Pp +The +.Cm special +command is used to specify +.Xr sh 1 +commands that are to be executed on the remote host after the file in +.Ar name list +is updated or installed. +If the +.Ar name list +is omitted then the shell commands will be executed for every file +updated or installed. +.Ar string +starts and ends with +.Sq \&" +and can cross multiple lines in +.Pa distfile . +Multiple commands to the shell should be separated by `;'. +Commands are executed in the user's home directory on the host +being updated. +The +.Cm special +command can be used, for example, to rebuild private databases +after a program has been updated. +The following environment variables are set for each +.Cm special +command: +.Pp +.Bl -tag -width "BASEFILE" -offset 3n -compact +.It Ev FILE +The full pathname of the local file that was just updated. +.It Ev REMFILE +The full pathname of the remote file that was just updated. +.It BASEFILE +The basename of the remote file that was just updated. +.El +.Pp +The +.Cm cmdspecial +command is similar to the +.Cm special +command, except it is executed only when the entire command is completed +instead of after each file is updated. +The list of files is placed in the +.Ev FILES +environment variable. +Each file name in +.Ev FILES +is separated by a +.Sq :\& +(colon). +.Pp +If a hostname ends in a +.Sq + +(plus sign), +then the plus +is stripped off and NFS checks are disabled. +This is equivalent to disabling the +.Fl o Ar chknfs +option just for this one host. +.Sh MESSAGE LOGGING +.Nm +uses a collection of predefined message +.Em facilities +that each contain a list of message +.Em types +specifying which types of messages to send to that facility. +The local client +and the remote server +each maintain their own copy +of what types of messages to log to what facilities. +.Pp +The +.Fl l +.Ar local_logopts +option specifies the logging options to use locally; +.Fl L +.Ar remote_logopts +specifies the logging options to pass to the remote server. +.Pp +Logging options should be of the form: +.Pp +.D1 facility=types:facility=types... +.Pp +The valid facility names are: +.Bl -tag -width Ds -offset indent +.It Ic file +Log to a file. +To specify the file name, use the format +.Dq file=filename=types . +For example: +.Pp +.Dl file=/tmp/rdist.log=all,debug +.It Ic notify +Use the internal +.Nm +.Ic notify +facility. +This facility is used in conjunction with the +.Ic notify +keyword in a +.Pa distfile +to specify what messages are mailed to the +.Ic notify +address. +.It Ic stdout +Messages to standard output. +.It Ic syslog +Use the +.Xr syslogd 8 +facility. +.El +.Pp +.Ar types +should be a comma separated list of message types. +Each message type specified enables that message level. +This is unlike the +.Xr syslog 3 +system facility which uses an ascending order scheme. +The following are the valid types: +.Bl -tag -width Ds -offset indent +.It Ic all +All but debug messages. +.It Ic change +Things that change. +This includes files that are installed or updated in some way. +.It Ic debug +Debugging information. +.It Ic ferror +Fatal errors. +.It Ic info +General information. +.It Ic nerror +Normal errors that are not fatal. +.It Ic notice +General info about things that change. +This includes things like making directories which are needed in order +to install a specific target, but which are not explicitly specified in the +.Pa distfile . +.It Ic warning +Warnings about errors which are not as serious as +.Ic nerror +type messages. +.El +.Pp +Here is a sample command line option: +.Bd -literal -offset indent +-l stdout=all:syslog=change,notice:file=/tmp/rdist.log=all +.Ed +.Pp +This entry will set local message logging to have all but debug +messages sent to standard output, change and notice messages will +be sent to +.Xr syslog 3 , +and all messages will be written to the file +.Pa /tmp/rdist.log . +.Sh ENVIRONMENT +.Bl -tag -width "TMPDIR" +.It RSH +Name of the default remote shell program to use. +The default is +.Xr ssh 1 . +.It TMPDIR +Name of the temporary directory to use. +The default is +.Pa /tmp . +.El +.Sh FILES +.Bl -tag -width "$TMPDIR/rdist*XXX" -compact +.It Pa {d,D}istfile +.Nm +command file. +.It Pa /etc/Distfile +System-wide +.Nm +command file. +.It Pa $TMPDIR/rdist* +Temporary file for update lists. +.El +.Sh EXAMPLES +The following is an example +.Pa distfile : +.Bd -literal -offset indent +HOSTS = ( matisse root@arpa) + +FILES = ( /bin /lib /usr/bin /usr/games + /usr/include/{*.h,{stand,sys,vax*,pascal,machine}/*.h} + /usr/lib /usr/man/man? /usr/ucb /usr/local/rdist ) + +EXLIB = ( Mail.rc aliases aliases.db crontab dshrc + sendmail.cf sendmail.hf sendmail.st uucp vfont ) + +${FILES} -> ${HOSTS} + install -oremove,chknfs ; + except /usr/lib/${EXLIB} ; + except /usr/games/lib ; + special /usr/lib/sendmail "/usr/lib/sendmail -bi" ; + +srcs: +/usr/src/bin -> arpa + except_pat ( \e\e.o\e$ /SCCS\e$ ) ; + +IMAGEN = (ips dviimp catdvi) + +imagen: +/usr/local/${IMAGEN} -> arpa + install /usr/local/lib ; + notify ralph ; + +sendmail.cf :: stamp.cory + notify root@cory ; +.Ed +.Pp +Using the above +.Pa distfile : +.Pp +Update everything that's out of date, +making any relevant notifications: +.Pp +.Dl $ rdist +.Pp +Update files in +.Pa /usr/src/bin +to host +.Dq arpa , +except for files with names ending +.Dq .o +or +.Dq /SCCS : +.Pp +.Dl $ rdist srcs +.Pp +Update +.Pa sendmail.cf +if it's older than timestamp file +.Pa stamp.cory , +notifying root@cory if an update has happened: +.Pp +.Dl $ rdist sendmail.cf +.Sh SEE ALSO +.Xr rdistd 1 , +.Xr sh 1 , +.Xr ssh 1 , +.Xr re_format 7 , +.Xr daily 8 , +.Xr syslogd 8 +.Sh STANDARDS +The options +.Op Fl bhiNOqRrsvwxy +are still recognized for backwards compatibility. +.Sh CAVEATS +If the basename of a file +(the last component in the pathname) +is +.Sq .\& , +.Nm +assumes the remote (destination) name is a directory. +That is, +.Pa /tmp/.\& +means that +.Pa /tmp +should be a directory on the remote host. +.Sh BUGS +Source files must reside on the local host where +.Nm +is executed. +.Pp +Variable expansion only works for name lists; +there should be a general macro facility. +.Pp +.Nm +aborts on files which have a negative mtime (before Jan 1, 1970). +.Pp +If a hardlinked file is listed more than once in the same target, +.Nm +will report missing links. +Only one instance of a link should be listed in each target. +.Pp +The +.Ic defowner , +.Ic defgroup , +and +.Ic updateperm +options are extensions to the 6.1.0 protocol and will not work with earlier +versions of rdist 6. diff --git a/man/test_files/mdoc/read.2 b/man/test_files/mdoc/read.2 new file mode 100644 index 00000000..c60f84ab --- /dev/null +++ b/man/test_files/mdoc/read.2 @@ -0,0 +1,282 @@ +.\" $OpenBSD: read.2,v 1.38 2021/11/21 23:44:55 jan Exp $ +.\" $NetBSD: read.2,v 1.6 1995/02/27 12:35:47 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)read.2 8.4 (Berkeley) 2/26/94 +.\" +.Dd $Mdocdate: November 21 2021 $ +.Dt READ 2 +.Os +.Sh NAME +.Nm read , +.Nm readv , +.Nm pread , +.Nm preadv +.Nd read input +.Sh SYNOPSIS +.In unistd.h +.Ft ssize_t +.Fn read "int d" "void *buf" "size_t nbytes" +.Ft ssize_t +.Fn pread "int d" "void *buf" "size_t nbytes" "off_t offset" +.Pp +.In sys/uio.h +.Ft ssize_t +.Fn readv "int d" "const struct iovec *iov" "int iovcnt" +.In sys/types.h +.In sys/uio.h +.Ft ssize_t +.Fn preadv "int d" "const struct iovec *iov" "int iovcnt" "off_t offset" +.Sh DESCRIPTION +.Fn read +attempts to read +.Fa nbytes +of data from the object referenced by the descriptor +.Fa d +into the buffer pointed to by +.Fa buf . +.Fn readv +performs the same action, but scatters the input data into the +.Fa iovcnt +buffers specified by the members of the +.Fa iov +array: iov[0], iov[1], ..., iov[iovcnt-1]. +.Fn pread +and +.Fn preadv +perform the same functions, but read from the specified position +.Fa offset +in the file without modifying the file pointer. +.Pp +For +.Fn readv +and +.Fn preadv , +the +.Fa iovec +structure is defined as: +.Bd -literal -offset indent +struct iovec { + void *iov_base; + size_t iov_len; +}; +.Ed +.Pp +Each +.Fa iovec +entry specifies the base address and length of an area +in memory where data should be placed. +.Fn readv +and +.Fn preadv +will always fill an area completely before proceeding to the next. +.Pp +On objects capable of seeking, the +.Fn read +starts at a position given by the pointer associated with +.Fa d +(see +.Xr lseek 2 ) . +Upon return from +.Fn read , +the pointer is incremented by the number of bytes actually read. +.Pp +Objects that are not capable of seeking always read from the current +position. +The value of the pointer associated with such an object is undefined. +.Pp +Upon successful completion, +.Fn read , +.Fn readv , +.Fn pread , +and +.Fn preadv +return the number of bytes actually read and placed in the buffer. +The system guarantees to read the number of bytes requested if +the descriptor references a normal file that has that many bytes left +before the end-of-file, but in no other case. +.Pp +Note that +.Fn readv +and +.Fn preadv +will fail if the value of +.Fa iovcnt +exceeds the constant +.Dv IOV_MAX . +.Sh RETURN VALUES +If successful, the +number of bytes actually read is returned. +Upon reading end-of-file, zero is returned. +Otherwise, a \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn read , +.Fn readv , +.Fn pread , +and +.Fn preadv +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa d +is not a valid file or socket descriptor open for reading. +.It Bq Er EFAULT +Part of +.Fa buf +points outside the process's allocated address space. +.It Bq Er EINTR +A read from a slow device +(i.e. one that might block for an arbitrary amount of time) +was interrupted by the delivery of a signal +before any data arrived. +.It Bq Er EIO +An I/O error occurred while reading from the file system. +.It Bq Er EISDIR +The underlying file is a directory. +.El +.Pp +In addition, +.Fn read +and +.Fn readv +may return the following errors: +.Bl -tag -width Er +.It Bq Er EAGAIN +The file was marked for non-blocking I/O, +and no data were ready to be read. +.It Bq Er ENOTCONN +The file is a socket associated with a connection-oriented protocol +and has not been connected. +.It Bq Er EIO +The process is a member of a background process attempting to read +from its controlling terminal, the process is ignoring or blocking +the +.Dv SIGTTIN +signal or the process group is orphaned. +.El +.Pp +.Fn read +and +.Fn pread +may return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa nbytes +was larger than +.Dv SSIZE_MAX . +.El +.Pp +.Fn pread +and +.Fn preadv +may return the following errors: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa offset +was negative. +.It Bq Er ESPIPE +.Fa d +is associated with a pipe, socket, FIFO, or tty. +.El +.Pp +.Fn readv +and +.Fn preadv +may return the following errors: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa iovcnt +was less than or equal to 0, or greater than +.Dv IOV_MAX . +.It Bq Er EINVAL +The sum of the +.Fa iov_len +values in the +.Fa iov +array overflowed an +.Vt ssize_t . +.It Bq Er EFAULT +Part of +.Fa iov +points outside the process's allocated address space. +.El +.Sh SEE ALSO +.Xr dup 2 , +.Xr fcntl 2 , +.Xr open 2 , +.Xr pipe 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr socket 2 , +.Xr socketpair 2 +.Sh STANDARDS +The +.Fn read , +.Fn readv , +and +.Fn pread +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +A +.Fn read +system call first appeared in +.At v1 ; +.Fn readv +in +.Bx 4.1c ; +.Fn pread +in +.At V.4 ; +and +.Fn preadv +in +.Ox 2.7 . +.Sh CAVEATS +Error checks should explicitly test for \-1. +Code such as +.Bd -literal -offset indent +while ((nr = read(fd, buf, sizeof(buf))) > 0) +.Ed +.Pp +is not maximally portable, as some platforms allow for +.Fa nbytes +to range between +.Dv SSIZE_MAX +and +.Dv SIZE_MAX +\- 2, in which case the return value of an error-free +.Fn read +may appear as a negative number distinct from \-1. +Proper loops should use +.Bd -literal -offset indent +while ((nr = read(fd, buf, sizeof(buf))) != -1 && nr != 0) +.Ed diff --git a/man/test_files/mdoc/reboot.2 b/man/test_files/mdoc/reboot.2 new file mode 100644 index 00000000..ebc13dd5 --- /dev/null +++ b/man/test_files/mdoc/reboot.2 @@ -0,0 +1,163 @@ +.\" $OpenBSD: reboot.2,v 1.19 2017/04/15 18:55:27 guenther Exp $ +.\" $NetBSD: reboot.2,v 1.5 1995/02/27 12:36:02 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)reboot.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: April 15 2017 $ +.Dt REBOOT 2 +.Os +.Sh NAME +.Nm reboot +.Nd reboot system or halt processor +.Sh SYNOPSIS +.In unistd.h +.In sys/reboot.h +.Ft int +.Fn reboot "int howto" +.Sh DESCRIPTION +.Fn reboot +reboots the system. +Only the superuser may reboot a machine on demand. +However, a reboot is invoked +automatically in the event of unrecoverable system failures. +.Pp +.Fa howto +is a mask of options; the system call interface allows the following +options, defined in the include file +.In sys/reboot.h , +to be passed +to the new kernel or the new bootstrap and init programs. +.Bl -tag -width RB_INITNAMEA +.It Dv RB_AUTOBOOT +The default, causing the system to reboot in its usual fashion. +.It Dv RB_ASKNAME +Interpreted by the bootstrap program itself, causing it to +prompt on the console as to what file should be booted. +Normally, the system is booted from the file +.Dq Em xx Ns (0,0)bsd , +where +.Em xx +is the default disk name, +without prompting for the file name. +.It Dv RB_DUMP +Dump kernel memory before rebooting; see +.Xr savecore 8 +for more information. +.It Dv RB_HALT +The processor is simply halted; no reboot takes place. +.It Dv RB_POWERDOWN +If used in conjunction with +.Dv RB_HALT , +and if the system hardware supports the function, the system will be +powered off. +.It Dv RB_USERREQ +By default, the system will halt if +.Fn reboot +is called during startup (before the system has finished autoconfiguration), +even if +.Dv RB_HALT +is not specified. +This is because +.Xr panic 9 Ns s +during startup will probably just repeat on the next boot. +Use of this option implies that the user has requested the action +specified (for example, using the +.Xr ddb 4 +.Ic boot reboot +command), +so the system will reboot if a halt is not explicitly requested. +.It Dv RB_KDB +Load the symbol table and enable a built-in debugger in the system. +This option will have no useful function if the kernel is not configured +for debugging. +Several other options have different meaning if combined +with this option, although their use may not be possible via the +.Fn reboot +call. +See +.Xr ddb 4 +for more information. +.It Dv RB_NOSYNC +Normally, the disks are sync'd (see +.Xr sync 8 ) +before the processor is halted or rebooted. +This option may be useful if file system changes have been made manually +or if the processor is on fire. +.It Dv RB_SINGLE +Normally, the reboot procedure involves an automatic disk consistency +check and then multi-user operations. +.Dv RB_SINGLE +prevents this, booting the system with a single-user shell +on the console. +.Dv RB_SINGLE +is actually interpreted by the +.Xr init 8 +program in the newly booted system. +.Pp +When no options are given (i.e., +.Dv RB_AUTOBOOT +is used), the system is +rebooted from file +.Pa /bsd +in the root file system of unit 0 +of a disk chosen in a processor specific way. +An automatic consistency check of the disks is normally performed +(see +.Xr fsck 8 ) . +.It Dv RB_TIMEBAD +Don't update the hardware clock from the system clock, +presumably because the system clock is suspect. +.El +.Sh RETURN VALUES +If successful, this call never returns. +Otherwise, a \-1 is returned and an error is returned in the global +variable +.Va errno . +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EPERM +The caller is not the superuser. +.El +.Sh SEE ALSO +.Xr ddb 4 , +.Xr crash 8 , +.Xr halt 8 , +.Xr init 8 , +.Xr reboot 8 , +.Xr savecore 8 , +.Xr boot 9 , +.Xr panic 9 +.Sh HISTORY +The +.Fn reboot +system call finally appeared in +.Bx 4.0 . +.Sh BUGS +Not all platforms support all possible arguments. diff --git a/man/test_files/mdoc/rename.2 b/man/test_files/mdoc/rename.2 new file mode 100644 index 00000000..4a27805b --- /dev/null +++ b/man/test_files/mdoc/rename.2 @@ -0,0 +1,296 @@ +.\" $OpenBSD: rename.2,v 1.22 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: rename.2,v 1.7 1995/02/27 12:36:15 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rename.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt RENAME 2 +.Os +.Sh NAME +.Nm rename , +.Nm renameat +.Nd change the name of a file +.Sh SYNOPSIS +.In stdio.h +.Ft int +.Fn rename "const char *from" "const char *to" +.In fcntl.h +.In stdio.h +.Ft int +.Fn renameat "int fromfd" "const char *from" "int tofd" "const char *to" +.Sh DESCRIPTION +The +.Fn rename +function causes the link named +.Fa from +to be renamed as +.Fa to . +If +.Fa to +exists, it is first removed. +Both +.Fa from +and +.Fa to +must be of the same type (that is, both directories or both +non-directories), and must reside on the same file system. +.Pp +.Fn rename +guarantees that if +.Fa to +already exists, an instance of +.Fa to +will always exist, even if the system should crash in +the middle of the operation. +.Pp +If the final component of +.Fa from +is a symbolic link, +the symbolic link is renamed, +not the file or directory to which it points. +.Pp +The +.Fn renameat +function is equivalent to +.Fn rename +except that where +.Fa from +or +.Fa to +specifies a relative path, +the directory entry names used are resolved relative to +the directories associated with file descriptors +.Fa fromfd +or +.Fa tofd +(respectively) instead of the current working directory. +.Pp +If +.Fn renameat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fromfd +or +.Fa tofd +parameter, the current working directory is used for resolving the respective +.Fa from +or +.Fa to +argument. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn rename +and +.Fn renameat +will fail and neither of the argument files will be +affected if: +.Bl -tag -width Er +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +A component of the +.Fa from +path does not exist, +or a path prefix of +.Fa to +does not exist. +.It Bq Er EACCES +A component of either path prefix denies search permission. +.It Bq Er EACCES +The requested change requires writing in a directory that denies write +permission. +.It Bq Er EACCES +The +.Fa from +argument is a directory and denies write permission. +.It Bq Er EPERM +The directory containing +.Fa from +is marked sticky, +and neither the containing directory nor +.Fa from +are owned by the effective user ID. +.It Bq Er EPERM +The +.Fa to +file exists, +the directory containing +.Fa to +is marked sticky, +and neither the containing directory nor +.Fa to +are owned by the effective user ID. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating either pathname. +.It Bq Er EMLINK +The link count on the source file or destination directory is at the maximum. +A rename cannot be completed under these conditions. +.It Bq Er ENOTDIR +A component of either path prefix is not a directory. +.It Bq Er ENOTDIR +.Fa from +is a directory, but +.Fa to +is not a directory. +.It Bq Er EISDIR +.Fa to +is a directory, but +.Fa from +is not a directory. +.It Bq Er EXDEV +The link named by +.Fa to +and the file named by +.Fa from +are on different logical devices (file systems). +Note that this error code will not be returned if the implementation +permits cross-device links. +.It Bq Er ENOSPC +The directory in which the entry for the new name is being placed +cannot be extended because there is no space left on the file +system containing the directory. +.It Bq Er EDQUOT +The directory in which the entry for the new name +is being placed cannot be extended because the +user's quota of disk blocks on the file system +containing the directory has been exhausted. +.It Bq Er EIO +An I/O error occurred while making or updating a directory entry. +.It Bq Er EROFS +The requested link requires writing in a directory on a read-only file +system. +.It Bq Er EFAULT +.Fa from +or +.Fa to +points outside the process's allocated address space. +.It Bq Er EINVAL +.Fa from +is a parent directory of +.Fa to , +or an attempt is made to rename +.Ql \&. +or +.Ql \&.. . +.It Bq Er ENOTEMPTY +.Fa to +is a directory and is not empty. +.El +.Pp +Additionally, +.Fn renameat +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa from +or +.Fa to +argument specifies a relative path and the +.Fa fromfd +or +.Fa tofd +argument, respectively, is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa from +or +.Fa to +argument specifies a relative path and the +.Fa fromfd +or +.Fa tofd +argument, respectively, +is a valid file descriptor but it does not reference a directory. +.It Bq Er EACCES +The +.Fa from +or +.Fa to +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fromfd +or +.Fa tofd +file descriptor, respectively, references. +.El +.Sh SEE ALSO +.Xr mv 1 , +.Xr open 2 , +.Xr symlink 7 +.Sh STANDARDS +The +.Fn rename +and +.Fn renameat +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn renameat +function appeared in +.Ox 5.0 . +.Sh CAVEATS +The system can deadlock if a loop in the file system graph is present. +This loop takes the form of an entry in directory +.Sq Pa a , +say +.Sq Pa a/foo , +being a hard link to directory +.Sq Pa b , +and an entry in +directory +.Sq Pa b , +say +.Sq Pa b/bar , +being a hard link +to directory +.Sq Pa a . +When such a loop exists and two separate processes attempt to +perform +.Ql rename a/foo b/bar +and +.Ql rename b/bar a/foo , +respectively, +the system may deadlock attempting to lock +both directories for modification. +Hard links to directories should be +replaced by symbolic links by the system administrator. diff --git a/man/test_files/mdoc/rev.1 b/man/test_files/mdoc/rev.1 new file mode 100644 index 00000000..408bbeed --- /dev/null +++ b/man/test_files/mdoc/rev.1 @@ -0,0 +1,59 @@ +.\" $OpenBSD: rev.1,v 1.8 2016/10/28 07:28:27 schwarze Exp $ +.\" $NetBSD: rev.1,v 1.3 1995/09/28 08:49:39 tls Exp $ +.\" +.\" Copyright (c) 1985, 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rev.1 8.1 (Berkeley) 6/9/93 +.\" +.Dd $Mdocdate: October 28 2016 $ +.Dt REV 1 +.Os +.Sh NAME +.Nm rev +.Nd reverse lines of a file +.Sh SYNOPSIS +.Nm rev +.Op Ar +.Sh DESCRIPTION +The +.Nm rev +utility copies the specified files to the standard output, reversing the +order of characters in every line. +If no files are specified, the standard input is read. +.Sh ENVIRONMENT +.Bl -tag -width LC_CTYPE +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 . +It decides which byte sequences form characters. +If unset or set to "C", "POSIX", or an unsupported value, +the order of individual bytes is reversed. +.El +.Sh SEE ALSO +.Xr cat 1 , +.Xr cut 1 diff --git a/man/test_files/mdoc/rlog.1 b/man/test_files/mdoc/rlog.1 new file mode 100644 index 00000000..142d4040 --- /dev/null +++ b/man/test_files/mdoc/rlog.1 @@ -0,0 +1,208 @@ +.\" $OpenBSD: rlog.1,v 1.25 2016/08/31 13:09:09 jcs Exp $ +.\" +.\" Copyright (c) 2005 Xavier Santolaria +.\" All rights reserved. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: August 31 2016 $ +.Dt RLOG 1 +.Os +.Sh NAME +.Nm rlog +.Nd display information about RCS files +.Sh SYNOPSIS +.Nm +.Op Fl bhLNRtV +.Op Fl d Ns Ar dates +.Op Fl E Ns Ar endsep +.Op Fl l Ns Op Ar lockers +.Op Fl r Ns Op Ar revs +.Op Fl S Ns Ar revsep +.Op Fl s Ns Ar states +.Op Fl w Ns Op Ar logins +.Op Fl x Ns Ar suffixes +.Op Fl z Ns Ar tz +.Ar +.Sh DESCRIPTION +The +.Nm +program displays information about RCS files. +.Pp +A file's complete RCS history can be displayed +(the default if no options are specified) +or a subset of its history log can be requested, +depending on which options are specified. +RCS keywords are displayed using the +.Xr ident 1 +utility. +.Pp +The following options are supported: +.Bl -tag -width Ds +.It Fl b +Print information about revisions of the default branch only. +.It Fl d Ns Ar dates +Specify revisions with dates matching the specification. +The specification might be as follows: +.Bl -tag -width Ds +.It date1date1 +Select all revisions between +.Ar date1 +and +.Ar date2 . +.It +Select all revisions before +.Ar date . +.It >date or date< +Select all revisions after +.Ar date . +.It date +Select the latest revision before or equal to +.Ar date . +.El +.Pp +The +.Sq \*(Gt +and +.Sq \*(Lt +characters can be followed by the +.Sq = +character to imply an inclusive specification. +Several specifications can be used by separating them with the +.Sq \&; +character. +.Pp +See also the +.Fl z +option, below. +.It Fl E Ns Ar endsep +Print +.Ar endsep +at the end of each RCS file, instead of the default string of +77 equal signs. +.It Fl h +Print the RCS header, +describing a file's branch, lock details, symbolic names, etc. +.It Fl L +Ignore RCS files with no locks set. +.It Fl l Ns Op Ar lockers +Print information about locked revisions only. +If a comma-separated list of login names is specified, +ignore all locks other than those held in the list. +.It Fl N +Do not print symbolic names. +.It Fl R +Print name of RCS file only. +.It Fl r Ns Op Ar revs +Specify revision(s) to list: +.Bl -tag -width Ds +.It rev1,rev2,... +A list of revisions is specified by separating names or numbers +of revisions by the +.Sq \&, +character. +.It rev1:rev2 +List all revisions between +.Ar rev1 +and +.Ar rev2 +(they must be on the same branch). +.It :rev +List all revisions since the beginning of the branch until +.Ar rev +included. +.It rev: +List all revisions of the branch beginning with +.Ar rev . +.It branch +List all revisions of a branch. +.It branch. +List the latest revision of the branch +.Ar branch . +.It branch1:branch2 +List all revisions of branches between +.Ar branch1 +and +.Ar branch2 . +.El +.Pp +Without argument, the +.Fl r +option means the latest revision of the default branch. +.It Fl S Ns Ar revsep +Print +.Ar revsep +at the end of each RCS revision, instead of the default string of +28 dash signs. +.It Fl s Ns Ar states +Print information about revisions whose state matches one of the +specified +.Ar states . +Multiple states may be specified as a comma-separated list. +.It Fl t +Print header and description only. +.It Fl V +Print RCS's version number. +.It Fl w Ns Op Ar logins +Print information about revisions checked in by users specified +in a comma-separated list. +If +.Ar logins +is omitted, the user's login is assumed. +.It Fl x Ns Ar suffixes +Specifies the suffixes for RCS files. +Suffixes should be separated by the +.Sq / +character. +.It Fl z Ns Ar tz +Specify the date output format. +The +.Ar tz +argument should be a numeric UTC offset +(e.g. +02:45 would specify an offset of 2 hours 45 minutes). +.Sq LT +may instead be used to specify local time. +If no argument is given, a default format is used. +This option is also used to set the default time zone for +dates used in the +.Fl d +option. +.El +.Sh ENVIRONMENT +.Bl -tag -width RCSINIT +.It Ev RCSINIT +If set, this variable should contain a list of space-delimited options that +are prepended to the argument list. +.El +.Sh EXIT STATUS +.Ex -std rlog +.Sh EXAMPLES +Print complete information about files: +.Pp +.Dl $ rlog RCS/* +.Pp +Print the names of RCS files with locks set: +.Pp +.Dl $ rlog -L -R RCS/* +.Sh SEE ALSO +.Xr ci 1 , +.Xr co 1 , +.Xr ident 1 , +.Xr rcs 1 , +.Xr rcsclean 1 , +.Xr rcsdiff 1 , +.Xr rcsmerge 1 +.Sh STANDARDS +The flags +.Op Fl qT +have no effect and are provided +for compatibility only. diff --git a/man/test_files/mdoc/rup.1 b/man/test_files/mdoc/rup.1 new file mode 100644 index 00000000..2740fcd8 --- /dev/null +++ b/man/test_files/mdoc/rup.1 @@ -0,0 +1,101 @@ +.\" $OpenBSD: rup.1,v 1.14 2014/04/24 15:03:04 tedu Exp $ +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd $Mdocdate: April 24 2014 $ +.Dt RUP 1 +.Os +.Sh NAME +.Nm rup +.Nd remote status display +.Sh SYNOPSIS +.Nm rup +.Op Fl dhlt +.Op Ar host ... +.Sh DESCRIPTION +.Nm +displays a summary of the current system status of a particular +.Ar host +or all hosts on the local network. +The output shows the current time of day, how long the system has +been up, +and the load averages. +The load average numbers give the number of jobs in the run queue +averaged over 1, 5, and 15 minutes. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +For each host, report what its local time is. +This is useful for checking time synchronization on a network. +.It Fl h +Sort the display alphabetically by host name. +.It Fl l +Sort the display by load average. +.It Fl t +Sort the display by up time. +.El +.Pp +The +.Xr rpc.rstatd 8 +daemon must be running on the remote host for this command to +work. +.Nm +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rstat.x . +.Sh EXAMPLES +.Bd -literal -offset indent +$ rup otherhost +otherhost up 6 days, 16:45, load average: 0.20, 0.23, 0.18 +.Ed +.Sh DIAGNOSTICS +.Bl -diag +.It rup: RPC: Program not registered +The +.Xr rpc.rstatd 8 +daemon has not been started on the remote host. +.It rup: RPC: Timed out +A communication error occurred. +Either the network is excessively congested, or the +.Xr rpc.rstatd 8 +daemon has terminated on the remote host. +.It rup: RPC: Port mapper failure - RPC: Timed out +The remote host is not running the portmapper (see +.Xr portmap 8 ) , +and cannot accommodate any RPC-based services. +The host may be down. +.El +.Sh SEE ALSO +.Xr portmap 8 , +.Xr rpc.rstatd 8 +.Sh HISTORY +The +.Nm +command +appeared in SunOS. diff --git a/man/test_files/mdoc/sched_yield.2 b/man/test_files/mdoc/sched_yield.2 new file mode 100644 index 00000000..ca7c6df7 --- /dev/null +++ b/man/test_files/mdoc/sched_yield.2 @@ -0,0 +1,49 @@ +.\" $OpenBSD: sched_yield.2,v 1.1 2014/11/14 00:24:28 guenther Exp $ +.\" +.\" Copyright (c) 2014 Philip Guenther +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: November 14 2014 $ +.Dt SCHED_YIELD 2 +.Os +.Sh NAME +.Nm sched_yield +.Nd yield the processor +.Sh SYNOPSIS +.In sched.h +.Ft int +.Fn sched_yield void +.Sh DESCRIPTION +The +.Fn sched_yield +function makes the current thread yield the processor and be put at +the end of its run queue without altering its priority. +.Sh RETURN VALUES +.Rv -std +.Sh STANDARDS +The +.Fn sched_yield +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn sched_yield +system call appeared in +.Ox 4.2 . +.Sh CAVEATS +The effect of +.Fn sched_yield +is only precisely defined for real-time scheduling classes, +none of which are currently supported by +.Ox . diff --git a/man/test_files/mdoc/scp.1 b/man/test_files/mdoc/scp.1 new file mode 100644 index 00000000..aa2e2d8b --- /dev/null +++ b/man/test_files/mdoc/scp.1 @@ -0,0 +1,364 @@ +.\" +.\" scp.1 +.\" +.\" Author: Tatu Ylonen +.\" +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" Created: Sun May 7 00:14:37 1995 ylo +.\" +.\" $OpenBSD: scp.1,v 1.113 2024/12/06 15:12:56 djm Exp $ +.\" +.Dd $Mdocdate: December 6 2024 $ +.Dt SCP 1 +.Os +.Sh NAME +.Nm scp +.Nd OpenSSH secure file copy +.Sh SYNOPSIS +.Nm scp +.Op Fl 346ABCOpqRrsTv +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path +.Op Fl F Ar ssh_config +.Op Fl i Ar identity_file +.Op Fl J Ar destination +.Op Fl l Ar limit +.Op Fl o Ar ssh_option +.Op Fl P Ar port +.Op Fl S Ar program +.Op Fl X Ar sftp_option +.Ar source ... target +.Sh DESCRIPTION +.Nm +copies files between hosts on a network. +.Pp +.Nm +uses the SFTP protocol over a +.Xr ssh 1 +connection for data transfer, and uses the same authentication and provides +the same security as a login session. +.Pp +.Nm +will ask for passwords or passphrases if they are needed for +authentication. +.Pp +The +.Ar source +and +.Ar target +may be specified as a local pathname, a remote host with optional path +in the form +.Sm off +.Oo user @ Oc host : Op path , +.Sm on +or a URI in the form +.Sm off +.No scp:// Oo user @ Oc host Oo : port Oc Op / path . +.Sm on +Local file names can be made explicit using absolute or relative pathnames +to avoid +.Nm +treating file names containing +.Sq :\& +as host specifiers. +.Pp +When copying between two remote hosts, if the URI format is used, a +.Ar port +cannot be specified on the +.Ar target +if the +.Fl R +option is used. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 3 +Copies between two remote hosts are transferred through the local host. +Without this option the data is copied directly between the two remote +hosts. +Note that, when using the legacy SCP protocol (via the +.Fl O +flag), this option +selects batch mode for the second host as +.Nm +cannot ask for passwords or passphrases for both hosts. +This mode is the default. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl A +Allows forwarding of +.Xr ssh-agent 1 +to the remote system. +The default is not to forward an authentication agent. +.It Fl B +Selects batch mode (prevents asking for passwords or passphrases). +.It Fl C +Compression enable. +Passes the +.Fl C +flag to +.Xr ssh 1 +to enable compression. +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfer. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_path +Connect directly to a local SFTP server program rather than a +remote one via +.Xr ssh 1 . +This option may be useful in debugging the client and server. +.It Fl F Ar ssh_config +Specifies an alternative +per-user configuration file for +.Nm ssh . +This option is directly passed to +.Xr ssh 1 . +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . +.It Fl J Ar destination +Connect to the target host by first making an +.Nm +connection to the jump host described by +.Ar destination +and then establishing a TCP forwarding to the ultimate destination from +there. +Multiple jump hops may be specified separated by comma characters. +This is a shortcut to specify a +.Cm ProxyJump +configuration directive. +This option is directly passed to +.Xr ssh 1 . +.It Fl l Ar limit +Limits the used bandwidth, specified in Kbit/s. +.It Fl O +Use the legacy SCP protocol for file transfers instead of the SFTP protocol. +Forcing the use of the SCP protocol may be necessary for servers that do +not implement SFTP, for backwards-compatibility for particular filename +wildcard patterns and for expanding paths with a +.Sq ~ +prefix for older SFTP servers. +.It Fl o Ar ssh_option +Can be used to pass options to +.Nm ssh +in the format used in +.Xr ssh_config 5 . +This is useful for specifying options +for which there is no separate +.Nm scp +command-line flag. +For full details of the options listed below, and their possible values, see +.Xr ssh_config 5 . +.Pp +.Bl -tag -width Ds -offset indent -compact +.It AddKeysToAgent +.It AddressFamily +.It BatchMode +.It BindAddress +.It BindInterface +.It CASignatureAlgorithms +.It CanonicalDomains +.It CanonicalizeFallbackLocal +.It CanonicalizeHostname +.It CanonicalizeMaxDots +.It CanonicalizePermittedCNAMEs +.It CertificateFile +.It ChannelTimeout +.It CheckHostIP +.It Ciphers +.It ClearAllForwardings +.It Compression +.It ConnectTimeout +.It ConnectionAttempts +.It ControlMaster +.It ControlPath +.It ControlPersist +.It DynamicForward +.It EnableEscapeCommandline +.It EnableSSHKeysign +.It EscapeChar +.It ExitOnForwardFailure +.It FingerprintHash +.It ForkAfterAuthentication +.It ForwardAgent +.It ForwardX11 +.It ForwardX11Timeout +.It ForwardX11Trusted +.It GSSAPIAuthentication +.It GSSAPIDelegateCredentials +.It GatewayPorts +.It GlobalKnownHostsFile +.It HashKnownHosts +.It Host +.It HostKeyAlgorithms +.It HostKeyAlias +.It HostbasedAcceptedAlgorithms +.It HostbasedAuthentication +.It Hostname +.It IPQoS +.It IdentitiesOnly +.It IdentityAgent +.It IdentityFile +.It IgnoreUnknown +.It Include +.It KbdInteractiveAuthentication +.It KbdInteractiveDevices +.It KexAlgorithms +.It KnownHostsCommand +.It LocalCommand +.It LocalForward +.It LogLevel +.It LogVerbose +.It MACs +.It NoHostAuthenticationForLocalhost +.It NumberOfPasswordPrompts +.It ObscureKeystrokeTiming +.It PKCS11Provider +.It PasswordAuthentication +.It PermitLocalCommand +.It PermitRemoteOpen +.It Port +.It PreferredAuthentications +.It ProxyCommand +.It ProxyJump +.It ProxyUseFdpass +.It PubkeyAcceptedAlgorithms +.It PubkeyAuthentication +.It RekeyLimit +.It RemoteCommand +.It RemoteForward +.It RequestTTY +.It RequiredRSASize +.It RevokedHostKeys +.It SecurityKeyProvider +.It SendEnv +.It ServerAliveCountMax +.It ServerAliveInterval +.It SessionType +.It SetEnv +.It StdinNull +.It StreamLocalBindMask +.It StreamLocalBindUnlink +.It StrictHostKeyChecking +.It SyslogFacility +.It TCPKeepAlive +.It Tag +.It Tunnel +.It TunnelDevice +.It UpdateHostKeys +.It User +.It UserKnownHostsFile +.It VerifyHostKeyDNS +.It VisualHostKey +.It XAuthLocation +.El +.It Fl P Ar port +Specifies the port to connect to on the remote host. +Note that this option is written with a capital +.Sq P , +because +.Fl p +is already reserved for preserving the times and mode bits of the file. +.It Fl p +Preserves modification times, access times, and file mode bits from the +source file. +.It Fl q +Quiet mode: disables the progress meter as well as warning and diagnostic +messages from +.Xr ssh 1 . +.It Fl R +Copies between two remote hosts are performed by connecting to the origin +host and executing +.Nm +there. +This requires that +.Nm +running on the origin host can authenticate to the destination host without +requiring a password. +.It Fl r +Recursively copy entire directories. +Note that +.Nm +follows symbolic links encountered in the tree traversal. +.It Fl S Ar program +Name of +.Ar program +to use for the encrypted connection. +The program must understand +.Xr ssh 1 +options. +.It Fl T +Disable strict filename checking. +By default when copying files from a remote host to a local directory +.Nm +checks that the received filenames match those requested on the command-line +to prevent the remote end from sending unexpected or unwanted files. +Because of differences in how various operating systems and shells interpret +filename wildcards, these checks may cause wanted files to be rejected. +This option disables these checks at the expense of fully trusting that +the server will not send unexpected filenames. +.It Fl v +Verbose mode. +Causes +.Nm +and +.Xr ssh 1 +to print debugging messages about their progress. +This is helpful in +debugging connection, authentication, and configuration problems. +.It Fl X Ar sftp_option +Specify an option that controls aspects of SFTP protocol behaviour. +The valid options are: +.Bl -tag -width Ds +.It Cm nrequests Ns = Ns Ar value +Controls how many concurrent SFTP read or write requests may be in progress +at any point in time during a download or upload. +By default 64 requests may be active concurrently. +.It Cm buffer Ns = Ns Ar value +Controls the maximum buffer size for a single SFTP read/write operation used +during download or upload. +By default a 32KB buffer is used. +.El +.El +.Sh EXIT STATUS +.Ex -std scp +.Sh SEE ALSO +.Xr sftp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr sftp-server 8 , +.Xr sshd 8 +.Sh HISTORY +.Nm +is based on the rcp program in +.Bx +source code from the Regents of the University of California. +.Pp +Since OpenSSH 9.0, +.Nm +has used the SFTP protocol for transfers by default. +.Sh AUTHORS +.An Timo Rinne Aq Mt tri@iki.fi +.An Tatu Ylonen Aq Mt ylo@cs.hut.fi +.Sh CAVEATS +The legacy SCP protocol (selected by the +.Fl O +flag) requires execution of the remote user's shell to perform +.Xr glob 3 +pattern matching. +This requires careful quoting of any characters that have special meaning to +the remote shell, such as quote characters. diff --git a/man/test_files/mdoc/select.2 b/man/test_files/mdoc/select.2 new file mode 100644 index 00000000..3dc9d35a --- /dev/null +++ b/man/test_files/mdoc/select.2 @@ -0,0 +1,248 @@ +.\" $OpenBSD: select.2,v 1.45 2022/01/21 16:18:16 deraadt Exp $ +.\" $NetBSD: select.2,v 1.5 1995/06/27 22:32:28 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)select.2 8.2 (Berkeley) 3/25/94 +.\" +.Dd $Mdocdate: January 21 2022 $ +.Dt SELECT 2 +.Os +.Sh NAME +.Nm select , +.Nm pselect , +.Nm FD_SET , +.Nm FD_CLR , +.Nm FD_ISSET , +.Nm FD_ZERO +.Nd synchronous I/O multiplexing +.Sh SYNOPSIS +.In sys/select.h +.Ft int +.Fn select "int nfds" "fd_set *readfds" "fd_set *writefds" "fd_set *exceptfds" "struct timeval *timeout" +.Ft int +.Fn pselect "int nfds" "fd_set *readfds" "fd_set *writefds" "fd_set *exceptfds" "const struct timespec *timeout" "const sigset_t *mask" +.Fn FD_SET fd &fdset +.Fn FD_CLR fd &fdset +.Fn FD_ISSET fd &fdset +.Fn FD_ZERO &fdset +.Sh DESCRIPTION +.Fn select +examines the I/O descriptor sets whose addresses are passed in +.Fa readfds , +.Fa writefds , +and +.Fa exceptfds +to see if some of their descriptors +are ready for reading, are ready for writing, or have an exceptional +condition pending, respectively. +Exceptional conditions include the presence of out-of-band data +on a socket. +The first +.Fa nfds +descriptors are checked in each set; +i.e., the descriptors from 0 through +.Fa nfds Ns -1 +in the descriptor sets are examined. +On return, +.Fn select +replaces the given descriptor sets +with subsets consisting of those descriptors that are ready +for the requested operation. +.Fn select +returns the total number of ready descriptors in all the sets. +.Pp +The descriptor sets are stored as bit fields in arrays of integers. +The following macros are provided for manipulating such descriptor sets: +.Fn FD_ZERO &fdset +initializes a descriptor set +.Fa fdset +to the null set. +.Fn FD_SET fd &fdset +includes a particular descriptor +.Fa fd +in +.Fa fdset . +.Fn FD_CLR fd &fdset +removes +.Fa fd +from +.Fa fdset . +.Fn FD_ISSET fd &fdset +is non-zero if +.Fa fd +is a member of +.Fa fdset , +zero otherwise. +The behavior of these macros is undefined if +a descriptor value is less than zero or greater than or equal to +.Dv FD_SETSIZE , +which is normally at least equal +to the maximum number of descriptors supported by the system. +.Pp +If +.Fa timeout +is a non-null pointer, it specifies a maximum interval to wait for the +selection to complete. +If +.Fa timeout +is a null pointer, the select blocks indefinitely. +To effect a poll, the +.Fa timeout +argument should be non-null, pointing to a zero-valued timeval structure. +.Fa timeout +is not changed by +.Fn select , +and may be reused on subsequent calls; however, it is good style to +re-initialize it before each invocation of +.Fn select . +.Pp +Any of +.Fa readfds , +.Fa writefds , +and +.Fa exceptfds +may be given as null pointers if no descriptors are of interest. +.Pp +The +.Fn pselect +function is similar to +.Fn select +except that it specifies the timeout using a timespec structure. +Also, if +.Fa mask +is a non-null pointer, +.Fn pselect +atomically sets the calling thread's signal mask to the signal set +pointed to by +.Fa mask +for the duration of the function call. +In this case, the original signal mask will be restored before +.Fn pselect +returns. +.Sh RETURN VALUES +If successful, +.Fn select +and +.Fn pselect +return the number of ready descriptors that are contained in +the descriptor sets. +If a descriptor is included in multiple descriptor sets, +each inclusion is counted separately. +If the time limit expires before any descriptors become ready, +they return 0. +.Pp +Otherwise, if +.Fn select +or +.Fn pselect +return with an error, including one due to an interrupted call, +they return \-1, +and the descriptor sets will be unmodified. +.Sh ERRORS +An error return from +.Fn select +or +.Fn pselect +indicates: +.Bl -tag -width Er +.It Bq Er EFAULT +One or more of +.Fa readfds , +.Fa writefds , +or +.Fa exceptfds +points outside the process's allocated address space. +.It Bq Er EBADF +One of the descriptor sets specified an invalid descriptor. +.It Bq Er EINTR +A signal was delivered before the time limit expired and +before any of the selected descriptors became ready. +.It Bq Er EINVAL +The specified time limit is invalid. +One of its components is negative or too large. +.It Bq Er EINVAL +.Fa nfds +was less than 0. +.El +.Sh SEE ALSO +.Xr accept 2 , +.Xr clock_gettime 2 , +.Xr connect 2 , +.Xr gettimeofday 2 , +.Xr poll 2 , +.Xr read 2 , +.Xr recv 2 , +.Xr send 2 , +.Xr write 2 , +.Xr getdtablesize 3 +.Sh STANDARDS +The +.Fn select +and +.Fn pselect +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn select +system call first appeared in +.Bx 4.1c . +The +.Fn pselect +system call has been available since +.Ox 5.4 . +.Sh BUGS +Although the provision of +.Xr getdtablesize 3 +was intended to allow user programs to be written independent +of the kernel limit on the number of open files, the dimension +of a sufficiently large bit field for select remains a problem. +If descriptor values greater than FD_SETSIZE are possible in +a program, use +.Xr poll 2 +instead. +.Pp +.Fn select +should probably have been designed to return the time remaining from the +original timeout, if any, by modifying the time value in place. +Even though some systems stupidly act in this different way, it is +unlikely this semantic will ever be commonly implemented, as the +change causes massive source code compatibility problems. +Furthermore, recent new standards have dictated the current behaviour. +In general, due to the existence of those brain-damaged +non-conforming systems, it is unwise to assume that the timeout +value will be unmodified by the +.Fn select +call, and the caller should reinitialize it on each invocation. +Calculating the delta is easily done by calling +.Xr gettimeofday 2 +before and after the call to +.Fn select , +and using +.Xr timersub 3 . diff --git a/man/test_files/mdoc/semget.2 b/man/test_files/mdoc/semget.2 new file mode 100644 index 00000000..44dc62f4 --- /dev/null +++ b/man/test_files/mdoc/semget.2 @@ -0,0 +1,154 @@ +.\" $OpenBSD: semget.2,v 1.20 2021/10/23 21:17:45 jmc Exp $ +.\" $NetBSD: semget.2,v 1.2 1997/03/27 08:20:41 mikel Exp $ +.\" +.\" Copyright (c) 1995 Frank van der Linden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project +.\" by Frank van der Linden +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\"/ +.Dd $Mdocdate: October 23 2021 $ +.Dt SEMGET 2 +.Os +.Sh NAME +.Nm semget +.Nd get semaphore set +.Sh SYNOPSIS +.In sys/sem.h +.Ft int +.Fn semget "key_t key" "int nsems" "int semflg" +.Sh DESCRIPTION +The +.Fn semget +system call returns the semaphore identifier associated with +.Fa key . +.Pp +A new set containing +.Fa nsems +semaphores is created if either +.Fa key +is equal to +.Dv IPC_PRIVATE , +or +.Fa key +does not have a semaphore set associated with it and the +.Dv IPC_CREAT +bit is set in +.Fa semflg . +.Pp +The access modes of the created semaphores is specified in +.Fa semflg +as a bitwise OR of zero or more of the following values: +.Bd -literal -offset indent +SEM_A alter permission for owner +SEM_R read permission for owner + +SEM_A >> 3 alter permission for group +SEM_R >> 3 read permission for group + +SEM_A >> 6 alter permission for other +SEM_R >> 6 read permission for other +.Ed +.Pp +If a new set of semaphores is created, the data structure associated with it +(the +.Va semid_ds +structure, see +.Xr semctl 2 ) +is initialized as follows: +.Bl -bullet +.It +.Va sem_perm.cuid +and +.Va sem_perm.uid +are set to the effective UID of the calling process. +.It +.Va sem_perm.gid +and +.Va sem_perm.cgid +are set to the effective GID of the calling process. +.It +.Va sem_perm.mode +is set to the lower 9 bits of +.Fa semflg . +.It +.Va sem_nsems +is set to the value of +.Fa nsems . +.It +.Va sem_ctime +is set to the current time. +.It +.Va sem_otime +is set to 0. +.El +.Sh RETURN VALUES +.Fn semget +returns a non-negative semaphore identifier if successful. +Otherwise, \-1 is returned and +.Va errno +is set to reflect the error. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EACCES +The caller has no permission to access a semaphore set already associated with +.Fa key . +.It Bq Er EEXIST +Both +.Dv IPC_CREAT +and +.Dv IPC_EXCL +are set in +.Fa semflg , +and a semaphore set is already associated with +.Fa key . +.It Bq Er EINVAL +.Va nsems +is less than or equal to 0 or greater than the system limit for the +number in a semaphore set. +.Pp +A semaphore set associated with +.Fa key +exists, but has fewer semaphores than the number specified in +.Fa nsems . +.It Bq Er ENOSPC +A new set of semaphores could not be created because the system limit +for the number of semaphores or the number of semaphore sets has been +reached. +.It Bq Er ENOENT +.Dv IPC_CREAT +was not set in +.Fa semflg +and no semaphore set associated with +.Fa key +was found. +.El +.Sh SEE ALSO +.Xr ipcrm 1 , +.Xr ipcs 1 , +.Xr semctl 2 , +.Xr semop 2 , +.Xr ftok 3 diff --git a/man/test_files/mdoc/send.2 b/man/test_files/mdoc/send.2 new file mode 100644 index 00000000..960df1d1 --- /dev/null +++ b/man/test_files/mdoc/send.2 @@ -0,0 +1,289 @@ +.\" $OpenBSD: send.2,v 1.35 2022/09/09 13:52:59 mbuhl Exp $ +.\" $NetBSD: send.2,v 1.6 1996/01/15 01:17:18 thorpej Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)send.2 8.2 (Berkeley) 2/21/94 +.\" +.Dd $Mdocdate: September 9 2022 $ +.Dt SEND 2 +.Os +.Sh NAME +.Nm send , +.Nm sendto , +.Nm sendmsg , +.Nm sendmmsg +.Nd send a message from a socket +.Sh SYNOPSIS +.In sys/socket.h +.Ft ssize_t +.Fn send "int s" "const void *msg" "size_t len" "int flags" +.Ft ssize_t +.Fn sendto "int s" "const void *msg" "size_t len" "int flags" "const struct sockaddr *to" "socklen_t tolen" +.Ft ssize_t +.Fn sendmsg "int s" "const struct msghdr *msg" "int flags" +.Ft int +.Fn sendmmsg "int s" "const struct mmsghdr *mmsg" "unsigned int vlen" "int flags" +.Sh DESCRIPTION +.Fn send , +.Fn sendto , +.Fn sendmsg , +and +.Fn sendmmsg +are used to transmit a message to another socket. +.Fn send +may be used only when the socket is in a +.Em connected +state, while +.Fn sendto , +.Fn sendmsg , +and +.Fn sendmmsg +may be used at any time. +.Pp +The address of the target is given by +.Fa to +with +.Fa tolen +specifying its size. +The length of the message is given by +.Fa len . +If the message is too long to pass atomically through the +underlying protocol, the error +.Er EMSGSIZE +is returned, and +the message is not transmitted. +.Pp +No indication of failure to deliver is implicit in a +.Fn send . +Locally detected errors are indicated by a return value of \-1. +.Pp +If no messages space is available at the socket to hold +the message to be transmitted, then +.Fn send +normally blocks, unless the socket has been placed in +non-blocking I/O mode. +The +.Xr select 2 +or +.Xr poll 2 +system calls may be used to determine when it is possible to +send more data. +.Pp +The +.Fa flags +parameter may include one or more of the following: +.Pp +.Bl -tag -width "MSG_DONTROUTEXX" -offset indent -compact +.It Dv MSG_DONTROUTE +bypass routing tables, silently ignored +.It Dv MSG_DONTWAIT +don't block +.It Dv MSG_EOR +terminate the record (SOCK_SEQPACKET only) +.It Dv MSG_NOSIGNAL +don't send +.Dv SIGPIPE +.It Dv MSG_OOB +process out-of-band data +.El +.Pp +The flag +.Dv MSG_OOB +is used to send +.Dq out-of-band +data on sockets that support this notion (e.g., +.Dv SOCK_STREAM ) ; +the underlying protocol must also support +.Dq out-of-band +data. +.Dv MSG_NOSIGNAL +is used to request not to send the +.Dv SIGPIPE +signal if an attempt to send is made on a socket that is shut down for +writing or no longer connected. +.Pp +See +.Xr recv 2 +for a description of the +.Fa msghdr +and +.Fa mmsghdr +structures. +.Sh RETURN VALUES +The +.Fn send , +.Fn sendto , +and +.Fn sendmsg +calls return the number of characters sent, or \-1 +if an error occurred. +The +.Fn sendmmsg +call returns the number of messages sent, or \-1 +if an error occurred before the first message has been sent. +.Sh ERRORS +.Fn send , +.Fn sendto , +and +.Fn sendmsg +fail if: +.Bl -tag -width Er +.It Bq Er EBADF +An invalid descriptor was specified. +.It Bq Er ENOTSOCK +The argument +.Fa s +is not a socket. +.It Bq Er EFAULT +An invalid user space address was specified for a parameter. +.It Bq Er EMSGSIZE +The socket requires that message be sent atomically, +and the size of the message to be sent made this impossible. +.It Bq Er EAGAIN +The socket is marked non-blocking or the +.Dv MSG_DONTWAIT +flag is set and the requested operation +would block. +.It Bq Er ENOBUFS +The system was unable to allocate an internal buffer. +The operation may succeed when buffers become available. +.It Bq Er ENOBUFS +The output queue for a network interface was full. +This generally indicates that the interface has stopped sending, +but may be caused by transient congestion. +.It Bq Er EACCES +The connection was blocked by +.Xr pf 4 , +or +.Dv SO_BROADCAST +is not set on the socket +and a broadcast address was given as the destination. +.It Bq Er EHOSTUNREACH +The destination address specified an unreachable host. +.It Bq Er EINVAL +The +.Fa flags +parameter is invalid. +.It Bq Er EHOSTDOWN +The destination address specified a host that is down. +.It Bq Er ENETDOWN +The destination address specified a network that is down. +.It Bq Er ECONNREFUSED +The destination host rejected the message (or a previous one). +This error can only be returned by connected sockets. +.It Bq Er ENOPROTOOPT +There was a problem sending the message. +This error can only be returned by connected sockets. +.It Bq Er EDESTADDRREQ +The socket is not connected, and no destination address was specified. +.It Bq Er EPIPE +The socket is shut down for writing or not longer connected and the +.Dv MSG_NOSIGNAL +flag is set. +.El +.Pp +In addition, +.Fn send +and +.Fn sendto +may return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa len +was larger than +.Dv SSIZE_MAX . +.El +.Pp +.Fn sendto +and +.Fn sendmsg +may return the following errors: +.Bl -tag -width Er +.It Bq Er EADDRNOTAVAIL +No suitable address is available on the local machine. +.It Bq Er EAFNOSUPPORT +Addresses in the specified address family cannot be used with this socket. +.It Bq Er EISCONN +The socket is already connected, and a destination address was specified. +.El +.Pp +.Fn sendmsg +may return the following errors: +.Bl -tag -width Er +.It Bq Er EINVAL +The sum of the +.Fa iov_len +values in the +.Fa msg_iov +array overflowed an +.Em ssize_t . +.It Bq Er EMSGSIZE +The +.Fa msg_iovlen +member of +.Fa msg +was less than 0 or larger than +.Dv IOV_MAX . +.It Bq Er EMFILE +The message contains control information utilizing +.Xr CMSG_DATA 3 +to pass file descriptors, but too many file descriptors +are already in-flight. +.El +.Sh SEE ALSO +.Xr fcntl 2 , +.Xr getsockopt 2 , +.Xr poll 2 , +.Xr recv 2 , +.Xr select 2 , +.Xr socket 2 , +.Xr write 2 , +.Xr CMSG_DATA 3 +.Sh STANDARDS +The +.Fn send , +.Fn sendto , +and +.Fn sendmsg +functions conform to +.St -p1003.1-2008 . +The +.Dv MSG_DONTWAIT +and +.Dv MSG_NOSIGNAL +flags are extensions to that specification. +.Sh HISTORY +The +.Fn send +function call appeared in +.Bx 4.1c . +The +.Fn sendmmsg +syscall first appeared in Linux 3.0 and was added to +.Ox 7.2 . diff --git a/man/test_files/mdoc/setuid.2 b/man/test_files/mdoc/setuid.2 new file mode 100644 index 00000000..2e5e41e5 --- /dev/null +++ b/man/test_files/mdoc/setuid.2 @@ -0,0 +1,152 @@ +.\" $OpenBSD: setuid.2,v 1.23 2014/09/09 08:16:12 jmc Exp $ +.\" $NetBSD: setuid.2,v 1.3 1995/02/27 12:37:06 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)setuid.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 9 2014 $ +.Dt SETUID 2 +.Os +.Sh NAME +.Nm setuid , +.Nm seteuid , +.Nm setgid , +.Nm setegid +.Nd set user and group ID +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn setuid "uid_t uid" +.Ft int +.Fn seteuid "uid_t euid" +.Ft int +.Fn setgid "gid_t gid" +.Ft int +.Fn setegid "gid_t egid" +.Sh DESCRIPTION +The +.Fn setuid +function sets the real and effective user IDs and the saved set-user-ID +of the current process to the specified value. +The +.Fn setuid +function is permitted if the effective user ID is that of the superuser, +or if the specified user ID is the same as the effective user ID. +If not, but the specified user ID is the same as the real user ID, +.Fn setuid +will set the effective user ID to the real user ID. +.Pp +The +.Fn setgid +function sets the real and effective group IDs and the saved set-group-ID +of the current process to the specified value. +The +.Fn setgid +function is permitted if the effective user ID is that of the superuser, +or if the specified group ID is the same as the effective group ID. +If not, but the specified group ID is the same as the real group ID, +.Fn setgid +will set the effective group ID to the real group ID. +Supplementary group IDs remain unchanged. +.Pp +The +.Fn seteuid +function +.Pq Fn setegid +sets the effective user ID (group ID) of the current process. +The effective user ID may be set to the value +of the real user ID or the saved set-user-ID (see +.Xr intro 2 +and +.Xr execve 2 ) ; +in this way, the effective user ID of a set-user-ID executable +may be toggled by switching to the real user ID, then re-enabled +by reverting to the set-user-ID value. +Similarly, the effective group ID may be set to the value +of the real group ID or the saved set-group-ID. +.Sh RETURN VALUES +.Rv -std setuid seteuid setgid setegid +.Sh ERRORS +.Fn setuid +and +.Fn seteuid +will succeed unless: +.Bl -tag -width Er +.It Bq Er EPERM +The user is not the superuser and the requested +.Fa uid +or +.Fa euid +is not the process's real, effective, or saved UID. +.El +.Pp +.Fn setgid +and +.Fn setegid +will succeed unless: +.Bl -tag -width Er +.It Bq Er EPERM +The user is not the superuser and the requested +.Fa gid +or +.Fa egid +is not the process's real, effective, or saved GID. +.El +.Sh SEE ALSO +.Xr getgid 2 , +.Xr getuid 2 , +.Xr issetugid 2 , +.Xr setgroups 2 , +.Xr setregid 2 , +.Xr setresgid 2 , +.Xr setresuid 2 , +.Xr setreuid 2 +.Sh STANDARDS +The +.Fn setuid , +.Fn seteuid , +.Fn setgid , +and +.Fn setegid +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn setuid +system call first appeared in +.At v1 ; +.Fn setgid +in +.At v4 ; +and +.Fn seteuid +and +.Fn setegid +in +.Bx 4.2 . diff --git a/man/test_files/mdoc/sftp.1 b/man/test_files/mdoc/sftp.1 new file mode 100644 index 00000000..651baaf8 --- /dev/null +++ b/man/test_files/mdoc/sftp.1 @@ -0,0 +1,767 @@ +.\" $OpenBSD: sftp.1,v 1.144 2024/12/06 15:12:56 djm Exp $ +.\" +.\" Copyright (c) 2001 Damien Miller. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: December 6 2024 $ +.Dt SFTP 1 +.Os +.Sh NAME +.Nm sftp +.Nd OpenSSH secure file transfer +.Sh SYNOPSIS +.Nm sftp +.Op Fl 46AaCfNpqrv +.Op Fl B Ar buffer_size +.Op Fl b Ar batchfile +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_command +.Op Fl F Ar ssh_config +.Op Fl i Ar identity_file +.Op Fl J Ar destination +.Op Fl l Ar limit +.Op Fl o Ar ssh_option +.Op Fl P Ar port +.Op Fl R Ar num_requests +.Op Fl S Ar program +.Op Fl s Ar subsystem | sftp_server +.Op Fl X Ar sftp_option +.Ar destination +.Sh DESCRIPTION +.Nm +is a file transfer program, similar to +.Xr ftp 1 , +which performs all operations over an encrypted +.Xr ssh 1 +transport. +It may also use many features of ssh, such as public key authentication and +compression. +.Pp +The +.Ar destination +may be specified either as +.Sm off +.Oo user @ Oc host Op : path +.Sm on +or as a URI in the form +.Sm off +.No sftp:// Oo user @ Oc host Oo : port Oc Op / path . +.Sm on +.Pp +If the +.Ar destination +includes a +.Ar path +and it is not a directory, +.Nm +will retrieve files automatically if a non-interactive +authentication method is used; otherwise it will do so after +successful interactive authentication. +.Pp +If no +.Ar path +is specified, or if the +.Ar path +is a directory, +.Nm +will log in to the specified +.Ar host +and enter interactive command mode, changing to the remote directory +if one was specified. +An optional trailing slash can be used to force the +.Ar path +to be interpreted as a directory. +.Pp +Since the destination formats use colon characters to delimit host +names from path names or port numbers, IPv6 addresses must be +enclosed in square brackets to avoid ambiguity. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl A +Allows forwarding of +.Xr ssh-agent 1 +to the remote system. +The default is not to forward an authentication agent. +.It Fl a +Attempt to continue interrupted transfers rather than overwriting +existing partial or complete copies of files. +If the partial contents differ from those being transferred, +then the resultant file is likely to be corrupt. +.It Fl B Ar buffer_size +Specify the size of the buffer that +.Nm +uses when transferring files. +Larger buffers require fewer round trips at the cost of higher +memory consumption. +The default is 32768 bytes. +.It Fl b Ar batchfile +Batch mode reads a series of commands from an input +.Ar batchfile +instead of +.Em stdin . +Since it lacks user interaction, it should be used in conjunction with +non-interactive authentication to obviate the need to enter a password +at connection time (see +.Xr sshd 8 +and +.Xr ssh-keygen 1 +for details). +.Pp +A +.Ar batchfile +of +.Sq \- +may be used to indicate standard input. +.Nm +will abort if any of the following +commands fail: +.Ic get , put , reget , reput , rename , ln , +.Ic rm , mkdir , chdir , ls , +.Ic lchdir , copy , cp , chmod , chown , +.Ic chgrp , lpwd , df , symlink , +and +.Ic lmkdir . +.Pp +Termination on error can be suppressed on a command by command basis by +prefixing the command with a +.Sq \- +character (for example, +.Ic -rm /tmp/blah* ) . +Echo of the command may be suppressed by prefixing the command with a +.Sq @ +character. +These two prefixes may be combined in any order, for example +.Ic -@ls /bsd . +.It Fl C +Enables compression (via ssh's +.Fl C +flag). +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfers. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_command +Connect directly to a local sftp server +(rather than via +.Xr ssh 1 ) . +A command and arguments may be specified, for example +.Qq /path/sftp-server -el debug3 . +This option may be useful in debugging the client and server. +.It Fl F Ar ssh_config +Specifies an alternative +per-user configuration file for +.Xr ssh 1 . +This option is directly passed to +.Xr ssh 1 . +.It Fl f +Requests that files be flushed to disk immediately after transfer. +When uploading files, this feature is only enabled if the server +implements the "fsync@openssh.com" extension. +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . +.It Fl J Ar destination +Connect to the target host by first making an +.Nm +connection to the jump host described by +.Ar destination +and then establishing a TCP forwarding to the ultimate destination from +there. +Multiple jump hops may be specified separated by comma characters. +This is a shortcut to specify a +.Cm ProxyJump +configuration directive. +This option is directly passed to +.Xr ssh 1 . +.It Fl l Ar limit +Limits the used bandwidth, specified in Kbit/s. +.It Fl N +Disables quiet mode, e.g. to override the implicit quiet mode set by the +.Fl b +flag. +.It Fl o Ar ssh_option +Can be used to pass options to +.Nm ssh +in the format used in +.Xr ssh_config 5 . +This is useful for specifying options +for which there is no separate +.Nm sftp +command-line flag. +For example, to specify an alternate port use: +.Ic sftp -oPort=24 . +For full details of the options listed below, and their possible values, see +.Xr ssh_config 5 . +.Pp +.Bl -tag -width Ds -offset indent -compact +.It AddKeysToAgent +.It AddressFamily +.It BatchMode +.It BindAddress +.It BindInterface +.It CASignatureAlgorithms +.It CanonicalDomains +.It CanonicalizeFallbackLocal +.It CanonicalizeHostname +.It CanonicalizeMaxDots +.It CanonicalizePermittedCNAMEs +.It CertificateFile +.It ChannelTimeout +.It CheckHostIP +.It Ciphers +.It ClearAllForwardings +.It Compression +.It ConnectTimeout +.It ConnectionAttempts +.It ControlMaster +.It ControlPath +.It ControlPersist +.It DynamicForward +.It EnableEscapeCommandline +.It EnableSSHKeysign +.It EscapeChar +.It ExitOnForwardFailure +.It FingerprintHash +.It ForkAfterAuthentication +.It ForwardAgent +.It ForwardX11 +.It ForwardX11Timeout +.It ForwardX11Trusted +.It GSSAPIAuthentication +.It GSSAPIDelegateCredentials +.It GatewayPorts +.It GlobalKnownHostsFile +.It HashKnownHosts +.It Host +.It HostKeyAlgorithms +.It HostKeyAlias +.It HostbasedAcceptedAlgorithms +.It HostbasedAuthentication +.It Hostname +.It IPQoS +.It IdentitiesOnly +.It IdentityAgent +.It IdentityFile +.It IgnoreUnknown +.It Include +.It KbdInteractiveAuthentication +.It KbdInteractiveDevices +.It KexAlgorithms +.It KnownHostsCommand +.It LocalCommand +.It LocalForward +.It LogLevel +.It LogVerbose +.It MACs +.It NoHostAuthenticationForLocalhost +.It NumberOfPasswordPrompts +.It ObscureKeystrokeTiming +.It PKCS11Provider +.It PasswordAuthentication +.It PermitLocalCommand +.It PermitRemoteOpen +.It Port +.It PreferredAuthentications +.It ProxyCommand +.It ProxyJump +.It ProxyUseFdpass +.It PubkeyAcceptedAlgorithms +.It PubkeyAuthentication +.It RekeyLimit +.It RemoteCommand +.It RemoteForward +.It RequestTTY +.It RequiredRSASize +.It RevokedHostKeys +.It SecurityKeyProvider +.It SendEnv +.It ServerAliveCountMax +.It ServerAliveInterval +.It SessionType +.It SetEnv +.It StdinNull +.It StreamLocalBindMask +.It StreamLocalBindUnlink +.It StrictHostKeyChecking +.It SyslogFacility +.It TCPKeepAlive +.It Tag +.It Tunnel +.It TunnelDevice +.It UpdateHostKeys +.It User +.It UserKnownHostsFile +.It VerifyHostKeyDNS +.It VisualHostKey +.It XAuthLocation +.El +.It Fl P Ar port +Specifies the port to connect to on the remote host. +.It Fl p +Preserves modification times, access times, and modes from the +original files transferred. +.It Fl q +Quiet mode: disables the progress meter as well as warning and +diagnostic messages from +.Xr ssh 1 . +.It Fl R Ar num_requests +Specify how many requests may be outstanding at any one time. +Increasing this may slightly improve file transfer speed +but will increase memory usage. +The default is 64 outstanding requests. +.It Fl r +Recursively copy entire directories when uploading and downloading. +Note that +.Nm +does not follow symbolic links encountered in the tree traversal. +.It Fl S Ar program +Name of the +.Ar program +to use for the encrypted connection. +The program must understand +.Xr ssh 1 +options. +.It Fl s Ar subsystem | sftp_server +Specifies the SSH2 subsystem or the path for an sftp server +on the remote host. +A path is useful when the remote +.Xr sshd 8 +does not have an sftp subsystem configured. +.It Fl v +Raise logging level. +This option is also passed to ssh. +.It Fl X Ar sftp_option +Specify an option that controls aspects of SFTP protocol behaviour. +The valid options are: +.Bl -tag -width Ds +.It Cm nrequests Ns = Ns Ar value +Controls how many concurrent SFTP read or write requests may be in progress +at any point in time during a download or upload. +By default 64 requests may be active concurrently. +.It Cm buffer Ns = Ns Ar value +Controls the maximum buffer size for a single SFTP read/write operation used +during download or upload. +By default a 32KB buffer is used. +.El +.El +.Sh INTERACTIVE COMMANDS +Once in interactive mode, +.Nm +understands a set of commands similar to those of +.Xr ftp 1 . +Commands are case insensitive. +Pathnames that contain spaces must be enclosed in quotes. +Any special characters contained within pathnames that are recognized by +.Xr glob 3 +must be escaped with backslashes +.Pq Sq \e . +.Bl -tag -width Ds +.It Ic bye +Quit +.Nm sftp . +.It Ic cd Op Ar path +Change remote directory to +.Ar path . +If +.Ar path +is not specified, then change directory to the one the session started in. +.It Xo Ic chgrp +.Op Fl h +.Ar grp +.Ar path +.Xc +Change group of file +.Ar path +to +.Ar grp . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Ar grp +must be a numeric GID. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Xo Ic chmod +.Op Fl h +.Ar mode +.Ar path +.Xc +Change permissions of file +.Ar path +to +.Ar mode . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Xo Ic chown +.Op Fl h +.Ar own +.Ar path +.Xc +Change owner of file +.Ar path +to +.Ar own . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Ar own +must be a numeric UID. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Ic copy Ar oldpath Ar newpath +Copy remote file from +.Ar oldpath +to +.Ar newpath . +.Pp +Note that this is only supported by servers that implement the "copy-data" +extension. +.It Ic cp Ar oldpath Ar newpath +Alias to +.Ic copy +command. +.It Xo Ic df +.Op Fl hi +.Op Ar path +.Xc +Display usage information for the filesystem holding the current directory +(or +.Ar path +if specified). +If the +.Fl h +flag is specified, the capacity information will be displayed using +"human-readable" suffixes. +The +.Fl i +flag requests display of inode information in addition to capacity information. +This command is only supported on servers that implement the +.Dq statvfs@openssh.com +extension. +.It Ic exit +Quit +.Nm sftp . +.It Xo Ic get +.Op Fl afpR +.Ar remote-path +.Op Ar local-path +.Xc +Retrieve the +.Ar remote-path +and store it on the local machine. +If the local +path name is not specified, it is given the same name it has on the +remote machine. +.Ar remote-path +may contain +.Xr glob 7 +characters and may match multiple files. +If it does and +.Ar local-path +is specified, then +.Ar local-path +must specify a directory. +.Pp +If the +.Fl a +flag is specified, then attempt to resume partial transfers of existing files. +Note that resumption assumes that any partial copy of the local file matches +the remote copy. +If the remote file contents differ from the partial local copy then the +resultant file is likely to be corrupt. +.Pp +If the +.Fl f +flag is specified, then +.Xr fsync 2 +will be called after the file transfer has completed to flush the file +to disk. +.Pp +If the +.Fl p +.\" undocumented redundant alias +.\" or +.\" .Fl P +flag is specified, then full file permissions and access times are +copied too. +.Pp +If the +.Fl R +.\" undocumented redundant alias +.\" or +.\" .Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. +.It Ic help +Display help text. +.It Ic lcd Op Ar path +Change local directory to +.Ar path . +If +.Ar path +is not specified, then change directory to the local user's home directory. +.It Ic lls Op Ar ls-options Op Ar path +Display local directory listing of either +.Ar path +or current directory if +.Ar path +is not specified. +.Ar ls-options +may contain any flags supported by the local system's +.Xr ls 1 +command. +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.It Ic lmkdir Ar path +Create local directory specified by +.Ar path . +.It Xo Ic ln +.Op Fl s +.Ar oldpath +.Ar newpath +.Xc +Create a link from +.Ar oldpath +to +.Ar newpath . +If the +.Fl s +flag is specified the created link is a symbolic link, otherwise it is +a hard link. +.It Ic lpwd +Print local working directory. +.It Xo Ic ls +.Op Fl 1afhlnrSt +.Op Ar path +.Xc +Display a remote directory listing of either +.Ar path +or the current directory if +.Ar path +is not specified. +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Pp +The following flags are recognized and alter the behaviour of +.Ic ls +accordingly: +.Bl -tag -width Ds +.It Fl 1 +Produce single columnar output. +.It Fl a +List files beginning with a dot +.Pq Sq \&. . +.It Fl f +Do not sort the listing. +The default sort order is lexicographical. +.It Fl h +When used with a long format option, use unit suffixes: Byte, Kilobyte, +Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce +the number of digits to four or fewer using powers of 2 for sizes (K=1024, +M=1048576, etc.). +.It Fl l +Display additional details including permissions +and ownership information. +.It Fl n +Produce a long listing with user and group information presented +numerically. +.It Fl r +Reverse the sort order of the listing. +.It Fl S +Sort the listing by file size. +.It Fl t +Sort the listing by last modification time. +.El +.It Ic lumask Ar umask +Set local umask to +.Ar umask . +.It Ic mkdir Ar path +Create remote directory specified by +.Ar path . +.It Ic progress +Toggle display of progress meter. +.It Xo Ic put +.Op Fl afpR +.Ar local-path +.Op Ar remote-path +.Xc +Upload +.Ar local-path +and store it on the remote machine. +If the remote path name is not specified, it is given the same name it has +on the local machine. +.Ar local-path +may contain +.Xr glob 7 +characters and may match multiple files. +If it does and +.Ar remote-path +is specified, then +.Ar remote-path +must specify a directory. +.Pp +If the +.Fl a +flag is specified, then attempt to resume partial +transfers of existing files. +Note that resumption assumes that any partial copy of the remote file +matches the local copy. +If the local file contents differ from the remote local copy then +the resultant file is likely to be corrupt. +.Pp +If the +.Fl f +flag is specified, then a request will be sent to the server to call +.Xr fsync 2 +after the file has been transferred. +Note that this is only supported by servers that implement +the "fsync@openssh.com" extension. +.Pp +If the +.Fl p +.\" undocumented redundant alias +.\" or +.\" .Fl P +flag is specified, then full file permissions and access times are +copied too. +.Pp +If the +.Fl R +.\" undocumented redundant alias +.\" or +.\" .Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. +.It Ic pwd +Display remote working directory. +.It Ic quit +Quit +.Nm sftp . +.It Xo Ic reget +.Op Fl fpR +.Ar remote-path +.Op Ar local-path +.Xc +Resume download of +.Ar remote-path . +Equivalent to +.Ic get +with the +.Fl a +flag set. +.It Xo Ic reput +.Op Fl fpR +.Ar local-path +.Op Ar remote-path +.Xc +Resume upload of +.Ar local-path . +Equivalent to +.Ic put +with the +.Fl a +flag set. +.It Ic rename Ar oldpath newpath +Rename remote file from +.Ar oldpath +to +.Ar newpath . +.It Ic rm Ar path +Delete remote file specified by +.Ar path . +.It Ic rmdir Ar path +Remove remote directory specified by +.Ar path . +.It Ic symlink Ar oldpath newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . +.It Ic version +Display the +.Nm +protocol version. +.It Ic \&! Ns Ar command +Execute +.Ar command +in local shell. +.It Ic \&! +Escape to local shell. +.It Ic \&? +Synonym for help. +.El +.Sh SEE ALSO +.Xr ftp 1 , +.Xr ls 1 , +.Xr scp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr glob 7 , +.Xr sftp-server 8 , +.Xr sshd 8 +.Rs +.%A T. Ylonen +.%A S. Lehtinen +.%T "SSH File Transfer Protocol" +.%N draft-ietf-secsh-filexfer-00.txt +.%D January 2001 +.%O work in progress material +.Re diff --git a/man/test_files/mdoc/shar.1 b/man/test_files/mdoc/shar.1 new file mode 100644 index 00000000..a4016e5b --- /dev/null +++ b/man/test_files/mdoc/shar.1 @@ -0,0 +1,102 @@ +.\" $OpenBSD: shar.1,v 1.12 2011/05/02 11:14:11 jmc Exp $ +.\" $NetBSD: shar.1,v 1.4 1995/08/18 14:55:40 pk Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shar.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: May 2 2011 $ +.Dt SHAR 1 +.Os +.Sh NAME +.Nm shar +.Nd create a shell archive of files +.Sh SYNOPSIS +.Nm shar +.Ar +.Sh DESCRIPTION +.Nm shar +writes an +.Xr sh 1 +shell script to the standard output which will recreate the file +hierarchy specified by the command line operands. +Directories will be recreated and must be specified before the +files they contain (the +.Xr find 1 +utility does this correctly). +.Pp +.Nm shar +is normally used for distributing files by +.Xr ftp 1 +or +.Xr mail 1 . +.Sh EXAMPLES +To create a shell archive of the program +.Xr ls 1 +and mail it to Rick: +.Bd -literal -offset indent +$ cd ls +$ shar `find . -print` | mail -s "ls source" rick +.Ed +.Pp +To recreate the program directory: +.Bd -literal -offset indent +$ mkdir ls +$ cd ls +\&... + +\&... +$ sh archive +.Ed +.Sh SEE ALSO +.Xr compress 1 , +.Xr mail 1 , +.Xr tar 1 , +.Xr uuencode 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . +.Sh BUGS +.Nm shar +makes no provisions for special types of files or files containing +magic characters. +.Pp +It is easy to insert trojan horses into +.Nm shar +files. +It is strongly recommended that all shell archive files be examined +before running them through +.Xr sh 1 . +Archives produced using this implementation of +.Nm shar +may be easily examined with the command: +.Bd -literal -offset indent +$ egrep -v '^[X#]' shar.file +.Ed diff --git a/man/test_files/mdoc/shmctl.2 b/man/test_files/mdoc/shmctl.2 new file mode 100644 index 00000000..8cd89414 --- /dev/null +++ b/man/test_files/mdoc/shmctl.2 @@ -0,0 +1,198 @@ +.\" $OpenBSD: shmctl.2,v 1.19 2021/11/21 23:44:55 jan Exp $ +.\" $NetBSD: shmctl.2,v 1.3 1997/03/27 08:20:39 mikel Exp $ +.\" +.\" Copyright (c) 1995 Frank van der Linden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project +.\" by Frank van der Linden +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\"/ +.Dd $Mdocdate: November 21 2021 $ +.Dt SHMCTL 2 +.Os +.Sh NAME +.Nm shmctl +.Nd shared memory control operations +.Sh SYNOPSIS +.In sys/shm.h +.Ft int +.Fn shmctl "int shmid" "int cmd" "struct shmid_ds *buf" +.Sh DESCRIPTION +The +.Fn shmctl +system call performs some control operations on the shared memory area +specified by +.Fa shmid . +.Pp +Each shared memory segment has a data structure associated with it, +parts of which may be altered by +.Fn shmctl +and parts of which determine the actions of +.Fn shmctl . +.Pp +This structure is defined as follows in +.In sys/shm.h : +.Bd -literal +struct shmid_ds { + struct ipc_perm shm_perm; /* operation permissions */ + int shm_segsz; /* size of segment in bytes */ + pid_t shm_lpid; /* pid of last shm op */ + pid_t shm_cpid; /* pid of creator */ + short shm_nattch; /* # of current attaches */ + time_t shm_atime; /* last shmat() time*/ + time_t shm_dtime; /* last shmdt() time */ + time_t shm_ctime; /* last change by shmctl() */ + void *shm_internal; /* sysv stupidity */ +}; +.Ed +.Pp +The +.Bf -literal +ipc_perm +.Ef +structure used inside the +.Bf -literal +shmid_ds +.Ef +structure is defined in +.In sys/ipc.h +and looks like this: +.Bd -literal +struct ipc_perm { + uid_t cuid; /* creator user id */ + gid_t cgid; /* creator group id */ + uid_t uid; /* user id */ + gid_t gid; /* group id */ + mode_t mode; /* r/w permission (see chmod(2)) */ + u_short seq; /* sequence # */ + /* (to generate unique msg/sem/shm id) */ + key_t key; /* user specified msg/sem/shm key */ +}; +.Ed +.Pp +The operation to be performed by +.Fn shmctl +is specified in +.Fa cmd +and is one of: +.Bl -tag -width IPC_RMIDX +.It Dv IPC_STAT +Gather information about the shared memory segment and place it in the +structure pointed to by +.Fa buf . +.It Dv IPC_SET +Set the value of the +.Va shm_perm.uid , +.Va shm_perm.gid +and +.Va shm_perm.mode +fields in the structure associated with +.Fa shmid . +The values are taken from the corresponding fields in the structure +pointed to by +.Fa buf . +This operation can only be executed by the superuser, or a process that +has an effective user ID equal to either +.Va shm_perm.cuid +or +.Va shm_perm.uid +in the data structure associated with the shared memory segment. +.It Dv IPC_RMID +Mark the shared memory segment specified by +.Fa shmid +for removal when it is no longer in use by any process. +When it is removed, all data associated with it will be destroyed too. +Only the superuser or a process with an effective UID equal to the +.Va shm_perm.cuid +or +.Va shm_perm.uid +values in the data structure associated with the queue can do this. +.El +.Pp +The read and write permissions on a shared memory identifier +are determined by the +.Va shm_perm.mode +field in the same way as is +done with files (see +.Xr chmod 2 ) , +but the effective UID can match either the +.Va shm_perm.cuid +field or the +.Va shm_perm.uid +field, and the +effective GID can match either +.Va shm_perm.cgid +or +.Va shm_perm.gid . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn shmctl +will fail if: +.Bl -tag -width Er +.It Bq Er EPERM +.Fa cmd +is equal to +.Dv IPC_SET +or +.Dv IPC_RMID +and the caller is not the superuser, nor does +the effective UID match either the +.Va shm_perm.uid +or +.Va shm_perm.cuid +fields of the data structure associated with the shared memory segment. +.Pp +An attempt is made to increase the value of +.Va shm_qbytes +through +.Dv IPC_SET +but the caller is not the superuser. +.It Bq Er EACCES +The command is +.Dv IPC_STAT +and the caller has no read permission for this shared memory segment. +.It Bq Er EINVAL +.Fa shmid +is not a valid shared memory segment identifier. +.Pp +.Va cmd +is not a valid command. +.It Bq Er EFAULT +.Fa buf +specifies an invalid address. +.El +.Sh SEE ALSO +.Xr ipcrm 1 , +.Xr ipcs 1 , +.Xr shmat 2 , +.Xr shmget 2 +.Sh STANDARDS +Segments which are marked for removal (but not yet removed +since they are still in use) can be attached to by new callers +using +.Xr shmat 2 . +This is permitted as an extension beyond the standards. diff --git a/man/test_files/mdoc/shmget.2 b/man/test_files/mdoc/shmget.2 new file mode 100644 index 00000000..609105a8 --- /dev/null +++ b/man/test_files/mdoc/shmget.2 @@ -0,0 +1,142 @@ +.\" $OpenBSD: shmget.2,v 1.17 2014/11/15 22:19:53 guenther Exp $ +.\" $NetBSD: shmget.2,v 1.2 1997/03/27 08:20:39 mikel Exp $ +.\" +.\" Copyright (c) 1995 Frank van der Linden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project +.\" by Frank van der Linden +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\"/ +.Dd $Mdocdate: November 15 2014 $ +.Dt SHMGET 2 +.Os +.Sh NAME +.Nm shmget +.Nd get shared memory area identifier +.Sh SYNOPSIS +.In sys/shm.h +.Ft int +.Fn shmget "key_t key" "size_t size" "int shmflg" +.Sh DESCRIPTION +.Fn shmget +returns the shared memory identifier associated with the key +.Fa key . +.Pp +A shared memory segment is created if either +.Fa key +is equal to +.Dv IPC_PRIVATE , +or +.Fa key +does not have a shared memory segment identifier associated with it, and the +.Dv IPC_CREAT +bit is set in +.Fa shmflg . +.Pp +If a new shared memory segment is created, the data structure associated with +it (the +.Va shmid_ds +structure, see +.Xr shmctl 2 ) +is initialized as follows: +.Bl -bullet +.It +.Va shm_perm.cuid +and +.Va shm_perm.uid +are set to the effective uid of the calling process. +.It +.Va shm_perm.gid +and +.Va shm_perm.cgid +are set to the effective gid of the calling process. +.It +.Va shm_perm.mode +is set to the lower 9 bits of +.Fa shmflg . +.It +.Va shm_lpid , +.Va shm_nattch , +.Va shm_atime , +and +.Va shm_dtime +are set to 0. +.It +.Va shm_ctime +is set to the current time. +.It +.Va shm_segsz +is set to the value of +.Fa size . +.El +.Sh RETURN VALUES +Upon successful completion a positive shared memory segment identifier is +returned. +Otherwise, \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EACCES +A shared memory segment is already associated with +.Fa key +and the caller has no permission to access it. +.It Bq Er EEXIST +Both +.Dv IPC_CREAT +and +.Dv IPC_EXCL +are set in +.Fa shmflg , +and a shared memory segment is already associated with +.Fa key . +.It Bq Er EINVAL +A shared memory segment is already associated with +.Fa key +and its +.Fa size +is less than the requested size. +.It Bq Er ENOSPC +A new shared memory identifier could not be created because the system limit +for the number of shared memory identifiers has been reached. +.It Bq Er ENOENT +.Dv IPC_CREAT +was not set in +.Fa shmflg +and no shared memory segment associated with +.Fa key +was found. +.It Bq Er ENOMEM +There is not enough memory left to create a shared memory segment of the +requested size. +.El +.Sh SEE ALSO +.Xr ipcrm 1 , +.Xr ipcs 1 , +.Xr mmap 2 , +.Xr shmat 2 , +.Xr shmctl 2 , +.Xr ftok 3 diff --git a/man/test_files/mdoc/shutdown.2 b/man/test_files/mdoc/shutdown.2 new file mode 100644 index 00000000..9f05eaae --- /dev/null +++ b/man/test_files/mdoc/shutdown.2 @@ -0,0 +1,154 @@ +.\" $OpenBSD: shutdown.2,v 1.15 2014/09/09 09:00:17 guenther Exp $ +.\" $NetBSD: shutdown.2,v 1.5 1995/02/27 12:37:11 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shutdown.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: September 9 2014 $ +.Dt SHUTDOWN 2 +.Os +.Sh NAME +.Nm shutdown +.Nd disable sends or receives on a socket +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn shutdown "int s" "int how" +.Sh DESCRIPTION +The +.Fn shutdown +system call disables sends or receives on a socket. +.Pp +If the file descriptor +.Fa s +is associated with a +.Dv SOCK_STREAM +socket, all or part of the full-duplex connection will be shut down. +.Pp +The +.Fa how +argument specifies the type of shutdown. +Possible values are: +.Bl -tag -width "SHUT_RDWRXXX" -offset indent +.It Dv SHUT_RD +Further receives will be disallowed. +.It Dv SHUT_WR +Further sends will be disallowed. +This may cause actions specific to the protocol family of the socket +.Fa s +to happen. +.It Dv SHUT_RDWR +Further sends and receives will be disallowed. +.El +.Pp +The following protocol specific actions apply to the use of +.Dv SHUT_WR +based on the properties of the socket associated with the file descriptor +.Fa s : +.Bl -column "AF_INET6" "SOCK_STREAM" "IPPROTO_UDP" -offset indent +.It DOMAIN Ta TYPE Ta PROTOCOL Ta "RETURN VALUE AND ACTION" +.Pp +.It Dv AF_INET Ta Dv SOCK_DGRAM Ta Dv IPPROTO_UDP Ta +Return 0. +ICMP messages will +.Em not +be generated. +.It Dv AF_INET Ta Dv SOCK_STREAM Ta Dv IPPROTO_TCP Ta +Return 0. +Send queued data, wait for ACK, then send FIN. +.It Dv AF_INET6 Ta Dv SOCK_DGRAM Ta Dv IPPROTO_UDP Ta +Return 0. +ICMP messages will +.Em not +be generated. +.It Dv AF_INET6 Ta Dv SOCK_STREAM Ta Dv IPPROTO_TCP Ta +Return 0. +Send queued data, wait for ACK, then send FIN. +.El +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn shutdown +system call fails if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa s +argument is not a valid file descriptor. +.It Bq Er EINVAL +The +.Fa how +argument is invalid. +.It Bq Er ENOTCONN +The +.Fa s +argument specifies a +.Dv SOCK_STREAM +socket which is not connected. +.It Bq Er ENOTSOCK +The +.Fa s +argument does not refer to a socket. +.It Bq Er EOPNOTSUPP +The socket associated with the file descriptor +.Fa s +does not support this operation. +.El +.Sh SEE ALSO +.Xr connect 2 , +.Xr socket 2 , +.Xr inet 4 , +.Xr inet6 4 +.Sh STANDARDS +The +.Fn shutdown +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn shutdown +system call first appeared in +.Bx 4.1c . +The +.Dv SHUT_RD , SHUT_WR , +and +.Dv SHUT_RDWR +constants appeared in +.St -p1003.1g-2000 . +.Sh BUGS +The ICMP +.Dq port unreachable +message should be generated in response to +datagrams received on a local port to which +.Fa s +is bound +after +.Fn shutdown +is called. diff --git a/man/test_files/mdoc/signify.1 b/man/test_files/mdoc/signify.1 new file mode 100644 index 00000000..96f93bf7 --- /dev/null +++ b/man/test_files/mdoc/signify.1 @@ -0,0 +1,206 @@ +.\" $OpenBSD: signify.1,v 1.61 2025/03/01 19:44:07 deraadt Exp $ +.\" +.\"Copyright (c) 2013 Marc Espie +.\"Copyright (c) 2013 Ted Unangst +.\" +.\"Permission to use, copy, modify, and distribute this software for any +.\"purpose with or without fee is hereby granted, provided that the above +.\"copyright notice and this permission notice appear in all copies. +.\" +.\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: March 1 2025 $ +.Dt SIGNIFY 1 +.Os +.Sh NAME +.Nm signify +.Nd cryptographically sign and verify files +.Sh SYNOPSIS +.Nm signify +.Fl C +.Op Fl q +.Op Fl p Ar pubkey +.Op Fl t Ar keytype +.Fl x Ar sigfile +.Op Ar +.Nm signify +.Fl G +.Op Fl n +.Op Fl c Ar comment +.Fl p Ar pubkey +.Fl s Ar seckey +.Nm signify +.Fl S +.Op Fl enz +.Op Fl x Ar sigfile +.Fl s Ar seckey +.Fl m Ar message +.Nm signify +.Fl V +.Op Fl eqz +.Op Fl p Ar pubkey +.Op Fl t Ar keytype +.Op Fl x Ar sigfile +.Fl m Ar message +.Sh DESCRIPTION +The +.Nm +utility creates and verifies cryptographic signatures. +A signature verifies the integrity of a +.Ar message . +The mode of operation is selected with the following options: +.Bl -tag -width Dsssigfile +.It Fl C +Verify a signed checksum list, and then verify the checksum for +each file. +If no files are specified, all of them are checked. +.Ar sigfile +should be the signed output of +.Xr sha256 1 . +.It Fl G +Generate a new key pair. +Keynames should follow the convention of +.Pa keyname.pub +and +.Pa keyname.sec +for the public and secret keys, respectively. +.It Fl S +Sign the specified message file and create a signature. +.It Fl V +Verify the message and signature match. +.El +.Pp +The other options are as follows: +.Bl -tag -width Dsssignature +.It Fl c Ar comment +Specify the comment to be added during key generation. +.It Fl e +When signing, embed the message after the signature. +When verifying, extract the message from the signature. +(This requires that the signature was created using +.Fl e +and creates a new message file as output.) +.It Fl m Ar message +When signing, the file containing the message to sign. +When verifying, the file containing the message to verify. +When verifying with +.Fl e , +the file to create. +.It Fl n +When generating a key pair, do not ask for a passphrase. +Otherwise, +.Nm +will prompt the user for a passphrase to protect the secret key. +When signing with +.Fl z , +store a zero time stamp in the +.Xr gzip 1 +header. +.It Fl p Ar pubkey +Public key produced by +.Fl G , +and used by +.Fl V +to check a signature. +.It Fl q +Quiet mode. +Suppress informational output. +.It Fl s Ar seckey +Secret (private) key produced by +.Fl G , +and used by +.Fl S +to sign a message. +.It Fl t Ar keytype +When deducing the correct key to check a signature, make sure +the actual key matches +.Pa /etc/signify/*-keytype.pub . +.It Fl x Ar sigfile +The signature file to create or verify. +The default is +.Ar message Ns .sig . +.It Fl z +Sign and verify +.Xr gzip 1 +archives, where the signing data +is embedded in the +.Xr gzip 1 +header. +.El +.Pp +The key and signature files created by +.Nm +have the same format. +The first line of the file is a free form text comment that may be edited, +so long as it does not exceed a single line. +Signature comments will be generated based on the name of the secret +key used for signing. +This comment can then be used as a hint for the name of the public key +when verifying. +The second line of the file is the actual key or signature base64 encoded. +.Sh EXIT STATUS +.Ex -std signify +It may fail because of one of the following reasons: +.Pp +.Bl -bullet -compact +.It +Some necessary files do not exist. +.It +Entered passphrase is incorrect. +.It +The message file was corrupted and its signature does not match. +.It +The message file is too large. +.El +.Sh EXAMPLES +Create a new key pair: +.Dl $ signify -G -p newkey.pub -s newkey.sec +.Pp +Sign a file, specifying a signature name: +.Dl $ signify -S -s key.sec -m message.txt -x msg.sig +.Pp +Verify a signature, using the default signature name: +.Dl $ signify -V -p key.pub -m generalsorders.txt +.Pp +Verify a release directory containing +.Pa SHA256.sig +and a full set of release files: +.Bd -literal -offset indent -compact +$ signify -C -p /etc/signify/openbsd-78-base.pub -x SHA256.sig +.Ed +.Pp +Verify a bsd.rd before an upgrade: +.Bd -literal -offset indent -compact +$ signify -C -p /etc/signify/openbsd-78-base.pub -x SHA256.sig bsd.rd +.Ed +.Pp +Sign a gzip archive: +.Bd -literal -offset indent -compact +$ signify -Sz -s key-arc.sec -m in.tgz -x out.tgz +.Ed +.Pp +Verify a gzip pipeline: +.Bd -literal -offset indent -compact +$ ftp url | signify -Vz -t arc | tar ztf - +.Ed +.Sh SEE ALSO +.Xr gzip 1 , +.Xr pkg_add 1 , +.Xr sha256 1 , +.Xr fw_update 8 , +.Xr sysupgrade 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Ox 5.5 . +.Sh AUTHORS +.An -nosplit +.An Ted Unangst Aq Mt tedu@openbsd.org +and +.An Marc Espie Aq Mt espie@openbsd.org . diff --git a/man/test_files/mdoc/sigreturn.2 b/man/test_files/mdoc/sigreturn.2 new file mode 100644 index 00000000..4ff03ca4 --- /dev/null +++ b/man/test_files/mdoc/sigreturn.2 @@ -0,0 +1,86 @@ +.\" $OpenBSD: sigreturn.2,v 1.12 2016/05/09 23:57:10 guenther Exp $ +.\" $NetBSD: sigreturn.2,v 1.6 1995/02/27 12:37:40 cgd Exp $ +.\" +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sigreturn.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: May 9 2016 $ +.Dt SIGRETURN 2 +.Os +.Sh NAME +.Nm sigreturn +.Nd return from signal +.Sh SYNOPSIS +.Ft int +.Fn sigreturn "struct sigcontext *scp" +.Sh DESCRIPTION +The +.Fn sigreturn +syscall is used by the signal handling facility to +atomically switch stacks, restore registers and the thread's signal mask, +and return from a signal context +to resume the processing that was interrupted by the signal. +.Pp +Note that sigcontext contains machine dependent information. +.Pp +Direct use of +.Nm +is no longer supported and it is not provided as a function. +As used in the signal trampoline provided by the system, +if +.Nm +fails and returns then the process is terminated. +.Sh RETURN VALUES +If successful, the system call does not return. +Otherwise, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn sigreturn +will fail and the process context will remain unchanged +if one of the following occurs. +.Bl -tag -width Er +.It Bq Er EFAULT +.Fa scp +points to memory that is not a valid part of the process +address space. +.It Bq Er EINVAL +The sigcontext provided is invalid or would improperly +raise the privilege level of the process. +.El +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr setjmp 3 +.Sh HISTORY +The +.Fn sigreturn +function appeared in +.Bx 4.3 . +The function was removed from libc in +.Ox 6.0 . diff --git a/man/test_files/mdoc/sigsuspend.2 b/man/test_files/mdoc/sigsuspend.2 new file mode 100644 index 00000000..02ba8ad2 --- /dev/null +++ b/man/test_files/mdoc/sigsuspend.2 @@ -0,0 +1,78 @@ +.\" $OpenBSD: sigsuspend.2,v 1.14 2017/05/29 09:40:02 deraadt Exp $ +.\" $NetBSD: sigsuspend.2,v 1.4 1995/02/27 12:37:46 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sigsuspend.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: May 29 2017 $ +.Dt SIGSUSPEND 2 +.Os +.Sh NAME +.Nm sigsuspend +.Nd atomically change the signal mask and wait for interrupt +.Sh SYNOPSIS +.In signal.h +.Ft int +.Fn sigsuspend "const sigset_t *sigmask" +.Sh DESCRIPTION +.Fn sigsuspend +temporarily changes the blocked signal mask to the set to which +.Fa sigmask +points, +and then waits for a signal to arrive; +on return the previous set of masked signals is restored. +The signal mask set +is usually empty to indicate that all +signals are to be unblocked for the duration of the call. +.Pp +In normal usage, a signal is blocked using +.Xr sigprocmask 2 +to begin a critical section, variables modified on the occurrence +of the signal are examined to determine that there is no work +to be done, and the process pauses awaiting work by using +.Fn sigsuspend +with the previous mask returned by +.Xr sigprocmask 2 . +.Sh RETURN VALUES +The +.Fn sigsuspend +function always terminates by being interrupted, returning \-1 with +.Va errno +set to +.Er EINTR . +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr sigprocmask 2 , +.Xr sigaddset 3 +.Sh STANDARDS +The +.Fn sigsuspend +function call +conforms to +.St -p1003.1-2008 . diff --git a/man/test_files/mdoc/size.1 b/man/test_files/mdoc/size.1 new file mode 100644 index 00000000..2eefb9fd --- /dev/null +++ b/man/test_files/mdoc/size.1 @@ -0,0 +1,78 @@ +.\" $OpenBSD: size.1,v 1.8 2022/03/31 17:27:26 naddy Exp $ +.\" $NetBSD: size.1,v 1.6 1996/01/14 23:07:11 pk Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)size.1 8.2 (Berkeley) 4/18/94 +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SIZE 1 +.Os +.Sh NAME +.Nm size +.Nd display object file segment sizes (text, data and bss) +.Sh SYNOPSIS +.Nm size +.Op Fl tw +.Op Ar +.Sh DESCRIPTION +.Nm +displays the text, data and bss segment sizes of the specified +.Ar file(s) +in bytes (in decimal), and the sum of the three segments (in +decimal and hexadecimal). +If a library (archive) is given, +.Nm +displays the segment sizes for each object archive member. +If no +.Ar file +is specified, +.Nm +attempts to report on the file +.Pa a.out . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl t +At the end of the output print a total of the +sizes of all the object files processed. +.It Fl w +Warn about non-object archive members. +Normally, +.Nm +will silently ignore all archive members which are not +object files. +.El +.Sh SEE ALSO +.Xr nm 1 , +.Xr elf 5 +.Sh HISTORY +A +.Nm +command appeared in +.At v3 . diff --git a/man/test_files/mdoc/snmp.1 b/man/test_files/mdoc/snmp.1 new file mode 100644 index 00000000..52b662a3 --- /dev/null +++ b/man/test_files/mdoc/snmp.1 @@ -0,0 +1,568 @@ +.\" $OpenBSD: snmp.1,v 1.22 2022/03/31 17:27:27 naddy Exp $ +.\" +.\" Copyright (c) 2019 Martijn van Duren +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SNMP 1 +.Os +.Sh NAME +.Nm snmp +.Nd simple SNMP client +.Sh SYNOPSIS +.Nm +.Cm get | getnext | bulkget +.Op Ar options +.Ar agent +.Ar oid ... +.Nm +.Cm walk | bulkwalk +.Op Ar options +.Ar agent +.Op Ar oid +.Nm +.Cm set +.Op Ar options +.Ar agent +.Ar varoid type value +.Oo Ar varoid type value Oc ... +.Nm +.Cm trap +.Op Ar options +.Ar agent uptime trapoid +.Oo Ar varoid type value Oc ... +.Nm +.Cm df +.Op Ar options +.Ar agent +.Nm +.Cm mibtree +.Op Fl O Ar fns +.Op Ar oid ... +.Sh DESCRIPTION +The +.Nm +utility is a simple SNMP client. +.Pp +The subcommands are as follows: +.Bl -tag -width Ds +.It Xo +.Nm snmp +.Cm get +.Op Ar options +.Ar agent oid ... +.Xc +Retrieve the varbind for +.Ar oid +from the +.Ar agent . +If more than one +.Ar oid +is specified, retrieve the varbind for each one. +.It Xo +.Nm snmp +.Cm getnext +.Op Ar options +.Ar agent oid ... +.Xc +Retrieve the varbind that follows +.Ar oid +from the +.Ar agent . +If more than one +.Ar oid +is specified, retrieve the varbind following each one of them. +.It Nm snmp Cm walk Oo Ar options Oc Ar agent Op Ar oid +Retrieve all the varbinds that are branches of +.Ar oid +from the +.Ar agent . +This uses the +.Cm getnext +subcommand internally and requests a single varbind at a time. +If no +.Ar oid +is specified, it defaults to mib-2 +.Pq .1.3.6.1.2.1 . +.It Xo +.Nm snmp +.Cm bulkget +.Op Ar options +.Ar agent oid ... +.Xc +Retrieve the next 10 varbinds following each +.Ar oid +from the +.Ar agent . +This command is not available for +.Fl v Cm 1 . +.It Xo +.Nm snmp +.Cm bulkwalk +.Op Ar options +.Ar agent +.Op Ar oid +.Xc +Retrieve all the varbinds from the +.Ar agent +that are branches of +.Ar oid . +This uses the +.Cm bulkget +subcommand internally to retrieve multiple varbinds at a time. +This command is not available for +.Fl v Cm 1 . +.It Xo +.Nm snmp +.Cm set +.Op Ar options +.Ar agent varoid type value ... +.Xc +Set one or more +.Ar varoid to a new +.Ar value . +The format of the +.Ar varoid type value +triple is described in +.Sx Data types , +below. +.It Xo +.Nm snmp +.Cm trap +.Op Ar options +.Ar agent uptime trapoid +.Op Ar varoid type value ... +.Xc +Send a trap message to the +.Ar agent . +The +.Ar uptime +is specified in timeticks +.Pq centiseconds +or defaults to the system uptime if an empty string is given. +The +.Ar trapoid +is the identification OID used by the trap handler to determine its action. +This command is not available for +.Fl v Cm 1 . +.It Xo +.Nm +.Cm df +.Op Ar options +.Ar agent +.Xc +An SNMP based version of the +.Xr df 1 +command. +If no size suffix is shown, the sizes are in kilobytes. +.It Nm Cm mibtree Oo Fl O Ar fnS Oc Op Ar oid ... +Dump the tree of compiled-in MIB objects. +If +.Ar oid +is specified it will print the objects in the requested output format if +available, or print a warning if the object can't be found. +.El +.Pp +The +.Ar options +are as follows: +.Bl -tag -width Ds +.It Fl A Ar authpass +The authentication password for the user. +This will be transformed to +.Ar localauth . +This option is only used by +.Fl v Cm 3 . +.It Fl a Ar digest +Set the digest +.Pq authentication +protocol. +Options are +.Cm MD5 , +.Cm SHA , +.Cm SHA-224 , +.Cm SHA-256 , +.Cm SHA-384 +or +.Cm SHA-512 . +This option defaults to +.Cm SHA . +This option is only used by +.Fl v Cm 3 . +.It Fl C Ar appopt +For the +.Cm bulkget , +.Cm bulkwalk , +.Cm df , +and +.Cm walk +subcommands, set the application specific +.Ar appopt +options by supplying a string of one or more +of the following modifier letters: +.Bl -tag -width Ds +.It Cm c +For +.Cm walk +and +.Cm bulkwalk , +disable checking the order of MIBs. +On some devices that return MIBs out of order, +this may cause an infinite loop. +.It Cm E Ar endoid +For +.Cm walk , +walk the tree up to but excluding +.Ar endoid . +The blank before +.Ar endoid +is mandatory. +.It Cm h +For +.Cm df +print the output in +.Dq human-readable +format. +.It Cm I +For +.Cm walk , +do not fall back to returning the original MIB via a +.Cm get +request. +.It Cm i +For +.Cm walk +and +.Cm bulkwalk , +always do a +.Cm get +request on the specified +.Ar oid +first. +.It Cm n Ns Ar nonrep +For +.Cm bulkget +and +.Cm bulkwalk , +Set the non-repeaters field in the request to the non-negative integer +.Ar nonrep . +This causes the first +.Ar nonrep +.Ar oid +arguments to only return a single MIB instead of +.Ar maxrep . +This value defaults to 0. +No blank is allowed before +.Ar nonrep . +.It Cm p +For +.Cm walk +or +.Cm bulkwalk , +also show a summary of the total variables received. +.It Cm r Ns Ar maxrep +For +.Cm bulkget , +.Cm bulkwalk +and +.Cm df , +set the max-repetitions field in the request to the positive integer +.Ar maxrep . +This determines the amount of MIBs to return for each specified OID. +This value defaults to 10. +No blank is allowed before +.Ar maxrep . +.It Cm s Ar skipoid +For +.Cm walk +or +.Cm bulkwalk +don't include +.Ar skipoid +or its children in the walk output. +The blank before +.Ar skipoid +is mandatory. +.It Cm t +For +.Cm walk , +Show how long it took to walk the entire tree. +.El +.It Fl c Ar community +Set the +.Ar community +string. +This option is only used by +.Fl v Cm 1 +and +.Fl v Cm 2c +and has no default. +.It Fl e Ar secengineid +The USM security engine id. +Under normal circumstances this value is discovered via snmpv3 discovery and +does not need to be specified. +This option is only used by +.Fl v Cm 3 . +.It Fl E Ar ctxengineid +The snmpv3 context engine id. +Most of the time this value can be safely ignored. +This option is only used by +.Fl v Cm 3 . +.It Fl K Ar localpriv +The localized privacy password for the user in hexadecimal format +.Po +optionally prefixed with a +.Cm 0x +.Pc . +This option is only used by +.Fl v Cm 3 . +.It Fl k Ar localauth +The localized authentication password for the user in hexadecimal format +.Po +optionally prefixed with a +.Cm 0x +.Pc . +This option is only used by +.Fl v Cm 3 . +.It Fl l Ar seclevel +The security level. +Values can be +.Cm noAuthNoPriv Pq default , +.Cm authNoPriv +.Po +requires either +.Fl A +or +.Fl k +.Pc +or +.Cm authPriv +.Po +requires either +.Fl X +or +.Fl K +in addition to the +.Cm authNoPriv +requirements +.Pc . +This option is only used by +.Fl v Cm 3 . +.It Fl n Ar ctxname +Sets the context name. +Defaults to an empty string. +This option is only used by +.Fl v Cm 3 . +.It Fl O Ar output +Set the +.Ar output +options by supplying a string of one or more +of the following modifier letters: +.Bl -tag -width 1n +.It Cm a +Print the varbind string unchanged +rather than replacing non-printable bytes with dots. +.It Cm f +When displaying an OID, include the full list of MIB objects. +By default only the last textual MIB object is shown. +.It Cm n +Display the OID numerically. +.It Cm Q +Remove the type information. +.It Cm q +Remove the type information and the equal sign. +.It Cm S +Display the MIB name and the type information. +This is the default behaviour. +.It Cm v +Only display the varbind value, removing the OID. +.It Cm x +Display the varbind string values as hexadecimal strings. +.El +.Pp +The +.Cm mibtree +subcommand may only use the +.Op Fl fnS +output options; +no output options are available for +.Cm trap . +.It Fl r Ar retries +Set the number of +.Ar retries +in case of packet loss. +Defaults to 5. +.It Fl t Ar timeout +Set the +.Ar timeout +to wait for a reply, in seconds. +Defaults to 1. +.It Fl u Ar user +Sets the username. +If +.Fl v Cm 3 +is used, this option is required. +This option is only used by +.Fl v Cm 3 . +.It Fl v Ar version +Set the snmp protocol +.Ar version +to either +.Cm 1 , +.Cm 2c +or +.Cm 3 . +Currently defaults to +.Cm 3 . +.It Fl X Ar privpass +The privacy password for the user. +This will be transformed to +.Ar localpriv . +This option is only used by +.Fl v Cm 3 . +.It Fl x Ar cipher +Sets the cipher +.Pq privacy +protocol. +Options are +.Cm DES +and +.Cm AES . +This option defaults to +.Cm AES . +This option is only used by +.Fl v Cm 3 . +.It Fl Z Ar boots , Ns Ar time +Set the engine boots and engine time. +Under normal circumstances this value is discovered via snmpv3 discovery and +does not need to be specified. +This option is only used by +.Fl v Cm 3 . +.El +.Pp +The syntax for the +.Ar agent +argument is +.Oo Ar protocol : Oc Ns Ar address , +with the following format: +.Bl -column udp6XXXtcp6X address -offset indent +.It Ar protocol Ta Ar address +.It Cm udp | tcp Ta Ar hostname Ns Oo Pf : Ar port Oc | +.Ar IPv4-address Ns Op Pf : Ar port +.It Cm udp6 | tcp6 Ta Ar hostname Ns Oo Pf : Ar port Oc | +.Cm \&[ Ns Ar IPv6-address Ns Cm \&] Ns Oo Pf : Ar port Oc | +.Ar IPv6-address Ns Pf : Ar port +.It Cm unix Ta Ar pathname +.El +.Pp +The default +.Ar protocol +is +.Cm udp +and the default +.Ar port +is 161, except for the +.Cm trap +subcommand, which uses 162. +.Cm udpv6 +and +.Cm udpipv6 +are aliases for +.Cm udp6 ; +.Cm tcpv6 +and +.Cm tcpipv6 +for +.Cm tcp6 . +To specify an IPv6-address without a +.Ar port , +the +.Ar IPv6-address +must be enclosed in square brackets. +If the square brackets are omitted, +the value after the last colon is always interpreted as a +.Ar port . +.Ss Data types +Additional data sent to the server is formatted by specifying one or more +triples of +.Ar varoid , +.Ar type , +and +.Ar value . +Supported types are: +.Bl -tag -width 1n -offset indent +.It Cm a +An IPv4 Address. +.It Cm b +A bitstring. +A list of individual bit offsets separated by comma, space or tab. +Must be supplied as a single argument. +.It Cm c +A counter32. +.It Cm d +A decimal string. +A list of individual bytes in decimal form separated by space or tab. +.It Cm i +An integer. +.It Cm n +A null object. +.It Cm o +An OID. +.It Cm s +A regular string. +.It Cm t +Timeticks in centiseconds. +.It Cm u +Unsigned integer. +.It Cm x +A hex string. +Similar to a decimal string, but in hexadecimal format. +.El +.Sh ENVIRONMENT +.Bl -tag -width LC_CTYPE +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 +used for output. +It decides whether objects having a display format of UTF-8 are printed as +UTF-8, and whether each byte invalid according to the object's display format is +printed as a UTF-8 replacement character +.Pq Sq \[uFFFD] . +.Pp +If unset or set to +.Qq C , +.Qq POSIX , +or an unsupported value, for objects having a display format of UTF-8, each +.Em printable +non-ASCII character is replaced with a single dot +.Pq Sq \&. . +Each byte invalid according to the object's display format is printed as a +question mark +.Pq Sq \&? . +.Pp +Each non-printable character is always replaced with a single dot +.Pq Sq \&. . +.El +.Sh SEE ALSO +.Xr snmpd 8 +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 6.6 . +.Sh AUTHORS +The +.Nm +program was written by +.An Martijn van Duren Aq Mt martijn@openbsd.org . diff --git a/man/test_files/mdoc/socket.2 b/man/test_files/mdoc/socket.2 new file mode 100644 index 00000000..89848869 --- /dev/null +++ b/man/test_files/mdoc/socket.2 @@ -0,0 +1,311 @@ +.\" $OpenBSD: socket.2,v 1.44 2022/03/31 17:27:16 naddy Exp $ +.\" $NetBSD: socket.2,v 1.5 1995/02/27 12:37:53 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)socket.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SOCKET 2 +.Os +.Sh NAME +.Nm socket +.Nd create an endpoint for communication +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn socket "int domain" "int type" "int protocol" +.Sh DESCRIPTION +.Fn socket +creates an endpoint for communication and returns a descriptor. +.Pp +The +.Fa domain +parameter specifies a communications domain within which +communication will take place; this selects the protocol family +which should be used. +These families are defined in the include file +.In sys/socket.h . +The currently understood formats are: +.Pp +.Bl -tag -width "AF_INET6XXX" -offset indent -compact +.It AF_UNIX +UNIX internal protocols +.It AF_INET +Internet Protocol version 4 (IPv4) protocol family +.It AF_INET6 +Internet Protocol version 6 (IPv6) protocol family +.El +.Pp +The socket has the indicated +.Fa type , +which specifies the semantics of communication. +Currently defined types are: +.Pp +.Bl -tag -width "SOCK_SEQPACKETXXX" -offset indent -compact +.It SOCK_STREAM +.It SOCK_DGRAM +.It SOCK_RAW +.It SOCK_SEQPACKET +.El +.Pp +A +.Dv SOCK_STREAM +type provides sequenced, reliable, +two-way connection based byte streams. +An out-of-band data transmission mechanism may be supported. +A +.Dv SOCK_DGRAM +socket supports +datagrams (connectionless, unreliable messages of +a fixed (typically small) maximum length). +A +.Dv SOCK_SEQPACKET +socket may provide a sequenced, reliable, +two-way connection-based data transmission path for datagrams +of fixed maximum length; a consumer may be required to read +an entire packet with each read system call. +This facility is protocol specific, and presently implemented only for +.Dv AF_UNIX . +.Dv SOCK_RAW +sockets provide access to internal network protocols and interfaces, +and are available only to the superuser. +.Pp +Any combination of the following flags may additionally be used in the +.Fa type +argument: +.Pp +.Bl -tag -width "SOCK_NONBLOCKX" -offset indent -compact +.It SOCK_CLOEXEC +Set close-on-exec flag on the new descriptor. +.It SOCK_NONBLOCK +Set non-blocking I/O mode on the new socket. +.It SOCK_DNS +For domains +.Dv AF_INET +or +.Dv AF_INET6 , +only allow +.Xr connect 2 , +.Xr sendto 2 , +or +.Xr sendmsg 2 +to the DNS port (typically 53). +.El +.Pp +The +.Fa protocol +specifies a particular protocol to be used with the socket. +Normally only a single protocol exists to support a particular +socket type within a given protocol family. +However, it is possible that many protocols may exist, +in which case a particular protocol must be specified in this manner. +The protocol number to use is particular to the +.Dq communication domain +in which communication is to take place; see +.Xr protocols 5 . +A value of 0 for +.Fa protocol +will let the system select an appropriate protocol for the requested +socket type. +.Pp +Sockets of type +.Dv SOCK_STREAM +are full-duplex byte streams. +A stream socket must be in a +.Em connected +state before any data may be sent or received on it. +A connection to another socket is created with a +.Xr connect 2 +call. +Once connected, data may be transferred using +.Xr read 2 +and +.Xr write 2 +calls or some variant of the +.Xr send 2 +and +.Xr recv 2 +calls. +When a session has been completed, a +.Xr close 2 +may be performed. +Out-of-band data may also be transmitted as described in +.Xr send 2 +and received as described in +.Xr recv 2 . +.Pp +The communications protocols used to implement a +.Dv SOCK_STREAM +ensure that data is not lost or duplicated. +If a piece of data for which the peer protocol has buffer space cannot +be successfully transmitted within a reasonable length of time, then the +connection is considered broken and calls will indicate an error with \-1 +returns and with +.Er ETIMEDOUT +as the specific code in the global variable +.Va errno . +The protocols optionally keep sockets +.Dq warm +by forcing transmissions roughly every minute in the absence of other activity. +An error is then indicated if no response can be elicited on an otherwise +idle connection for an extended period (e.g., 5 minutes). +A +.Dv SIGPIPE +signal is raised if a process sends on a broken stream; this causes +naive processes, which do not handle the signal, to exit. +.Pp +.Dv SOCK_SEQPACKET +sockets employ the same system calls +as +.Dv SOCK_STREAM +sockets. +The only difference is that +.Xr read 2 +calls will return only the amount of data requested, +and any remaining in the arriving packet will be discarded. +.Pp +.Dv SOCK_DGRAM +and +.Dv SOCK_RAW +sockets allow sending of datagrams to correspondents named in +.Xr send 2 +calls. +Datagrams are generally received with +.Xr recvfrom 2 , +which returns the next datagram with its return address. +.Pp +An +.Xr fcntl 2 +call can be used to specify a process group to receive +a +.Dv SIGURG +signal when the out-of-band data arrives. +It may also enable non-blocking I/O and asynchronous notification +of I/O events via +.Dv SIGIO . +.Pp +The operation of sockets is controlled by socket level +.Em options . +These options are defined in the file +.In sys/socket.h . +.Xr setsockopt 2 +and +.Xr getsockopt 2 +are used to set and get options, respectively. +.Sh RETURN VALUES +If successful, +.Fn socket +returns a non-negative integer, the socket file descriptor. +Otherwise, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +The +.Fn socket +call fails if: +.Bl -tag -width Er +.It Bq Er EAFNOSUPPORT +The specified address family is not supported on this machine. +.It Bq Er EPROTONOSUPPORT +The protocol type or the specified protocol is not supported +within this domain. +.It Bq Er EPROTOTYPE +The combination of the specified protocol and type is not supported. +.It Bq Er EMFILE +The per-process descriptor table is full. +.It Bq Er ENFILE +The system file table is full. +.It Bq Er ENOBUFS +Insufficient resources were available in the system +to perform the operation. +.It Bq Er EACCES +Permission to create a socket of the specified type and/or protocol +is denied. +.El +.Sh SEE ALSO +.Xr accept 2 , +.Xr bind 2 , +.Xr connect 2 , +.Xr getsockname 2 , +.Xr getsockopt 2 , +.Xr ioctl 2 , +.Xr listen 2 , +.Xr poll 2 , +.Xr read 2 , +.Xr recv 2 , +.Xr select 2 , +.Xr send 2 , +.Xr setsockopt 2 , +.Xr shutdown 2 , +.Xr socketpair 2 , +.Xr write 2 , +.Xr getprotoent 3 , +.Xr inet 4 , +.Xr inet6 4 , +.Xr netintro 4 , +.Xr unix 4 +.Rs +.%T "An Introductory 4.3 BSD Interprocess Communication Tutorial" +.%O "reprinted in UNIX Programmer's Supplementary Documents Volume 1" +.Re +.Rs +.%T "BSD Interprocess Communication Tutorial" +.%O "reprinted in UNIX Programmer's Supplementary Documents Volume 1" +.Re +.Sh STANDARDS +The +.Fn socket +function conforms to +.St -p1003.1-2008 . +The +.Dv SOCK_CLOEXEC +and +.Dv SOCK_NONBLOCK +flags are expected to conform to a future revision of that standard. +.Pp +The +.Dv SOCK_DNS +flag is an +.Ox +extension. +.Sh HISTORY +The +.Fn socket +system call first appeared in +.Bx 4.1c . +Support for the +.Dv SOCK_CLOEXEC +and +.Dv SOCK_NONBLOCK +flags appeared in +.Ox 5.7 . +Support for the +.Dv SOCK_DNS +flag appeared in +.Ox 5.9 . diff --git a/man/test_files/mdoc/socketpair.2 b/man/test_files/mdoc/socketpair.2 new file mode 100644 index 00000000..28225c55 --- /dev/null +++ b/man/test_files/mdoc/socketpair.2 @@ -0,0 +1,132 @@ +.\" $OpenBSD: socketpair.2,v 1.21 2018/04/08 18:46:43 schwarze Exp $ +.\" $NetBSD: socketpair.2,v 1.5 1995/02/27 12:38:00 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)socketpair.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: April 8 2018 $ +.Dt SOCKETPAIR 2 +.Os +.Sh NAME +.Nm socketpair +.Nd create a pair of connected sockets +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn socketpair "int domain" "int type" "int protocol" "int sv[2]" +.Sh DESCRIPTION +The +.Fn socketpair +call creates an unnamed pair of connected sockets. +The descriptors used in referencing the new sockets +are returned in +.Fa sv Ns [0] +and +.Fa sv Ns [1] . +The two sockets are indistinguishable. +.Pp +The only supported +.Fa domain +is +.Dv AF_UNIX . +Possible values for the +.Fa type +and +.Fa protocol +arguments are explained in the +.Xr socket 2 +manual page. +The only useful value for +.Fa protocol +is 0, which will let the system select an appropriate protocol +for the requested socket +.Fa type . +.Pp +Any combination of the following flags may additionally be used in the +.Fa type +argument: +.Pp +.Bl -tag -width "SOCK_NONBLOCKX" -offset indent -compact +.It SOCK_CLOEXEC +Set close-on-exec flag on both the new descriptors. +.It SOCK_NONBLOCK +Set non-blocking I/O mode on both the new sockets. +.El +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The call succeeds unless: +.Bl -tag -width Er +.It Bq Er EAFNOSUPPORT +The specified address family is not supported on this machine. +.It Bq Er EPROTONOSUPPORT +The specified protocol is not supported on this machine. +.It Bq Er EOPNOTSUPP +The specified protocol does not support creation of socket pairs. +.It Bq Er EPROTOTYPE +The combination of the specified protocol and type is not supported. +.It Bq Er EMFILE +The per-process descriptor table is full. +.It Bq Er ENFILE +The system file table is full. +.It Bq Er ENOBUFS +Insufficient resources were available in the system +to perform the operation. +.It Bq Er EFAULT +The address +.Fa sv +does not specify a valid part of the +process address space. +.El +.Sh SEE ALSO +.Xr pipe 2 , +.Xr read 2 , +.Xr socket 2 , +.Xr write 2 +.Sh STANDARDS +The +.Fn socketpair +function conforms to +.St -p1003.1-2008 . +The +.Dv SOCK_CLOEXEC +and +.Dv SOCK_NONBLOCK +flags are expected to conform to a future revision of that standard. +.Sh HISTORY +The +.Fn socketpair +function call appeared in +.Bx 4.2 . +Support for the +.Dv SOCK_CLOEXEC +and +.Dv SOCK_NONBLOCK +flags appeared in +.Ox 5.7 . diff --git a/man/test_files/mdoc/statfs.2 b/man/test_files/mdoc/statfs.2 new file mode 100644 index 00000000..9c371aba --- /dev/null +++ b/man/test_files/mdoc/statfs.2 @@ -0,0 +1,158 @@ +.\" $OpenBSD: statfs.2,v 1.29 2022/07/30 07:19:30 jsg Exp $ +.\" $NetBSD: statfs.2,v 1.10 1995/06/29 11:40:48 cgd Exp $ +.\" +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)statfs.2 8.3 (Berkeley) 2/11/94 +.\" +.Dd $Mdocdate: July 30 2022 $ +.Dt STATFS 2 +.Os +.Sh NAME +.Nm statfs , +.Nm fstatfs +.Nd get file system statistics +.Sh SYNOPSIS +.In sys/types.h +.In sys/mount.h +.Ft int +.Fn statfs "const char *path" "struct statfs *buf" +.Ft int +.Fn fstatfs "int fd" "struct statfs *buf" +.Sh DESCRIPTION +.Fn statfs +returns information about a mounted file system. +.Fa path +is the pathname of any file within the mounted file system. +.Fa buf +is a pointer to a +.Nm statfs +structure defined as follows: +.Bd -literal +typedef struct { int32_t val[2]; } fsid_t; + +#define MFSNAMELEN 16 /* length of fs type name, including nul */ +#define MNAMELEN 90 /* length of buffer for returned name */ + +struct statfs { + u_int32_t f_flags; /* copy of mount flags */ + u_int32_t f_bsize; /* file system block size */ + u_int32_t f_iosize; /* optimal transfer block size */ + + /* unit is f_bsize */ + u_int64_t f_blocks; /* total data blocks in file system */ + u_int64_t f_bfree; /* free blocks in fs */ + int64_t f_bavail; /* free blocks avail to non-superuser */ + + u_int64_t f_files; /* total file nodes in file system */ + u_int64_t f_ffree; /* free file nodes in fs */ + int64_t f_favail; /* free file nodes avail to non-root */ + + u_int64_t f_syncwrites; /* count of sync writes since mount */ + u_int64_t f_syncreads; /* count of sync reads since mount */ + u_int64_t f_asyncwrites; /* count of async writes since mount */ + u_int64_t f_asyncreads; /* count of async reads since mount */ + + fsid_t f_fsid; /* file system id */ + u_int32_t f_namemax; /* maximum filename length */ + uid_t f_owner; /* user that mounted the file system */ + u_int64_t f_ctime; /* last mount [-u] time */ + + char f_fstypename[MFSNAMELEN]; /* fs type name */ + char f_mntonname[MNAMELEN]; /* directory on which mounted */ + char f_mntfromname[MNAMELEN]; /* mounted file system */ + char f_mntfromspec[MNAMELEN]; /* special for mount request */ + union mount_info mount_info; /* per-filesystem mount options */ +}; +.Ed +.Pp +.Fn fstatfs +returns the same information about an open file referenced by descriptor +.Fa fd . +.Pp +Note that +.Fa f_fsid +will be empty unless the user is the superuser. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn statfs +fails if one or more of the following are true: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix of +.Fa path +is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The file referred to by +.Fa path +does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix of +.Fa path . +.It Bq Er ELOOP +Too many symbolic links were encountered in translating +.Fa path . +.It Bq Er EFAULT +.Fa buf +or +.Fa path +points to an invalid address. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Pp +.Fn fstatfs +fails if one or more of the following are true: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa fd +is not a valid open file descriptor. +.It Bq Er EFAULT +.Fa buf +points to an invalid address. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.El +.Sh SEE ALSO +.Xr df 1 , +.Xr getfsstat 2 , +.Xr mount 2 , +.Xr stat 2 +.Sh HISTORY +The +.Fn statfs +function first appeared in +.Bx 4.3 Reno . diff --git a/man/test_files/mdoc/symlink.2 b/man/test_files/mdoc/symlink.2 new file mode 100644 index 00000000..829019f7 --- /dev/null +++ b/man/test_files/mdoc/symlink.2 @@ -0,0 +1,204 @@ +.\" $OpenBSD: symlink.2,v 1.22 2021/01/03 18:10:27 rob Exp $ +.\" $NetBSD: symlink.2,v 1.7 1995/02/27 12:38:34 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)symlink.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: January 3 2021 $ +.Dt SYMLINK 2 +.Os +.Sh NAME +.Nm symlink , +.Nm symlinkat +.Nd make symbolic link to a file +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn symlink "const char *name1" "const char *name2" +.In fcntl.h +.In unistd.h +.Ft int +.Fn symlinkat "const char *name1" "int fd" "const char *name2" +.Sh DESCRIPTION +A symbolic link +.Fa name2 +is created to +.Fa name1 +.Pf ( Fa name2 +is the name of the +file created, +.Fa name1 +is the string +used in creating the symbolic link). +Either name may be an arbitrary pathname; the files need not +be on the same file system, and the file specified by +.Fa name1 +need not exist at all. +.Pp +The +.Fn symlinkat +function is equivalent to +.Fn symlink +except that where +.Fa name2 +specifies a relative path, +the newly created symbolic link is created relative to +the directory associated with file descriptor +.Fa fd +instead of the current working directory. +.Pp +If +.Fn symlinkat +is passed the special value +.Dv AT_FDCWD +(defined in +.In fcntl.h ) +in the +.Fa fd +parameter, the current working directory is used +and the behavior is identical to a call to +.Fn symlink . +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The symbolic link will fail if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the +.Fa name2 +prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er EACCES +A component of the +.Fa name2 +path prefix denies search permission. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EEXIST +.Fa name2 +already exists. +.It Bq Er EIO +An I/O error occurred while making the directory entry for +.Fa name2 , +or allocating the inode for +.Fa name2 , +or writing out the link contents of +.Fa name2 . +.It Bq Er EROFS +The file +.Fa name2 +would reside on a read-only file system. +.It Bq Er ENOSPC +The directory in which the entry for the new symbolic link is being placed +cannot be extended because there is no space left on the file +system containing the directory. +.It Bq Er ENOSPC +The new symbolic link cannot be created because there +is no space left on the file +system that will contain the symbolic link. +.It Bq Er ENOSPC +There are no free inodes on the file system on which the +symbolic link is being created. +.It Bq Er EDQUOT +The directory in which the entry for the new symbolic link +is being placed cannot be extended because the +user's quota of disk blocks on the file system +containing the directory has been exhausted. +.It Bq Er EDQUOT +The new symbolic link cannot be created because the user's +quota of disk blocks on the file system that will +contain the symbolic link has been exhausted. +.It Bq Er EDQUOT +The user's quota of inodes on the file system on +which the symbolic link is being created has been exhausted. +.It Bq Er EIO +An I/O error occurred while making the directory entry or allocating the inode. +.It Bq Er EFAULT +.Fa name1 +or +.Fa name2 +points outside the process's allocated address space. +.El +.Pp +Additionally, +.Fn symlinkat +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa name2 +argument specifies a relative path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er ENOTDIR +The +.Fa name2 +argument specifies a relative path and the +.Fa fd +argument is a valid file descriptor but it does not reference a directory. +.It Bq Er EACCES +The +.Fa name2 +argument specifies a relative path but search permission is denied +for the directory which the +.Fa fd +file descriptor references. +.El +.Sh SEE ALSO +.Xr ln 1 , +.Xr link 2 , +.Xr readlink 2 , +.Xr unlink 2 , +.Xr symlink 7 +.Sh STANDARDS +The +.Fn symlink +and +.Fn symlinkat +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn symlink +system call first appeared in +.Bx 4.1c . +The +.Fn symlinkat +system call has been available since +.Ox 5.0 . diff --git a/man/test_files/mdoc/sync.2 b/man/test_files/mdoc/sync.2 new file mode 100644 index 00000000..91e6851b --- /dev/null +++ b/man/test_files/mdoc/sync.2 @@ -0,0 +1,73 @@ +.\" $OpenBSD: sync.2,v 1.17 2022/03/31 17:27:16 naddy Exp $ +.\" $NetBSD: sync.2,v 1.4 1995/02/27 12:38:41 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sync.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SYNC 2 +.Os +.Sh NAME +.Nm sync +.Nd synchronize disk block in-core status with that on disk +.Sh SYNOPSIS +.In unistd.h +.Ft void +.Fn sync void +.Sh DESCRIPTION +The +.Fn sync +function forces a write of dirty (modified) buffers +in the block buffer cache out to disk. +The kernel keeps this information in core to reduce +the number of disk I/O transfers required by the system. +As information in the cache is lost after a system crash, a +.Fn sync +call is issued frequently by the in-kernel process update +(about every 30 seconds). +.Pp +The function +.Xr fsync 2 +may be used to synchronize individual file descriptor attributes. +.Sh SEE ALSO +.Xr fsync 2 , +.Xr sync 8 +.Sh STANDARDS +The +.Fn sync +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +A +.Fn sync +function appeared in +.At v2 . +.Sh BUGS +.Fn sync +may return before the buffers are completely flushed. diff --git a/man/test_files/mdoc/sysarch.2 b/man/test_files/mdoc/sysarch.2 new file mode 100644 index 00000000..c09d6d96 --- /dev/null +++ b/man/test_files/mdoc/sysarch.2 @@ -0,0 +1,68 @@ +.\" $OpenBSD: sysarch.2,v 1.11 2015/09/10 17:55:21 schwarze Exp $ +.\" $NetBSD: sysarch.2,v 1.4 1995/02/27 12:38:47 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)syscall.2 6.3 (Berkeley) 3/10/91 +.\" +.Dd $Mdocdate: September 10 2015 $ +.Dt SYSARCH 2 +.Os +.Sh NAME +.Nm sysarch +.Nd architecture-dependent system call +.Sh SYNOPSIS +.In machine/sysarch.h +.Ft int +.Fn sysarch "int number" "void *args" +.Sh DESCRIPTION +.Fn sysarch +performs the architecture-dependent function specified by +.Fa number +with the arguments specified by the +.Fa args +pointer. +.Fa args +is a pointer to a structure defining the actual arguments of the function. +Symbolic constants and argument structures for the architecture-dependent +functions can be found in the header file +.In machine/sysarch.h . +.Pp +The +.Fn sysarch +system call should never be called directly by user programs. +Instead, they should access its functions using the architecture-dependent +library. +.Sh RETURN VALUES +See the manual pages for specific architecture-dependent function calls +for information about their return values. +.Sh HISTORY +The +.Fn sysarch +function call appeared in +.Nx 0.9a . diff --git a/man/test_files/mdoc/t11.2 b/man/test_files/mdoc/t11.2 new file mode 100644 index 00000000..b5579207 --- /dev/null +++ b/man/test_files/mdoc/t11.2 @@ -0,0 +1,908 @@ +.\" $OpenBSD: t11.2,v 1.1 2003/07/21 20:16:21 otto Exp $ +.\" +.Dd May 2, 1993 +.Dt ED 1 +.Os +.Sh NAME +.Nm ed +.Nd text editor +.Sh SYNOPSIS +.Nm ed +.Op Fl +.Op Fl sx +.Op Fl p Ar string +.Op Ar file +.Sh DESCRIPTION +.Nm +is a line-oriented text editor. +It is used to create, display, modify, and otherwise manipulate text files. +If invoked with a +.Ar file +argument, then a copy of +.Ar file +is read into the editor's buffer. +Changes are made to this copy and not directly to +.Ar file +itself. +Upon quitting +.Nm ed , +any changes not explicitly saved with a +.Em w +command are lost. +.Pp +Editing is done in two distinct modes: +.Em command +and +.Em input . +When first invoked, +.Nm +is in command mode. +In this mode, commands are read from the standard input and +executed to manipulate the contents of the editor buffer. +.Pp +A typical command might look like: +.Bd -literal -offset indent +,s/old/new/g +.Ed +.Pp +which replaces all occurrences of the string +.Pa old +with +.Pa new . +.Pp +When an input command, such as +.Em a +(append), +.Em i +(insert), +or +.Em c +(change) is given, +.Nm +enters input mode. +This is the primary means of adding text to a file. +In this mode, no commands are available; +instead, the standard input is written directory to the editor buffer. +Lines consist of text up to and including a newline character. +Input mode is terminated by entering a single period +.Pq Ql \&. +on a line. +.Pp +All +.Nm +commands operate on whole lines or ranges of lines; e.g., +the +.Em d +command deletes lines; the +.Em m +command moves lines, and so on. +It is possible to modify only a portion of a line by means of replacement, +as in the example above. +However, even here, the +.Em s +command is applied to whole lines at a time. +.Pp +In general, +.Nm +commands consist of zero or more line addresses, followed by a single +character command and possibly additional parameters; i.e., +commands have the structure: +.Bd -literal -offset indent +[address [,address]]command[parameters] +.Ed +.Pp +The address(es) indicate the line or range of lines to be affected by the +command. +If fewer addresses are given than the command accepts, then +default addresses are supplied. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl +Same as the +.Fl s +option (deprecated). +.It Fl s +Suppress diagnostics. +This should be used if +.Nm +standard input is from a script. +.Fl s +flag. +.It Fl x +Prompt for an encryption key to be used in subsequent reads and writes +(see the +.Em x +command). +.It Fl p Ar string +Specifies a command prompt. +This may be toggled on and off with the +.Em P +command. +.It Ar file +Specifies the name of a file to read. +If +.Ar file +is prefixed with a +bang +.Pq Ql \&! , +then it is interpreted as a shell command. +In this case, what is read is the standard output of +.Ar file +executed via +.Xr sh 1 . +To read a file whose name begins with a bang, prefix the +name with a backslash +.Pq Ql \e . +The default filename is set to +.Ar file +only if it is not prefixed with a bang. +.El +.Ss LINE ADDRESSING +An address represents the number of a line in the buffer. +.Nm +maintains a +.Em current address +which is typically supplied to commands as the default address +when none is specified. +When a file is first read, the current address is set to the last line +of the file. +In general, the current address is set to the last line affected by a command. +.Pp +A line address is +constructed from one of the bases in the list below, optionally followed +by a numeric offset. +The offset may include any combination of digits, operators (i.e., +.Em + , +.Em - , +and +.Em ^ ) , +and whitespace. +Addresses are read from left to right, and their values are computed +relative to the current address. +.Pp +One exception to the rule that addresses represent line numbers is the +address +.Em 0 +(zero). +This means +.Dq before the first line , +and is legal wherever it makes sense. +.Pp +An address range is two addresses separated either by a comma or semi-colon. +The value of the first address in a range cannot exceed the +value of the second. +If only one address is given in a range, +then the second address is set to the given address. +If an +.Em n Ns No -tuple +of addresses is given where +.Em n > 2 , +then the corresponding range is determined by the last two addresses in the +.Em n Ns No -tuple. +If only one address is expected, then the last address is used. +.Pp +Each address in a comma-delimited range is interpreted relative to the +current address. +In a semi-colon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. +.Pp +The following address symbols are recognized: +.Bl -tag -width Ds +.It Em \&. +The current line (address) in the buffer. +.It Em $ +The last line in the buffer. +.It Em n +The +.Em n Ns No th +line in the buffer where +.Em n +is a number in the range +.Em [0,$] . +.It Em - No or Em ^ +The previous line. +This is equivalent to +.Em -1 +and may be repeated with cumulative effect. +.It Em -n No or Em ^n +The +.Em n Ns No th +previous line, where +.Em n +is a non-negative number. +.It Em + +The next line. +This is equivalent to +.Em +1 +and may be repeated with cumulative effect. +.It Em +n +The +.Em n Ns No th +next line, where +.Em n +is a non-negative number. +.It Em \&, No or Em % +The first through last lines in the buffer. +This is equivalent to the address range +.Em 1,$ . +.It Em \&; +The current through last lines in the buffer. +This is equivalent to the address range +.Em .,$ . +.It Em / Ns No re Ns Em / +The next line containing the regular expression +.Em re . +The search wraps to the beginning of the buffer and continues down to the +current line, if necessary. +.Em // +repeats the last search. +.It Em ? Ns No re Ns Em ? +The previous line containing the regular expression +.Em re . +The search wraps to the end of the buffer and continues up to the +current line, if necessary. +.Em ?? +repeats the last search. +.It Em \&\' Ns No lc +The line previously marked by a +.Em k +(mark) command, where +.Em lc +is a lower case letter. +.El +.Ss REGULAR EXPRESSIONS +Regular expressions are patterns used in selecting text. +For example, the +.Nm +command +.Bd -literal -offset indent +g/string/ +.Ed +.Pp +prints all lines containing +.Em string . +Regular expressions are also used by the +.Em s +command for selecting old text to be replaced with new. +.Pp +In addition to a specifying string literals, regular expressions can +represent classes of strings. +Strings thus represented are said to be matched by the +corresponding regular expression. +If it is possible for a regular expression to match several strings in +a line, then the leftmost longest match is the one selected. +.Pp +The following symbols are used in constructing regular expressions: +.Bl -tag -width Dsasdfsd +.It Em c +Any character +.Em c +not listed below, including +.Em { Ns No , +.Em } Ns No , +.Em \&( Ns No , +.Em \&) Ns No , +.Em < Ns No , +and +.Em > +matches itself. +.It Em \ec +Any backslash-escaped character +.Em c Ns No , +except for +.Em { Ns No , +.Em } Ns No , +.Em \&( Ns No , +.Em \&) Ns No , +.Em < Ns No , and +.Em > +matches itself. +.It Em \&. +Matches any single character. +.It Em [char-class] +Matches any single character in +.Em char-class . +To include a +.Ql \&] +in +.Em char-class Ns No , +it must be the first character. +A range of characters may be specified by separating the end characters +of the range with a +.Ql - ; +e.g., +.Em a-z +specifies the lower case characters. +The following literal expressions can also be used in +.Em char-class +to specify sets of characters: +.Pp +.Em \ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] +.Em \ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] +.Em \ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] +.Pp +If +.Ql - +appears as the first or last character of +.Em char-class Ns No , +then it matches itself. +All other characters in +.Em char-class +match themselves. +.Pp +Patterns in +.Em char-class +of the form +.Em [.col-elm.] No or Em [=col-elm=] +where +.Em col-elm +is a collating element are interpreted according to +.Xr locale 5 +(not currently supported). +See +.Xr regex 3 +for an explanation of these constructs. +.It Em [^char-class] +Matches any single character, other than newline, not in +.Em char-class Ns No . +.Em char-class +is defined as above. +.It Em ^ +If +.Em ^ +is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. +Otherwise, it matches itself. +.It Em $ +If +.Em $ +is the last character of a regular expression, +it anchors the regular expression to the end of a line. +Otherwise, it matches itself. +.It Em \e< +Anchors the single character regular expression or subexpression +immediately following it to the beginning of a word. +(This may not be available.) +.It Em \e> +Anchors the single character regular expression or subexpression +immediately following it to the end of a word. +(This may not be available.) +.It Em \e( Ns No re Ns Em \e) +Defines a subexpression +.Em re . +Subexpressions may be nested. +A subsequent backreference of the form +.Em \en Ns No , +where +.Em n +is a number in the range [1,9], expands to the text matched by the +.Em n Ns No th +subexpression. +For example, the regular expression +.Em \e(.*\e)\e1 +matches any string consisting of identical adjacent substrings. +Subexpressions are ordered relative to their left delimiter. +.It Em * +Matches the single character regular expression or subexpression +immediately preceding it zero or more times. +If +.Em * +is the first character of a regular expression or subexpression, +then it matches itself. +The +.Em * +operator sometimes yields unexpected results. +For example, the regular expression +.Em b* +matches the beginning of the string +.Em abbb +(as opposed to the substring +.Em bbb Ns No ), +since a null match is the only leftmost match. +.Sm off +.It Xo Em \e{ No n,m +.Em \e}\ \e{ No n, Em \e}\ +.Em \e{ No n Em \e} +.Xc +.Sm on +Matches the single character regular expression or subexpression +immediately preceding it at least +.Em n +and at most +.Em m +times. +If +.Em m +is omitted, then it matches at least +.Em n +times. +If the comma is also omitted, then it matches exactly +.Em n +times. +.El +.Pp +Additional regular expression operators may be defined depending on the +particular +.Xr regex 3 +implementation. +.Ss COMMANDS +All +.Nm +commands are single characters, though some require additional parameters. +If a command's parameters extend over several lines, then +each line except for the last must be terminated with a backslash +.Pq Ql \e . +.Pp +In general, at most one command is allowed per line. +However, most commands accept a print suffix, which is any of +.Em p No (print), +.Em l No (list), +or +.Em n No (enumerate), +to print the last line affected by the command. +.Pp +An interrupt (typically ^C) has the effect of aborting the current command +and returning the editor to command mode. +.Pp +.Nm +recognizes the following commands. +The commands are shown together with +the default address or address range supplied if none is +specified (in parentheses), and other possible arguments on the right. +.Bl -tag -width Dxxs +.It (.) Ns Em a +Appends text to the buffer after the addressed line. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.) Ns Em c +Changes lines in the buffer. +The addressed lines are deleted from the buffer, +and text is appended in their place. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.) Ns Em d +Deletes the addressed lines from the buffer. +If there is a line after the deleted range, then the current address is set +to this line. +Otherwise the current address is set to the line before the deleted range. +.It Em e No file +Edits +.Em file Ns No , +and sets the default filename. +If +.Em file +is not specified, then the default filename is used. +Any lines in the buffer are deleted before the new file is read. +The current address is set to the last line read. +.It Em e No !command +Edits the standard output of +.Em !command Ns No , +(see +.Em ! No command +below). +The default filename is unchanged. +Any lines in the buffer are deleted before the output of +.Em command +is read. +The current address is set to the last line read. +.It Em E No file +Edits +.Em file +unconditionally. +This is similar to the +.Em e +command, except that unwritten changes are discarded without warning. +The current address is set to the last line read. +.It Em f No file +Sets the default filename to +.Em file Ns No . +If +.Em file +is not specified, then the default unescaped filename is printed. +.It (1,$) Ns Em g Ns No /re/command-list +Applies +.Em command-list +to each of the addressed lines matching a regular expression +.Em re Ns No . +The current address is set to the line currently matched before +.Em command-list +is executed. +At the end of the +.Em g +command, the current address is set to the last line affected by +.Em command-list Ns No . +.Pp +Each command in +.Em command-list +must be on a separate line, +and every line except for the last must be terminated by +.Em \e No (backslash). +Any commands are allowed, except for +.Em g Ns No , +.Em G Ns No , +.Em v Ns No , +and +.Em V Ns No . +A newline alone in +.Em command-list +is equivalent to a +.Em p +command. +.It (1,$) Ns Em G Ns No /re/ +Interactively edits the addressed lines matching a regular expression +.Em re Ns No . +For each matching line, the line is printed, the current address is set, +and the user is prompted to enter a +.Em command-list Ns No . +At the end of the +.Em g +command, the current address is set to the last line affected by (the last) +.Em command-list Ns No . +.Pp +The format of +.Em command-list +is the same as that of the +.Em g +command. +A newline alone acts as a null command list. +A single +.Em & +repeats the last non-null command list. +.It Em H +Toggles the printing of error explanations. +By default, explanations are not printed. +It is recommended that +.Nm +scripts begin with this command to aid in debugging. +.It Em h +Prints an explanation of the last error. +.It (.) Ns Em i +Inserts text in the buffer before the current line. +Text is entered in input mode. +The current address is set to the last line entered. +.It (.,.+1) Ns Em j +Joins the addressed lines. +The addressed lines are deleted from the buffer and replaced by a single +line containing their joined text. +The current address is set to the resultant line. +.It (.) Ns Em klc +Marks a line with a lower case letter +.Em lc Ns No \&. +The line can then be addressed as +.Em \&'lc +(i.e., a single quote followed by +.Em lc Ns No ) +in subsequent commands. +The mark is not cleared until the line is deleted or otherwise modified. +.It (.,.) Ns Em l +Prints the addressed lines unambiguously. +If a single line fills more than one screen (as might be the case +when viewing a binary file, for instance), a +.Dq --More-- +prompt is printed on the last line. +.Nm +waits until the RETURN key is pressed before displaying the next screen. +The current address is set to the last line printed. +.It (.,.) Ns Em m Ns No (.) +Moves lines in the buffer. +The addressed lines are moved to after the +right-hand destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line moved. +.It (.,.) Ns Em n +Prints the addressed lines along with their line numbers. +The current address is set to the last line printed. +.It (.,.) Ns Em p +Prints the addressed lines. +The current address is set to the last line printed. +.It Em P +Toggles the command prompt on and off. +Unless a prompt was specified by with command-line option +.Fl p Ar string Ns No , +the command prompt is by default turned off. +.It Em q +Quits +.Nm ed . +.It Em Q +Quits +.Nm +unconditionally. +This is similar to the +.Em q +command, except that unwritten changes are discarded without warning. +.It ($) Ns Em r No file +Reads +.Em file +to after the addressed line. +If +.Em file +is not specified, then the default filename is used. +If there was no default filename prior to the command, +then the default filename is set to +.Em file Ns No . +Otherwise, the default filename is unchanged. +The current address is set to the last line read. +.It ($) Ns Em r No !command +Reads to after the addressed line the standard output of +.Em !command Ns No , +(see the +.Em ! +command below). +The default filename is unchanged. +The current address is set to the last line read. +.Sm off +.It Xo (.,.) Em s No /re/replacement/ , \ (.,.) +.Em s No /re/replacement/ Em g , No \ (.,.) +.Em s No /re/replacement/ Em n +.Xc +.Sm on +Replaces text in the addressed lines matching a regular expression +.Em re +with +.Em replacement Ns No . +By default, only the first match in each line is replaced. +If the +.Em g +(global) suffix is given, then every match to be replaced. +The +.Em n +suffix, where +.Em n +is a positive number, causes only the +.Em n Ns No th +match to be replaced. +It is an error if no substitutions are performed on any of the addressed +lines. +The current address is set the last line affected. +.Pp +.Em re +and +.Em replacement +may be delimited by any character other than space and newline +(see the +.Em s +command below). +If one or two of the last delimiters is omitted, then the last line +affected is printed as though the print suffix +.Em p +were specified. +.Pp +An unescaped +.Ql \e +in +.Em replacement +is replaced by the currently matched text. +The character sequence +.Em \em Ns No , +where +.Em m +is a number in the range [1,9], is replaced by the +.Em m Ns No th +backreference expression of the matched text. +If +.Em replacement +consists of a single +.Ql % , +then +.Em replacement +from the last substitution is used. +Newlines may be embedded in +.Em replacement +if they are escaped with a backslash +.Pq Ql \e . +.It (.,.) Ns Em s +Repeats the last substitution. +This form of the +.Em s +command accepts a count suffix +.Em n Ns No , +or any combination of the characters +.Em r Ns No , +.Em g Ns No , +and +.Em p Ns No . +If a count suffix +.Em n +is given, then only the +.Em n Ns No th +match is replaced. +The +.Em r +suffix causes +the regular expression of the last search to be used instead of the +that of the last substitution. +The +.Em g +suffix toggles the global suffix of the last substitution. +The +.Em p +suffix toggles the print suffix of the last substitution +The current address is set to the last line affected. +.It (.,.) Ns Em t Ns No (.) +Copies (i.e., transfers) the addressed lines to after the right-hand +destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line copied. +.It Em u +Undoes the last command and restores the current address +to what it was before the command. +The global commands +.Em g Ns No , +.Em G Ns No , +.Em v Ns No , +and +.Em V Ns No . +are treated as a single command by undo. +.Em u +is its own inverse. +.It (1,$) Ns Em v Ns No /re/command-list +Applies +.Em command-list +to each of the addressed lines not matching a regular expression +.Em re Ns No . +This is similar to the +.Em g +command. +.It (1,$) Ns Em V Ns No /re/ +Interactively edits the addressed lines not matching a regular expression +.Em re Ns No . +This is similar to the +.Em G +command. +.It (1,$) Ns Em w No file +Writes the addressed lines to +.Em file Ns No . +Any previous contents of +.Em file +is lost without warning. +If there is no default filename, then the default filename is set to +.Em file Ns No , +otherwise it is unchanged. +If no filename is specified, then the default filename is used. +The current address is unchanged. +.It (1,$) Ns Em wq No file +Writes the addressed lines to +.Em file Ns No , +and then executes a +.Em q +command. +.It (1,$) Ns Em w No !command +Writes the addressed lines to the standard input of +.Em !command Ns No , +(see the +.Em ! +command below). +The default filename and current address are unchanged. +.It (1,$) Ns Em W No file +Appends the addressed lines to the end of +.Em file Ns No . +This is similar to the +.Em w +command, expect that the previous contents of file is not clobbered. +The current address is unchanged. +.It Em x +Prompts for an encryption key which is used in subsequent reads and writes. +If a newline alone is entered as the key, then encryption is turned off. +Otherwise, echoing is disabled while a key is read. +Encryption/decryption is done using the +.Xr bdes 1 +algorithm. +.It (.+1) Ns Em z Ns No n +Scrolls +.Em n +lines at a time starting at addressed line. +If +.Em n +is not specified, then the current window size is used. +The current address is set to the last line printed. +.It ($) Ns Em = +Prints the line number of the addressed line. +.It (.+1) Ns Em newline +Prints the addressed line, and sets the current address to that line. +.It Em ! Ns No command +Executes +.Em command +via +.Xr sh 1 . +If the first character of +.Em command +is +.Em ! Ns No , +then it is replaced by text of the previous +.Em !command Ns No . +.Nm +does not process +.Em command +for +.Em \e +(backslash) escapes. +However, an unescaped +.Em % +is replaced by the default filename. +When the shell returns from execution, a +.Em ! +is printed to the standard output. +The current line is unchanged. +.El +.Sh LIMITATIONS +.Nm +processes +.Em file +arguments for backslash escapes, i.e., in a filename, +any characters preceded by a backslash +.Pq Ql \e +are interpreted literally. +.Pp +If a text (non-binary) file is not terminated by a newline character, +then +.Nm +appends one on reading/writing it. +In the case of a binary file, +.Nm +does not append a newline on reading/writing. +.Sh DIAGNOSTICS +When an error occurs, +.Nm +prints a +.Dq ? +and either returns to command mode or exits if its input is from a script. +An explanation of the last error can be printed with the +.Em h +(help) command. +.Pp +Since the +.Em g +(global) command masks any errors from failed searches and substitutions, +it can be used to perform conditional operations in scripts; e.g., +.Bd -literal -offset indent +g/old/s//new/ +.Ed +.Pp +replaces any occurrences of +.Em old +with +.Em new Ns No . +.Pp +If the +.Em u +(undo) command occurs in a global command list, then +the command list is executed only once. +.Pp +If diagnostics are not disabled, attempting to quit +.Nm +or edit another file before writing a modified buffer results in an error. +If the command is entered a second time, it succeeds, +but any changes to the buffer are lost. +.Sh FILES +.Bl -tag -width /tmp/ed.* -compact +.It Pa /tmp/ed.* +buffer file +.It Pa ed.hup +where +.Nm +attempts to write the buffer if the terminal hangs up +.El +.Sh SEE ALSO +.Xr bdes 1 , +.Xr sed 1 , +.Xr sh 1 , +.Xr vi 1 , +.Xr regex 3 +.Pp +USD:12-13 +.Rs +.%A B. W. Kernighan +.%A P. J. Plauger +.%B Software Tools in Pascal +.%O Addison-Wesley +.%D 1981 +.Re +.Sh HISTORY +An +.Nm +command appeared in +.At v1 . diff --git a/man/test_files/mdoc/talk.1 b/man/test_files/mdoc/talk.1 new file mode 100644 index 00000000..59dc0522 --- /dev/null +++ b/man/test_files/mdoc/talk.1 @@ -0,0 +1,160 @@ +.\" $OpenBSD: talk.1,v 1.27 2017/05/25 20:25:50 tedu Exp $ +.\" $NetBSD: talk.1,v 1.3 1994/12/09 02:14:23 jtc Exp $ +.\" +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)talk.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: May 25 2017 $ +.Dt TALK 1 +.Os +.Sh NAME +.Nm talk +.Nd talk to another user +.Sh SYNOPSIS +.Nm talk +.Op Fl Hs +.Ar person +.Op Ar ttyname +.Sh DESCRIPTION +.Nm +is a visual communication program which copies lines from your +terminal to that of another user. +.Pp +The command arguments are as follows: +.Bl -tag -width ttyname +.It Fl H +Don't escape characters with the high bit set. +This may be useful for certain character sets, but could cause erratic +behaviour on some terminals. +.It Fl s +Use smooth scrolling in the +.Nm +window. +The default is to clear the next two rows and jump from the bottom of +the window to the top. +.It Ar person +If you wish to talk to someone on your own machine, then +.Ar person +is just the person's login name. +If you wish to talk to a user on another host, then +.Ar person +is of the form +.Ql user@host . +.It Ar ttyname +If you wish to talk to a user who is logged in more than once, the +.Ar ttyname +argument may be used to indicate the appropriate terminal +name, where +.Ar ttyname +is of the form +.Ql ttyXX . +.El +.Pp +When first called, +.Nm +sends the message +.Bd -literal -offset indent +Message from Talk_Daemon@localhost... +talk: connection requested by your_name@your_machine. +talk: respond with: talk your_name@your_machine +.Ed +.Pp +to the user you wish to talk to. +At this point, the recipient of the message should reply by typing +.Pp +.Dl $ talk \ your_name@your_machine +.Pp +It doesn't matter from which machine the recipient replies, as +long as the login name is the same. +If the machine is not the one to which +the talk request was sent, it is noted on the screen. +Once communication is established, +the two parties may type simultaneously, with their output appearing +in separate windows. +Typing control-L +.Pq Ql ^L +will cause the screen to +be reprinted, while the erase, kill, and word kill characters will +behave normally. +To exit, just type the interrupt character; +.Nm +then moves the cursor to the bottom of the screen and restores the +terminal to its previous state. +.Pp +Permission to talk may be denied or granted by use of the +.Xr mesg 1 +command. +At the outset talking is allowed. +Certain commands, such as +.Xr pr 1 , +disallow messages in order to +prevent messy output. +.Sh ASYNCHRONOUS EVENTS +.Bl -tag -width SIGINTXXX +.It Dv SIGINT +Terminate +.Nm +and exit with a zero status. +.El +.Sh FILES +.Bl -tag -width /var/run/utmp -compact +.It Pa /etc/hosts +to find the recipient's machine +.It Pa /var/run/utmp +to find the recipient's tty +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if either an error occurred or +.Nm +is +invoked on an unsupported terminal. +.Sh SEE ALSO +.Xr mail 1 , +.Xr mesg 1 , +.Xr who 1 , +.Xr write 1 , +.Xr talkd 8 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification, +though its presence is optional. +.Pp +The flags +.Op Fl Hs +are extensions to that specification. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/man/test_files/mdoc/test.1 b/man/test_files/mdoc/test.1 new file mode 100644 index 00000000..cb7b35bb --- /dev/null +++ b/man/test_files/mdoc/test.1 @@ -0,0 +1,7799 @@ +.\" $OpenBSD: tmux.1,v 1.987 2025/03/24 20:01:03 nicm Exp $ +.\" +.\" Copyright (c) 2007 Nicholas Marriott +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 24 2025 $ +.Dt TMUX 1 +.Os +.Sh NAME +.Nm tmux +.Nd terminal multiplexer +.Sh SYNOPSIS +.Nm tmux +.Bk -words +.Op Fl 2CDlNuVv +.Op Fl c Ar shell-command +.Op Fl f Ar file +.Op Fl L Ar socket-name +.Op Fl S Ar socket-path +.Op Fl T Ar features +.Op Ar command Op Ar flags +.Ek +.Sh DESCRIPTION +.Nm +is a terminal multiplexer: +it enables a number of terminals to be created, accessed, and +controlled from a single screen. +.Nm +may be detached from a screen +and continue running in the background, +then later reattached. +.Pp +When +.Nm +is started, it creates a new +.Em session +with a single +.Em window +and displays it on screen. +A status line at the bottom of the screen +shows information on the current session +and is used to enter interactive commands. +.Pp +A session is a single collection of +.Em pseudo terminals +under the management of +.Nm . +Each session has one or more +windows linked to it. +A window occupies the entire screen +and may be split into rectangular panes, +each of which is a separate pseudo terminal +(the +.Xr pty 4 +manual page documents the technical details of pseudo terminals). +Any number of +.Nm +instances may connect to the same session, +and any number of windows may be present in the same session. +Once all sessions are killed, +.Nm +exits. +.Pp +Each session is persistent and will survive accidental disconnection +(such as +.Xr ssh 1 +connection timeout) or intentional detaching (with the +.Ql C-b d +key strokes). +.Nm +may be reattached using: +.Pp +.Dl $ tmux attach +.Pp +In +.Nm , +a session is displayed on screen by a +.Em client +and all sessions are managed by a single +.Em server . +The server and each client are separate processes which communicate through a +socket in +.Pa /tmp . +.Pp +The options are as follows: +.Bl -tag -width "XXXXXXXXXXXX" +.It Fl 2 +Force +.Nm +to assume the terminal supports 256 colours. +This is equivalent to +.Fl T Ar 256 . +.It Fl C +Start in control mode (see the +.Sx CONTROL MODE +section). +Given twice +.Xo ( Fl CC ) Xc +disables echo. +.It Fl c Ar shell-command +Execute +.Ar shell-command +using the default shell. +If necessary, the +.Nm +server will be started to retrieve the +.Ic default-shell +option. +This option is for compatibility with +.Xr sh 1 +when +.Nm +is used as a login shell. +.It Fl D +Do not start the +.Nm +server as a daemon. +This also turns the +.Ic exit-empty +option off. +With +.Fl D , +.Ar command +may not be specified. +.It Fl f Ar file +Specify an alternative configuration file. +By default, +.Nm +loads the system configuration file from +.Pa /etc/tmux.conf , +if present, then looks for a user configuration file at +.Pa \[ti]/.tmux.conf . +.Pp +The configuration file is a set of +.Nm +commands which are executed in sequence when the server is first started. +.Nm +loads configuration files once when the server process has started. +The +.Ic source-file +command may be used to load a file later. +.Pp +.Nm +shows any error messages from commands in configuration files in the first +session created, and continues to process the rest of the configuration file. +.It Fl L Ar socket-name +.Nm +stores the server socket in a directory under +.Ev TMUX_TMPDIR +or +.Pa /tmp +if it is unset. +The default socket is named +.Em default . +This option allows a different socket name to be specified, allowing several +independent +.Nm +servers to be run. +Unlike +.Fl S +a full path is not necessary: the sockets are all created in a directory +.Pa tmux-UID +under the directory given by +.Ev TMUX_TMPDIR +or in +.Pa /tmp . +The +.Pa tmux-UID +directory is created by +.Nm +and must not be world readable, writable or executable. +.Pp +If the socket is accidentally removed, the +.Dv SIGUSR1 +signal may be sent to the +.Nm +server process to recreate it (note that this will fail if any parent +directories are missing). +.It Fl l +Behave as a login shell. +This flag currently has no effect and is for compatibility with other shells +when using tmux as a login shell. +.It Fl N +Do not start the server even if the command would normally do so (for example +.Ic new-session +or +.Ic start-server ) . +.It Fl S Ar socket-path +Specify a full alternative path to the server socket. +If +.Fl S +is specified, the default socket directory is not used and any +.Fl L +flag is ignored. +.It Fl T Ar features +Set terminal features for the client. +This is a comma-separated list of features. +See the +.Ic terminal-features +option. +.It Fl u +Write UTF-8 output to the terminal even if the first environment +variable of +.Ev LC_ALL , +.Ev LC_CTYPE , +or +.Ev LANG +that is set does not contain +.Qq UTF-8 +or +.Qq UTF8 . +.It Fl V +Report the +.Nm +version. +.It Fl v +Request verbose logging. +Log messages will be saved into +.Pa tmux-client-PID.log +and +.Pa tmux-server-PID.log +files in the current directory, where +.Em PID +is the PID of the server or client process. +If +.Fl v +is specified twice, an additional +.Pa tmux-out-PID.log +file is generated with a copy of everything +.Nm +writes to the terminal. +.Pp +The +.Dv SIGUSR2 +signal may be sent to the +.Nm +server process to toggle logging between on (as if +.Fl v +was given) and off. +.It Ar command Op Ar flags +This specifies one of a set of commands used to control +.Nm , +as described in the following sections. +If no commands are specified, the command in +.Ic default-client-command +is assumed, which defaults to +.Ic new-session . +.El +.Sh DEFAULT KEY BINDINGS +.Nm +may be controlled from an attached client by using a key combination of a +prefix key, +.Ql C-b +(Ctrl-b) by default, followed by a command key. +.Pp +The default command key bindings are: +.Pp +.Bl -tag -width "XXXXXXXXXX" -offset indent -compact +.It C-b +Send the prefix key (C-b) through to the application. +.It C-o +Rotate the panes in the current window forwards. +.It C-z +Suspend the +.Nm +client. +.It ! +Break the current pane out of the window. +.It \&" +.\" " +Split the current pane into two, top and bottom. +.It # +List all paste buffers. +.It $ +Rename the current session. +.It % +Split the current pane into two, left and right. +.It & +Kill the current window. +.It \[aq] +Prompt for a window index to select. +.It \&( +Switch the attached client to the previous session. +.It \&) +Switch the attached client to the next session. +.It , +Rename the current window. +.It - +Delete the most recently copied buffer of text. +.It . +Prompt for an index to move the current window. +.It 0 to 9 +Select windows 0 to 9. +.It : +Enter the +.Nm +command prompt. +.It ; +Move to the previously active pane. +.It = +Choose which buffer to paste interactively from a list. +.It \&? +List all key bindings. +.It D +Choose a client to detach. +.It L +Switch the attached client back to the last session. +.It \&[ +Enter copy mode to copy text or view the history. +.It \&] +Paste the most recently copied buffer of text. +.It c +Create a new window. +.It d +Detach the current client. +.It f +Prompt to search for text in open windows. +.It i +Display some information about the current window. +.It l +Move to the previously selected window. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. +.It n +Change to the next window. +.It o +Select the next pane in the current window. +.It p +Change to the previous window. +.It q +Briefly display pane indexes. +.It r +Force redraw of the attached client. +.It s +Select a new session for the attached client interactively. +.It t +Show the time. +.It w +Choose the current window interactively. +.It x +Kill the current pane. +.It z +Toggle zoom state of the current pane. +.It { +Swap the current pane with the previous pane. +.It } +Swap the current pane with the next pane. +.It \[ti] +Show previous messages from +.Nm , +if any. +.It Page Up +Enter copy mode and scroll one page up. +.It Up, Down +.It Left, Right +Change to the pane above, below, to the left, or to the right of the current +pane. +.It M-1 to M-7 +Arrange panes in one of the seven preset layouts: +even-horizontal, even-vertical, +main-horizontal, main-horizontal-mirrored, +main-vertical, main-vertical-mirrored, +or tiled. +.It Space +Arrange the current window in the next preset layout. +.It M-n +Move to the next window with a bell or activity marker. +.It M-o +Rotate the panes in the current window backwards. +.It M-p +Move to the previous window with a bell or activity marker. +.It C-Up, C-Down +.It C-Left, C-Right +Resize the current pane in steps of one cell. +.It M-Up, M-Down +.It M-Left, M-Right +Resize the current pane in steps of five cells. +.El +.Pp +Key bindings may be changed with the +.Ic bind-key +and +.Ic unbind-key +commands. +.Sh COMMAND PARSING AND EXECUTION +.Nm +supports a large number of commands which can be used to control its +behaviour. +Each command is named and can accept zero or more flags and arguments. +They may be bound to a key with the +.Ic bind-key +command or run from the shell prompt, a shell script, a configuration file or +the command prompt. +For example, the same +.Ic set-option +command run from the shell prompt, from +.Pa \[ti]/.tmux.conf +and bound to a key may look like: +.Bd -literal -offset indent +$ tmux set-option -g status-style bg=cyan + +set-option -g status-style bg=cyan + +bind-key C set-option -g status-style bg=cyan +.Ed +.Pp +Here, the command name is +.Ql set-option , +.Ql Fl g +is a flag and +.Ql status-style +and +.Ql bg=cyan +are arguments. +.Pp +.Nm +distinguishes between command parsing and execution. +In order to execute a command, +.Nm +needs it to be split up into its name and arguments. +This is command parsing. +If a command is run from the shell, the shell parses it; from inside +.Nm +or from a configuration file, +.Nm +does. +Examples of when +.Nm +parses commands are: +.Bl -dash -offset indent +.It +in a configuration file; +.It +typed at the command prompt (see +.Ic command-prompt ) ; +.It +given to +.Ic bind-key ; +.It +passed as arguments to +.Ic if-shell +or +.Ic confirm-before . +.El +.Pp +To execute commands, each client has a +.Ql command queue . +A global command queue not attached to any client is used on startup +for configuration files like +.Pa \[ti]/.tmux.conf . +Parsed commands added to the queue are executed in order. +Some commands, like +.Ic if-shell +and +.Ic confirm-before , +parse their argument to create a new command which is inserted immediately +after themselves. +This means that arguments can be parsed twice or more - once when the parent +command (such as +.Ic if-shell ) +is parsed and again when it parses and executes its command. +Commands like +.Ic if-shell , +.Ic run-shell +and +.Ic display-panes +stop execution of subsequent commands on the queue until something happens - +.Ic if-shell +and +.Ic run-shell +until a shell command finishes and +.Ic display-panes +until a key is pressed. +For example, the following commands: +.Bd -literal -offset indent +new-session; new-window +if-shell "true" "split-window" +kill-session +.Ed +.Pp +Will execute +.Ic new-session , +.Ic new-window , +.Ic if-shell , +the shell command +.Xr true 1 , +.Ic split-window +and +.Ic kill-session +in that order. +.Pp +The +.Sx COMMANDS +section lists the +.Nm +commands and their arguments. +.Sh PARSING SYNTAX +This section describes the syntax of commands parsed by +.Nm , +for example in a configuration file or at the command prompt. +Note that when commands are entered into the shell, they are parsed by the shell +- see for example +.Xr ksh 1 +or +.Xr csh 1 . +.Pp +Each command is terminated by a newline or a semicolon (;). +Commands separated by semicolons together form a +.Ql command sequence +- if a command in the sequence encounters an error, no subsequent commands are +executed. +.Pp +It is recommended that a semicolon used as a command separator should be +written as an individual token, for example from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux neww \\; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux neww \[aq];\[aq] splitw +.Ed +.Pp +Or from the tmux command prompt: +.Bd -literal -offset indent +neww ; splitw +.Ed +.Pp +However, a trailing semicolon is also interpreted as a command separator, +for example in these +.Xr sh 1 +commands: +.Bd -literal -offset indent +$ tmux neww\e; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux \[aq]neww;\[aq] splitw +.Ed +.Pp +As in these examples, when running tmux from the shell extra care must be taken +to properly quote semicolons: +.Bl -enum -offset Ds +.It +Semicolons that should be interpreted as a command separator +should be escaped according to the shell conventions. +For +.Xr sh 1 +this typically means quoted (such as +.Ql neww \[aq];\[aq] splitw ) +or escaped (such as +.Ql neww \e\e\e\e; splitw ) . +.It +Individual semicolons or trailing semicolons that should be interpreted as +arguments should be escaped twice: once according to the shell conventions and +a second time for +.Nm ; +for example: +.Bd -literal -offset indent +$ tmux neww \[aq]foo\e\e;\[aq] bar +$ tmux neww foo\e\e\e\e; bar +.Ed +.It +Semicolons that are not individual tokens or trailing another token should only +be escaped once according to shell conventions; for example: +.Bd -literal -offset indent +$ tmux neww \[aq]foo-;-bar\[aq] +$ tmux neww foo-\e\e;-bar +.Ed +.El +.Pp +Comments are marked by the unquoted # character - any remaining text after a +comment is ignored until the end of the line. +.Pp +If the last character of a line is \e, the line is joined with the following +line (the \e and the newline are completely removed). +This is called line continuation and applies both inside and outside quoted +strings and in comments, but not inside braces. +.Pp +Command arguments may be specified as strings surrounded by single (\[aq]) +quotes, double quotes (\[dq]) or braces ({}). +.\" " +This is required when the argument contains any special character. +Single and double quoted strings cannot span multiple lines except with line +continuation. +Braces can span multiple lines. +.Pp +Outside of quotes and inside double quotes, these replacements are performed: +.Bl -dash -offset indent +.It +Environment variables preceded by $ are replaced with their value from the +global environment (see the +.Sx GLOBAL AND SESSION ENVIRONMENT +section). +.It +A leading \[ti] or \[ti]user is expanded to the home directory of the current or +specified user. +.It +\euXXXX or \euXXXXXXXX is replaced by the Unicode codepoint corresponding to +the given four or eight digit hexadecimal number. +.It +When preceded (escaped) by a \e, the following characters are replaced: \ee by +the escape character; \er by a carriage return; \en by a newline; and \et by a +tab. +.It +\eooo is replaced by a character of the octal value ooo. +Three octal digits are required, for example \e001. +The largest valid character is \e377. +.It +Any other characters preceded by \e are replaced by themselves (that is, the \e +is removed) and are not treated as having any special meaning - so for example +\e; will not mark a command sequence and \e$ will not expand an environment +variable. +.El +.Pp +Braces are parsed as a configuration file (so conditions such as +.Ql %if +are processed) and then converted into a string. +They are designed to avoid the need for additional escaping when passing a +group of +.Nm +commands as an argument (for example to +.Ic if-shell ) . +These two examples produce an identical command - note that no escaping is +needed when using {}: +.Bd -literal -offset indent +if-shell true { + display -p \[aq]brace-dollar-foo: }$foo\[aq] +} + +if-shell true "display -p \[aq]brace-dollar-foo: }\e$foo\[aq]" +.Ed +.Pp +Braces may be enclosed inside braces, for example: +.Bd -literal -offset indent +bind x if-shell "true" { + if-shell "true" { + display "true!" + } +} +.Ed +.Pp +Environment variables may be set by using the syntax +.Ql name=value , +for example +.Ql HOME=/home/user . +Variables set during parsing are added to the global environment. +A hidden variable may be set with +.Ql %hidden , +for example: +.Bd -literal -offset indent +%hidden MYVAR=42 +.Ed +.Pp +Hidden variables are not passed to the environment of processes created +by tmux. +See the +.Sx GLOBAL AND SESSION ENVIRONMENT +section. +.Pp +Commands may be parsed conditionally by surrounding them with +.Ql %if , +.Ql %elif , +.Ql %else +and +.Ql %endif . +The argument to +.Ql %if +and +.Ql %elif +is expanded as a format (see +.Sx FORMATS ) +and if it evaluates to false (zero or empty), subsequent text is ignored until +the closing +.Ql %elif , +.Ql %else +or +.Ql %endif . +For example: +.Bd -literal -offset indent +%if "#{==:#{host},myhost}" +set -g status-style bg=red +%elif "#{==:#{host},myotherhost}" +set -g status-style bg=green +%else +set -g status-style bg=blue +%endif +.Ed +.Pp +Will change the status line to red if running on +.Ql myhost , +green if running on +.Ql myotherhost , +or blue if running on another host. +Conditionals may be given on one line, for example: +.Bd -literal -offset indent +%if #{==:#{host},myhost} set -g status-style bg=red %endif +.Ed +.Sh COMMANDS +This section describes the commands supported by +.Nm . +Most commands accept the optional +.Fl t +(and sometimes +.Fl s ) +argument with one of +.Ar target-client , +.Ar target-session , +.Ar target-window , +or +.Ar target-pane . +These specify the client, session, window or pane which a command should affect. +.Pp +.Ar target-client +should be the name of the client, +typically the +.Xr pty 4 +file to which the client is connected, for example either of +.Pa /dev/ttyp1 +or +.Pa ttyp1 +for the client attached to +.Pa /dev/ttyp1 . +If no client is specified, +.Nm +attempts to work out the client currently in use; if that fails, an error is +reported. +Clients may be listed with the +.Ic list-clients +command. +.Pp +.Ar target-session +is tried as, in order: +.Bl -enum -offset Ds +.It +A session ID prefixed with a $. +.It +An exact name of a session (as listed by the +.Ic list-sessions +command). +.It +The start of a session name, for example +.Ql mysess +would match a session named +.Ql mysession . +.It +A +.Xr glob 7 +pattern which is matched against the session name. +.El +.Pp +If the session name is prefixed with an +.Ql = , +only an exact match is accepted (so +.Ql =mysess +will only match exactly +.Ql mysess , +not +.Ql mysession ) . +.Pp +If a single session is found, it is used as the target session; multiple matches +produce an error. +If a session is omitted, the current session is used if available; if no +current session is available, the most recently used is chosen. +.Pp +.Ar target-window +(or +.Ar src-window +or +.Ar dst-window ) +specifies a window in the form +.Em session Ns \&: Ns Em window . +.Em session +follows the same rules as for +.Ar target-session , +and +.Em window +is looked for in order as: +.Bl -enum -offset Ds +.It +A special token, listed below. +.It +A window index, for example +.Ql mysession:1 +is window 1 in session +.Ql mysession . +.It +A window ID, such as @1. +.It +An exact window name, such as +.Ql mysession:mywindow . +.It +The start of a window name, such as +.Ql mysession:mywin . +.It +As a +.Xr glob 7 +pattern matched against the window name. +.El +.Pp +Like sessions, a +.Ql = +prefix will do an exact match only. +An empty window name specifies the next unused index if appropriate (for +example the +.Ic new-window +and +.Ic link-window +commands) +otherwise the current window in +.Em session +is chosen. +.Pp +The following special tokens are available to indicate particular windows. +Each has a single-character alternative form. +.Bl -column "XXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{start}" Ta "^" Ta "The lowest-numbered window" +.It Li "{end}" Ta "$" Ta "The highest-numbered window" +.It Li "{last}" Ta "!" Ta "The last (previously current) window" +.It Li "{next}" Ta "+" Ta "The next window by number" +.It Li "{previous}" Ta "-" Ta "The previous window by number" +.El +.Pp +.Ar target-pane +(or +.Ar src-pane +or +.Ar dst-pane ) +may be a pane ID or takes a similar form to +.Ar target-window +but with the optional addition of a period followed by a pane index or pane ID, +for example: +.Ql mysession:mywindow.1 . +If the pane index is omitted, the currently active pane in the specified +window is used. +The following special tokens are available for the pane index: +.Bl -column "XXXXXXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{last}" Ta "!" Ta "The last (previously active) pane" +.It Li "{next}" Ta "+" Ta "The next pane by number" +.It Li "{previous}" Ta "-" Ta "The previous pane by number" +.It Li "{top}" Ta "" Ta "The top pane" +.It Li "{bottom}" Ta "" Ta "The bottom pane" +.It Li "{left}" Ta "" Ta "The leftmost pane" +.It Li "{right}" Ta "" Ta "The rightmost pane" +.It Li "{top-left}" Ta "" Ta "The top-left pane" +.It Li "{top-right}" Ta "" Ta "The top-right pane" +.It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" +.It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" +.It Li "{up-of}" Ta "" Ta "The pane above the active pane" +.It Li "{down-of}" Ta "" Ta "The pane below the active pane" +.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" +.El +.Pp +The tokens +.Ql + +and +.Ql - +may be followed by an offset, for example: +.Bd -literal -offset indent +select-window -t:+2 +.Ed +.Pp +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the session, window or pane where the most recent mouse event +occurred (see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql \[ti] ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . +.Pp +Sessions, window and panes are each numbered with a unique ID; session IDs are +prefixed with a +.Ql $ , +windows with a +.Ql @ , +and panes with a +.Ql % . +These are unique and are unchanged for the life of the session, window or pane +in the +.Nm +server. +The pane ID is passed to the child process of the pane in the +.Ev TMUX_PANE +environment variable. +IDs may be displayed using the +.Ql session_id , +.Ql window_id , +or +.Ql pane_id +formats (see the +.Sx FORMATS +section) and the +.Ic display-message , +.Ic list-sessions , +.Ic list-windows +or +.Ic list-panes +commands. +.Pp +.Ar shell-command +arguments are +.Xr sh 1 +commands. +This may be a single argument passed to the shell, for example: +.Bd -literal -offset indent +new-window \[aq]vi \[ti]/.tmux.conf\[aq] +.Ed +.Pp +Will run: +.Bd -literal -offset indent +/bin/sh -c \[aq]vi \[ti]/.tmux.conf\[aq] +.Ed +.Pp +Additionally, the +.Ic new-window , +.Ic new-session , +.Ic split-window , +.Ic respawn-window +and +.Ic respawn-pane +commands allow +.Ar shell-command +to be given as multiple arguments and executed directly (without +.Ql sh -c ) . +This can avoid issues with shell quoting. +For example: +.Bd -literal -offset indent +$ tmux new-window vi \[ti]/.tmux.conf +.Ed +.Pp +Will run +.Xr vi 1 +directly without invoking the shell. +.Pp +.Ar command +.Op Ar argument ... +refers to a +.Nm +command, either passed with the command and arguments separately, for example: +.Bd -literal -offset indent +bind-key F1 set-option status off +.Ed +.Pp +Or passed as a single string argument in +.Pa .tmux.conf , +for example: +.Bd -literal -offset indent +bind-key F1 { set-option status off } +.Ed +.Pp +Example +.Nm +commands include: +.Bd -literal -offset indent +refresh-client -t/dev/ttyp2 + +rename-session -tfirst newname + +set-option -wt:0 monitor-activity on + +new-window ; split-window -d + +bind-key R source-file \[ti]/.tmux.conf \e; \e + display-message "source-file done" +.Ed +.Pp +Or from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux kill-window -t :1 + +$ tmux new-window \e; split-window -d + +$ tmux new-session -d \[aq]vi \[ti]/.tmux.conf\[aq] \e; split-window -d \e; attach +.Ed +.Sh CLIENTS AND SESSIONS +The +.Nm +server manages clients, sessions, windows and panes. +Clients are attached to sessions to interact with them, either +when they are created with the +.Ic new-session +command, or later with the +.Ic attach-session +command. +Each session has one or more windows +.Em linked +into it. +Windows may be linked to multiple sessions and are made up of one or +more panes, +each of which contains a pseudo terminal. +Commands for creating, linking and otherwise manipulating windows +are covered +in the +.Sx WINDOWS AND PANES +section. +.Pp +The following commands are available to manage clients and sessions: +.Bl -tag -width Ds +.Tg attach +.It Xo Ic attach-session +.Op Fl dErx +.Op Fl c Ar working-directory +.Op Fl f Ar flags +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic attach +If run from outside +.Nm , +attach to +.Ar target-session +in the current terminal. +.Ar target-session +must already exist - to create a new session, see the +.Ic new-session +command (with +.Fl A +to create or attach). +If used from inside, switch the currently attached session to +.Ar target-session . +If +.Fl d +is specified, any other clients attached to the session are detached. +If +.Fl x +is given, send +.Dv SIGHUP +to the parent process of the client as well as +detaching the client, typically causing it to exit. +.Fl f +sets a comma-separated list of client flags. +The flags are: +.Bl -tag -width Ds +.It active-pane +the client has an independent active pane +.It ignore-size +the client does not affect the size of other clients +.It no-detach-on-destroy +do not detach the client when the session it is attached to is destroyed if +there are any other sessions +.It no-output +the client does not receive pane output in control mode +.It pause-after=seconds +output is paused once the pane is +.Ar seconds +behind in control mode +.It read-only +the client is read-only +.It wait-exit +wait for an empty line input before exiting in control mode +.El +.Pp +A leading +.Ql \&! +turns a flag off if the client is already attached. +.Fl r +is an alias for +.Fl f +.Ar read-only,ignore-size . +When a client is read-only, only keys bound to the +.Ic detach-client +or +.Ic switch-client +commands have any effect. +A client with the +.Ar active-pane +flag allows the active pane to be selected independently of the window's active +pane used by clients without the flag. +This only affects the cursor position and commands issued from the client; +other features such as hooks and styles continue to use the window's active +pane. +.Pp +If no server is started, +.Ic attach-session +will attempt to start it; this will fail unless sessions are created in the +configuration file. +.Pp +The +.Ar target-session +rules for +.Ic attach-session +are slightly adjusted: if +.Nm +needs to select the most recently used session, it will prefer the most +recently used +.Em unattached +session. +.Pp +.Fl c +will set the session working directory (used for new windows) to +.Ar working-directory . +.Pp +If +.Fl E +is used, the +.Ic update-environment +option will not be applied. +.Tg detach +.It Xo Ic detach-client +.Op Fl aP +.Op Fl E Ar shell-command +.Op Fl s Ar target-session +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic detach +Detach the current client if bound to a key, the client specified with +.Fl t , +or all clients currently attached to the session specified by +.Fl s . +The +.Fl a +option kills all but the client given with +.Fl t . +If +.Fl P +is given, send +.Dv SIGHUP +to the parent process of the client, typically causing it +to exit. +With +.Fl E , +run +.Ar shell-command +to replace the client. +.Tg has +.It Ic has-session Op Fl t Ar target-session +.D1 Pq alias: Ic has +Report an error and exit with 1 if the specified session does not exist. +If it does exist, exit with 0. +.It Ic kill-server +Kill the +.Nm +server and clients and destroy all sessions. +.It Xo Ic kill-session +.Op Fl aC +.Op Fl t Ar target-session +.Xc +Destroy the given session, closing any windows linked to it and no other +sessions, and detaching all clients attached to it. +If +.Fl a +is given, all sessions but the specified one is killed. +The +.Fl C +flag clears alerts (bell, activity, or silence) in all windows linked to the +session. +.Tg lsc +.It Xo Ic list-clients +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic lsc +List all clients attached to the server. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only clients for which the filter is true are shown. +See the +.Sx FORMATS +section. +If +.Ar target-session +is specified, list only clients connected to that session. +.Tg lscm +.It Xo Ic list-commands +.Op Fl F Ar format +.Op Ar command +.Xc +.D1 Pq alias: Ic lscm +List the syntax of +.Ar command +or - if omitted - of all commands supported by +.Nm . +.Tg ls +.It Xo Ic list-sessions +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc +.D1 Pq alias: Ic ls +List all sessions managed by the server. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only sessions for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg lockc +.It Ic lock-client Op Fl t Ar target-client +.D1 Pq alias: Ic lockc +Lock +.Ar target-client , +see the +.Ic lock-server +command. +.Tg locks +.It Ic lock-session Op Fl t Ar target-session +.D1 Pq alias: Ic locks +Lock all clients attached to +.Ar target-session . +.Tg new +.It Xo Ic new-session +.Op Fl AdDEPX +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl f Ar flags +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl s Ar session-name +.Op Fl t Ar group-name +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic new +Create a new session with name +.Ar session-name . +.Pp +The new session is attached to the current terminal unless +.Fl d +is given. +.Ar window-name +and +.Ar shell-command +are the name of and shell command to execute in the initial window. +With +.Fl d , +the initial size comes from the global +.Ic default-size +option; +.Fl x +and +.Fl y +can be used to specify a different size. +.Ql - +uses the size of the current client if any. +If +.Fl x +or +.Fl y +is given, the +.Ic default-size +option is set for the session. +.Fl f +sets a comma-separated list of client flags (see +.Ic attach-session ) . +.Pp +If run from a terminal, any +.Xr termios 4 +special characters are saved and used for new windows in the new session. +.Pp +The +.Fl A +flag makes +.Ic new-session +behave like +.Ic attach-session +if +.Ar session-name +already exists; +if +.Fl A +is given, +.Fl D +behaves like +.Fl d +to +.Ic attach-session , +and +.Fl X +behaves like +.Fl x +to +.Ic attach-session . +.Pp +If +.Fl t +is given, it specifies a +.Ic session group . +Sessions in the same group share the same set of windows - new windows are +linked to all sessions in the group and any windows closed removed from all +sessions. +The current and previous window and any session options remain independent and +any session in a group may be killed without affecting the others. +The +.Ar group-name +argument may be: +.Bl -enum -width Ds +.It +the name of an existing group, in which case the new session is added to that +group; +.It +the name of an existing session - the new session is added to the same group +as that session, creating a new group if necessary; +.It +the name for a new group containing only the new session. +.El +.Pp +.Fl n +and +.Ar shell-command +are invalid if +.Fl t +is used. +.Pp +The +.Fl P +option prints information about the new session after it has been created. +By default, it uses the format +.Ql #{session_name}:\& +but a different format may be specified with +.Fl F . +.Pp +If +.Fl E +is used, the +.Ic update-environment +option will not be applied. +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created session; it may be +specified multiple times. +.Tg refresh +.It Xo Ic refresh-client +.Op Fl cDLRSU +.Op Fl A Ar pane:state +.Op Fl B Ar name:what:format +.Op Fl C Ar size +.Op Fl f Ar flags +.Op Fl l Op Ar target-pane +.Op Fl r Ar pane:report +.Op Fl t Ar target-client +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic refresh +Refresh the current client if bound to a key, or a single client if one is given +with +.Fl t . +If +.Fl S +is specified, only update the client's status line. +.Pp +The +.Fl U , +.Fl D , +.Fl L +.Fl R , +and +.Fl c +flags allow the visible portion of a window which is larger than the client +to be changed. +.Fl U +moves the visible part up by +.Ar adjustment +rows and +.Fl D +down, +.Fl L +left by +.Ar adjustment +columns and +.Fl R +right. +.Fl c +returns to tracking the cursor automatically. +If +.Ar adjustment +is omitted, 1 is used. +Note that the visible position is a property of the client not of the +window, changing the current window in the attached session will reset +it. +.Pp +.Fl C +sets the width and height of a control mode client or of a window for a +control mode client, +.Ar size +must be one of +.Ql widthxheight +or +.Ql window ID:widthxheight , +for example +.Ql 80x24 +or +.Ql @0:80x24 . +.Fl A +allows a control mode client to trigger actions on a pane. +The argument is a pane ID (with leading +.Ql % ) , +a colon, then one of +.Ql on , +.Ql off , +.Ql continue +or +.Ql pause . +If +.Ql off , +.Nm +will not send output from the pane to the client and if all clients have turned +the pane off, will stop reading from the pane. +If +.Ql continue , +.Nm +will return to sending output to the pane if it was paused (manually or with the +.Ar pause-after +flag). +If +.Ql pause , +.Nm +will pause the pane. +.Fl A +may be given multiple times for different panes. +.Pp +.Fl B +sets a subscription to a format for a control mode client. +The argument is split into three items by colons: +.Ar name +is a name for the subscription; +.Ar what +is a type of item to subscribe to; +.Ar format +is the format. +After a subscription is added, changes to the format are reported with the +.Ic %subscription-changed +notification, at most once a second. +If only the name is given, the subscription is removed. +.Ar what +may be empty to check the format only for the attached session, or one of: +a pane ID such as +.Ql %0 ; +.Ql %* +for all panes in the attached session; +a window ID such as +.Ql @0 ; +or +.Ql @* +for all windows in the attached session. +.Pp +.Fl f +sets a comma-separated list of client flags, see +.Ic attach-session . +.Fl r +allows a control mode client to provide information about a pane via a report +(such as the response to OSC 10). +The argument is a pane ID (with a leading +.Ql % ) , +a colon, then a report escape sequence. +.Pp +.Fl l +requests the clipboard from the client using the +.Xr xterm 1 +escape sequence. +If +.Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. +.Pp +.Fl L , +.Fl R , +.Fl U +and +.Fl D +move the visible portion of the window left, right, up or down +by +.Ar adjustment , +if the window is larger than the client. +.Fl c +resets so that the position follows the cursor. +See the +.Ic window-size +option. +.Tg rename +.It Xo Ic rename-session +.Op Fl t Ar target-session +.Ar new-name +.Xc +.D1 Pq alias: Ic rename +Rename the session to +.Ar new-name . +.It Xo Ic server-access +.Op Fl adlrw +.Op Ar user +.Xc +Change the access or read/write permission of +.Ar user . +The user running the +.Nm +server (its owner) and the root user cannot be changed and are always +permitted access. +.Pp +.Fl a +and +.Fl d +are used to give or revoke access for the specified user. +If the user is already attached, the +.Fl d +flag causes their clients to be detached. +.Pp +.Fl r +and +.Fl w +change the permissions for +.Ar user : +.Fl r +makes their clients read-only and +.Fl w +writable. +.Fl l +lists current access permissions. +.Pp +By default, the access list is empty and +.Nm +creates sockets with file system permissions preventing access by any user +other than the owner (and root). +These permissions must be changed manually. +Great care should be taken not to allow access to untrusted users even +read-only. +.Tg showmsgs +.It Xo Ic show-messages +.Op Fl JT +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic showmsgs +Show server messages or information. +Messages are stored, up to a maximum of the limit set by the +.Ar message-limit +server option. +.Fl J +and +.Fl T +show debugging information about jobs and terminals. +.Tg source +.It Xo Ic source-file +.Op Fl Fnqv +.Op Fl t Ar target-pane +.Ar path ... +.Xc +.D1 Pq alias: Ic source +Execute commands from one or more files specified by +.Ar path +(which may be +.Xr glob 7 +patterns). +If +.Fl F +is present, then +.Ar path +is expanded as a format. +If +.Fl q +is given, no error will be returned if +.Ar path +does not exist. +With +.Fl n , +the file is parsed but no commands are executed. +.Fl v +shows the parsed commands and line numbers if possible. +.Tg start +.It Ic start-server +.D1 Pq alias: Ic start +Start the +.Nm +server, if not already running, without creating any sessions. +.Pp +Note that as by default the +.Nm +server will exit with no sessions, this is only useful if a session is created +in +.Pa \[ti]/.tmux.conf , +.Ic exit-empty +is turned off, or another command is run as part of the same command sequence. +For example: +.Bd -literal -offset indent +$ tmux start \\; show -g +.Ed +.Tg suspendc +.It Xo Ic suspend-client +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic suspendc +Suspend a client by sending +.Dv SIGTSTP +(tty stop). +.Tg switchc +.It Xo Ic switch-client +.Op Fl ElnprZ +.Op Fl c Ar target-client +.Op Fl t Ar target-session +.Op Fl T Ar key-table +.Xc +.D1 Pq alias: Ic switchc +Switch the current session for client +.Ar target-client +to +.Ar target-session . +As a special case, +.Fl t +may refer to a pane (a target that contains +.Ql \&: , +.Ql \&. +or +.Ql % ) , +to change session, window and pane. +In that case, +.Fl Z +keeps the window zoomed if it was zoomed. +If +.Fl l , +.Fl n +or +.Fl p +is used, the client is moved to the last, next or previous session +respectively. +.Fl r +toggles the client +.Ic read-only +and +.Ic ignore-size +flags (see the +.Ic attach-session +command). +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Pp +.Fl T +sets the client's key table; the next key from the client will be interpreted +from +.Ar key-table . +This may be used to configure multiple prefix keys, or to bind commands to +sequences of keys. +For example, to make typing +.Ql abc +run the +.Ic list-keys +command: +.Bd -literal -offset indent +bind-key -Ttable2 c list-keys +bind-key -Ttable1 b switch-client -Ttable2 +bind-key -Troot a switch-client -Ttable1 +.Ed +.El +.Sh WINDOWS AND PANES +Each window displayed by +.Nm +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +Windows may be split horizontally (with the +.Fl h +flag) or vertically. +Panes may be resized with the +.Ic resize-pane +command (bound to +.Ql C-Up , +.Ql C-Down +.Ql C-Left +and +.Ql C-Right +by default), the current pane may be changed with the +.Ic select-pane +command and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing their position. +Panes are numbered beginning from zero in the order they are created. +.Pp +By default, a +.Nm +pane permits direct access to the terminal contained in the pane. +A pane may also be put into one of several modes: +.Bl -dash -offset indent +.It +Copy mode, which permits a section of a window or its +history to be copied to a +.Em paste buffer +for later insertion into another window. +This mode is entered with the +.Ic copy-mode +command, bound to +.Ql \&[ +by default. +Copied text can be pasted with the +.Ic paste-buffer +command, bound to +.Ql \&] . +.It +View mode, which is like copy mode but is entered when a command that produces +output, such as +.Ic list-keys , +is executed from a key binding. +.It +Choose mode, which allows an item to be chosen from a list. +This may be a client, a session or window or pane, or a buffer. +This mode is entered with the +.Ic choose-buffer , +.Ic choose-client +and +.Ic choose-tree +commands. +.El +.Pp +In copy mode an indicator is displayed in the top-right corner of the pane with +the current position and the number of lines in the history. +.Pp +Commands are sent to copy mode using the +.Fl X +flag to the +.Ic send-keys +command. +When a key is pressed, copy mode automatically uses one of two key tables, +depending on the +.Ic mode-keys +option: +.Ic copy-mode +for emacs, or +.Ic copy-mode-vi +for vi. +Key tables may be viewed with the +.Ic list-keys +command. +.Pp +The following commands are supported in copy mode: +.Bl -tag -width Ds +.It Xo +.Ic append-selection +.Xc +Append the selection to the top paste buffer. +.It Xo +.Ic append-selection-and-cancel +(vi: A) +.Xc +Append the selection to the top paste buffer and exit copy mode. +.It Xo +.Ic back-to-indentation +(vi: ^) +(emacs: M-m) +.Xc +Move the cursor back to the indentation. +.It Xo +.Ic begin-selection +(vi: Space) +(emacs: C-Space) +.Xc +Begin selection. +.It Xo +.Ic bottom-line +(vi: L) +.Xc +Move to the bottom line. +.It Xo +.Ic cancel +(vi: q) +(emacs: Escape) +.Xc +Exit copy mode. +.It Xo +.Ic clear-selection +(vi: Escape) +(emacs: C-g) +.Xc +Clear the current selection. +.It Xo +.Ic copy-end-of-line +.Op Fl CP +.Op Ar prefix +.Xc +Copy from the cursor position to the end of the line. +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-end-of-line-and-cancel +.Op Fl CP +.Op Ar prefix +.Xc +Copy from the cursor position and exit copy mode. +.It Xo +.Ic copy-pipe-end-of-line +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy from the cursor position to the end of the line and pipe the text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-end-of-line-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe-end-of-line +but also exit copy mode. +.It Xo +.Ic copy-line +.Op Fl CP +.Op Ar prefix +.Xc +Copy the entire line. +.It Xo +.Ic copy-line-and-cancel +.Op Fl CP +.Op Ar prefix +.Xc +Copy the entire line and exit copy mode. +.It Xo +.Ic copy-pipe-line +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy the entire line and pipe the text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-line-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe-line +but also exit copy mode. +.It Xo +.Ic copy-pipe +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy the selection, clear it and pipe its text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-no-clear +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe +but do not clear the selection. +.It Xo +.Ic copy-pipe-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe +but also exit copy mode. +.It Xo +.Ic copy-selection +.Op Fl CP +.Op Ar prefix +.Xc +Copies the current selection. +.It Xo +.Ic copy-selection-no-clear +.Op Fl CP +.Op Ar prefix +.Xc +Same as +.Ic copy-selection +but do not clear the selection. +.It Xo +.Ic copy-selection-and-cancel +.Op Fl CP +.Op Ar prefix +(vi: Enter) +(emacs: M-w) +.Xc +Copy the current selection and exit copy mode. +.It Xo +.Ic cursor-down +(vi: j) +(emacs: Down) +.Xc +Move the cursor down. +.It Xo +.Ic cursor-down-and-cancel +.Xc +Same as +.Ic cursor-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic cursor-left +(vi: h) +(emacs: Left) +.Xc +Move the cursor left. +.It Xo +.Ic cursor-right +(vi: l) +(emacs: Right) +.Xc +Move the cursor right. +.It Xo +.Ic cursor-up +(vi: k) +(emacs: Up) +.Xc +Move the cursor up. +.It Xo +.Ic end-of-line +(vi: $) +(emacs: C-e) +.Xc +Move the cursor to the end of the line. +.It Xo +.Ic goto-line +.Ar line +(vi: :) +(emacs: g) +.Xc +Move the cursor to a specific line. +.It Xo +.Ic halfpage-down +(vi: C-d) +(emacs: M-Down) +.Xc +Scroll down by half a page. +.It Xo +.Ic halfpage-down-and-cancel +.Xc +Same as +.Ic halfpage-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic halfpage-up +(vi: C-u) +(emacs: M-Up) +.Xc +Scroll up by half a page. +.It Xo +.Ic history-bottom +(vi: G) +(emacs: M->) +.Xc +Scroll to the bottom of the history. +.It Xo +.Ic history-top +(vi: g) +(emacs: M-<) +.Xc +Scroll to the top of the history. +.It Xo +.Ic jump-again +(vi: ;) +(emacs: ;) +.Xc +Repeat the last jump. +.It Xo +.Ic jump-backward +.Ar to +(vi: F) +(emacs: F) +.Xc +Jump backwards to the specified text. +.It Xo +.Ic jump-forward +.Ar to +(vi: f) +(emacs: f) +.Xc +Jump forward to the specified text. +.It Xo +.Ic jump-reverse +(vi: ,) +(emacs: ,) +.Xc +Repeat the last jump in the reverse direction (forward becomes backward and +backward becomes forward). +.It Xo +.Ic jump-to-backward +.Ar to +(vi: T) +.Xc +Jump backwards, but one character less, placing the cursor on the character +after the target. +.It Xo +.Ic jump-to-forward +.Ar to +(vi: t) +.Xc +Jump forward, but one character less, placing the cursor on the character +before the target. +.It Xo +.Ic jump-to-mark +(vi: M-x) +(emacs: M-x) +.Xc +Jump to the last mark. +.It Xo +.Ic middle-line +(vi: M) +(emacs: M-r) +.Xc +Move to the middle line. +.It Xo +.Ic next-matching-bracket +(vi: %) +(emacs: M-C-f) +.Xc +Move to the next matching bracket. +.It Xo +.Ic next-paragraph +(vi: }) +(emacs: M-}) +.Xc +Move to the next paragraph. +.It Xo +.Ic next-prompt +.Op Fl o +.Xc +Move to the next prompt. +.It Xo +.Ic next-word +(vi: w) +.Xc +Move to the next word. +.It Xo +.Ic next-word-end +(vi: e) +(emacs: M-f) +.Xc +Move to the end of the next word. +.It Xo +.Ic next-space +(vi: W) +.Xc +Same as +.Ic next-word +but use a space alone as the word separator. +.It Xo +.Ic next-space-end +(vi: E) +.Xc +Same as +.Ic next-word-end +but use a space alone as the word separator. +.It Xo +.Ic other-end +(vi: o) +.Xc +Switch at which end of the selection the cursor sits. +.It Xo +.Ic page-down +(vi: C-f) +(emacs: PageDown) +.Xc +Scroll down by one page. +.It Xo +.Ic page-down-and-cancel +.Xc +Same as +.Ic page-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic page-up +(vi: C-b) +(emacs: PageUp) +.Xc +Scroll up by one page. +.It Xo +.Ic pipe +.Op Ar command +.Xc +Pipe the selected text to +.Ar command +and clear the selection. +.It Xo +.Ic pipe-no-clear +.Op Ar command +.Xc +Same as +.Ic pipe +but do not clear the selection. +.It Xo +.Ic pipe-and-cancel +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic pipe +but also exit copy mode. +.It Xo +.Ic previous-matching-bracket +(emacs: M-C-b) +.Xc +Move to the previous matching bracket. +.It Xo +.Ic previous-paragraph +(vi: {) +(emacs: M-{) +.Xc +Move to the previous paragraph. +.It Xo +.Ic previous-prompt +.Op Fl o +.Xc +Move to the previous prompt. +.It Xo +.Ic previous-word +(vi: b) +(emacs: M-b) +.Xc +Move to the previous word. +.It Xo +.Ic previous-space +(vi: B) +.Xc +Same as +.Ic previous-word +but use a space alone as the word separator. +.It Xo +.Ic rectangle-on +.Xc +Turn on rectangle selection mode. +.It Xo +.Ic rectangle-off +.Xc +Turn off rectangle selection mode. +.It Xo +.Ic rectangle-toggle +(vi: v) +(emacs: R) +.Xc +Toggle rectangle selection mode. +.It Xo +.Ic refresh-from-pane +(vi: r) +(emacs: r) +.Xc +Refresh the content from the pane. +.It Xo +.Ic scroll-bottom +.Xc +Scroll up until the current line is at the bottom while keeping the cursor on +that line. +.It Xo +.Ic scroll-down +(vi: C-e) +(emacs: C-Down) +.Xc +Scroll down. +.It Xo +.Ic scroll-down-and-cancel +.Xc +Same as +.Ic scroll-down +but also exit copy mode if the cursor reaches the bottom. +.It Xo +.Ic scroll-middle +(vi: z) +.Xc +Scroll so that the current line becomes the middle one while keeping the +cursor on that line. +.It Xo +.Ic scroll-top +.Xc +Scroll down until the current line is at the top while keeping the cursor on +that line. +.It Xo +.Ic scroll-up +(vi: C-y) +(emacs: C-Up) +.Xc +Scroll up. +.It Xo +.Ic search-again +(vi: n) +(emacs: n) +.Xc +Repeat the last search. +.It Xo +.Ic search-backward +.Ar text +(vi: ?) +.Xc +Search backwards for the specified text. +.It Xo +.Ic search-backward-incremental +.Ar text +(emacs: C-r) +.Xc +Search backwards incrementally for the specified text. +Is expected to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.It Xo +.Ic search-backward-text +.Ar text +.Xc +Search backwards for the specified plain text. +.It Xo +.Ic search-forward +.Ar text +(vi: /) +.Xc +Search forward for the specified text. +.It Xo +.Ic search-forward-incremental +.Ar text +(emacs: C-s) +.Xc +Search forward incrementally for the specified text. +Is expected to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.It Xo +.Ic search-forward-text +.Ar text +.Xc +Search forward for the specified plain text. +.It Xo +.Ic search-reverse +(vi: N) +(emacs: N) +.Xc +Repeat the last search in the reverse direction (forward becomes backward and +backward becomes forward). +.It Xo +.Ic select-line +(vi: V) +.Xc +Select the current line. +.It Xo +.Ic select-word +.Xc +Select the current word. +.It Xo +.Ic set-mark +(vi: X) +(emacs: X) +.Xc +Mark the current line. +.It Xo +.Ic start-of-line +(vi: 0) +(emacs: C-a) +.Xc +Move the cursor to the start of the line. +.It Xo +.Ic stop-selection +.Xc +Stop selecting without clearing the current selection. +.It Xo +.Ic toggle-position +(vi: P) +(emacs: P) +.Xc +Toggle the visibility of the position indicator in the top right. +.It Xo +.Ic top-line +(vi: H) +(emacs: M-R) +.Xc +Move to the top line. +.El +.Pp +The search commands come in several varieties: +.Ql search-forward +and +.Ql search-backward +search for a regular expression; +the +.Ql -text +variants search for a plain text string rather than a regular expression; +.Ql -incremental +perform an incremental search and expect to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.Ql search-again +repeats the last search and +.Ql search-reverse +does the same but reverses the direction (forward becomes backward and backward +becomes forward). +.Pp +The default incremental search key bindings, +.Ql C-r +and +.Ql C-s , +are designed to emulate +.Xr emacs 1 . +When first pressed they allow a new search term to be entered; if pressed with +an empty search term they repeat the previously used search term. +.Pp +The +.Ql next-prompt +and +.Ql previous-prompt +move between shell prompts, but require the shell to emit an escape sequence +(\e033]133;A\e033\e\e) to tell +.Nm +where the prompts are located; if the shell does not do this, these commands +will do nothing. +The +.Fl o +flag jumps to the beginning of the command output instead of the shell prompt. +Finding the beginning of command output requires the shell to emit an escape +sequence (\e033]133;C\e033\e\e) to tell tmux where the output begins. +If the shell does not send these escape sequences, these commands do nothing. +.Pp +Copy commands may take an optional buffer prefix argument which is used +to generate the buffer name (the default is +.Ql buffer +so buffers are named +.Ql buffer0 , +.Ql buffer1 +and so on). +Pipe commands take a command argument which is the command to which the +selected text is piped. +.Ql copy-pipe +variants also copy the selection. +The +.Ql -and-cancel +variants of some commands exit copy mode after they have completed (for copy +commands) or when the cursor reaches the bottom (for scrolling commands). +.Ql -no-clear +variants do not clear the selection. +All the copy commands can take the +.Fl C +and +.Fl P +flags. +The +.Fl C +flag suppresses setting the terminal clipboard when copying, while the +.Fl P +flag suppresses adding a paste buffer with the text. +.Pp +The next and previous word keys skip over whitespace and treat consecutive +runs of either word separators or other letters as words. +Word separators can be customized with the +.Em word-separators +session option. +Next word moves to the start of the next word, next word end to the end of the +next word and previous word to the start of the previous word. +The three next and previous space keys work similarly but use a space alone as +the word separator. +Setting +.Em word-separators +to the empty string makes next/previous word equivalent to next/previous space. +.Pp +The jump commands enable quick movement within a line. +For instance, typing +.Ql f +followed by +.Ql / +will move the cursor to the next +.Ql / +character on the current line. +A +.Ql \&; +will then jump to the next occurrence. +.Pp +Commands in copy mode may be prefaced by an optional repeat count. +With vi key bindings, a prefix is entered using the number keys; with +emacs, the Alt (meta) key and a number begins prefix entry. +.Pp +The synopsis for the +.Ic copy-mode +command is: +.Bl -tag -width Ds +.It Xo Ic copy-mode +.Op Fl deHMqSu +.Op Fl s Ar src-pane +.Op Fl t Ar target-pane +.Xc +Enter copy mode. +.Pp +.Fl u +enters copy mode and scrolls one page up and +.Fl d +one page down. +.Fl H +hides the position indicator in the top right. +.Fl q +cancels copy mode and any other modes. +.Pp +.Fl M +begins a mouse drag (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Fl S +scrolls when bound to a mouse drag event; for example, +.Ic copy-mode -Se +is bound to +.Ar MouseDrag1ScrollbarSlider +by default. +.Pp +.Fl s +copies from +.Ar src-pane +instead of +.Ar target-pane . +.Pp +.Fl e +specifies that scrolling to the bottom of the history (to the visible screen) +should exit copy mode. +While in copy mode, pressing a key other than those used for scrolling will +disable this behaviour. +This is intended to allow fast scrolling through a pane's history, for +example with: +.Bd -literal -offset indent +bind PageUp copy-mode -eu +bind PageDown copy-mode -ed +.Ed +.El +.Pp +A number of preset arrangements of panes are available, these are called +layouts. +These may be selected with the +.Ic select-layout +command or cycled with +.Ic next-layout +(bound to +.Ql Space +by default); once a layout is chosen, panes within it may be moved and resized +as normal. +.Pp +The following layouts are supported: +.Bl -tag -width Ds +.It Ic even-horizontal +Panes are spread out evenly from left to right across the window. +.It Ic even-vertical +Panes are spread evenly from top to bottom. +.It Ic main-horizontal +A large (main) pane is shown at the top of the window and the remaining panes +are spread from left to right in the leftover space at the bottom. +Use the +.Em main-pane-height +window option to specify the height of the top pane. +.It Ic main-horizontal-mirrored +The same as +.Ic main-horizontal +but mirrored so the main pane is at the bottom of the window. +.It Ic main-vertical +A large (main) pane is shown on the left of the window and the remaining panes +are spread from top to bottom in the leftover space on the right. +Use the +.Em main-pane-width +window option to specify the width of the left pane. +.It Ic main-vertical-mirrored +The same as +.Ic main-vertical +but mirrored so the main pane is on the right of the window. +.It Ic tiled +Panes are spread out as evenly as possible over the window in both rows and +columns. +.El +.Pp +In addition, +.Ic select-layout +may be used to apply a previously used layout - the +.Ic list-windows +command displays the layout of each window in a form suitable for use with +.Ic select-layout . +For example: +.Bd -literal -offset indent +$ tmux list-windows +0: ksh [159x48] + layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmux select-layout \[aq]bb62,159x48,0,0{79x48,0,0,79x48,80,0}\[aq] +.Ed +.Pp +.Nm +automatically adjusts the size of the layout for the current window size. +Note that a layout cannot be applied to a window with more panes than that +from which the layout was originally defined. +.Pp +Commands related to windows and panes are as follows: +.Bl -tag -width Ds +.Tg breakp +.It Xo Ic break-pane +.Op Fl abdP +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl s Ar src-pane +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic breakp +Break +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . +With +.Fl a +or +.Fl b , +the window is moved to the next index after or before (existing windows are +moved if necessary). +If +.Fl d +is given, the new window does not become the current window. +The +.Fl P +option prints information about the new window after it has been created. +By default, it uses the format +.Ql #{session_name}:#{window_index}.#{pane_index} +but a different format may be specified with +.Fl F . +.Tg capturep +.It Xo Ic capture-pane +.Op Fl aepPqCJMN +.Op Fl b Ar buffer-name +.Op Fl E Ar end-line +.Op Fl S Ar start-line +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic capturep +Capture the contents of a pane. +If +.Fl p +is given, the output goes to stdout, otherwise to the buffer specified with +.Fl b +or a new buffer if omitted. +If +.Fl a +is given, the alternate screen is used, and the history is not accessible. +If no alternate screen exists, an error will be returned unless +.Fl q +is given. +Similarly, if the pane is in a mode, +.Fl M +uses the screen for the mode. +If +.Fl e +is given, the output includes escape sequences for text and background +attributes. +.Fl C +also escapes non-printable characters as octal \exxx. +.Fl T +ignores trailing positions that do not contain a character. +.Fl N +preserves trailing spaces at each line's end and +.Fl J +preserves trailing spaces and joins any wrapped lines; +.Fl J +implies +.Fl T . +.Fl P +captures only any output that the pane has received that is the beginning of an +as-yet incomplete escape sequence. +.Pp +.Fl S +and +.Fl E +specify the starting and ending line numbers, zero is the first line of the +visible pane and negative numbers are lines in the history. +.Ql - +to +.Fl S +is the start of the history and to +.Fl E +the end of the visible pane. +The default is to capture only the visible contents of the pane. +.It Xo +.Ic choose-client +.Op Fl NryZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into client mode, allowing a client to be selected interactively from +a list. +Each client is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in client mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected client" +.It Li "Up" Ta "Select previous client" +.It Li "Down" Ta "Select next client" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if client is tagged" +.It Li "T" Ta "Tag no clients" +.It Li "C-t" Ta "Tag all clients" +.It Li "d" Ta "Detach selected client" +.It Li "D" Ta "Detach tagged clients" +.It Li "x" Ta "Detach and HUP selected client" +.It Li "X" Ta "Detach and HUP tagged clients" +.It Li "z" Ta "Suspend selected client" +.It Li "Z" Ta "Suspend tagged clients" +.It Li "f" Ta "Enter a format to filter items" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a client is chosen, +.Ql %% +is replaced by the client name in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "detach-client -t \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql name , +.Ql size , +.Ql creation +(time), +or +.Ql activity +(time). +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview or if given twice with the larger preview. +This command works only if at least one client is attached. +.It Xo +.Ic choose-tree +.Op Fl GNrswyZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into tree mode, where a session, window or pane may be chosen +interactively from a tree. +Each session, window or pane is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the tree may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl s +starts with sessions collapsed and +.Fl w +with windows collapsed. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in tree mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected item" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "S-Up" Ta "Swap the current window with the previous one" +.It Li "S-Down" Ta "Swap the current window with the next one" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "x" Ta "Kill selected item" +.It Li "X" Ta "Kill tagged items" +.It Li "<" Ta "Scroll list of previews left" +.It Li ">" Ta "Scroll list of previews right" +.It Li "C-s" Ta "Search by name" +.It Li "m" Ta "Set the marked pane" +.It Li "M" Ta "Clear the marked pane" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "\&:" Ta "Run a command for each tagged item" +.It Li "f" Ta "Enter a format to filter items" +.It Li "H" Ta "Jump to the starting pane" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a session, window or pane is chosen, the first instance of +.Ql %% +and all instances of +.Ql %1 +are replaced by the target in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "switch-client -t \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql index , +.Ql name , +or +.Ql time +(activity). +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview or if given twice with the larger preview. +.Fl G +includes all sessions in any session groups in the tree rather than only the +first. +This command works only if at least one client is attached. +.It Xo +.Ic customize-mode +.Op Fl NZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into customize mode, where options and key bindings may be browsed +and modified from a list. +Option values in the list are shown for the active pane in the current window. +.Fl Z +zooms the pane. +The following keys may be used in customize mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Set pane, window, session or global option value" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "s" Ta "Set option value or key attribute" +.It Li "S" Ta "Set global option value" +.It Li "w" Ta "Set window option value, if option is for pane and window" +.It Li "d" Ta "Set an option or key to the default" +.It Li "D" Ta "Set tagged options and tagged keys to the default" +.It Li "u" Ta "Unset an option (set to default value if global) or unbind a key" +.It Li "U" Ta "Unset tagged options and unbind tagged keys" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "f" Ta "Enter a format to filter items" +.It Li "v" Ta "Toggle option information" +.It Li "q" Ta "Exit mode" +.El +.Pp +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree. +.Fl N +starts without the option information. +This command works only if at least one client is attached. +.It Xo +.Tg displayp +.Ic display-panes +.Op Fl bN +.Op Fl d Ar duration +.Op Fl t Ar target-client +.Op Ar template +.Xc +.D1 Pq alias: Ic displayp +Display a visible indicator of each pane shown by +.Ar target-client . +See the +.Ic display-panes-colour +and +.Ic display-panes-active-colour +session options. +The indicator is closed when a key is pressed (unless +.Fl N +is given) or +.Ar duration +milliseconds have passed. +If +.Fl d +is not given, +.Ic display-panes-time +is used. +A duration of zero means the indicator stays until a key is pressed. +While the indicator is on screen, a pane may be chosen with the +.Ql 0 +to +.Ql 9 +keys, which will cause +.Ar template +to be executed as a command with +.Ql %% +substituted by the pane ID. +The default +.Ar template +is "select-pane -t \[aq]%%\[aq]". +With +.Fl b , +other commands are not blocked from running until the indicator is closed. +.Tg findw +.It Xo Ic find-window +.Op Fl iCNrTZ +.Op Fl t Ar target-pane +.Ar match-string +.Xc +.D1 Pq alias: Ic findw +Search for a +.Xr glob 7 +pattern or, with +.Fl r , +regular expression +.Ar match-string +in window names, titles, and visible content (but not history). +The flags control matching behavior: +.Fl C +matches only visible window contents, +.Fl N +matches only the window name and +.Fl T +matches only the window title. +.Fl i +makes the search ignore case. +The default is +.Fl CNT . +.Fl Z +zooms the pane. +.Pp +This command works only if at least one client is attached. +.Tg joinp +.It Xo Ic join-pane +.Op Fl bdfhv +.Op Fl l Ar size +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic joinp +Like +.Ic split-window , +but instead of splitting +.Ar dst-pane +and creating a new pane, split it and move +.Ar src-pane +into the space. +This can be used to reverse +.Ic break-pane . +The +.Fl b +option causes +.Ar src-pane +to be joined to left of or above +.Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. +.Tg killp +.It Xo Ic kill-pane +.Op Fl a +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic killp +Destroy the given pane. +If no panes remain in the containing window, it is also destroyed. +The +.Fl a +option kills all but the pane given with +.Fl t . +.Tg killw +.It Xo Ic kill-window +.Op Fl a +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic killw +Kill the current window or the window at +.Ar target-window , +removing it from any sessions to which it is linked. +The +.Fl a +option kills all but the window given with +.Fl t . +.Tg lastp +.It Xo Ic last-pane +.Op Fl deZ +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic lastp +Select the last (previously selected) pane. +.Fl Z +keeps the window zoomed if it was zoomed. +.Fl e +enables or +.Fl d +disables input to the pane. +.Tg last +.It Ic last-window Op Fl t Ar target-session +.D1 Pq alias: Ic last +Select the last (previously selected) window. +If no +.Ar target-session +is specified, select the last window of the current session. +.Tg link +.It Xo Ic link-window +.Op Fl abdk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic linkw +Link the window at +.Ar src-window +to the specified +.Ar dst-window . +If +.Ar dst-window +is specified and no such window exists, the +.Ar src-window +is linked there. +With +.Fl a +or +.Fl b +the window is moved to the next index after or before +.Ar dst-window +(existing windows are moved if necessary). +If +.Fl k +is given and +.Ar dst-window +exists, it is killed, otherwise an error is generated. +If +.Fl d +is given, the newly linked window is not selected. +.Tg lsp +.It Xo Ic list-panes +.Op Fl as +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target +.Xc +.D1 Pq alias: Ic lsp +If +.Fl a +is given, +.Ar target +is ignored and all panes on the server are listed. +If +.Fl s +is given, +.Ar target +is a session (or the current session). +If neither is given, +.Ar target +is a window (or the current window). +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only panes for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg lsw +.It Xo Ic list-windows +.Op Fl a +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic lsw +If +.Fl a +is given, list all windows on the server. +Otherwise, list windows in the current session or in +.Ar target-session . +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only windows for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg movep +.It Xo Ic move-pane +.Op Fl bdfhv +.Op Fl l Ar size +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic movep +Does the same as +.Ic join-pane . +.Tg movew +.It Xo Ic move-window +.Op Fl abrdk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic movew +This is similar to +.Ic link-window , +except the window at +.Ar src-window +is moved to +.Ar dst-window . +With +.Fl r , +all windows in the session are renumbered in sequential order, respecting +the +.Ic base-index +option. +.Tg neww +.It Xo Ic new-window +.Op Fl abdkPS +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic neww +Create a new window. +With +.Fl a +or +.Fl b , +the new window is inserted at the next index after or before the specified +.Ar target-window , +moving windows up if necessary; +otherwise +.Ar target-window +is the new window location. +.Pp +If +.Fl d +is given, the session does not make the new window the current window. +.Ar target-window +represents the window to be created; if the target already exists an error is +shown, unless the +.Fl k +flag is used, in which case it is destroyed. +If +.Fl S +is given and a window named +.Ar window-name +already exists, it is selected (unless +.Fl d +is also given in which case the command does nothing). +.Pp +.Ar shell-command +is the command to execute. +If +.Ar shell-command +is not specified, the value of the +.Ic default-command +option is used. +.Fl c +specifies the working directory in which the new window is created. +.Pp +When the shell command completes, the window closes. +See the +.Ic remain-on-exit +option to change this behaviour. +.Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created window; it may be +specified multiple times. +.Pp +The +.Ev TERM +environment variable must be set to +.Ql screen +or +.Ql tmux +for all programs running +.Em inside +.Nm . +New windows will automatically have +.Ql TERM=screen +added to their environment, but care must be taken not to reset this in shell +start-up files or by the +.Fl e +option. +.Pp +The +.Fl P +option prints information about the new window after it has been created. +By default, it uses the format +.Ql #{session_name}:#{window_index} +but a different format may be specified with +.Fl F . +.Tg nextl +.It Ic next-layout Op Fl t Ar target-window +.D1 Pq alias: Ic nextl +Move a window to the next layout and rearrange the panes to fit. +.Tg next +.It Xo Ic next-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic next +Move to the next window in the session. +If +.Fl a +is used, move to the next window with an alert. +.Tg pipep +.It Xo Ic pipe-pane +.Op Fl IOo +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic pipep +Pipe output sent by the program in +.Ar target-pane +to a shell command or vice versa. +A pane may only be connected to one command at a time, any existing pipe is +closed before +.Ar shell-command +is executed. +The +.Ar shell-command +string may contain the special character sequences supported by the +.Ic status-left +option. +If no +.Ar shell-command +is given, the current pipe (if any) is closed. +.Pp +.Fl I +and +.Fl O +specify which of the +.Ar shell-command +output streams are connected to the pane: +with +.Fl I +stdout is connected (so anything +.Ar shell-command +prints is written to the pane as if it were typed); +with +.Fl O +stdin is connected (so any output in the pane is piped to +.Ar shell-command ) . +Both may be used together and if neither are specified, +.Fl O +is used. +.Pp +The +.Fl o +option only opens a new pipe if no previous pipe exists, allowing a pipe to +be toggled with a single key, for example: +.Bd -literal -offset indent +bind-key C-p pipe-pane -o \[aq]cat >>\[ti]/output.#I-#P\[aq] +.Ed +.Tg prevl +.It Xo Ic previous-layout +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic prevl +Move to the previous layout in the session. +.Tg prev +.It Xo Ic previous-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic prev +Move to the previous window in the session. +With +.Fl a , +move to the previous window with an alert. +.Tg renamew +.It Xo Ic rename-window +.Op Fl t Ar target-window +.Ar new-name +.Xc +.D1 Pq alias: Ic renamew +Rename the current window, or the window at +.Ar target-window +if specified, to +.Ar new-name . +.Tg resizep +.It Xo Ic resize-pane +.Op Fl DLMRTUZ +.Op Fl t Ar target-pane +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic resizep +Resize a pane, up, down, left or right by +.Ar adjustment +with +.Fl U , +.Fl D , +.Fl L +or +.Fl R , +or +to an absolute size +with +.Fl x +or +.Fl y . +The +.Ar adjustment +is given in lines or columns (the default is 1); +.Fl x +and +.Fl y +may be a given as a number of lines or columns or followed by +.Ql % +for a percentage of the window size (for example +.Ql -x 10% ) . +With +.Fl Z , +the active pane is toggled between zoomed (occupying the whole of the window) +and unzoomed (its normal position in the layout). +.Pp +.Fl M +begins mouse resizing (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Pp +.Fl T +trims all lines below the current cursor position and moves lines out of the +history to replace them. +.Tg resizew +.It Xo Ic resize-window +.Op Fl aADLRU +.Op Fl t Ar target-window +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic resizew +Resize a window, up, down, left or right by +.Ar adjustment +with +.Fl U , +.Fl D , +.Fl L +or +.Fl R , +or +to an absolute size +with +.Fl x +or +.Fl y . +The +.Ar adjustment +is given in lines or cells (the default is 1). +.Fl A +sets the size of the largest session containing the window; +.Fl a +the size of the smallest. +This command will automatically set +.Ic window-size +to manual in the window options. +.Tg respawnp +.It Xo Ic respawn-pane +.Op Fl k +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic respawnp +Reactivate a pane in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the pane was created or last respawned is +executed. +The pane must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.Fl c +specifies a new working directory for the pane. +The +.Fl e +option has the same meaning as for the +.Ic new-window +command. +.Tg respawnw +.It Xo Ic respawn-window +.Op Fl k +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic respawnw +Reactivate a window in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the window was created or last respawned is +executed. +The window must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.Fl c +specifies a new working directory for the window. +The +.Fl e +option has the same meaning as for the +.Ic new-window +command. +.Tg rotatew +.It Xo Ic rotate-window +.Op Fl DUZ +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic rotatew +Rotate the positions of the panes within a window, either upward (numerically +lower) with +.Fl U +or downward (numerically higher). +.Fl Z +keeps the window zoomed if it was zoomed. +.Tg selectl +.It Xo Ic select-layout +.Op Fl Enop +.Op Fl t Ar target-pane +.Op Ar layout-name +.Xc +.D1 Pq alias: Ic selectl +Choose a specific layout for a window. +If +.Ar layout-name +is not given, the last preset layout used (if any) is reapplied. +.Fl n +and +.Fl p +are equivalent to the +.Ic next-layout +and +.Ic previous-layout +commands. +.Fl o +applies the last set layout if possible (undoes the most recent layout change). +.Fl E +spreads the current pane and any panes next to it out evenly. +.Tg selectp +.It Xo Ic select-pane +.Op Fl DdeLlMmRUZ +.Op Fl T Ar title +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic selectp +Make pane +.Ar target-pane +the active pane in its window. +If one of +.Fl D , +.Fl L , +.Fl R , +or +.Fl U +is used, respectively the pane below, to the left, to the right, or above the +target pane is used. +.Fl Z +keeps the window zoomed if it was zoomed. +.Fl l +is the same as using the +.Ic last-pane +command. +.Fl e +enables or +.Fl d +disables input to the pane. +.Fl T +sets the pane title. +.Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic move-pane , +.Ic swap-pane +and +.Ic swap-window . +.Tg selectw +.It Xo Ic select-window +.Op Fl lnpT +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic selectw +Select the window at +.Ar target-window . +.Fl l , +.Fl n +and +.Fl p +are equivalent to the +.Ic last-window , +.Ic next-window +and +.Ic previous-window +commands. +If +.Fl T +is given and the selected window is already the current window, +the command behaves like +.Ic last-window . +.Tg splitw +.It Xo Ic split-window +.Op Fl bdfhIvPZ +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl l Ar size +.Op Fl t Ar target-pane +.Op Ar shell-command +.Op Fl F Ar format +.Xc +.D1 Pq alias: Ic splitw +Create a new pane by splitting +.Ar target-pane : +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target-pane . +The +.Fl f +option creates a new pane spanning the full window height (with +.Fl h ) +or full window width (with +.Fl v ) , +instead of splitting the active pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +.Pp +An empty +.Ar shell-command +(\[aq]\[aq]) will create a pane with no command running in it. +Output can be sent to such a pane with the +.Ic display-message +command. +The +.Fl I +flag (if +.Ar shell-command +is not specified or empty) +will create an empty pane and forward any output from stdin to it. +For example: +.Bd -literal -offset indent +$ make 2>&1|tmux splitw -dI & +.Ed +.Pp +All other options have the same meaning as for the +.Ic new-window +command. +.Tg swapp +.It Xo Ic swap-pane +.Op Fl dDUZ +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic swapp +Swap two panes. +If +.Fl U +is used and no source pane is specified with +.Fl s , +.Ar dst-pane +is swapped with the previous pane (before it numerically); +.Fl D +swaps with the next pane (after it numerically). +.Fl d +instructs +.Nm +not to change the active pane and +.Fl Z +keeps the window zoomed if it was zoomed. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. +.Tg swapw +.It Xo Ic swap-window +.Op Fl d +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic swapw +This is similar to +.Ic link-window , +except the source and destination windows are swapped. +It is an error if no window exists at +.Ar src-window . +If +.Fl d +is given, the new window does not become the current window. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. +.Tg unlinkw +.It Xo Ic unlink-window +.Op Fl k +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic unlinkw +Unlink +.Ar target-window . +Unless +.Fl k +is given, a window may be unlinked only if it is linked to multiple sessions - +windows may not be linked to no sessions; +if +.Fl k +is specified and the window is linked to only one session, it is unlinked and +destroyed. +.El +.Sh KEY BINDINGS +.Nm +allows a command to be bound to most keys, with or without a prefix key. +When specifying keys, most represent themselves (for example +.Ql A +to +.Ql Z ) . +Ctrl keys may be prefixed with +.Ql C- +or +.Ql ^ , +Shift keys with +.Ql S- +and Alt (meta) with +.Ql M- . +In addition, the following special key names are accepted: +.Em Up , +.Em Down , +.Em Left , +.Em Right , +.Em BSpace , +.Em BTab , +.Em DC +(Delete), +.Em End , +.Em Enter , +.Em Escape , +.Em F1 +to +.Em F12 , +.Em Home , +.Em IC +(Insert), +.Em NPage/PageDown/PgDn , +.Em PPage/PageUp/PgUp , +.Em Space , +and +.Em Tab . +Note that to bind the +.Ql \&" +or +.Ql \[aq] +keys, quotation marks are necessary, for example: +.Bd -literal -offset indent +bind-key \[aq]"\[aq] split-window +bind-key "\[aq]" new-window +.Ed +.Pp +A command bound to the +.Em Any +key will execute for all keys which do not have a more specific binding. +.Pp +Commands related to key bindings are as follows: +.Bl -tag -width Ds +.Tg bind +.It Xo Ic bind-key +.Op Fl nr +.Op Fl N Ar note +.Op Fl T Ar key-table +.Ar key command Op Ar argument ... +.Xc +.D1 Pq alias: Ic bind +Bind key +.Ar key +to +.Ar command . +Keys are bound in a key table. +By default (without -T), the key is bound in +the +.Em prefix +key table. +This table is used for keys pressed after the prefix key (for example, +by default +.Ql c +is bound to +.Ic new-window +in the +.Em prefix +table, so +.Ql C-b c +creates a new window). +The +.Em root +table is used for keys pressed without the prefix key: binding +.Ql c +to +.Ic new-window +in the +.Em root +table (not recommended) means a plain +.Ql c +will create a new window. +.Fl n +is an alias +for +.Fl T Ar root . +Keys may also be bound in custom key tables and the +.Ic switch-client +.Fl T +command used to switch to them from a key binding. +The +.Fl r +flag indicates this key may repeat, see the +.Ic initial-repeat-time +and +.Ic repeat-time +options. +.Fl N +attaches a note to the key (shown with +.Ic list-keys +.Fl N ) . +.Pp +To view the default bindings and possible commands, see the +.Ic list-keys +command. +.Tg lsk +.It Xo Ic list-keys +.Op Fl 1aN +.Op Fl P Ar prefix-string Fl T Ar key-table +.Op Ar key +.Xc +.D1 Pq alias: Ic lsk +List key bindings. +There are two forms: the default lists keys as +.Ic bind-key +commands; +.Fl N +lists only keys with attached notes and shows only the key and note for each +key. +.Pp +With the default form, all key tables are listed by default. +.Fl T +lists only keys in +.Ar key-table . +.Pp +With the +.Fl N +form, only keys in the +.Em root +and +.Em prefix +key tables are listed by default; +.Fl T +also lists only keys in +.Ar key-table . +.Fl P +specifies a prefix to print before each key and +.Fl 1 +lists only the first matching key. +.Fl a +lists the command for keys that do not have a note rather than skipping them. +.Tg send +.It Xo Ic send-keys +.Op Fl FHKlMRX +.Op Fl c Ar target-client +.Op Fl N Ar repeat-count +.Op Fl t Ar target-pane +.Ar key ... +.Xc +.D1 Pq alias: Ic send +Send a key or keys to a window or client. +Each argument +.Ar key +is the name of the key (such as +.Ql C-a +or +.Ql NPage ) +to send; if the string is not recognised as a key, it is sent as a series of +characters. +If +.Fl K +is given, keys are sent to +.Ar target-client , +so they are looked up in the client's key table, rather than to +.Ar target-pane . +All arguments are sent sequentially from first to last. +If no keys are given and the command is bound to a key, then that key is used. +.Pp +The +.Fl l +flag disables key name lookup and processes the keys as literal UTF-8 +characters. +The +.Fl H +flag expects each key to be a hexadecimal number for an ASCII character. +.Pp +The +.Fl R +flag causes the terminal state to be reset. +.Pp +.Fl M +passes through a mouse event (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Pp +.Fl X +is used to send a command into copy mode - see +the +.Sx WINDOWS AND PANES +section. +.Fl N +specifies a repeat count and +.Fl F +expands formats in arguments where appropriate. +.It Xo Ic send-prefix +.Op Fl 2 +.Op Fl t Ar target-pane +.Xc +Send the prefix key, or with +.Fl 2 +the secondary prefix key, to a window as if it was pressed. +.Tg unbind +.It Xo Ic unbind-key +.Op Fl anq +.Op Fl T Ar key-table +.Ar key +.Xc +.D1 Pq alias: Ic unbind +Unbind the command bound to +.Ar key . +.Fl n +and +.Fl T +are the same as for +.Ic bind-key . +If +.Fl a +is present, all key bindings are removed. +The +.Fl q +option prevents errors being returned. +.El +.Sh OPTIONS +The appearance and behaviour of +.Nm +may be modified by changing the value of various options. +There are four types of option: +.Em server options , +.Em session options , +.Em window options , +and +.Em pane options . +.Pp +The +.Nm +server has a set of global server options which do not apply to any particular +window or session or pane. +These are altered with the +.Ic set-option +.Fl s +command, or displayed with the +.Ic show-options +.Fl s +command. +.Pp +In addition, each individual session may have a set of session options, and +there is a separate set of global session options. +Sessions which do not have a particular option configured inherit the value +from the global session options. +Session options are set or unset with the +.Ic set-option +command and may be listed with the +.Ic show-options +command. +The available server and session options are listed under the +.Ic set-option +command. +.Pp +Similarly, a set of window options is attached to each window and a set of pane +options to each pane. +Pane options inherit from window options. +This means any pane option may be set as a window option to apply the option to +all panes in the window without the option set, for example these commands will +set the background colour to red for all panes except pane 0: +.Bd -literal -offset indent +set -w window-style bg=red +set -pt:.0 window-style bg=blue +.Ed +.Pp +There is also a set of global window options from which any unset window or +pane options are inherited. +Window and pane options are altered with +.Ic set-option +.Fl w +and +.Fl p +commands and displayed with +.Ic show-option +.Fl w +and +.Fl p . +.Pp +.Nm +also supports user options which are prefixed with a +.Ql \&@ . +User options may have any name, so long as they are prefixed with +.Ql \&@ , +and be set to any string. +For example: +.Bd -literal -offset indent +$ tmux set -wq @foo "abc123" +$ tmux show -wv @foo +abc123 +.Ed +.Pp +Commands which set options are as follows: +.Bl -tag -width Ds +.Tg set +.It Xo Ic set-option +.Op Fl aFgopqsuUw +.Op Fl t Ar target-pane +.Ar option Ar value +.Xc +.D1 Pq alias: Ic set +Set a pane option with +.Fl p , +a window option with +.Fl w , +a server option with +.Fl s , +otherwise a session option. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +If +.Fl g +is given, the global session or window option is set. +.Pp +.Fl F +expands formats in the option value. +The +.Fl u +flag unsets an option, so a session inherits the option from the global +options (or with +.Fl g , +restores a global option to the default). +.Fl U +unsets an option (like +.Fl u ) +but if the option is a pane option also unsets the option on any panes in the +window. +.Ar value +depends on the option and may be a number, a string, or a flag (on, off, or +omitted to toggle). +.Pp +The +.Fl o +flag prevents setting an option that is already set and the +.Fl q +flag suppresses errors about unknown or ambiguous options. +.Pp +With +.Fl a , +and if the option expects a string or a style, +.Ar value +is appended to the existing setting. +For example: +.Bd -literal -offset indent +set -g status-left "foo" +set -ag status-left "bar" +.Ed +.Pp +Will result in +.Ql foobar . +And: +.Bd -literal -offset indent +set -g status-style "bg=red" +set -ag status-style "fg=blue" +.Ed +.Pp +Will result in a red background +.Em and +blue foreground. +Without +.Fl a , +the result would be the default background and a blue foreground. +.Tg show +.It Xo Ic show-options +.Op Fl AgHpqsvw +.Op Fl t Ar target-pane +.Op Ar option +.Xc +.D1 Pq alias: Ic show +Show the pane options (or a single option if +.Ar option +is provided) with +.Fl p , +the window options with +.Fl w , +the server options with +.Fl s , +otherwise the session options. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +Global session or window options are listed if +.Fl g +is used. +.Fl v +shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. +.Fl H +includes hooks (omitted by default). +.Fl A +includes options inherited from a parent set of options, such options are +marked with an asterisk. +.El +.Pp +Available server options are: +.Bl -tag -width Ds +.It Ic backspace Ar key +Set the key sent by +.Nm +for backspace. +.It Ic buffer-limit Ar number +Set the number of buffers; as new buffers are added to the top of the stack, +old ones are removed from the bottom if necessary to maintain this maximum +length. +.It Xo Ic command-alias[] +.Ar name=value +.Xc +This is an array of custom aliases for commands. +If an unknown command matches +.Ar name , +it is replaced with +.Ar value . +For example, after: +.Pp +.Dl set -s command-alias[100] zoom=\[aq]resize-pane -Z\[aq] +.Pp +Using: +.Pp +.Dl zoom -t:.1 +.Pp +Is equivalent to: +.Pp +.Dl resize-pane -Z -t:.1 +.Pp +Note that aliases are expanded when a command is parsed rather than when it is +executed, so binding an alias with +.Ic bind-key +will bind the expanded form. +.It Ic codepoint-widths[] Ar string +An array option allowing widths of Unicode codepoints to be overridden. +Note the new width applies to all clients. +Each entry is of the form +.Em codepoint=width , +where codepoint may be a UTF-8 character or an identifier of the form +.Ql U+number +where the number is a hexadecimal number. +.It Ic copy-command Ar shell-command +Give the command to pipe to if the +.Ic copy-pipe +copy mode command is used without arguments. +.It Ic default-client-command Ar command +Set the default command to run when tmux is called without a command. +The default is +.Ic new-session . +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen , +.Ql tmux +or a derivative of them. +.It Ic escape-time Ar time +Set the time in milliseconds for which +.Nm +waits after an escape is input to determine if it is part of a function or meta +key sequences. +.It Ic editor Ar shell-command +Set the command used when +.Nm +runs an editor. +.It Xo Ic exit-empty +.Op Ic on | off +.Xc +If enabled (the default), the server will exit when there are no active +sessions. +.It Xo Ic exit-unattached +.Op Ic on | off +.Xc +If enabled, the server will exit when there are no attached clients. +.It Xo Ic extended-keys +.Op Ic on | off | always +.Xc +Controls how modified keys (keys pressed together with Control, Meta, or Shift) +are reported. +This is the equivalent of the +.Ic modifyOtherKeys +.Xr xterm 1 +resource. +.Pp +When set to +.Ic on , +the program inside the pane can request one of two modes: mode 1 which changes +the sequence for only keys which lack an existing well-known representation; or +mode 2 which changes the sequence for all keys. +When set to +.Ic always , +modes 1 and 2 can still be requested by applications, but mode 1 will be forced +instead of the standard mode. +When set to +.Ic off , +this feature is disabled and only standard keys are reported. +.Pp +.Nm +will always request extended keys itself if the terminal supports them. +See also the +.Ic extkeys +feature for the +.Ic terminal-features +option, the +.Ic extended-keys-format +option and the +.Ic pane_key_mode +variable. +.It Xo Ic extended-keys-format +.Op Ic csi-u | xterm +.Xc +Selects one of the two possible formats for reporting modified keys to +applications. +This is the equivalent of the +.Ic formatOtherKeys +.Xr xterm 1 +resource. +For example, C-S-a will be reported as +.Ql ^[[27;6;65~ +when set to +.Ic xterm , +and as +.Ql ^[[65;6u +when set to +.Ic csi-u . +.It Xo Ic focus-events +.Op Ic on | off +.Xc +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. +.It Ic input-buffer-size Ar bytes +Maximum of bytes allowed to read in escape and control sequences. +Once reached, the sequence will be discarded. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +.It Ic prompt-history-limit Ar number +Set the number of history items to save in the history file for each type of +command prompt. +.It Xo Ic set-clipboard +.Op Ic on | external | off +.Xc +Attempt to set the terminal clipboard content using the +.Xr xterm 1 +escape sequence, if there is an +.Em \&Ms +entry in the +.Xr terminfo 5 +description (see the +.Sx TERMINFO EXTENSIONS +section). +.Pp +If set to +.Ic on , +.Nm +will both accept the escape sequence to create a buffer and attempt to set +the terminal clipboard. +If set to +.Ic external , +.Nm +will attempt to set the terminal clipboard but ignore attempts +by applications to set +.Nm +buffers. +If +.Ic off , +.Nm +will neither accept the clipboard escape sequence nor attempt to set the +clipboard. +.Pp +Note that this feature needs to be enabled in +.Xr xterm 1 +by setting the resource: +.Bd -literal -offset indent +disallowedWindowOps: 20,21,SetXprop +.Ed +.Pp +Or changing this property from the +.Xr xterm 1 +interactive menu when required. +.It Ic terminal-features[] Ar string +Set terminal features for terminal types read from +.Xr terminfo 5 . +.Nm +has a set of named terminal features. +Each will apply appropriate changes to the +.Xr terminfo 5 +entry in use. +.Pp +.Nm +can detect features for a few common terminals; this option can be used to +easily tell tmux about features supported by terminals it cannot detect. +The +.Ic terminal-overrides +option allows individual +.Xr terminfo 5 +capabilities to be set instead, +.Ic terminal-features +is intended for classes of functionality supported in a standard way but not +reported by +.Xr terminfo 5 . +Care must be taken to configure this only with features the terminal actually +supports. +.Pp +This is an array option where each entry is a colon-separated string made up +of a terminal type pattern (matched using +.Xr glob 7 +patterns) followed by a list of terminal features. +The available features are: +.Bl -tag -width Ds +.It 256 +Supports 256 colours with the SGR escape sequences. +.It clipboard +Allows setting the system clipboard. +.It ccolour +Allows setting the cursor colour. +.It cstyle +Allows setting the cursor style. +.It extkeys +Supports extended keys. +.It focus +Supports focus reporting. +.It hyperlinks +Supports OSC 8 hyperlinks. +.It ignorefkeys +Ignore function keys from +.Xr terminfo 5 +and use the +.Nm +internal set only. +.It margins +Supports DECSLRM margins. +.It mouse +Supports +.Xr xterm 1 +mouse sequences. +.It osc7 +Supports the OSC 7 working directory extension. +.It overline +Supports the overline SGR attribute. +.It rectfill +Supports the DECFRA rectangle fill escape sequence. +.It RGB +Supports RGB colour with the SGR escape sequences. +.It sixel +Supports SIXEL graphics. +.It strikethrough +Supports the strikethrough SGR escape sequence. +.It sync +Supports synchronized updates. +.It title +Supports +.Xr xterm 1 +title setting. +.It usstyle +Allows underscore style and colour to be set. +.El +.It Ic terminal-overrides[] Ar string +Allow terminal descriptions read using +.Xr terminfo 5 +to be overridden. +Each entry is a colon-separated string made up of a terminal type pattern +(matched using +.Xr glob 7 +patterns) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types matching +.Ql rxvt* : +.Pp +.Dl "rxvt*:clear=\ee[H\ee[2J" +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +.It Ic user-keys[] Ar key +Set list of user-defined key escape sequences. +Each item is associated with a key named +.Ql User0 , +.Ql User1 , +and so on. +.Pp +For example: +.Bd -literal -offset indent +set -s user-keys[0] "\ee[5;30012\[ti]" +bind User0 resize-pane -L 3 +.Ed +.El +.Pp +Available session options are: +.Bl -tag -width Ds +.It Xo Ic activity-action +.Op Ic any | none | current | other +.Xc +Set action on window activity when +.Ic monitor-activity +is on. +.Ic any +means activity in any window linked to a session causes a bell or message +(depending on +.Ic visual-activity ) +in the current window of that session, +.Ic none +means all activity is ignored (equivalent to +.Ic monitor-activity +being off), +.Ic current +means only activity in windows other than the current window are ignored and +.Ic other +means activity in the current window is ignored but not those in other windows. +.It Ic assume-paste-time Ar milliseconds +If keys are entered faster than one in +.Ar milliseconds , +they are assumed to have been pasted rather than typed and +.Nm +key bindings are not processed. +The default is one millisecond and zero disables. +.It Ic base-index Ar index +Set the base index from which an unused index should be searched when a new +window is created. +The default is zero. +.It Xo Ic bell-action +.Op Ic any | none | current | other +.Xc +Set action on a bell in a window when +.Ic monitor-bell +is on. +The values are the same as those for +.Ic activity-action . +.It Ic default-command Ar shell-command +Set the command used for new windows (if not specified when the window is +created) to +.Ar shell-command , +which may be any +.Xr sh 1 +command. +The default is an empty string, which instructs +.Nm +to create a login shell using the value of the +.Ic default-shell +option. +.It Ic default-shell Ar path +Specify the default shell. +This is used as the login shell for new windows when the +.Ic default-command +option is set to empty, and must be the full path of the executable. +When started +.Nm +tries to set a default value from the first suitable of the +.Ev SHELL +environment variable, the shell returned by +.Xr getpwuid 3 , +or +.Pa /bin/sh . +This option should be configured when +.Nm +is used as a login shell. +.It Ic default-size Ar XxY +Set the default size of new windows when the +.Ic window-size +option is set to manual or when a session is created with +.Ic new-session +.Fl d . +The value is the width and height separated by an +.Ql x +character. +The default is 80x24. +.It Xo Ic destroy-unattached +.Op Ic off | on | keep-last | keep-group +.Xc +If +.Ic on , +destroy the session after the last client has detached. +If +.Ic off +(the default), leave the session orphaned. +If +.Ic keep-last , +destroy the session only if it is in a group and has other sessions in that +group. +If +.Ic keep-group , +destroy the session unless it is in a group and is the only session in that +group. +.It Xo Ic detach-on-destroy +.Op Ic off | on | no-detached | previous | next +.Xc +If +.Ic on +(the default), the client is detached when the session it is attached to +is destroyed. +If +.Ic off , +the client is switched to the most recently active of the remaining +sessions. +If +.Ic no-detached , +the client is detached only if there are no detached sessions; if detached +sessions exist, the client is switched to the most recently active. +If +.Ic previous +or +.Ic next , +the client is switched to the previous or next session in alphabetical order. +.It Ic display-panes-active-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicator for the active pane. +.It Ic display-panes-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicators for inactive panes. +.It Ic display-panes-time Ar time +Set the time in milliseconds for which the indicators shown by the +.Ic display-panes +command appear. +.It Ic display-time Ar time +Set the amount of time for which status line messages and other on-screen +indicators are displayed. +If set to 0, messages and indicators are displayed until a key is pressed. +.Ar time +is in milliseconds. +.It Ic history-limit Ar lines +Set the maximum number of lines held in window history. +This setting applies only to new windows - existing window histories are not +resized and retain the limit at the point they were created. +.It Ic initial-repeat-time Ar time +Set the time in milliseconds for the initial repeat when a key is bound with the +.Fl r +flag. +This allows multiple commands to be entered without pressing the prefix key +again. +See also the +.Ic repeat-time +option. +If +.Ic initial-repeat-time +is zero, +.Ic repeat-time +is used for the first key press. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . +.It Ic lock-after-time Ar number +Lock the session (like the +.Ic lock-session +command) after +.Ar number +seconds of inactivity. +The default is not to lock (set to 0). +.It Ic lock-command Ar shell-command +Command to run when locking each client. +The default is to run +.Xr lock 1 +with +.Fl np . +.It Ic menu-style Ar style +Set the menu style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-selected-style Ar style +Set the selected menu item style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-border-style Ar style +Set the menu border style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-border-lines Ar type +Set the type of characters used for drawing menu borders. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.It Ic message-command-style Ar style +Set status line message command style. +This is used for the command prompt with +.Xr vi 1 +keys when in command mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic message-line +.Op Ic 0 | 1 | 2 | 3 | 4 +.Xc +Set line on which status line messages and the command prompt are shown. +.It Ic message-style Ar style +Set status line message style. +This is used for messages and for the command prompt. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic mouse +.Op Ic on | off +.Xc +If on, +.Nm +captures the mouse and allows mouse events to be bound as key bindings. +See the +.Sx MOUSE SUPPORT +section for details. +.It Ic prefix Ar key +Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. +.It Ic prefix2 Ar key +Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . +.It Ic prefix-timeout Ar time +Set the time in milliseconds for which +.Nm +waits after +.Ic prefix +is input before dismissing it. +Can be set to zero to disable any timeout. +.It Ic prompt-cursor-colour Ar colour +Set the colour of the cursor in the command prompt. +.It Ic prompt-cursor-style Ar style +Set the style of the cursor in the command prompt. +See the +.Ic cursor-style +options for available styles. +.It Xo Ic renumber-windows +.Op Ic on | off +.Xc +If on, when a window is closed in a session, automatically renumber the other +windows in numerical order. +This respects the +.Ic base-index +option if it has been set. +If off, do not renumber the windows. +.It Ic repeat-time Ar time +Allow multiple commands to be entered without pressing the prefix key again +in the specified +.Ar time +milliseconds (the default is 500). +Whether a key repeats may be set when it is bound using the +.Fl r +flag to +.Ic bind-key . +Repeat is enabled for the default keys bound to the +.Ic resize-pane +command. +See also the +.Ic initial-repeat-time +option. +.It Xo Ic set-titles +.Op Ic on | off +.Xc +Attempt to set the client terminal title using the +.Em tsl +and +.Em fsl +.Xr terminfo 5 +entries if they exist. +.Nm +automatically sets these to the \ee]0;...\e007 sequence if +the terminal appears to be +.Xr xterm 1 . +This option is off by default. +.It Ic set-titles-string Ar string +String used to set the client terminal title if +.Ic set-titles +is on. +Formats are expanded, see the +.Sx FORMATS +section. +.It Xo Ic silence-action +.Op Ic any | none | current | other +.Xc +Set action on window silence when +.Ic monitor-silence +is on. +The values are the same as those for +.Ic activity-action . +.It Xo Ic status +.Op Ic off | on | 2 | 3 | 4 | 5 +.Xc +Show or hide the status line or specify its size. +Using +.Ic on +gives a status line one row in height; +.Ic 2 , +.Ic 3 , +.Ic 4 +or +.Ic 5 +more rows. +.It Ic status-format[] Ar format +Specify the format to be used for each line of the status line. +The default builds the top status line from the various individual status +options below. +.It Ic status-interval Ar interval +Update the status line every +.Ar interval +seconds. +By default, updates will occur every 15 seconds. +A setting of zero disables redrawing at interval. +.It Xo Ic status-justify +.Op Ic left | centre | right | absolute-centre +.Xc +Set the position of the window list in the status line: left, centre or right. +centre puts the window list in the relative centre of the available free space; +absolute-centre uses the centre of the entire horizontal space. +.It Xo Ic status-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style +key bindings in the status line, for example at the command prompt. +The default is emacs, unless the +.Ev VISUAL +or +.Ev EDITOR +environment variables are set and contain the string +.Ql vi . +.It Ic status-left Ar string +Display +.Ar string +(by default the session name) to the left of the status line. +.Ar string +will be passed through +.Xr strftime 3 . +Also see the +.Sx FORMATS +and +.Sx STYLES +sections. +.Pp +For details on how the names and titles can be set see the +.Sx "NAMES AND TITLES" +section. +.Pp +Examples are: +.Bd -literal -offset indent +#(sysctl vm.loadavg) +#[fg=yellow,bold]#(apm -l)%%#[default] [#S] +.Ed +.Pp +The default is +.Ql "[#S] " . +.It Ic status-left-length Ar length +Set the maximum +.Ar length +of the left component of the status line. +The default is 10. +.It Ic status-left-style Ar style +Set the style of the left part of the status line. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic status-position +.Op Ic top | bottom +.Xc +Set the position of the status line. +.It Ic status-right Ar string +Display +.Ar string +to the right of the status line. +By default, the current pane title in double quotes, the date and the time +are shown. +As with +.Ic status-left , +.Ar string +will be passed to +.Xr strftime 3 +and character pairs are replaced. +.It Ic status-right-length Ar length +Set the maximum +.Ar length +of the right component of the status line. +The default is 40. +.It Ic status-right-style Ar style +Set the style of the right part of the status line. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Ic status-style Ar style +Set status line style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Ic update-environment[] Ar variable +Set list of environment variables to be copied into the session environment +when a new session is created or an existing session is attached. +Any variables that do not exist in the source environment are set to be +removed from the session environment (as if +.Fl r +was given to the +.Ic set-environment +command). +.It Xo Ic visual-activity +.Op Ic on | off | both +.Xc +If on, display a message instead of sending a bell when activity occurs in a +window for which the +.Ic monitor-activity +window option is enabled. +If set to both, a bell and a message are produced. +.It Xo Ic visual-bell +.Op Ic on | off | both +.Xc +If on, a message is shown on a bell in a window for which the +.Ic monitor-bell +window option is enabled instead of it being passed through to the +terminal (which normally makes a sound). +If set to both, a bell and a message are produced. +Also see the +.Ic bell-action +option. +.It Xo Ic visual-silence +.Op Ic on | off | both +.Xc +If +.Ic monitor-silence +is enabled, prints a message after the interval has expired on a given window +instead of sending a bell. +If set to both, a bell and a message are produced. +.It Ic word-separators Ar string +Sets the session's conception of what characters are considered word +separators, for the purposes of the next and previous word commands in +copy mode. +.El +.Pp +Available window options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic aggressive-resize +.Op Ic on | off +.Xc +Aggressively resize the chosen window. +This means that +.Nm +will resize the window to the size of the smallest or largest session +(see the +.Ic window-size +option) for which it is the current window, rather than the session to +which it is attached. +The window may resize when the current window is changed on another +session; this option is good for full-screen programs which support +.Dv SIGWINCH +and poor for interactive programs such as shells. +.Pp +.It Xo Ic automatic-rename +.Op Ic on | off +.Xc +Control automatic window renaming. +When this setting is enabled, +.Nm +will rename the window automatically using the format specified by +.Ic automatic-rename-format . +This flag is automatically disabled for an individual window when a name +is specified at creation with +.Ic new-window +or +.Ic new-session , +or later with +.Ic rename-window , +or with a terminal escape sequence. +It may be switched off globally with: +.Bd -literal -offset indent +set-option -wg automatic-rename off +.Ed +.Pp +.It Ic automatic-rename-format Ar format +The format (see +.Sx FORMATS ) +used when the +.Ic automatic-rename +option is enabled. +.Pp +.It Ic clock-mode-colour Ar colour +Set clock colour. +.Pp +.It Xo Ic clock-mode-style +.Op Ic 12 | 24 +.Xc +Set clock hour format. +.Pp +.It Ic fill-character Ar character +Set the character used to fill areas of the terminal unused by a window. +.Pp +.It Ic main-pane-height Ar height +.It Ic main-pane-width Ar width +Set the width or height of the main (left or top) pane in the +.Ic main-horizontal , +.Ic main-horizontal-mirrored , +.Ic main-vertical , +or +.Ic main-vertical-mirrored +layouts. +If suffixed by +.Ql % , +this is a percentage of the window size. +.Pp +.It Ic copy-mode-match-style Ar style +Set the style of search matches in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-mark-style Ar style +Set the style of the line containing the mark in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-current-match-style Ar style +Set the style of the current search match in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-position-format Ar format +Format of the position indicator in copy mode. +.Pp +.It Xo Ic mode-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style key bindings in copy mode. +The default is emacs, unless +.Ev VISUAL +or +.Ev EDITOR +contains +.Ql vi . +.Pp +.It Ic copy-mode-position-style Ar style +Set the style of the position indicator in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-selection-style Ar style +Set the style of the selection in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic mode-style Ar style +Set window modes style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Xo Ic monitor-activity +.Op Ic on | off +.Xc +Monitor for activity in the window. +Windows with activity are highlighted in the status line. +.Pp +.It Xo Ic monitor-bell +.Op Ic on | off +.Xc +Monitor for a bell in the window. +Windows with a bell are highlighted in the status line. +.Pp +.It Xo Ic monitor-silence +.Op Ic interval +.Xc +Monitor for silence (no activity) in the window within +.Ic interval +seconds. +Windows that have been silent for the interval are highlighted in the +status line. +An interval of zero disables the monitoring. +.Pp +.It Ic other-pane-height Ar height +Set the height of the other panes (not the main pane) in the +.Ic main-horizontal +and +.Ic main-horizontal-mirrored +layouts. +If this option is set to 0 (the default), it will have no effect. +If both the +.Ic main-pane-height +and +.Ic other-pane-height +options are set, the main pane will grow taller to make the other panes the +specified height, but will never shrink to do so. +If suffixed by +.Ql % , +this is a percentage of the window size. +.Pp +.It Ic other-pane-width Ar width +Like +.Ic other-pane-height , +but set the width of other panes in the +.Ic main-vertical +and +.Ic main-vertical-mirrored +layouts. +.Pp +.It Ic pane-active-border-style Ar style +Set the pane border style for the currently active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic pane-base-index Ar index +Like +.Ic base-index , +but set the starting index for pane numbers. +.Pp +.It Ic pane-border-format Ar format +Set the text shown in pane border status lines. +.Pp +.It Xo Ic pane-border-indicators +.Op Ic off | colour | arrows | both +.Xc +Indicate active pane by colouring only half of the border in windows with +exactly two panes, by displaying arrow markers, by drawing both or neither. +.Pp +.It Ic pane-border-lines Ar type +Set the type of characters used for drawing pane borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It number +the pane number +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp +.It Xo Ic pane-border-status +.Op Ic off | top | bottom +.Xc +Turn pane border status lines off or set their position. +.Pp +.It Ic pane-border-style Ar style +Set the pane border style for panes aside from the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic popup-style Ar style +Set the popup style. +See the +.Sx STYLES +section on how to specify +.Ar style . +Attributes are ignored. +.Pp +.It Ic popup-border-style Ar style +Set the popup border style. +See the +.Sx STYLES +section on how to specify +.Ar style . +Attributes are ignored. +.Pp +.It Ic popup-border-lines Ar type +Set the type of characters used for drawing popup borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters (default) +.It rounded +variation of single with rounded corners using UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It padded +simple ASCII space character +.It none +no border +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp +.It Xo Ic pane-scrollbars +.Op Ic off | modal | on +.Xc +When enabled, a character based scrollbar appears on the left or right +of each pane. +A filled section of the scrollbar, known as the +.Ql slider , +represents the position and size of the visible part of the pane content. +.Pp +If set to +.Ic on +the scrollbar is visible all the time. +If set to +.Ic modal +the scrollbar only appears when the pane is in copy mode or view mode. +When the scrollbar is visible, the pane is narrowed by the width of the +scrollbar and the text in the pane is reflowed. +If set to +.Ic modal , +the pane is narrowed only when the scrollbar is visible. +.Pp +See also +.Ic pane-scrollbars-style . +.Pp +.It Ic pane-scrollbars-style Ar style +Set the scrollbars style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +The foreground colour is used for the slider, the background for the rest of the +scrollbar. +The +.Ar width +attribute sets the width of the scrollbar and the +.Ar pad +attribute the padding between the scrollbar and the pane. +Other attributes are ignored. +.Pp +.It Xo Ic pane-scrollbars-position +.Op Ic left | right +.Xc +Sets which side of the pane to display pane scrollbars on. +.Pp +.It Ic window-status-activity-style Ar style +Set status line style for windows with an activity alert. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-bell-style Ar style +Set status line style for windows with a bell alert. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-current-format Ar string +Like +.Ar window-status-format , +but is the format used when the window is the current window. +.Pp +.It Ic window-status-current-style Ar style +Set status line style for the currently active window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-format Ar string +Set the format in which the window is displayed in the status line window list. +See the +.Sx FORMATS +and +.Sx STYLES +sections. +.Pp +.It Ic window-status-last-style Ar style +Set status line style for the last active window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-separator Ar string +Sets the separator drawn between windows in the status line. +The default is a single space character. +.Pp +.It Ic window-status-style Ar style +Set status line style for a single window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Xo Ic window-size +.Ar largest | Ar smallest | Ar manual | Ar latest +.Xc +Configure how +.Nm +determines the window size. +If set to +.Ar largest , +the size of the largest attached session is used; if +.Ar smallest , +the size of the smallest. +If +.Ar manual , +the size of a new window is set from the +.Ic default-size +option and windows are resized automatically. +With +.Ar latest , +.Nm +uses the size of the client that had the most recent activity. +See also the +.Ic resize-window +command and the +.Ic aggressive-resize +option. +.Pp +.It Xo Ic wrap-search +.Op Ic on | off +.Xc +If this option is set, searches will wrap around the end of the pane contents. +The default is on. +.El +.Pp +Available pane options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic allow-passthrough +.Op Ic on | off | all +.Xc +Allow programs in the pane to bypass +.Nm +using a terminal escape sequence (\eePtmux;...\ee\e\e). +If set to +.Ic on , +passthrough sequences will be allowed only if the pane is visible. +If set to +.Ic all , +they will be allowed even if the pane is invisible. +.Pp +.It Xo Ic allow-rename +.Op Ic on | off +.Xc +Allow programs in the pane to change the window name using a terminal escape +sequence (\eek...\ee\e\e). +.Pp +.It Xo Ic allow-set-title +.Op Ic on | off +.Xc +Allow programs in the pane to change the title using the terminal escape +sequences (\ee]2;...\ee\e\e or \ee]0;...\ee\e\e). +.Pp +.It Xo Ic alternate-screen +.Op Ic on | off +.Xc +This option configures whether programs running inside the pane may use the +terminal alternate screen feature, which allows the +.Em smcup +and +.Em rmcup +.Xr terminfo 5 +capabilities. +The alternate screen feature preserves the contents of the window when an +interactive application starts and restores it on exit, so that any output +visible before the application starts reappears unchanged after it exits. +.Pp +.It Ic cursor-colour Ar colour +Set the colour of the cursor. +.Pp +.It Ic cursor-style Ar style +Set the style of the cursor. +Available styles are: +.Ic default , +.Ic blinking-block , +.Ic block , +.Ic blinking-underline , +.Ic underline , +.Ic blinking-bar , +.Ic bar . +.Pp +.It Ic pane-colours[] Ar colour +The default colour palette. +Each entry in the array defines the colour +.Nm +uses when the colour with that index is requested. +The index may be from zero to 255. +.Pp +.It Xo Ic remain-on-exit +.Op Ic on | off | failed +.Xc +A pane with this flag set is not destroyed when the program running in it +exits. +If set to +.Ic failed , +then only when the program exit status is not zero. +The pane may be reactivated with the +.Ic respawn-pane +command. +.Pp +.It Ic remain-on-exit-format Ar string +Set the text shown at the bottom of exited panes when +.Ic remain-on-exit +is enabled. +.Pp +.It Xo Ic scroll-on-clear +.Op Ic on | off +.Xc +When the entire screen is cleared and this option is on, scroll the contents of +the screen into history before clearing it. +.Pp +.It Xo Ic synchronize-panes +.Op Ic on | off +.Xc +Duplicate input to all other panes in the same window where this option is also +on (only for panes that are not in any mode). +.Pp +.It Ic window-active-style Ar style +Set the pane style when it is the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-style Ar style +Set the pane style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Most +.Nm +commands have an +.Em after +hook and there are a number of hooks not associated with commands. +.Pp +Hooks are stored as array options, members of the array are executed in +order when the hook is triggered. +Like options different hooks may be global or belong to a session, window or +pane. +Hooks may be configured with the +.Ic set-hook +or +.Ic set-option +commands and displayed with +.Ic show-hooks +or +.Ic show-options +.Fl H . +The following two commands are equivalent: +.Bd -literal -offset indent. +set-hook -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq] +set-option -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq] +.Ed +.Pp +Setting a hook without specifying an array index clears the hook and sets the +first member of the array. +.Pp +A command's after +hook is run after it completes, except when the command is run as part of a hook +itself. +They are named with an +.Ql after- +prefix. +For example, the following command adds a hook to select the even-vertical +layout after every +.Ic split-window : +.Bd -literal -offset indent +set-hook -g after-split-window "selectl even-vertical" +.Ed +.Pp +If a command fails, the +.Ql command-error +hook will be fired. +For example, this could be used to write to a log file: +.Bd -literal -offset indent +set-hook -g command-error "run-shell \\"echo 'a tmux command failed' >>/tmp/log\\"" +.Ed +.Pp +All the notifications listed in the +.Sx CONTROL MODE +section are hooks (without any arguments), except +.Ic %exit . +The following additional hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX" +.It alert-activity +Run when a window has activity. +See +.Ic monitor-activity . +.It alert-bell +Run when a window has received a bell. +See +.Ic monitor-bell . +.It alert-silence +Run when a window has been silent. +See +.Ic monitor-silence . +.It client-active +Run when a client becomes the latest active client of its session. +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-focus-in +Run when focus enters a client +.It client-focus-out +Run when focus exits a client +.It client-resized +Run when a client is resized. +.It client-session-changed +Run when a client's attached session is changed. +.It command-error +Run when a command fails. +.It pane-died +Run when the program running in a pane exits, but +.Ic remain-on-exit +is on so the pane has not closed. +.It pane-exited +Run when the program running in a pane exits. +.It pane-focus-in +Run when the focus enters a pane, if the +.Ic focus-events +option is on. +.It pane-focus-out +Run when the focus exits a pane, if the +.Ic focus-events +option is on. +.It pane-set-clipboard +Run when the terminal clipboard is set using the +.Xr xterm 1 +escape sequence. +.It session-created +Run when a new session created. +.It session-closed +Run when a session closed. +.It session-renamed +Run when a session is renamed. +.It window-layout-changed +Run when a window layout is changed. +.It window-linked +Run when a window is linked into a session. +.It window-renamed +Run when a window is renamed. +.It window-resized +Run when a window is resized. +This may be after the +.Ar client-resized +hook is run. +.It window-unlinked +Run when a window is unlinked from a session. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl agpRuw +.Op Fl t Ar target-pane +.Ar hook-name +.Ar command +.Xc +Without +.Fl R , +sets (or with +.Fl u +unsets) hook +.Ar hook-name +to +.Ar command . +The flags are the same as for +.Ic set-option . +.Pp +With +.Fl R , +run +.Ar hook-name +immediately. +.It Xo Ic show-hooks +.Op Fl gpw +.Op Fl t Ar target-pane +.Xc +Shows hooks. +The flags are the same as for +.Ic show-options . +.El +.Sh MOUSE SUPPORT +If the +.Ic mouse +option is on (the default is off), +.Nm +allows mouse events to be bound as keys. +The name of each key is made up of a mouse event (such as +.Ql MouseUp1 ) +and a location suffix, one of the following: +.Bl -column "XXXXXXXXXXXXX" -offset indent +.It Li "Pane" Ta "the contents of a pane" +.It Li "Border" Ta "a pane border" +.It Li "Status" Ta "the status line window list" +.It Li "StatusLeft" Ta "the left part of the status line" +.It Li "StatusRight" Ta "the right part of the status line" +.It Li "StatusDefault" Ta "any other part of the status line" +.It Li "ScrollbarSlider" Ta "the scrollbar slider" +.It Li "ScrollbarUp" Ta "above the scrollbar slider" +.It Li "ScrollbarDown" Ta "below the scrollbar slider" +.El +.Pp +The following mouse events are available: +.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent +.It Li "WheelUp" Ta "WheelDown" Ta "" +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "SecondClick1" Ta "SecondClick2" Ta "SecondClick3" +.It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" +.It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" +.El +.Pp +The +.Ql SecondClick +events are fired for the second click of a double click, even if there may be a +third click which will fire +.Ql TripleClick +instead of +.Ql DoubleClick . +.Pp +Each should be suffixed with a location, for example +.Ql MouseDown1Status . +.Pp +The special token +.Ql {mouse} +or +.Ql = +may be used as +.Ar target-window +or +.Ar target-pane +in commands bound to mouse key bindings. +It resolves to the window or pane over which the mouse event took place +(for example, the window in the status line over which button 1 was released +for a +.Ql MouseUp1Status +binding, or the pane over which the wheel was scrolled for a +.Ql WheelDownPane +binding). +.Pp +The +.Ic send-keys +.Fl M +flag may be used to forward a mouse event to a pane. +.Pp +The default key bindings allow the mouse to be used to select and resize panes, +to copy text and to change window using the status line. +These take effect if the +.Ic mouse +option is turned on. +.Sh FORMATS +Certain commands accept the +.Fl F +flag with a +.Ar format +argument. +This is a string which controls the output format of the command. +Format variables are enclosed in +.Ql #{ +and +.Ql } , +for example +.Ql #{session_name} . +The possible variables are listed in the table below, or the name of a +.Nm +option may be used for an option's value. +Some variables have a shorter alias such as +.Ql #S ; +.Ql ## +is replaced by a single +.Ql # , +.Ql #, +by a +.Ql \&, +and +.Ql #} +by a +.Ql } . +.Pp +Conditionals are available by prefixing with +.Ql \&? +and separating two alternatives with a comma; +if the specified variable exists and is not zero, the first alternative +is chosen, otherwise the second is used. +For example +.Ql #{?session_attached,attached,not attached} +will include the string +.Ql attached +if the session is attached and the string +.Ql not attached +if it is unattached, or +.Ql #{?automatic-rename,yes,no} +will include +.Ql yes +if +.Ic automatic-rename +is enabled, or +.Ql no +if not. +Conditionals can be nested arbitrarily. +Inside a conditional, +.Ql \&, +and +.Ql } +must be escaped as +.Ql #, +and +.Ql #} , +unless they are part of a +.Ql #{...} +replacement. +For example: +.Bd -literal -offset indent +#{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W . +.Ed +.Pp +String comparisons may be expressed by prefixing two comma-separated +alternatives by +.Ql == , +.Ql != , +.Ql < , +.Ql > , +.Ql <= +or +.Ql >= +and a colon. +For example +.Ql #{==:#{host},myhost} +will be replaced by +.Ql 1 +if running on +.Ql myhost , +otherwise by +.Ql 0 . +.Ql || +and +.Ql && +evaluate to true if either or both of two comma-separated alternatives are +true, for example +.Ql #{||:#{pane_in_mode},#{alternate_on}} . +.Pp +An +.Ql m +specifies a +.Xr glob 7 +pattern or regular expression comparison. +The first argument is the pattern and the second the string to compare. +An optional argument specifies flags: +.Ql r +means the pattern is a regular expression instead of the default +.Xr glob 7 +pattern, and +.Ql i +means to ignore case. +For example: +.Ql #{m:*foo*,#{host}} +or +.Ql #{m/ri:^A,MYVAR} . +A +.Ql C +performs a search for a +.Xr glob 7 +pattern or regular expression in the pane content and evaluates to zero if not +found, or a line number if found. +Like +.Ql m , +an +.Ql r +flag means search for a regular expression and +.Ql i +ignores case. +For example: +.Ql #{C/r:^Start} +.Pp +Numeric operators may be performed by prefixing two comma-separated alternatives +with an +.Ql e +and an operator. +An optional +.Ql f +flag may be given after the operator to use floating point numbers, otherwise +integers are used. +This may be followed by a number giving the number of decimal places to use for +the result. +The available operators are: +addition +.Ql + , +subtraction +.Ql - , +multiplication +.Ql * , +division +.Ql / , +modulus +.Ql m +or +.Ql % +(note that +.Ql % +must be escaped as +.Ql %% +in formats which are also expanded by +.Xr strftime 3 ) +and numeric comparison operators +.Ql == , +.Ql != , +.Ql < , +.Ql <= , +.Ql > +and +.Ql >= . +For example, +.Ql #{e|*|f|4:5.5,3} +multiplies 5.5 by 3 for a result with four decimal places and +.Ql #{e|%%:7,3} +returns the modulus of 7 and 3. +.Ql a +replaces a numeric argument by its ASCII equivalent, so +.Ql #{a:98} +results in +.Ql b . +.Ql c +replaces a +.Nm +colour by its six-digit hexadecimal RGB value. +.Pp +A limit may be placed on the length of the resultant string by prefixing it +by an +.Ql = , +a number and a colon. +Positive numbers count from the start of the string and negative from the end, +so +.Ql #{=5:pane_title} +will include at most the first five characters of the pane title, or +.Ql #{=-5:pane_title} +the last five characters. +A suffix or prefix may be given as a second argument - if provided then it is +appended or prepended to the string if the length has been trimmed, for example +.Ql #{=/5/...:pane_title} +will append +.Ql ... +if the pane title is more than five characters. +Similarly, +.Ql p +pads the string to a given width, for example +.Ql #{p10:pane_title} +will result in a width of at least 10 characters. +A positive width pads on the left, a negative on the right. +.Ql n +expands to the length of the variable and +.Ql w +to its width when displayed, for example +.Ql #{n:window_name} . +.Pp +Prefixing a time variable with +.Ql t:\& +will convert it to a string, so if +.Ql #{window_activity} +gives +.Ql 1445765102 , +.Ql #{t:window_activity} +gives +.Ql Sun Oct 25 09:25:02 2015 . +Adding +.Ql p ( +.Ql `t/p` ) +will use shorter but less accurate time format for times in the past. +A custom format may be given using an +.Ql f +suffix (note that +.Ql % +must be escaped as +.Ql %% +if the format is separately being passed through +.Xr strftime 3 , +for example in the +.Ic status-left +option): +.Ql #{t/f/%%H#:%%M:window_activity} , +see +.Xr strftime 3 . +.Pp +The +.Ql b:\& +and +.Ql d:\& +prefixes are +.Xr basename 3 +and +.Xr dirname 3 +of the variable respectively. +.Ql q:\& +will escape +.Xr sh 1 +special characters or with a +.Ql h +suffix, escape hash characters (so +.Ql # +becomes +.Ql ## ) . +.Ql E:\& +will expand the format twice, for example +.Ql #{E:status-left} +is the result of expanding the content of the +.Ic status-left +option rather than the option itself. +.Ql T:\& +is like +.Ql E:\& +but also expands +.Xr strftime 3 +specifiers. +.Ql S:\& , +.Ql W:\& , +.Ql P:\& +or +.Ql L:\& +will loop over each session, window, pane or client and insert the format once +for each. +For windows and panes, two comma-separated formats may be given: +the second is used for the current window or active pane. +For example, to get a list of windows formatted like the status line: +.Bd -literal -offset indent +#{W:#{E:window-status-format} ,#{E:window-status-current-format} } +.Ed +.Pp +.Ql N:\& +checks if a window (without any suffix or with the +.Ql w +suffix) or a session (with the +.Ql s +suffix) name exists, for example +.Ql `N/w:foo` +is replaced with 1 if a window named +.Ql foo +exists. +.Pp +A prefix of the form +.Ql s/foo/bar/:\& +will substitute +.Ql foo +with +.Ql bar +throughout. +The first argument may be an extended regular expression and a final argument +may be +.Ql i +to ignore case, for example +.Ql s/a(.)/\e1x/i:\& +would change +.Ql abABab +into +.Ql bxBxbx . +A different delimiter character may also be used, to avoid collisions with +literal slashes in the pattern. +For example, +.Ql s|foo/|bar/|:\& +will substitute +.Ql foo/ +with +.Ql bar/ +throughout. +.Pp +Multiple modifiers may be separated with a semicolon (;) as in +.Ql #{T;=10:status-left} , +which limits the resulting +.Xr strftime 3 -expanded +string to at most 10 characters. +.Pp +In addition, the last line of a shell command's output may be inserted using +.Ql #() . +For example, +.Ql #(uptime) +will insert the system's uptime. +When constructing formats, +.Nm +does not wait for +.Ql #() +commands to finish; instead, the previous result from running the same command +is used, or a placeholder if the command has not been run before. +If the command hasn't exited, the most recent line of output will be used, but +the status line will not be updated more than once a second. +Commands are executed using +.Pa /bin/sh +and with the +.Nm +global environment set (see the +.Sx GLOBAL AND SESSION ENVIRONMENT +section). +.Pp +An +.Ql l +specifies that a string should be interpreted literally and not expanded. +For example +.Ql #{l:#{?pane_in_mode,yes,no}} +will be replaced by +.Ql #{?pane_in_mode,yes,no} . +.Pp +The following variables are available, where appropriate: +.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" +.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" +.It Li "active_window_index" Ta "" Ta "Index of active window in session" +.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" +.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" +.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" +.It Li "buffer_created" Ta "" Ta "Time buffer created" +.It Li "buffer_name" Ta "" Ta "Name of buffer" +.It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" +.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" +.It Li "client_activity" Ta "" Ta "Time client last had activity" +.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" +.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" +.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" +.It Li "client_created" Ta "" Ta "Time client created" +.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" +.It Li "client_flags" Ta "" Ta "List of client flags" +.It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_key_table" Ta "" Ta "Current key table" +.It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_name" Ta "" Ta "Name of client" +.It Li "client_pid" Ta "" Ta "PID of client process" +.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" +.It Li "client_readonly" Ta "" Ta "1 if client is read-only" +.It Li "client_session" Ta "" Ta "Name of the client's session" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" +.It Li "client_termname" Ta "" Ta "Terminal name of client" +.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" +.It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_uid" Ta "" Ta "UID of client process" +.It Li "client_user" Ta "" Ta "User of client process" +.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" +.It Li "client_width" Ta "" Ta "Width of client" +.It Li "client_written" Ta "" Ta "Bytes written to client" +.It Li "command" Ta "" Ta "Name of command in use, if any" +.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" +.It Li "command_list_name" Ta "" Ta "Command name if listing commands" +.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" +.It Li "config_files" Ta "" Ta "List of configuration files loaded" +.It Li "cursor_blinking" Ta "" Ta "1 if the cursor is blinking" +.It Li "copy_cursor_hyperlink" Ta "" Ta "Hyperlink under cursor in copy mode" +.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" +.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" +.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" +.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" +.It Li "current_file" Ta "" Ta "Current configuration file" +.It Li "cursor_character" Ta "" Ta "Character at cursor in pane" +.It Li "cursor_colour" Ta "" Ta "Cursor colour in pane" +.It Li "cursor_flag" Ta "" Ta "Pane cursor flag" +.It Li "cursor_shape" Ta "" Ta "Cursor shape in pane" +.It Li "cursor_very_visible" Ta "" Ta "1 if the cursor is in very visible mode" +.It Li "cursor_x" Ta "" Ta "Cursor X position in pane" +.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" +.It Li "history_bytes" Ta "" Ta "Number of bytes in window history" +.It Li "history_limit" Ta "" Ta "Maximum window history lines" +.It Li "history_size" Ta "" Ta "Size of history in lines" +.It Li "hook" Ta "" Ta "Name of running hook, if any" +.It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any" +.It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any" +.It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any" +.It Li "hook_session_name" Ta "" Ta "Name of session where hook was run, if any" +.It Li "hook_window" Ta "" Ta "ID of window where hook was run, if any" +.It Li "hook_window_name" Ta "" Ta "Name of window where hook was run, if any" +.It Li "host" Ta "#H" Ta "Hostname of local host" +.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" +.It Li "insert_flag" Ta "" Ta "Pane insert flag" +.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" +.It Li "keypad_flag" Ta "" Ta "Pane keypad flag" +.It Li "last_window_index" Ta "" Ta "Index of last window in session" +.It Li "line" Ta "" Ta "Line number in the list" +.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" +.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" +.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any" +.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" +.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_status_line" Ta "" Ta "Status line on which mouse event took place" +.It Li "mouse_status_range" Ta "" Ta "Range type or argument of mouse event on status line" +.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" +.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" +.It Li "mouse_x" Ta "" Ta "Mouse X position, if any" +.It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" +.It Li "next_session_id" Ta "" Ta "Unique session ID for next new session" +.It Li "origin_flag" Ta "" Ta "Pane origin flag" +.It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" +.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" +.It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window" +.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" +.It Li "pane_bg" Ta "" Ta "Pane background colour" +.It Li "pane_bottom" Ta "" Ta "Bottom of pane" +.It Li "pane_current_command" Ta "" Ta "Current command if available" +.It Li "pane_current_path" Ta "" Ta "Current path if available" +.It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_signal" Ta "" Ta "Exit signal of process in dead pane" +.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane" +.It Li "pane_fg" Ta "" Ta "Pane foreground colour" +.It Li "pane_format" Ta "" Ta "1 if format is for a pane" +.It Li "pane_height" Ta "" Ta "Height of pane" +.It Li "pane_id" Ta "#D" Ta "Unique pane ID" +.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" +.It Li "pane_index" Ta "#P" Ta "Index of pane" +.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" +.It Li "pane_key_mode" Ta "" Ta "Extended key reporting mode in this pane" +.It Li "pane_last" Ta "" Ta "1 if last pane" +.It Li "pane_left" Ta "" Ta "Left of pane" +.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" +.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" +.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" +.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)" +.It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" +.It Li "pane_right" Ta "" Ta "Right of pane" +.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" +.It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_path" Ta "" Ta "Path pane started with" +.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" +.It Li "pane_tabs" Ta "" Ta "Pane tab positions" +.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" +.It Li "pane_top" Ta "" Ta "Top of pane" +.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" +.It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode" +.It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pid" Ta "" Ta "Server PID" +.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" +.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" +.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" +.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "search_count" Ta "" Ta "Count of search results" +.It Li "search_count_partial" Ta "" Ta "1 if search count is partial count" +.It Li "search_match" Ta "" Ta "Search match if any" +.It Li "search_present" Ta "" Ta "1 if search started in copy mode" +.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" +.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" +.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" +.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" +.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection" +.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection" +.It Li "server_sessions" Ta "" Ta "Number of sessions" +.It Li "session_activity" Ta "" Ta "Time of session last activity" +.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" +.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_attached_list" Ta "" Ta "List of clients session is attached to" +.It Li "session_created" Ta "" Ta "Time session created" +.It Li "session_format" Ta "" Ta "1 if format is for a session" +.It Li "session_group" Ta "" Ta "Name of session group" +.It Li "session_group_attached" Ta "" Ta "Number of clients sessions in group are attached to" +.It Li "session_group_attached_list" Ta "" Ta "List of clients sessions in group are attached to" +.It Li "session_group_list" Ta "" Ta "List of sessions in group" +.It Li "session_group_many_attached" Ta "" Ta "1 if multiple clients attached to sessions in group" +.It Li "session_group_size" Ta "" Ta "Size of session group" +.It Li "session_grouped" Ta "" Ta "1 if session in a group" +.It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_last_attached" Ta "" Ta "Time session last attached" +.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" +.It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane" +.It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_path" Ta "" Ta "Working directory of session" +.It Li "session_stack" Ta "" Ta "Window indexes in most recent order" +.It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "socket_path" Ta "" Ta "Server socket path" +.It Li "sixel_support" Ta "" Ta "1 if server has support for SIXEL" +.It Li "start_time" Ta "" Ta "Server start time" +.It Li "uid" Ta "" Ta "Server UID" +.It Li "user" Ta "" Ta "Server user" +.It Li "version" Ta "" Ta "Server version" +.It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" +.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window" +.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active" +.It Li "window_active_sessions_list" Ta "" Ta "List of sessions on which this window is active" +.It Li "window_activity" Ta "" Ta "Time of window last activity" +.It Li "window_activity_flag" Ta "" Ta "1 if window has activity" +.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_bigger" Ta "" Ta "1 if window is larger than client" +.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" +.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" +.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" +.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" +.It Li "window_format" Ta "" Ta "1 if format is for a window" +.It Li "window_height" Ta "" Ta "Height of window" +.It Li "window_id" Ta "" Ta "Unique window ID" +.It Li "window_index" Ta "#I" Ta "Index of window" +.It Li "window_last_flag" Ta "" Ta "1 if window is the last used" +.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" +.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to" +.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to" +.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane" +.It Li "window_name" Ta "#W" Ta "Name of window" +.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" +.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" +.It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" +.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" +.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" +.It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" +.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" +.It Li "window_width" Ta "" Ta "Width of window" +.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" +.It Li "wrap_flag" Ta "" Ta "Pane wrap flag" +.El +.Sh STYLES +.Nm +offers various options to specify the colour and attributes of aspects of the +interface, for example +.Ic status-style +for the status line. +In addition, embedded styles may be specified in format options, such as +.Ic status-left , +by enclosing them in +.Ql #[ +and +.Ql \&] . +.Pp +A style may be the single term +.Ql default +to specify the default style (which may come from an option, for example +.Ic status-style +in the status line) or a space +or comma separated list of the following: +.Bl -tag -width Ds +.It Ic fg=colour +Set the foreground colour. +The colour is one of: +.Ic black , +.Ic red , +.Ic green , +.Ic yellow , +.Ic blue , +.Ic magenta , +.Ic cyan , +.Ic white ; +if supported the bright variants +.Ic brightred , +.Ic brightgreen , +.Ic brightyellow ; +.Ic colour0 +to +.Ic colour255 +from the 256-colour set; +.Ic default +for the default colour; +.Ic terminal +for the terminal default colour; or a hexadecimal RGB string such as +.Ql #ffffff . +.It Ic bg=colour +Set the background colour. +.It Ic us=colour +Set the underscore colour. +.It Ic none +Set no attributes (turn off any active attributes). +.It Xo Ic acs , +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +.Ic italics , +.Ic overline , +.Ic strikethrough , +.Ic double-underscore , +.Ic curly-underscore , +.Ic dotted-underscore , +.Ic dashed-underscore +.Xc +Set an attribute. +Any of the attributes may be prefixed with +.Ql no +to unset. +.Ic acs +is the terminal alternate character set. +.It Xo Ic align=left +(or +.Ic noalign ) , +.Ic align=centre , +.Ic align=right +.Xc +Align text to the left, centre or right of the available space if appropriate. +.It Ic fill=colour +Fill the available space with a background colour if appropriate. +.It Xo Ic list=on , +.Ic list=focus , +.Ic list=left-marker , +.Ic list=right-marker , +.Ic nolist +.Xc +Mark the position of the various window list components in the +.Ic status-format +option: +.Ic list=on +marks the start of the list; +.Ic list=focus +is the part of the list that should be kept in focus if the entire list won't +fit in the available space (typically the current window); +.Ic list=left-marker +and +.Ic list=right-marker +mark the text to be used to mark that text has been trimmed from the left or +right of the list if there is not enough space. +.It Xo Ic push-default , +.Ic pop-default +.Xc +Store the current colours and attributes as the default or reset to the previous +default. +A +.Ic push-default +affects any subsequent use of the +.Ic default +term until a +.Ic pop-default . +Only one default may be pushed (each +.Ic push-default +replaces the previous saved default). +.It Xo Ic range=left , +.Ic range=right , +.Ic range=session|X , +.Ic range=window|X , +.Ic range=pane|X , +.Ic range=user|X , +.Ic norange +.Xc +Mark a range for mouse events in the +.Ic status-format +option. +When a mouse event occurs in the +.Ic range=left +or +.Ic range=right +range, the +.Ql StatusLeft +and +.Ql StatusRight +key bindings are triggered. +.Pp +.Ic range=session|X , +.Ic range=window|X +and +.Ic range=pane|X +are ranges for a session, window or pane. +These trigger the +.Ql Status +mouse key with the target session, window or pane given by the +.Ql X +argument. +.Ql X +is a session ID, window index in the current session or a pane ID. +For these, the +.Ic mouse_status_range +format variable will be set to +.Ql session , +.Ql window +or +.Ql pane . +.Pp +.Ic range=user|X +is a user-defined range; it triggers the +.Ql Status +mouse key. +The argument +.Ql X +will be available in the +.Ic mouse_status_range +format variable. +.Ql X +must be at most 15 bytes in length. +.El +.Pp +Examples are: +.Bd -literal -offset indent +fg=yellow bold underscore blink +bg=black,fg=default,noreverse +.Ed +.Sh NAMES AND TITLES +.Nm +distinguishes between names and titles. +Windows and sessions have names, which may be used to specify them in targets +and are displayed in the status line and various lists: the name is the +.Nm +identifier for a window or session. +Only panes have titles. +A pane's title is typically set by the program running inside the pane using +an escape sequence (like it would set the +.Xr xterm 1 +window title in +.Xr X 7 ) . +Windows themselves do not have titles - a window's title is the title of its +active pane. +.Nm +itself may set the title of the terminal in which the client is running, see +the +.Ic set-titles +option. +.Pp +A session's name is set with the +.Ic new-session +and +.Ic rename-session +commands. +A window's name is set with one of: +.Bl -enum -width Ds +.It +A command argument (such as +.Fl n +for +.Ic new-window +or +.Ic new-session ) . +.It +An escape sequence (if the +.Ic allow-rename +option is turned on): +.Bd -literal -offset indent +$ printf \[aq]\e033kWINDOW_NAME\e033\e\e\[aq] +.Ed +.It +Automatic renaming, which sets the name to the active command in the window's +active pane. +See the +.Ic automatic-rename +option. +.El +.Pp +When a pane is first created, its title is the hostname. +A pane's title can be set via the title setting escape sequence, for example: +.Bd -literal -offset indent +$ printf \[aq]\e033]2;My Title\e033\e\e\[aq] +.Ed +.Pp +It can also be modified with the +.Ic select-pane +.Fl T +command. +.Sh GLOBAL AND SESSION ENVIRONMENT +When the server is started, +.Nm +copies the environment into the +.Em global environment ; +in addition, each session has a +.Em session environment . +When a window is created, the session and global environments are merged. +If a variable exists in both, the value from the session environment is used. +The result is the initial environment passed to the new process. +.Pp +The +.Ic update-environment +session option may be used to update the session environment from the client +when a new session is created or an old reattached. +.Nm +also initialises the +.Ev TMUX +variable with some internal information to allow commands to be executed +from inside, and the +.Ev TERM +variable with the correct terminal setting of +.Ql screen . +.Pp +Variables in both session and global environments may be marked as hidden. +Hidden variables are not passed into the environment of new processes and +instead can only be used by tmux itself (for example in formats, see the +.Sx FORMATS +section). +.Pp +Commands to alter and view the environment are: +.Bl -tag -width Ds +.Tg setenv +.It Xo Ic set-environment +.Op Fl Fhgru +.Op Fl t Ar target-session +.Ar name Op Ar value +.Xc +.D1 Pq alias: Ic setenv +Set or unset an environment variable. +If +.Fl g +is used, the change is made in the global environment; otherwise, it is applied +to the session environment for +.Ar target-session . +If +.Fl F +is present, then +.Ar value +is expanded as a format. +The +.Fl u +flag unsets a variable. +.Fl r +indicates the variable is to be removed from the environment before starting a +new process. +.Fl h +marks the variable as hidden. +.Tg showenv +.It Xo Ic show-environment +.Op Fl hgs +.Op Fl t Ar target-session +.Op Ar variable +.Xc +.D1 Pq alias: Ic showenv +Display the environment for +.Ar target-session +or the global environment with +.Fl g . +If +.Ar variable +is omitted, all variables are shown. +Variables removed from the environment are prefixed with +.Ql - . +If +.Fl s +is used, the output is formatted as a set of Bourne shell commands. +.Fl h +shows hidden variables (omitted by default). +.El +.Sh STATUS LINE +.Nm +includes an optional status line which is displayed in the bottom line of each +terminal. +.Pp +By default, the status line is enabled and one line in height (it may be +disabled or made multiple lines with the +.Ic status +session option) and contains, from left-to-right: the name of the current +session in square brackets; the window list; the title of the active pane +in double quotes; and the time and date. +.Pp +Each line of the status line is configured with the +.Ic status-format +option. +The default is made of three parts: configurable left and right sections (which +may contain dynamic content such as the time or output from a shell command, +see the +.Ic status-left , +.Ic status-left-length , +.Ic status-right , +and +.Ic status-right-length +options below), and a central window list. +By default, the window list shows the index, name and (if any) flag of the +windows present in the current session in ascending numerical order. +It may be customised with the +.Ar window-status-format +and +.Ar window-status-current-format +options. +The flag is one of the following symbols appended to the window name: +.Bl -column "Symbol" "Meaning" -offset indent +.It Sy "Symbol" Ta Sy "Meaning" +.It Li "*" Ta "Denotes the current window." +.It Li "-" Ta "Marks the last window (previously selected)." +.It Li "#" Ta "Window activity is monitored and activity has been detected." +.It Li "\&!" Ta "Window bells are monitored and a bell has occurred in the window." +.It Li "\[ti]" Ta "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." +.It Li "Z" Ta "The window's active pane is zoomed." +.El +.Pp +The # symbol relates to the +.Ic monitor-activity +window option. +The window name is printed in inverted colours if an alert (bell, activity or +silence) is present. +.Pp +The colour and attributes of the status line may be configured, the entire +status line using the +.Ic status-style +session option and individual windows using the +.Ic window-status-style +window option. +.Pp +The status line is automatically refreshed at interval if it has changed, the +interval may be controlled with the +.Ic status-interval +session option. +.Pp +Commands related to the status line are as follows: +.Bl -tag -width Ds +.Tg clearphist +.It Xo Ic clear-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 Pq alias: Ic clearphist +Clear status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then clear history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . +.It Xo Ic command-prompt +.Op Fl 1bFikN +.Op Fl I Ar inputs +.Op Fl p Ar prompts +.Op Fl t Ar target-client +.Op Fl T Ar prompt-type +.Op Ar template +.Xc +Open the command prompt in a client. +This may be used from inside +.Nm +to execute commands interactively. +.Pp +If +.Ar template +is specified, it is used as the command. +With +.Fl F , +.Ar template +is expanded as a format. +.Pp +If present, +.Fl I +is a comma-separated list of the initial text for each prompt. +If +.Fl p +is given, +.Ar prompts +is a comma-separated list of prompts which are displayed in order; otherwise +a single prompt is displayed, constructed from +.Ar template +if it is present, or +.Ql \&: +if not. +.Pp +Before the command is executed, the first occurrence of the string +.Ql %% +and all occurrences of +.Ql %1 +are replaced by the response to the first prompt, all +.Ql %2 +are replaced with the response to the second prompt, and so on for further +prompts. +Up to nine prompt responses may be replaced +.Po +.Ql %1 +to +.Ql %9 +.Pc . +.Ql %%% +is like +.Ql %% +but any quotation marks are escaped. +.Pp +.Fl 1 +makes the prompt only accept one key press, in this case the resulting input +is a single character. +.Fl k +is like +.Fl 1 +but the key press is translated to a key name. +.Fl N +makes the prompt only accept numeric key presses. +.Fl i +executes the command every time the prompt input changes instead of when the +user exits the command prompt. +.Pp +.Fl T +tells +.Nm +the prompt type. +This affects what completions are offered when +.Em Tab +is pressed. +Available types are: +.Ql command , +.Ql search , +.Ql target +and +.Ql window-target . +.Pp +The following keys have a special meaning in the command prompt, depending +on the value of the +.Ic status-keys +option: +.Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent +.It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Cancel command prompt" Ta "q" Ta "Escape" +.It Li "Delete from cursor to start of word" Ta "" Ta "C-w" +.It Li "Delete entire command" Ta "d" Ta "C-u" +.It Li "Delete from cursor to end" Ta "D" Ta "C-k" +.It Li "Execute command" Ta "Enter" Ta "Enter" +.It Li "Get next command from history" Ta "" Ta "Down" +.It Li "Get previous command from history" Ta "" Ta "Up" +.It Li "Insert top paste buffer" Ta "p" Ta "C-y" +.It Li "Look for completions" Ta "Tab" Ta "Tab" +.It Li "Move cursor left" Ta "h" Ta "Left" +.It Li "Move cursor right" Ta "l" Ta "Right" +.It Li "Move cursor to end" Ta "$" Ta "C-e" +.It Li "Move cursor to next word" Ta "w" Ta "M-f" +.It Li "Move cursor to previous word" Ta "b" Ta "M-b" +.It Li "Move cursor to start" Ta "0" Ta "C-a" +.It Li "Transpose characters" Ta "" Ta "C-t" +.El +.Pp +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. +.Tg confirm +.It Xo Ic confirm-before +.Op Fl by +.Op Fl c Ar confirm-key +.Op Fl p Ar prompt +.Op Fl t Ar target-client +.Ar command +.Xc +.D1 Pq alias: Ic confirm +Ask for confirmation before executing +.Ar command . +If +.Fl p +is given, +.Ar prompt +is the prompt to display; otherwise a prompt is constructed from +.Ar command . +It may contain the special character sequences supported by the +.Ic status-left +option. +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. +.Fl y +changes the default behaviour (if Enter alone is pressed) of the prompt to +run the command. +.Fl c +changes the confirmation key to +.Ar confirm-key ; +the default is +.Ql y . +.Tg menu +.It Xo Ic display-menu +.Op Fl OM +.Op Fl b Ar border-lines +.Op Fl c Ar target-client +.Op Fl C Ar starting-choice +.Op Fl H Ar selected-style +.Op Fl s Ar style +.Op Fl S Ar border-style +.Op Fl t Ar target-pane +.Op Fl T Ar title +.Op Fl x Ar position +.Op Fl y Ar position +.Ar name +.Ar key +.Ar command Op Ar argument ... +.Xc +.D1 Pq alias: Ic menu +Display a menu on +.Ar target-client . +.Ar target-pane +gives the target for any commands run from the menu. +.Pp +A menu is passed as a series of arguments: first the menu item name, +second the key shortcut (or empty for none) and third the command +to run when the menu item is chosen. +The name and command are formats, see the +.Sx FORMATS +and +.Sx STYLES +sections. +If the name begins with a hyphen (-), then the item is disabled (shown dim) and +may not be chosen. +The name may be empty for a separator line, in which case both the key and +command should be omitted. +.Pp +.Fl b +sets the type of characters used for drawing menu borders. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.Pp +.Fl H +sets the style for the selected menu item (see +.Sx STYLES ) . +.Pp +.Fl s +sets the style for the menu and +.Fl S +sets the style for the menu border (see +.Sx STYLES ) . +.Pp +.Fl T +is a format for the menu title (see +.Sx FORMATS ) . +.Pp +.Fl C +sets the menu item selected by default, if the menu is not bound to a mouse key +binding. +.Pp +.Fl x +and +.Fl y +give the position of the menu. +Both may be a row or column number, or one of the following special values: +.Bl -column "XXXXX" "XXXX" -offset indent +.It Sy "Value" Ta Sy "Flag" Ta Sy "Meaning" +.It Li "C" Ta "Both" Ta "The centre of the terminal" +.It Li "R" Ta Fl x Ta "The right side of the terminal" +.It Li "P" Ta "Both" Ta "The bottom left of the pane" +.It Li "M" Ta "Both" Ta "The mouse position" +.It Li "W" Ta "Both" Ta "The window position on the status line" +.It Li "S" Ta Fl y Ta "The line above or below the status line" +.El +.Pp +Or a format, which is expanded including the following additional variables: +.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent +.It Sy "Variable name" Ta Sy "Replaced with" +.It Li "popup_centre_x" Ta "Centered in the client" +.It Li "popup_centre_y" Ta "Centered in the client" +.It Li "popup_height" Ta "Height of menu or popup" +.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse" +.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse" +.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse" +.It Li "popup_mouse_top" Ta "Top at the mouse" +.It Li "popup_mouse_x" Ta "Mouse X position" +.It Li "popup_mouse_y" Ta "Mouse Y position" +.It Li "popup_pane_bottom" Ta "Bottom of the pane" +.It Li "popup_pane_left" Ta "Left of the pane" +.It Li "popup_pane_right" Ta "Right of the pane" +.It Li "popup_pane_top" Ta "Top of the pane" +.It Li "popup_status_line_y" Ta "Above or below the status line" +.It Li "popup_width" Ta "Width of menu or popup" +.It Li "popup_window_status_line_x" Ta "At the window position in status line" +.It Li "popup_window_status_line_y" Ta "At the status line showing the window" +.El +.Pp +Each menu consists of items followed by a key shortcut shown in brackets. +If the menu is too large to fit on the terminal, it is not displayed. +Pressing the key shortcut chooses the corresponding item. +If the mouse is enabled and the menu is opened from a mouse key binding, +releasing the mouse button with an item selected chooses that item and +releasing the mouse button without an item selected closes the menu. +.Fl O +changes this behaviour so that the menu does not close when the mouse button is +released without an item selected the menu is not closed and a mouse button +must be clicked to choose an item. +.Pp +.Fl M +tells +.Nm +the menu should handle mouse events; by default only menus opened from mouse +key bindings do so. +.Pp +The following keys are available in menus: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected item" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "q" Ta "Exit menu" +.El +.Tg display +.It Xo Ic display-message +.Op Fl aCIlNpv +.Op Fl c Ar target-client +.Op Fl d Ar delay +.Op Fl t Ar target-pane +.Op Ar message +.Xc +.D1 Pq alias: Ic display +Display a message. +If +.Fl p +is given, the output is printed to stdout, otherwise it is displayed in the +.Ar target-client +status line for up to +.Ar delay +milliseconds. +If +.Ar delay +is not given, the +.Ic display-time +option is used; a delay of zero waits for a key press. +.Ql N +ignores key presses and closes only after the delay expires. +If +.Fl C +is given, the pane will continue to be updated while the message is displayed. +If +.Fl l +is given, +.Ar message +is printed unchanged. +Otherwise, the format of +.Ar message +is described in the +.Sx FORMATS +section; information is taken from +.Ar target-pane +if +.Fl t +is given, otherwise the active pane. +.Pp +.Fl v +prints verbose logging as the format is parsed and +.Fl a +lists the format variables and their values. +.Pp +.Fl I +forwards any input read from stdin to the empty pane given by +.Ar target-pane . +.Tg popup +.It Xo Ic display-popup +.Op Fl BCE +.Op Fl b Ar border-lines +.Op Fl c Ar target-client +.Op Fl d Ar start-directory +.Op Fl e Ar environment +.Op Fl h Ar height +.Op Fl s Ar border-style +.Op Fl S Ar style +.Op Fl t Ar target-pane +.Op Fl T Ar title +.Op Fl w Ar width +.Op Fl x Ar position +.Op Fl y Ar position +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic popup +Display a popup running +.Ar shell-command +on +.Ar target-client . +A popup is a rectangular box drawn over the top of any panes. +Panes are not updated while a popup is present. +.Pp +.Fl E +closes the popup automatically when +.Ar shell-command +exits. +Two +.Fl E +closes the popup only if +.Ar shell-command +exited with success. +.Pp +.Fl x +and +.Fl y +give the position of the popup, they have the same meaning as for the +.Ic display-menu +command. +.Fl w +and +.Fl h +give the width and height - both may be a percentage (followed by +.Ql % ) . +If omitted, half of the terminal size is used. +.Pp +.Fl B +does not surround the popup by a border. +.Pp +.Fl b +sets the type of characters used for drawing popup borders. +When +.Fl B +is specified, the +.Fl b +option is ignored. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.Pp +.Fl s +sets the style for the popup and +.Fl S +sets the style for the popup border (see +.Sx STYLES ) . +.Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the popup; it may be specified multiple +times. +.Pp +.Fl T +is a format for the popup title (see +.Sx FORMATS ) . +.Pp +The +.Fl C +flag closes any popup on the client. +.Tg showphist +.It Xo Ic show-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 Pq alias: Ic showphist +Display status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then show history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . +.El +.Sh BUFFERS +.Nm +maintains a set of named +.Em paste buffers . +Each buffer may be either explicitly or automatically named. +Explicitly named buffers are named when created with the +.Ic set-buffer +or +.Ic load-buffer +commands, or by renaming an automatically named buffer with +.Ic set-buffer +.Fl n . +Automatically named buffers are given a name such as +.Ql buffer0001 , +.Ql buffer0002 +and so on. +When the +.Ic buffer-limit +option is reached, the oldest automatically named buffer is deleted. +Explicitly named buffers are not subject to +.Ic buffer-limit +and may be deleted with the +.Ic delete-buffer +command. +.Pp +Buffers may be added using +.Ic copy-mode +or the +.Ic set-buffer +and +.Ic load-buffer +commands, and pasted into a window using the +.Ic paste-buffer +command. +If a buffer command is used and no buffer is specified, the most +recently added automatically named buffer is assumed. +.Pp +A configurable history buffer is also maintained for each window. +By default, up to 2000 lines are kept; this can be altered with the +.Ic history-limit +option (see the +.Ic set-option +command above). +.Pp +The buffer commands are as follows: +.Bl -tag -width Ds +.It Xo +.Ic choose-buffer +.Op Fl NryZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into buffer mode, where a buffer may be chosen interactively from +a list. +Each buffer is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in buffer mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Paste selected buffer" +.It Li "Up" Ta "Select previous buffer" +.It Li "Down" Ta "Select next buffer" +.It Li "C-s" Ta "Search by name or content" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if buffer is tagged" +.It Li "T" Ta "Tag no buffers" +.It Li "C-t" Ta "Tag all buffers" +.It Li "p" Ta "Paste selected buffer" +.It Li "P" Ta "Paste tagged buffers" +.It Li "d" Ta "Delete selected buffer" +.It Li "D" Ta "Delete tagged buffers" +.It Li "e" Ta "Open the buffer in an editor" +.It Li "f" Ta "Enter a format to filter items" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a buffer is chosen, +.Ql %% +is replaced by the buffer name in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "paste-buffer -p -b \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql time +(creation), +.Ql name +or +.Ql size . +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview. +This command works only if at least one client is attached. +.Tg clearhist +.It Xo Ic clear-history +.Op Fl H +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic clearhist +Remove and free the history for the specified pane. +.Fl H +also removes all hyperlinks. +.Tg deleteb +.It Ic delete-buffer Op Fl b Ar buffer-name +.D1 Pq alias: Ic deleteb +Delete the buffer named +.Ar buffer-name , +or the most recently added automatically named buffer if not specified. +.Tg lsb +.It Xo Ic list-buffers +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc +.D1 Pq alias: Ic lsb +List the global buffers. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only buffers for which the filter is true are shown. +See the +.Sx FORMATS +section. +.It Xo Ic load-buffer +.Op Fl w +.Op Fl b Ar buffer-name +.Op Fl t Ar target-client +.Ar path +.Xc +.Tg loadb +.D1 Pq alias: Ic loadb +Load the contents of the specified paste buffer from +.Ar path . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. +If +.Ar path +is +.Ql - , +the contents are read from stdin. +.Tg pasteb +.It Xo Ic paste-buffer +.Op Fl dpr +.Op Fl b Ar buffer-name +.Op Fl s Ar separator +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic pasteb +Insert the contents of a paste buffer into the specified pane. +If not specified, paste into the current one. +With +.Fl d , +also delete the paste buffer. +When output, any linefeed (LF) characters in the paste buffer are replaced with +a separator, by default carriage return (CR). +A custom separator may be specified using the +.Fl s +flag. +The +.Fl r +flag means to do no replacement (equivalent to a separator of LF). +If +.Fl p +is specified, paste bracket control codes are inserted around the +buffer if the application has requested bracketed paste mode. +.Tg saveb +.It Xo Ic save-buffer +.Op Fl a +.Op Fl b Ar buffer-name +.Ar path +.Xc +.D1 Pq alias: Ic saveb +Save the contents of the specified paste buffer to +.Ar path . +The +.Fl a +option appends to rather than overwriting the file. +If +.Ar path +is +.Ql - , +the contents are written to stdout. +.It Xo Ic set-buffer +.Op Fl aw +.Op Fl b Ar buffer-name +.Op Fl t Ar target-client +.Tg setb +.Op Fl n Ar new-buffer-name +.Ar data +.Xc +.D1 Pq alias: Ic setb +Set the contents of the specified buffer to +.Ar data . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. +The +.Fl a +option appends to rather than overwriting the buffer. +The +.Fl n +option renames the buffer to +.Ar new-buffer-name . +.Tg showb +.It Xo Ic show-buffer +.Op Fl b Ar buffer-name +.Xc +.D1 Pq alias: Ic showb +Display the contents of the specified buffer. +.El +.Sh MISCELLANEOUS +Miscellaneous commands are as follows: +.Bl -tag -width Ds +.It Ic clock-mode Op Fl t Ar target-pane +Display a large clock. +.Tg if +.It Xo Ic if-shell +.Op Fl bF +.Op Fl t Ar target-pane +.Ar shell-command command +.Op Ar command +.Xc +.D1 Pq alias: Ic if +Execute the first +.Ar command +if +.Ar shell-command +(run with +.Pa /bin/sh ) +returns success or the second +.Ar command +otherwise. +Before being executed, +.Ar shell-command +is expanded using the rules specified in the +.Sx FORMATS +section, including those relevant to +.Ar target-pane . +With +.Fl b , +.Ar shell-command +is run in the background. +.Pp +If +.Fl F +is given, +.Ar shell-command +is not executed but considered success if neither empty nor zero (after formats +are expanded). +.Tg lock +.It Ic lock-server +.D1 Pq alias: Ic lock +Lock each client individually by running the command specified by the +.Ic lock-command +option. +.Tg run +.It Xo Ic run-shell +.Op Fl bC +.Op Fl c Ar start-directory +.Op Fl d Ar delay +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic run +Execute +.Ar shell-command +using +.Pa /bin/sh +or (with +.Fl C ) +a +.Nm +command in the background without creating a window. +Before being executed, +.Ar shell-command +is expanded using the rules specified in the +.Sx FORMATS +section. +With +.Fl b , +the command is run in the background. +.Fl d +waits for +.Ar delay +seconds before starting the command. +If +.Fl c +is given, the current working directory is set to +.Ar start-directory . +If +.Fl C +is not given, any output to stdout is displayed in view mode (in the pane +specified by +.Fl t +or the current pane if omitted) after the command finishes. +If the command fails, the exit status is also displayed. +.Tg wait +.It Xo Ic wait-for +.Op Fl L | S | U +.Ar channel +.Xc +.D1 Pq alias: Ic wait +When used without options, prevents the client from exiting until woken using +.Ic wait-for +.Fl S +with the same channel. +When +.Fl L +is used, the channel is locked and any clients that try to lock the same +channel are made to wait until the channel is unlocked with +.Ic wait-for +.Fl U . +.El +.Sh EXIT MESSAGES +When a +.Nm +client detaches, it prints a message. +This may be one of: +.Bl -tag -width Ds +.It detached (from session ...) +The client was detached normally. +.It detached and SIGHUP +The client was detached and its parent sent the +.Dv SIGHUP +signal (for example with +.Ic detach-client +.Fl P ) . +.It lost tty +The client's +.Xr tty 4 +or +.Xr pty 4 +was unexpectedly destroyed. +.It terminated +The client was killed with +.Dv SIGTERM . +.It too far behind +The client is in control mode and became unable to keep up with the data from +.Nm . +.It exited +The server exited when it had no sessions. +.It server exited +The server exited when it received +.Dv SIGTERM . +.It server exited unexpectedly +The server crashed or otherwise exited without telling the client the reason. +.El +.Sh TERMINFO EXTENSIONS +.Nm +understands some unofficial extensions to +.Xr terminfo 5 . +It is not normally necessary to set these manually, instead the +.Ic terminal-features +option should be used. +.Bl -tag -width Ds +.It Em \&AX +An existing extension that tells +.Nm +the terminal supports default colours. +.It Em \&Bidi +Tell +.Nm +that the terminal supports the VTE bidirectional text extensions. +.It Em \&Cs , Cr +Set the cursor colour. +The first takes a single string argument and is used to set the colour; +the second takes no arguments and restores the default cursor colour. +If set, a sequence such as this may be used +to change the cursor colour from inside +.Nm : +.Bd -literal -offset indent +$ printf \[aq]\e033]12;red\e033\e\e\[aq] +.Ed +.Pp +The colour is an +.Xr X 7 +colour, see +.Xr XParseColor 3 . +.It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg +Set, clear, disable or enable DECSLRM margins. +These are set automatically if the terminal reports it is +.Em VT420 +compatible. +.It Em \&Dsbp , \&Enbp +Disable and enable bracketed paste. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Dseks , \&Eneks +Disable and enable extended keys. +.It Em \&Dsfcs , \&Enfcs +Disable and enable focus reporting. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Hls +Set or clear a hyperlink annotation. +.It Em \&Nobr +Tell +.Nm +that the terminal does not use bright colors for bold display. +.It Em \&Rect +Tell +.Nm +that the terminal supports rectangle operations. +.It Em \&Smol +Enable the overline attribute. +.It Em \&Smulx +Set a styled underscore. +The single parameter is one of: 0 for no underscore, 1 for normal +underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted +underscore and 5 for dashed underscore. +.It Em \&Setulc , \&Setulc1, \&ol +Set the underscore colour or reset to the default. +.Em Setulc +is for RGB colours and +.Em Setulc1 +for ANSI or 256 colours. +The +.Em Setulc +argument is (red * 65536) + (green * 256) + blue where each is between 0 +and 255. +.It Em \&Ss , Se +Set or reset the cursor style. +If set, a sequence such as this may be used +to change the cursor to an underline: +.Bd -literal -offset indent +$ printf \[aq]\e033[4 q\[aq] +.Ed +.Pp +If +.Em Se +is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Swd +Set the opening sequence for the working directory notification. +The sequence is terminated using the standard +.Em fsl +capability. +.It Em \&Sxl +Indicates that the terminal supports SIXEL. +.It Em \&Sync +Start (parameter is 1) or end (parameter is 2) a synchronized update. +.It Em \&Tc +Indicate that the terminal supports the +.Ql direct colour +RGB escape sequence (for example, \ee[38;2;255;255;255m). +.Pp +If supported, this is used for the initialize colour escape sequence (which +may be enabled by adding the +.Ql initc +and +.Ql ccc +capabilities to the +.Nm +.Xr terminfo 5 +entry). +.Pp +This is equivalent to the +.Em RGB +.Xr terminfo 5 +capability. +.It Em \&Ms +Store the current buffer in the host terminal's selection (clipboard). +See the +.Em set-clipboard +option above and the +.Xr xterm 1 +man page. +.It Em \&XT +This is an existing extension capability that tmux uses to mean that the +terminal supports the +.Xr xterm 1 +title set sequences and to automatically set some of the capabilities above. +.El +.Sh CONTROL MODE +.Nm +offers a textual interface called +.Em control mode . +This allows applications to communicate with +.Nm +using a simple text-only protocol. +.Pp +In control mode, a client sends +.Nm +commands or command sequences terminated by newlines on standard input. +Each command will produce one block of output on standard output. +An output block consists of a +.Em %begin +line followed by the output (which may be empty). +The output block ends with a +.Em %end +or +.Em %error . +.Em %begin +and matching +.Em %end +or +.Em %error +have three arguments: an integer time (as seconds from epoch), command number +and flags (currently not used). +For example: +.Bd -literal -offset indent +%begin 1363006971 2 1 +0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) +%end 1363006971 2 1 +.Ed +.Pp +The +.Ic refresh-client +.Fl C +command may be used to set the size of a client in control mode. +.Pp +In control mode, +.Nm +outputs notifications. +A notification will never occur inside an output block. +.Pp +The following notifications are defined: +.Bl -tag -width Ds +.It Ic %client-detached Ar client +The client has detached. +.It Ic %client-session-changed Ar client session-id name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %config-error Ar error +An error has happened in a configuration file. +.It Ic %continue Ar pane-id +The pane has been continued after being paused (if the +.Ar pause-after +flag is set, see +.Ic refresh-client +.Fl A ) . +.It Ic %exit Op Ar reason +The +.Nm +client is exiting immediately, either because it is not attached to any session +or an error occurred. +If present, +.Ar reason +describes why the client exited. +.It Ic %extended-output Ar pane-id Ar age Ar ... \& : Ar value +New form of +.Ic %output +sent when the +.Ar pause-after +flag is set. +.Ar age +is the time in milliseconds for which tmux had buffered the output before it +was sent. +Any subsequent arguments up until a single +.Ql \&: +are for future use and should be ignored. +.It Xo Ic %layout-change +.Ar window-id +.Ar window-layout +.Ar window-visible-layout +.Ar window-flags +.Xc +The layout of a window with ID +.Ar window-id +changed. +The new layout is +.Ar window-layout . +The window's visible layout is +.Ar window-visible-layout +and the window flags are +.Ar window-flags . +.It Ic %message Ar message +A message sent with the +.Ic display-message +command. +.It Ic %output Ar pane-id Ar value +A window pane produced output. +.Ar value +escapes non-printable characters and backslash as octal \\xxx. +.It Ic %pane-mode-changed Ar pane-id +The pane with ID +.Ar pane-id +has changed mode. +.It Ic %paste-buffer-changed Ar name +Paste buffer +.Ar name +has been changed. +.It Ic %paste-buffer-deleted Ar name +Paste buffer +.Ar name +has been deleted. +.It Ic %pause Ar pane-id +The pane has been paused (if the +.Ar pause-after +flag is set). +.It Ic %session-changed Ar session-id Ar name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %session-renamed Ar name +The current session was renamed to +.Ar name . +.It Ic %session-window-changed Ar session-id Ar window-id +The session with ID +.Ar session-id +changed its active window to the window with ID +.Ar window-id . +.It Ic %sessions-changed +A session was created or destroyed. +.It Xo Ic %subscription-changed +.Ar name +.Ar session-id +.Ar window-id +.Ar window-index +.Ar pane-id ... \& : +.Ar value +.Xc +The value of the format associated with subscription +.Ar name +has changed to +.Ar value . +See +.Ic refresh-client +.Fl B . +Any arguments after +.Ar pane-id +up until a single +.Ql \&: +are for future use and should be ignored. +.It Ic %unlinked-window-add Ar window-id +The window with ID +.Ar window-id +was created but is not linked to the current session. +.It Ic %unlinked-window-close Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was closed. +.It Ic %unlinked-window-renamed Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was renamed. +.It Ic %window-add Ar window-id +The window with ID +.Ar window-id +was linked to the current session. +.It Ic %window-close Ar window-id +The window with ID +.Ar window-id +closed. +.It Ic %window-pane-changed Ar window-id Ar pane-id +The active pane in the window with ID +.Ar window-id +changed to the pane with ID +.Ar pane-id . +.It Ic %window-renamed Ar window-id Ar name +The window with ID +.Ar window-id +was renamed to +.Ar name . +.El +.Sh ENVIRONMENT +When +.Nm +is started, it inspects the following environment variables: +.Bl -tag -width LC_CTYPE +.It Ev EDITOR +If the command specified in this variable contains the string +.Ql vi +and +.Ev VISUAL +is unset, use vi-style key bindings. +Overridden by the +.Ic mode-keys +and +.Ic status-keys +options. +.It Ev HOME +The user's login directory. +If unset, the +.Xr passwd 5 +database is consulted. +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 . +It is used for two separate purposes. +For output to the terminal, UTF-8 is used if the +.Fl u +option is given or if +.Ev LC_CTYPE +contains +.Qq UTF-8 +or +.Qq UTF8 . +Otherwise, only ASCII characters are written and non-ASCII characters +are replaced with underscores +.Pq Ql _ . +For input, +.Nm +always runs with a UTF-8 locale. +If en_US.UTF-8 is provided by the operating system, it is used and +.Ev LC_CTYPE +is ignored for input. +Otherwise, +.Ev LC_CTYPE +tells +.Nm +what the UTF-8 locale is called on the current system. +If the locale specified by +.Ev LC_CTYPE +is not available or is not a UTF-8 locale, +.Nm +exits with an error message. +.It Ev LC_TIME +The date and time format +.Xr locale 1 . +It is used for locale-dependent +.Xr strftime 3 +format specifiers. +.It Ev PWD +The current working directory to be set in the global environment. +This may be useful if it contains symbolic links. +If the value of the variable does not match the current working +directory, the variable is ignored and the result of +.Xr getcwd 3 +is used instead. +.It Ev SHELL +The absolute path to the default shell for new windows. +See the +.Ic default-shell +option for details. +.It Ev TMUX_TMPDIR +The parent directory of the directory containing the server sockets. +See the +.Fl L +option for details. +.It Ev VISUAL +If the command specified in this variable contains the string +.Ql vi , +use vi-style key bindings. +Overridden by the +.Ic mode-keys +and +.Ic status-keys +options. +.El +.Sh FILES +.Bl -tag -width "/etc/tmux.confXXX" -compact +.It Pa \[ti]/.tmux.conf +Default +.Nm +configuration file. +.It Pa /etc/tmux.conf +System-wide configuration file. +.El +.Sh EXAMPLES +To create a new +.Nm +session running +.Xr vi 1 : +.Pp +.Dl $ tmux new-session vi +.Pp +Most commands have a shorter form, known as an alias. +For new-session, this is +.Ic new : +.Pp +.Dl $ tmux new vi +.Pp +Alternatively, the shortest unambiguous form of a command is accepted. +If there are several options, they are listed: +.Bd -literal -offset indent +$ tmux n +ambiguous command: n, could be: new-session, new-window, next-window +.Ed +.Pp +Within an active session, a new window may be created by typing +.Ql C-b c +(Ctrl +followed by the +.Ql b +key +followed by the +.Ql c +key). +.Pp +Windows may be navigated with: +.Ql C-b 0 +(to select window 0), +.Ql C-b 1 +(to select window 1), and so on; +.Ql C-b n +to select the next window; and +.Ql C-b p +to select the previous window. +.Pp +A session may be detached using +.Ql C-b d +(or by an external event such as +.Xr ssh 1 +disconnection) and reattached with: +.Pp +.Dl $ tmux attach-session +.Pp +Typing +.Ql C-b \&? +lists the current key bindings in the current window; up and down may be used +to navigate the list or +.Ql q +to exit from it. +.Pp +Commands to be run when the +.Nm +server is started may be placed in the +.Pa \[ti]/.tmux.conf +configuration file. +Common examples include: +.Pp +Changing the default prefix key: +.Bd -literal -offset indent +set-option -g prefix C-a +unbind-key C-b +bind-key C-a send-prefix +.Ed +.Pp +Turning the status line off, or changing its colour: +.Bd -literal -offset indent +set-option -g status off +set-option -g status-style bg=blue +.Ed +.Pp +Setting other options, such as the default command, +or locking after 30 minutes of inactivity: +.Bd -literal -offset indent +set-option -g default-command "exec /bin/ksh" +set-option -g lock-after-time 1800 +.Ed +.Pp +Creating new key bindings: +.Bd -literal -offset indent +bind-key b set-option status +bind-key / command-prompt "split-window \[aq]exec man %%\[aq]" +bind-key S command-prompt "new-window -n %1 \[aq]ssh %1\[aq]" +.Ed +.Sh SEE ALSO +.Xr pty 4 +.Sh AUTHORS +.An Nicholas Marriott Aq Mt nicholas.marriott@gmail.com diff --git a/man/test_files/mdoc/tmux.1 b/man/test_files/mdoc/tmux.1 new file mode 100644 index 00000000..cb7b35bb --- /dev/null +++ b/man/test_files/mdoc/tmux.1 @@ -0,0 +1,7799 @@ +.\" $OpenBSD: tmux.1,v 1.987 2025/03/24 20:01:03 nicm Exp $ +.\" +.\" Copyright (c) 2007 Nicholas Marriott +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 24 2025 $ +.Dt TMUX 1 +.Os +.Sh NAME +.Nm tmux +.Nd terminal multiplexer +.Sh SYNOPSIS +.Nm tmux +.Bk -words +.Op Fl 2CDlNuVv +.Op Fl c Ar shell-command +.Op Fl f Ar file +.Op Fl L Ar socket-name +.Op Fl S Ar socket-path +.Op Fl T Ar features +.Op Ar command Op Ar flags +.Ek +.Sh DESCRIPTION +.Nm +is a terminal multiplexer: +it enables a number of terminals to be created, accessed, and +controlled from a single screen. +.Nm +may be detached from a screen +and continue running in the background, +then later reattached. +.Pp +When +.Nm +is started, it creates a new +.Em session +with a single +.Em window +and displays it on screen. +A status line at the bottom of the screen +shows information on the current session +and is used to enter interactive commands. +.Pp +A session is a single collection of +.Em pseudo terminals +under the management of +.Nm . +Each session has one or more +windows linked to it. +A window occupies the entire screen +and may be split into rectangular panes, +each of which is a separate pseudo terminal +(the +.Xr pty 4 +manual page documents the technical details of pseudo terminals). +Any number of +.Nm +instances may connect to the same session, +and any number of windows may be present in the same session. +Once all sessions are killed, +.Nm +exits. +.Pp +Each session is persistent and will survive accidental disconnection +(such as +.Xr ssh 1 +connection timeout) or intentional detaching (with the +.Ql C-b d +key strokes). +.Nm +may be reattached using: +.Pp +.Dl $ tmux attach +.Pp +In +.Nm , +a session is displayed on screen by a +.Em client +and all sessions are managed by a single +.Em server . +The server and each client are separate processes which communicate through a +socket in +.Pa /tmp . +.Pp +The options are as follows: +.Bl -tag -width "XXXXXXXXXXXX" +.It Fl 2 +Force +.Nm +to assume the terminal supports 256 colours. +This is equivalent to +.Fl T Ar 256 . +.It Fl C +Start in control mode (see the +.Sx CONTROL MODE +section). +Given twice +.Xo ( Fl CC ) Xc +disables echo. +.It Fl c Ar shell-command +Execute +.Ar shell-command +using the default shell. +If necessary, the +.Nm +server will be started to retrieve the +.Ic default-shell +option. +This option is for compatibility with +.Xr sh 1 +when +.Nm +is used as a login shell. +.It Fl D +Do not start the +.Nm +server as a daemon. +This also turns the +.Ic exit-empty +option off. +With +.Fl D , +.Ar command +may not be specified. +.It Fl f Ar file +Specify an alternative configuration file. +By default, +.Nm +loads the system configuration file from +.Pa /etc/tmux.conf , +if present, then looks for a user configuration file at +.Pa \[ti]/.tmux.conf . +.Pp +The configuration file is a set of +.Nm +commands which are executed in sequence when the server is first started. +.Nm +loads configuration files once when the server process has started. +The +.Ic source-file +command may be used to load a file later. +.Pp +.Nm +shows any error messages from commands in configuration files in the first +session created, and continues to process the rest of the configuration file. +.It Fl L Ar socket-name +.Nm +stores the server socket in a directory under +.Ev TMUX_TMPDIR +or +.Pa /tmp +if it is unset. +The default socket is named +.Em default . +This option allows a different socket name to be specified, allowing several +independent +.Nm +servers to be run. +Unlike +.Fl S +a full path is not necessary: the sockets are all created in a directory +.Pa tmux-UID +under the directory given by +.Ev TMUX_TMPDIR +or in +.Pa /tmp . +The +.Pa tmux-UID +directory is created by +.Nm +and must not be world readable, writable or executable. +.Pp +If the socket is accidentally removed, the +.Dv SIGUSR1 +signal may be sent to the +.Nm +server process to recreate it (note that this will fail if any parent +directories are missing). +.It Fl l +Behave as a login shell. +This flag currently has no effect and is for compatibility with other shells +when using tmux as a login shell. +.It Fl N +Do not start the server even if the command would normally do so (for example +.Ic new-session +or +.Ic start-server ) . +.It Fl S Ar socket-path +Specify a full alternative path to the server socket. +If +.Fl S +is specified, the default socket directory is not used and any +.Fl L +flag is ignored. +.It Fl T Ar features +Set terminal features for the client. +This is a comma-separated list of features. +See the +.Ic terminal-features +option. +.It Fl u +Write UTF-8 output to the terminal even if the first environment +variable of +.Ev LC_ALL , +.Ev LC_CTYPE , +or +.Ev LANG +that is set does not contain +.Qq UTF-8 +or +.Qq UTF8 . +.It Fl V +Report the +.Nm +version. +.It Fl v +Request verbose logging. +Log messages will be saved into +.Pa tmux-client-PID.log +and +.Pa tmux-server-PID.log +files in the current directory, where +.Em PID +is the PID of the server or client process. +If +.Fl v +is specified twice, an additional +.Pa tmux-out-PID.log +file is generated with a copy of everything +.Nm +writes to the terminal. +.Pp +The +.Dv SIGUSR2 +signal may be sent to the +.Nm +server process to toggle logging between on (as if +.Fl v +was given) and off. +.It Ar command Op Ar flags +This specifies one of a set of commands used to control +.Nm , +as described in the following sections. +If no commands are specified, the command in +.Ic default-client-command +is assumed, which defaults to +.Ic new-session . +.El +.Sh DEFAULT KEY BINDINGS +.Nm +may be controlled from an attached client by using a key combination of a +prefix key, +.Ql C-b +(Ctrl-b) by default, followed by a command key. +.Pp +The default command key bindings are: +.Pp +.Bl -tag -width "XXXXXXXXXX" -offset indent -compact +.It C-b +Send the prefix key (C-b) through to the application. +.It C-o +Rotate the panes in the current window forwards. +.It C-z +Suspend the +.Nm +client. +.It ! +Break the current pane out of the window. +.It \&" +.\" " +Split the current pane into two, top and bottom. +.It # +List all paste buffers. +.It $ +Rename the current session. +.It % +Split the current pane into two, left and right. +.It & +Kill the current window. +.It \[aq] +Prompt for a window index to select. +.It \&( +Switch the attached client to the previous session. +.It \&) +Switch the attached client to the next session. +.It , +Rename the current window. +.It - +Delete the most recently copied buffer of text. +.It . +Prompt for an index to move the current window. +.It 0 to 9 +Select windows 0 to 9. +.It : +Enter the +.Nm +command prompt. +.It ; +Move to the previously active pane. +.It = +Choose which buffer to paste interactively from a list. +.It \&? +List all key bindings. +.It D +Choose a client to detach. +.It L +Switch the attached client back to the last session. +.It \&[ +Enter copy mode to copy text or view the history. +.It \&] +Paste the most recently copied buffer of text. +.It c +Create a new window. +.It d +Detach the current client. +.It f +Prompt to search for text in open windows. +.It i +Display some information about the current window. +.It l +Move to the previously selected window. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. +.It n +Change to the next window. +.It o +Select the next pane in the current window. +.It p +Change to the previous window. +.It q +Briefly display pane indexes. +.It r +Force redraw of the attached client. +.It s +Select a new session for the attached client interactively. +.It t +Show the time. +.It w +Choose the current window interactively. +.It x +Kill the current pane. +.It z +Toggle zoom state of the current pane. +.It { +Swap the current pane with the previous pane. +.It } +Swap the current pane with the next pane. +.It \[ti] +Show previous messages from +.Nm , +if any. +.It Page Up +Enter copy mode and scroll one page up. +.It Up, Down +.It Left, Right +Change to the pane above, below, to the left, or to the right of the current +pane. +.It M-1 to M-7 +Arrange panes in one of the seven preset layouts: +even-horizontal, even-vertical, +main-horizontal, main-horizontal-mirrored, +main-vertical, main-vertical-mirrored, +or tiled. +.It Space +Arrange the current window in the next preset layout. +.It M-n +Move to the next window with a bell or activity marker. +.It M-o +Rotate the panes in the current window backwards. +.It M-p +Move to the previous window with a bell or activity marker. +.It C-Up, C-Down +.It C-Left, C-Right +Resize the current pane in steps of one cell. +.It M-Up, M-Down +.It M-Left, M-Right +Resize the current pane in steps of five cells. +.El +.Pp +Key bindings may be changed with the +.Ic bind-key +and +.Ic unbind-key +commands. +.Sh COMMAND PARSING AND EXECUTION +.Nm +supports a large number of commands which can be used to control its +behaviour. +Each command is named and can accept zero or more flags and arguments. +They may be bound to a key with the +.Ic bind-key +command or run from the shell prompt, a shell script, a configuration file or +the command prompt. +For example, the same +.Ic set-option +command run from the shell prompt, from +.Pa \[ti]/.tmux.conf +and bound to a key may look like: +.Bd -literal -offset indent +$ tmux set-option -g status-style bg=cyan + +set-option -g status-style bg=cyan + +bind-key C set-option -g status-style bg=cyan +.Ed +.Pp +Here, the command name is +.Ql set-option , +.Ql Fl g +is a flag and +.Ql status-style +and +.Ql bg=cyan +are arguments. +.Pp +.Nm +distinguishes between command parsing and execution. +In order to execute a command, +.Nm +needs it to be split up into its name and arguments. +This is command parsing. +If a command is run from the shell, the shell parses it; from inside +.Nm +or from a configuration file, +.Nm +does. +Examples of when +.Nm +parses commands are: +.Bl -dash -offset indent +.It +in a configuration file; +.It +typed at the command prompt (see +.Ic command-prompt ) ; +.It +given to +.Ic bind-key ; +.It +passed as arguments to +.Ic if-shell +or +.Ic confirm-before . +.El +.Pp +To execute commands, each client has a +.Ql command queue . +A global command queue not attached to any client is used on startup +for configuration files like +.Pa \[ti]/.tmux.conf . +Parsed commands added to the queue are executed in order. +Some commands, like +.Ic if-shell +and +.Ic confirm-before , +parse their argument to create a new command which is inserted immediately +after themselves. +This means that arguments can be parsed twice or more - once when the parent +command (such as +.Ic if-shell ) +is parsed and again when it parses and executes its command. +Commands like +.Ic if-shell , +.Ic run-shell +and +.Ic display-panes +stop execution of subsequent commands on the queue until something happens - +.Ic if-shell +and +.Ic run-shell +until a shell command finishes and +.Ic display-panes +until a key is pressed. +For example, the following commands: +.Bd -literal -offset indent +new-session; new-window +if-shell "true" "split-window" +kill-session +.Ed +.Pp +Will execute +.Ic new-session , +.Ic new-window , +.Ic if-shell , +the shell command +.Xr true 1 , +.Ic split-window +and +.Ic kill-session +in that order. +.Pp +The +.Sx COMMANDS +section lists the +.Nm +commands and their arguments. +.Sh PARSING SYNTAX +This section describes the syntax of commands parsed by +.Nm , +for example in a configuration file or at the command prompt. +Note that when commands are entered into the shell, they are parsed by the shell +- see for example +.Xr ksh 1 +or +.Xr csh 1 . +.Pp +Each command is terminated by a newline or a semicolon (;). +Commands separated by semicolons together form a +.Ql command sequence +- if a command in the sequence encounters an error, no subsequent commands are +executed. +.Pp +It is recommended that a semicolon used as a command separator should be +written as an individual token, for example from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux neww \\; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux neww \[aq];\[aq] splitw +.Ed +.Pp +Or from the tmux command prompt: +.Bd -literal -offset indent +neww ; splitw +.Ed +.Pp +However, a trailing semicolon is also interpreted as a command separator, +for example in these +.Xr sh 1 +commands: +.Bd -literal -offset indent +$ tmux neww\e; splitw +.Ed +.Pp +Or: +.Bd -literal -offset indent +$ tmux \[aq]neww;\[aq] splitw +.Ed +.Pp +As in these examples, when running tmux from the shell extra care must be taken +to properly quote semicolons: +.Bl -enum -offset Ds +.It +Semicolons that should be interpreted as a command separator +should be escaped according to the shell conventions. +For +.Xr sh 1 +this typically means quoted (such as +.Ql neww \[aq];\[aq] splitw ) +or escaped (such as +.Ql neww \e\e\e\e; splitw ) . +.It +Individual semicolons or trailing semicolons that should be interpreted as +arguments should be escaped twice: once according to the shell conventions and +a second time for +.Nm ; +for example: +.Bd -literal -offset indent +$ tmux neww \[aq]foo\e\e;\[aq] bar +$ tmux neww foo\e\e\e\e; bar +.Ed +.It +Semicolons that are not individual tokens or trailing another token should only +be escaped once according to shell conventions; for example: +.Bd -literal -offset indent +$ tmux neww \[aq]foo-;-bar\[aq] +$ tmux neww foo-\e\e;-bar +.Ed +.El +.Pp +Comments are marked by the unquoted # character - any remaining text after a +comment is ignored until the end of the line. +.Pp +If the last character of a line is \e, the line is joined with the following +line (the \e and the newline are completely removed). +This is called line continuation and applies both inside and outside quoted +strings and in comments, but not inside braces. +.Pp +Command arguments may be specified as strings surrounded by single (\[aq]) +quotes, double quotes (\[dq]) or braces ({}). +.\" " +This is required when the argument contains any special character. +Single and double quoted strings cannot span multiple lines except with line +continuation. +Braces can span multiple lines. +.Pp +Outside of quotes and inside double quotes, these replacements are performed: +.Bl -dash -offset indent +.It +Environment variables preceded by $ are replaced with their value from the +global environment (see the +.Sx GLOBAL AND SESSION ENVIRONMENT +section). +.It +A leading \[ti] or \[ti]user is expanded to the home directory of the current or +specified user. +.It +\euXXXX or \euXXXXXXXX is replaced by the Unicode codepoint corresponding to +the given four or eight digit hexadecimal number. +.It +When preceded (escaped) by a \e, the following characters are replaced: \ee by +the escape character; \er by a carriage return; \en by a newline; and \et by a +tab. +.It +\eooo is replaced by a character of the octal value ooo. +Three octal digits are required, for example \e001. +The largest valid character is \e377. +.It +Any other characters preceded by \e are replaced by themselves (that is, the \e +is removed) and are not treated as having any special meaning - so for example +\e; will not mark a command sequence and \e$ will not expand an environment +variable. +.El +.Pp +Braces are parsed as a configuration file (so conditions such as +.Ql %if +are processed) and then converted into a string. +They are designed to avoid the need for additional escaping when passing a +group of +.Nm +commands as an argument (for example to +.Ic if-shell ) . +These two examples produce an identical command - note that no escaping is +needed when using {}: +.Bd -literal -offset indent +if-shell true { + display -p \[aq]brace-dollar-foo: }$foo\[aq] +} + +if-shell true "display -p \[aq]brace-dollar-foo: }\e$foo\[aq]" +.Ed +.Pp +Braces may be enclosed inside braces, for example: +.Bd -literal -offset indent +bind x if-shell "true" { + if-shell "true" { + display "true!" + } +} +.Ed +.Pp +Environment variables may be set by using the syntax +.Ql name=value , +for example +.Ql HOME=/home/user . +Variables set during parsing are added to the global environment. +A hidden variable may be set with +.Ql %hidden , +for example: +.Bd -literal -offset indent +%hidden MYVAR=42 +.Ed +.Pp +Hidden variables are not passed to the environment of processes created +by tmux. +See the +.Sx GLOBAL AND SESSION ENVIRONMENT +section. +.Pp +Commands may be parsed conditionally by surrounding them with +.Ql %if , +.Ql %elif , +.Ql %else +and +.Ql %endif . +The argument to +.Ql %if +and +.Ql %elif +is expanded as a format (see +.Sx FORMATS ) +and if it evaluates to false (zero or empty), subsequent text is ignored until +the closing +.Ql %elif , +.Ql %else +or +.Ql %endif . +For example: +.Bd -literal -offset indent +%if "#{==:#{host},myhost}" +set -g status-style bg=red +%elif "#{==:#{host},myotherhost}" +set -g status-style bg=green +%else +set -g status-style bg=blue +%endif +.Ed +.Pp +Will change the status line to red if running on +.Ql myhost , +green if running on +.Ql myotherhost , +or blue if running on another host. +Conditionals may be given on one line, for example: +.Bd -literal -offset indent +%if #{==:#{host},myhost} set -g status-style bg=red %endif +.Ed +.Sh COMMANDS +This section describes the commands supported by +.Nm . +Most commands accept the optional +.Fl t +(and sometimes +.Fl s ) +argument with one of +.Ar target-client , +.Ar target-session , +.Ar target-window , +or +.Ar target-pane . +These specify the client, session, window or pane which a command should affect. +.Pp +.Ar target-client +should be the name of the client, +typically the +.Xr pty 4 +file to which the client is connected, for example either of +.Pa /dev/ttyp1 +or +.Pa ttyp1 +for the client attached to +.Pa /dev/ttyp1 . +If no client is specified, +.Nm +attempts to work out the client currently in use; if that fails, an error is +reported. +Clients may be listed with the +.Ic list-clients +command. +.Pp +.Ar target-session +is tried as, in order: +.Bl -enum -offset Ds +.It +A session ID prefixed with a $. +.It +An exact name of a session (as listed by the +.Ic list-sessions +command). +.It +The start of a session name, for example +.Ql mysess +would match a session named +.Ql mysession . +.It +A +.Xr glob 7 +pattern which is matched against the session name. +.El +.Pp +If the session name is prefixed with an +.Ql = , +only an exact match is accepted (so +.Ql =mysess +will only match exactly +.Ql mysess , +not +.Ql mysession ) . +.Pp +If a single session is found, it is used as the target session; multiple matches +produce an error. +If a session is omitted, the current session is used if available; if no +current session is available, the most recently used is chosen. +.Pp +.Ar target-window +(or +.Ar src-window +or +.Ar dst-window ) +specifies a window in the form +.Em session Ns \&: Ns Em window . +.Em session +follows the same rules as for +.Ar target-session , +and +.Em window +is looked for in order as: +.Bl -enum -offset Ds +.It +A special token, listed below. +.It +A window index, for example +.Ql mysession:1 +is window 1 in session +.Ql mysession . +.It +A window ID, such as @1. +.It +An exact window name, such as +.Ql mysession:mywindow . +.It +The start of a window name, such as +.Ql mysession:mywin . +.It +As a +.Xr glob 7 +pattern matched against the window name. +.El +.Pp +Like sessions, a +.Ql = +prefix will do an exact match only. +An empty window name specifies the next unused index if appropriate (for +example the +.Ic new-window +and +.Ic link-window +commands) +otherwise the current window in +.Em session +is chosen. +.Pp +The following special tokens are available to indicate particular windows. +Each has a single-character alternative form. +.Bl -column "XXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{start}" Ta "^" Ta "The lowest-numbered window" +.It Li "{end}" Ta "$" Ta "The highest-numbered window" +.It Li "{last}" Ta "!" Ta "The last (previously current) window" +.It Li "{next}" Ta "+" Ta "The next window by number" +.It Li "{previous}" Ta "-" Ta "The previous window by number" +.El +.Pp +.Ar target-pane +(or +.Ar src-pane +or +.Ar dst-pane ) +may be a pane ID or takes a similar form to +.Ar target-window +but with the optional addition of a period followed by a pane index or pane ID, +for example: +.Ql mysession:mywindow.1 . +If the pane index is omitted, the currently active pane in the specified +window is used. +The following special tokens are available for the pane index: +.Bl -column "XXXXXXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{last}" Ta "!" Ta "The last (previously active) pane" +.It Li "{next}" Ta "+" Ta "The next pane by number" +.It Li "{previous}" Ta "-" Ta "The previous pane by number" +.It Li "{top}" Ta "" Ta "The top pane" +.It Li "{bottom}" Ta "" Ta "The bottom pane" +.It Li "{left}" Ta "" Ta "The leftmost pane" +.It Li "{right}" Ta "" Ta "The rightmost pane" +.It Li "{top-left}" Ta "" Ta "The top-left pane" +.It Li "{top-right}" Ta "" Ta "The top-right pane" +.It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" +.It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" +.It Li "{up-of}" Ta "" Ta "The pane above the active pane" +.It Li "{down-of}" Ta "" Ta "The pane below the active pane" +.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" +.El +.Pp +The tokens +.Ql + +and +.Ql - +may be followed by an offset, for example: +.Bd -literal -offset indent +select-window -t:+2 +.Ed +.Pp +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the session, window or pane where the most recent mouse event +occurred (see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql \[ti] ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . +.Pp +Sessions, window and panes are each numbered with a unique ID; session IDs are +prefixed with a +.Ql $ , +windows with a +.Ql @ , +and panes with a +.Ql % . +These are unique and are unchanged for the life of the session, window or pane +in the +.Nm +server. +The pane ID is passed to the child process of the pane in the +.Ev TMUX_PANE +environment variable. +IDs may be displayed using the +.Ql session_id , +.Ql window_id , +or +.Ql pane_id +formats (see the +.Sx FORMATS +section) and the +.Ic display-message , +.Ic list-sessions , +.Ic list-windows +or +.Ic list-panes +commands. +.Pp +.Ar shell-command +arguments are +.Xr sh 1 +commands. +This may be a single argument passed to the shell, for example: +.Bd -literal -offset indent +new-window \[aq]vi \[ti]/.tmux.conf\[aq] +.Ed +.Pp +Will run: +.Bd -literal -offset indent +/bin/sh -c \[aq]vi \[ti]/.tmux.conf\[aq] +.Ed +.Pp +Additionally, the +.Ic new-window , +.Ic new-session , +.Ic split-window , +.Ic respawn-window +and +.Ic respawn-pane +commands allow +.Ar shell-command +to be given as multiple arguments and executed directly (without +.Ql sh -c ) . +This can avoid issues with shell quoting. +For example: +.Bd -literal -offset indent +$ tmux new-window vi \[ti]/.tmux.conf +.Ed +.Pp +Will run +.Xr vi 1 +directly without invoking the shell. +.Pp +.Ar command +.Op Ar argument ... +refers to a +.Nm +command, either passed with the command and arguments separately, for example: +.Bd -literal -offset indent +bind-key F1 set-option status off +.Ed +.Pp +Or passed as a single string argument in +.Pa .tmux.conf , +for example: +.Bd -literal -offset indent +bind-key F1 { set-option status off } +.Ed +.Pp +Example +.Nm +commands include: +.Bd -literal -offset indent +refresh-client -t/dev/ttyp2 + +rename-session -tfirst newname + +set-option -wt:0 monitor-activity on + +new-window ; split-window -d + +bind-key R source-file \[ti]/.tmux.conf \e; \e + display-message "source-file done" +.Ed +.Pp +Or from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux kill-window -t :1 + +$ tmux new-window \e; split-window -d + +$ tmux new-session -d \[aq]vi \[ti]/.tmux.conf\[aq] \e; split-window -d \e; attach +.Ed +.Sh CLIENTS AND SESSIONS +The +.Nm +server manages clients, sessions, windows and panes. +Clients are attached to sessions to interact with them, either +when they are created with the +.Ic new-session +command, or later with the +.Ic attach-session +command. +Each session has one or more windows +.Em linked +into it. +Windows may be linked to multiple sessions and are made up of one or +more panes, +each of which contains a pseudo terminal. +Commands for creating, linking and otherwise manipulating windows +are covered +in the +.Sx WINDOWS AND PANES +section. +.Pp +The following commands are available to manage clients and sessions: +.Bl -tag -width Ds +.Tg attach +.It Xo Ic attach-session +.Op Fl dErx +.Op Fl c Ar working-directory +.Op Fl f Ar flags +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic attach +If run from outside +.Nm , +attach to +.Ar target-session +in the current terminal. +.Ar target-session +must already exist - to create a new session, see the +.Ic new-session +command (with +.Fl A +to create or attach). +If used from inside, switch the currently attached session to +.Ar target-session . +If +.Fl d +is specified, any other clients attached to the session are detached. +If +.Fl x +is given, send +.Dv SIGHUP +to the parent process of the client as well as +detaching the client, typically causing it to exit. +.Fl f +sets a comma-separated list of client flags. +The flags are: +.Bl -tag -width Ds +.It active-pane +the client has an independent active pane +.It ignore-size +the client does not affect the size of other clients +.It no-detach-on-destroy +do not detach the client when the session it is attached to is destroyed if +there are any other sessions +.It no-output +the client does not receive pane output in control mode +.It pause-after=seconds +output is paused once the pane is +.Ar seconds +behind in control mode +.It read-only +the client is read-only +.It wait-exit +wait for an empty line input before exiting in control mode +.El +.Pp +A leading +.Ql \&! +turns a flag off if the client is already attached. +.Fl r +is an alias for +.Fl f +.Ar read-only,ignore-size . +When a client is read-only, only keys bound to the +.Ic detach-client +or +.Ic switch-client +commands have any effect. +A client with the +.Ar active-pane +flag allows the active pane to be selected independently of the window's active +pane used by clients without the flag. +This only affects the cursor position and commands issued from the client; +other features such as hooks and styles continue to use the window's active +pane. +.Pp +If no server is started, +.Ic attach-session +will attempt to start it; this will fail unless sessions are created in the +configuration file. +.Pp +The +.Ar target-session +rules for +.Ic attach-session +are slightly adjusted: if +.Nm +needs to select the most recently used session, it will prefer the most +recently used +.Em unattached +session. +.Pp +.Fl c +will set the session working directory (used for new windows) to +.Ar working-directory . +.Pp +If +.Fl E +is used, the +.Ic update-environment +option will not be applied. +.Tg detach +.It Xo Ic detach-client +.Op Fl aP +.Op Fl E Ar shell-command +.Op Fl s Ar target-session +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic detach +Detach the current client if bound to a key, the client specified with +.Fl t , +or all clients currently attached to the session specified by +.Fl s . +The +.Fl a +option kills all but the client given with +.Fl t . +If +.Fl P +is given, send +.Dv SIGHUP +to the parent process of the client, typically causing it +to exit. +With +.Fl E , +run +.Ar shell-command +to replace the client. +.Tg has +.It Ic has-session Op Fl t Ar target-session +.D1 Pq alias: Ic has +Report an error and exit with 1 if the specified session does not exist. +If it does exist, exit with 0. +.It Ic kill-server +Kill the +.Nm +server and clients and destroy all sessions. +.It Xo Ic kill-session +.Op Fl aC +.Op Fl t Ar target-session +.Xc +Destroy the given session, closing any windows linked to it and no other +sessions, and detaching all clients attached to it. +If +.Fl a +is given, all sessions but the specified one is killed. +The +.Fl C +flag clears alerts (bell, activity, or silence) in all windows linked to the +session. +.Tg lsc +.It Xo Ic list-clients +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic lsc +List all clients attached to the server. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only clients for which the filter is true are shown. +See the +.Sx FORMATS +section. +If +.Ar target-session +is specified, list only clients connected to that session. +.Tg lscm +.It Xo Ic list-commands +.Op Fl F Ar format +.Op Ar command +.Xc +.D1 Pq alias: Ic lscm +List the syntax of +.Ar command +or - if omitted - of all commands supported by +.Nm . +.Tg ls +.It Xo Ic list-sessions +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc +.D1 Pq alias: Ic ls +List all sessions managed by the server. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only sessions for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg lockc +.It Ic lock-client Op Fl t Ar target-client +.D1 Pq alias: Ic lockc +Lock +.Ar target-client , +see the +.Ic lock-server +command. +.Tg locks +.It Ic lock-session Op Fl t Ar target-session +.D1 Pq alias: Ic locks +Lock all clients attached to +.Ar target-session . +.Tg new +.It Xo Ic new-session +.Op Fl AdDEPX +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl f Ar flags +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl s Ar session-name +.Op Fl t Ar group-name +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic new +Create a new session with name +.Ar session-name . +.Pp +The new session is attached to the current terminal unless +.Fl d +is given. +.Ar window-name +and +.Ar shell-command +are the name of and shell command to execute in the initial window. +With +.Fl d , +the initial size comes from the global +.Ic default-size +option; +.Fl x +and +.Fl y +can be used to specify a different size. +.Ql - +uses the size of the current client if any. +If +.Fl x +or +.Fl y +is given, the +.Ic default-size +option is set for the session. +.Fl f +sets a comma-separated list of client flags (see +.Ic attach-session ) . +.Pp +If run from a terminal, any +.Xr termios 4 +special characters are saved and used for new windows in the new session. +.Pp +The +.Fl A +flag makes +.Ic new-session +behave like +.Ic attach-session +if +.Ar session-name +already exists; +if +.Fl A +is given, +.Fl D +behaves like +.Fl d +to +.Ic attach-session , +and +.Fl X +behaves like +.Fl x +to +.Ic attach-session . +.Pp +If +.Fl t +is given, it specifies a +.Ic session group . +Sessions in the same group share the same set of windows - new windows are +linked to all sessions in the group and any windows closed removed from all +sessions. +The current and previous window and any session options remain independent and +any session in a group may be killed without affecting the others. +The +.Ar group-name +argument may be: +.Bl -enum -width Ds +.It +the name of an existing group, in which case the new session is added to that +group; +.It +the name of an existing session - the new session is added to the same group +as that session, creating a new group if necessary; +.It +the name for a new group containing only the new session. +.El +.Pp +.Fl n +and +.Ar shell-command +are invalid if +.Fl t +is used. +.Pp +The +.Fl P +option prints information about the new session after it has been created. +By default, it uses the format +.Ql #{session_name}:\& +but a different format may be specified with +.Fl F . +.Pp +If +.Fl E +is used, the +.Ic update-environment +option will not be applied. +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created session; it may be +specified multiple times. +.Tg refresh +.It Xo Ic refresh-client +.Op Fl cDLRSU +.Op Fl A Ar pane:state +.Op Fl B Ar name:what:format +.Op Fl C Ar size +.Op Fl f Ar flags +.Op Fl l Op Ar target-pane +.Op Fl r Ar pane:report +.Op Fl t Ar target-client +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic refresh +Refresh the current client if bound to a key, or a single client if one is given +with +.Fl t . +If +.Fl S +is specified, only update the client's status line. +.Pp +The +.Fl U , +.Fl D , +.Fl L +.Fl R , +and +.Fl c +flags allow the visible portion of a window which is larger than the client +to be changed. +.Fl U +moves the visible part up by +.Ar adjustment +rows and +.Fl D +down, +.Fl L +left by +.Ar adjustment +columns and +.Fl R +right. +.Fl c +returns to tracking the cursor automatically. +If +.Ar adjustment +is omitted, 1 is used. +Note that the visible position is a property of the client not of the +window, changing the current window in the attached session will reset +it. +.Pp +.Fl C +sets the width and height of a control mode client or of a window for a +control mode client, +.Ar size +must be one of +.Ql widthxheight +or +.Ql window ID:widthxheight , +for example +.Ql 80x24 +or +.Ql @0:80x24 . +.Fl A +allows a control mode client to trigger actions on a pane. +The argument is a pane ID (with leading +.Ql % ) , +a colon, then one of +.Ql on , +.Ql off , +.Ql continue +or +.Ql pause . +If +.Ql off , +.Nm +will not send output from the pane to the client and if all clients have turned +the pane off, will stop reading from the pane. +If +.Ql continue , +.Nm +will return to sending output to the pane if it was paused (manually or with the +.Ar pause-after +flag). +If +.Ql pause , +.Nm +will pause the pane. +.Fl A +may be given multiple times for different panes. +.Pp +.Fl B +sets a subscription to a format for a control mode client. +The argument is split into three items by colons: +.Ar name +is a name for the subscription; +.Ar what +is a type of item to subscribe to; +.Ar format +is the format. +After a subscription is added, changes to the format are reported with the +.Ic %subscription-changed +notification, at most once a second. +If only the name is given, the subscription is removed. +.Ar what +may be empty to check the format only for the attached session, or one of: +a pane ID such as +.Ql %0 ; +.Ql %* +for all panes in the attached session; +a window ID such as +.Ql @0 ; +or +.Ql @* +for all windows in the attached session. +.Pp +.Fl f +sets a comma-separated list of client flags, see +.Ic attach-session . +.Fl r +allows a control mode client to provide information about a pane via a report +(such as the response to OSC 10). +The argument is a pane ID (with a leading +.Ql % ) , +a colon, then a report escape sequence. +.Pp +.Fl l +requests the clipboard from the client using the +.Xr xterm 1 +escape sequence. +If +.Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. +.Pp +.Fl L , +.Fl R , +.Fl U +and +.Fl D +move the visible portion of the window left, right, up or down +by +.Ar adjustment , +if the window is larger than the client. +.Fl c +resets so that the position follows the cursor. +See the +.Ic window-size +option. +.Tg rename +.It Xo Ic rename-session +.Op Fl t Ar target-session +.Ar new-name +.Xc +.D1 Pq alias: Ic rename +Rename the session to +.Ar new-name . +.It Xo Ic server-access +.Op Fl adlrw +.Op Ar user +.Xc +Change the access or read/write permission of +.Ar user . +The user running the +.Nm +server (its owner) and the root user cannot be changed and are always +permitted access. +.Pp +.Fl a +and +.Fl d +are used to give or revoke access for the specified user. +If the user is already attached, the +.Fl d +flag causes their clients to be detached. +.Pp +.Fl r +and +.Fl w +change the permissions for +.Ar user : +.Fl r +makes their clients read-only and +.Fl w +writable. +.Fl l +lists current access permissions. +.Pp +By default, the access list is empty and +.Nm +creates sockets with file system permissions preventing access by any user +other than the owner (and root). +These permissions must be changed manually. +Great care should be taken not to allow access to untrusted users even +read-only. +.Tg showmsgs +.It Xo Ic show-messages +.Op Fl JT +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic showmsgs +Show server messages or information. +Messages are stored, up to a maximum of the limit set by the +.Ar message-limit +server option. +.Fl J +and +.Fl T +show debugging information about jobs and terminals. +.Tg source +.It Xo Ic source-file +.Op Fl Fnqv +.Op Fl t Ar target-pane +.Ar path ... +.Xc +.D1 Pq alias: Ic source +Execute commands from one or more files specified by +.Ar path +(which may be +.Xr glob 7 +patterns). +If +.Fl F +is present, then +.Ar path +is expanded as a format. +If +.Fl q +is given, no error will be returned if +.Ar path +does not exist. +With +.Fl n , +the file is parsed but no commands are executed. +.Fl v +shows the parsed commands and line numbers if possible. +.Tg start +.It Ic start-server +.D1 Pq alias: Ic start +Start the +.Nm +server, if not already running, without creating any sessions. +.Pp +Note that as by default the +.Nm +server will exit with no sessions, this is only useful if a session is created +in +.Pa \[ti]/.tmux.conf , +.Ic exit-empty +is turned off, or another command is run as part of the same command sequence. +For example: +.Bd -literal -offset indent +$ tmux start \\; show -g +.Ed +.Tg suspendc +.It Xo Ic suspend-client +.Op Fl t Ar target-client +.Xc +.D1 Pq alias: Ic suspendc +Suspend a client by sending +.Dv SIGTSTP +(tty stop). +.Tg switchc +.It Xo Ic switch-client +.Op Fl ElnprZ +.Op Fl c Ar target-client +.Op Fl t Ar target-session +.Op Fl T Ar key-table +.Xc +.D1 Pq alias: Ic switchc +Switch the current session for client +.Ar target-client +to +.Ar target-session . +As a special case, +.Fl t +may refer to a pane (a target that contains +.Ql \&: , +.Ql \&. +or +.Ql % ) , +to change session, window and pane. +In that case, +.Fl Z +keeps the window zoomed if it was zoomed. +If +.Fl l , +.Fl n +or +.Fl p +is used, the client is moved to the last, next or previous session +respectively. +.Fl r +toggles the client +.Ic read-only +and +.Ic ignore-size +flags (see the +.Ic attach-session +command). +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Pp +.Fl T +sets the client's key table; the next key from the client will be interpreted +from +.Ar key-table . +This may be used to configure multiple prefix keys, or to bind commands to +sequences of keys. +For example, to make typing +.Ql abc +run the +.Ic list-keys +command: +.Bd -literal -offset indent +bind-key -Ttable2 c list-keys +bind-key -Ttable1 b switch-client -Ttable2 +bind-key -Troot a switch-client -Ttable1 +.Ed +.El +.Sh WINDOWS AND PANES +Each window displayed by +.Nm +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +Windows may be split horizontally (with the +.Fl h +flag) or vertically. +Panes may be resized with the +.Ic resize-pane +command (bound to +.Ql C-Up , +.Ql C-Down +.Ql C-Left +and +.Ql C-Right +by default), the current pane may be changed with the +.Ic select-pane +command and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing their position. +Panes are numbered beginning from zero in the order they are created. +.Pp +By default, a +.Nm +pane permits direct access to the terminal contained in the pane. +A pane may also be put into one of several modes: +.Bl -dash -offset indent +.It +Copy mode, which permits a section of a window or its +history to be copied to a +.Em paste buffer +for later insertion into another window. +This mode is entered with the +.Ic copy-mode +command, bound to +.Ql \&[ +by default. +Copied text can be pasted with the +.Ic paste-buffer +command, bound to +.Ql \&] . +.It +View mode, which is like copy mode but is entered when a command that produces +output, such as +.Ic list-keys , +is executed from a key binding. +.It +Choose mode, which allows an item to be chosen from a list. +This may be a client, a session or window or pane, or a buffer. +This mode is entered with the +.Ic choose-buffer , +.Ic choose-client +and +.Ic choose-tree +commands. +.El +.Pp +In copy mode an indicator is displayed in the top-right corner of the pane with +the current position and the number of lines in the history. +.Pp +Commands are sent to copy mode using the +.Fl X +flag to the +.Ic send-keys +command. +When a key is pressed, copy mode automatically uses one of two key tables, +depending on the +.Ic mode-keys +option: +.Ic copy-mode +for emacs, or +.Ic copy-mode-vi +for vi. +Key tables may be viewed with the +.Ic list-keys +command. +.Pp +The following commands are supported in copy mode: +.Bl -tag -width Ds +.It Xo +.Ic append-selection +.Xc +Append the selection to the top paste buffer. +.It Xo +.Ic append-selection-and-cancel +(vi: A) +.Xc +Append the selection to the top paste buffer and exit copy mode. +.It Xo +.Ic back-to-indentation +(vi: ^) +(emacs: M-m) +.Xc +Move the cursor back to the indentation. +.It Xo +.Ic begin-selection +(vi: Space) +(emacs: C-Space) +.Xc +Begin selection. +.It Xo +.Ic bottom-line +(vi: L) +.Xc +Move to the bottom line. +.It Xo +.Ic cancel +(vi: q) +(emacs: Escape) +.Xc +Exit copy mode. +.It Xo +.Ic clear-selection +(vi: Escape) +(emacs: C-g) +.Xc +Clear the current selection. +.It Xo +.Ic copy-end-of-line +.Op Fl CP +.Op Ar prefix +.Xc +Copy from the cursor position to the end of the line. +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-end-of-line-and-cancel +.Op Fl CP +.Op Ar prefix +.Xc +Copy from the cursor position and exit copy mode. +.It Xo +.Ic copy-pipe-end-of-line +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy from the cursor position to the end of the line and pipe the text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-end-of-line-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe-end-of-line +but also exit copy mode. +.It Xo +.Ic copy-line +.Op Fl CP +.Op Ar prefix +.Xc +Copy the entire line. +.It Xo +.Ic copy-line-and-cancel +.Op Fl CP +.Op Ar prefix +.Xc +Copy the entire line and exit copy mode. +.It Xo +.Ic copy-pipe-line +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy the entire line and pipe the text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-line-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe-line +but also exit copy mode. +.It Xo +.Ic copy-pipe +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Copy the selection, clear it and pipe its text to +.Ar command . +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-pipe-no-clear +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe +but do not clear the selection. +.It Xo +.Ic copy-pipe-and-cancel +.Op Fl CP +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic copy-pipe +but also exit copy mode. +.It Xo +.Ic copy-selection +.Op Fl CP +.Op Ar prefix +.Xc +Copies the current selection. +.It Xo +.Ic copy-selection-no-clear +.Op Fl CP +.Op Ar prefix +.Xc +Same as +.Ic copy-selection +but do not clear the selection. +.It Xo +.Ic copy-selection-and-cancel +.Op Fl CP +.Op Ar prefix +(vi: Enter) +(emacs: M-w) +.Xc +Copy the current selection and exit copy mode. +.It Xo +.Ic cursor-down +(vi: j) +(emacs: Down) +.Xc +Move the cursor down. +.It Xo +.Ic cursor-down-and-cancel +.Xc +Same as +.Ic cursor-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic cursor-left +(vi: h) +(emacs: Left) +.Xc +Move the cursor left. +.It Xo +.Ic cursor-right +(vi: l) +(emacs: Right) +.Xc +Move the cursor right. +.It Xo +.Ic cursor-up +(vi: k) +(emacs: Up) +.Xc +Move the cursor up. +.It Xo +.Ic end-of-line +(vi: $) +(emacs: C-e) +.Xc +Move the cursor to the end of the line. +.It Xo +.Ic goto-line +.Ar line +(vi: :) +(emacs: g) +.Xc +Move the cursor to a specific line. +.It Xo +.Ic halfpage-down +(vi: C-d) +(emacs: M-Down) +.Xc +Scroll down by half a page. +.It Xo +.Ic halfpage-down-and-cancel +.Xc +Same as +.Ic halfpage-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic halfpage-up +(vi: C-u) +(emacs: M-Up) +.Xc +Scroll up by half a page. +.It Xo +.Ic history-bottom +(vi: G) +(emacs: M->) +.Xc +Scroll to the bottom of the history. +.It Xo +.Ic history-top +(vi: g) +(emacs: M-<) +.Xc +Scroll to the top of the history. +.It Xo +.Ic jump-again +(vi: ;) +(emacs: ;) +.Xc +Repeat the last jump. +.It Xo +.Ic jump-backward +.Ar to +(vi: F) +(emacs: F) +.Xc +Jump backwards to the specified text. +.It Xo +.Ic jump-forward +.Ar to +(vi: f) +(emacs: f) +.Xc +Jump forward to the specified text. +.It Xo +.Ic jump-reverse +(vi: ,) +(emacs: ,) +.Xc +Repeat the last jump in the reverse direction (forward becomes backward and +backward becomes forward). +.It Xo +.Ic jump-to-backward +.Ar to +(vi: T) +.Xc +Jump backwards, but one character less, placing the cursor on the character +after the target. +.It Xo +.Ic jump-to-forward +.Ar to +(vi: t) +.Xc +Jump forward, but one character less, placing the cursor on the character +before the target. +.It Xo +.Ic jump-to-mark +(vi: M-x) +(emacs: M-x) +.Xc +Jump to the last mark. +.It Xo +.Ic middle-line +(vi: M) +(emacs: M-r) +.Xc +Move to the middle line. +.It Xo +.Ic next-matching-bracket +(vi: %) +(emacs: M-C-f) +.Xc +Move to the next matching bracket. +.It Xo +.Ic next-paragraph +(vi: }) +(emacs: M-}) +.Xc +Move to the next paragraph. +.It Xo +.Ic next-prompt +.Op Fl o +.Xc +Move to the next prompt. +.It Xo +.Ic next-word +(vi: w) +.Xc +Move to the next word. +.It Xo +.Ic next-word-end +(vi: e) +(emacs: M-f) +.Xc +Move to the end of the next word. +.It Xo +.Ic next-space +(vi: W) +.Xc +Same as +.Ic next-word +but use a space alone as the word separator. +.It Xo +.Ic next-space-end +(vi: E) +.Xc +Same as +.Ic next-word-end +but use a space alone as the word separator. +.It Xo +.Ic other-end +(vi: o) +.Xc +Switch at which end of the selection the cursor sits. +.It Xo +.Ic page-down +(vi: C-f) +(emacs: PageDown) +.Xc +Scroll down by one page. +.It Xo +.Ic page-down-and-cancel +.Xc +Same as +.Ic page-down +but also exit copy mode if reaching the bottom. +.It Xo +.Ic page-up +(vi: C-b) +(emacs: PageUp) +.Xc +Scroll up by one page. +.It Xo +.Ic pipe +.Op Ar command +.Xc +Pipe the selected text to +.Ar command +and clear the selection. +.It Xo +.Ic pipe-no-clear +.Op Ar command +.Xc +Same as +.Ic pipe +but do not clear the selection. +.It Xo +.Ic pipe-and-cancel +.Op Ar command +.Op Ar prefix +.Xc +Same as +.Ic pipe +but also exit copy mode. +.It Xo +.Ic previous-matching-bracket +(emacs: M-C-b) +.Xc +Move to the previous matching bracket. +.It Xo +.Ic previous-paragraph +(vi: {) +(emacs: M-{) +.Xc +Move to the previous paragraph. +.It Xo +.Ic previous-prompt +.Op Fl o +.Xc +Move to the previous prompt. +.It Xo +.Ic previous-word +(vi: b) +(emacs: M-b) +.Xc +Move to the previous word. +.It Xo +.Ic previous-space +(vi: B) +.Xc +Same as +.Ic previous-word +but use a space alone as the word separator. +.It Xo +.Ic rectangle-on +.Xc +Turn on rectangle selection mode. +.It Xo +.Ic rectangle-off +.Xc +Turn off rectangle selection mode. +.It Xo +.Ic rectangle-toggle +(vi: v) +(emacs: R) +.Xc +Toggle rectangle selection mode. +.It Xo +.Ic refresh-from-pane +(vi: r) +(emacs: r) +.Xc +Refresh the content from the pane. +.It Xo +.Ic scroll-bottom +.Xc +Scroll up until the current line is at the bottom while keeping the cursor on +that line. +.It Xo +.Ic scroll-down +(vi: C-e) +(emacs: C-Down) +.Xc +Scroll down. +.It Xo +.Ic scroll-down-and-cancel +.Xc +Same as +.Ic scroll-down +but also exit copy mode if the cursor reaches the bottom. +.It Xo +.Ic scroll-middle +(vi: z) +.Xc +Scroll so that the current line becomes the middle one while keeping the +cursor on that line. +.It Xo +.Ic scroll-top +.Xc +Scroll down until the current line is at the top while keeping the cursor on +that line. +.It Xo +.Ic scroll-up +(vi: C-y) +(emacs: C-Up) +.Xc +Scroll up. +.It Xo +.Ic search-again +(vi: n) +(emacs: n) +.Xc +Repeat the last search. +.It Xo +.Ic search-backward +.Ar text +(vi: ?) +.Xc +Search backwards for the specified text. +.It Xo +.Ic search-backward-incremental +.Ar text +(emacs: C-r) +.Xc +Search backwards incrementally for the specified text. +Is expected to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.It Xo +.Ic search-backward-text +.Ar text +.Xc +Search backwards for the specified plain text. +.It Xo +.Ic search-forward +.Ar text +(vi: /) +.Xc +Search forward for the specified text. +.It Xo +.Ic search-forward-incremental +.Ar text +(emacs: C-s) +.Xc +Search forward incrementally for the specified text. +Is expected to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.It Xo +.Ic search-forward-text +.Ar text +.Xc +Search forward for the specified plain text. +.It Xo +.Ic search-reverse +(vi: N) +(emacs: N) +.Xc +Repeat the last search in the reverse direction (forward becomes backward and +backward becomes forward). +.It Xo +.Ic select-line +(vi: V) +.Xc +Select the current line. +.It Xo +.Ic select-word +.Xc +Select the current word. +.It Xo +.Ic set-mark +(vi: X) +(emacs: X) +.Xc +Mark the current line. +.It Xo +.Ic start-of-line +(vi: 0) +(emacs: C-a) +.Xc +Move the cursor to the start of the line. +.It Xo +.Ic stop-selection +.Xc +Stop selecting without clearing the current selection. +.It Xo +.Ic toggle-position +(vi: P) +(emacs: P) +.Xc +Toggle the visibility of the position indicator in the top right. +.It Xo +.Ic top-line +(vi: H) +(emacs: M-R) +.Xc +Move to the top line. +.El +.Pp +The search commands come in several varieties: +.Ql search-forward +and +.Ql search-backward +search for a regular expression; +the +.Ql -text +variants search for a plain text string rather than a regular expression; +.Ql -incremental +perform an incremental search and expect to be used with the +.Fl i +flag to the +.Ic command-prompt +command. +.Ql search-again +repeats the last search and +.Ql search-reverse +does the same but reverses the direction (forward becomes backward and backward +becomes forward). +.Pp +The default incremental search key bindings, +.Ql C-r +and +.Ql C-s , +are designed to emulate +.Xr emacs 1 . +When first pressed they allow a new search term to be entered; if pressed with +an empty search term they repeat the previously used search term. +.Pp +The +.Ql next-prompt +and +.Ql previous-prompt +move between shell prompts, but require the shell to emit an escape sequence +(\e033]133;A\e033\e\e) to tell +.Nm +where the prompts are located; if the shell does not do this, these commands +will do nothing. +The +.Fl o +flag jumps to the beginning of the command output instead of the shell prompt. +Finding the beginning of command output requires the shell to emit an escape +sequence (\e033]133;C\e033\e\e) to tell tmux where the output begins. +If the shell does not send these escape sequences, these commands do nothing. +.Pp +Copy commands may take an optional buffer prefix argument which is used +to generate the buffer name (the default is +.Ql buffer +so buffers are named +.Ql buffer0 , +.Ql buffer1 +and so on). +Pipe commands take a command argument which is the command to which the +selected text is piped. +.Ql copy-pipe +variants also copy the selection. +The +.Ql -and-cancel +variants of some commands exit copy mode after they have completed (for copy +commands) or when the cursor reaches the bottom (for scrolling commands). +.Ql -no-clear +variants do not clear the selection. +All the copy commands can take the +.Fl C +and +.Fl P +flags. +The +.Fl C +flag suppresses setting the terminal clipboard when copying, while the +.Fl P +flag suppresses adding a paste buffer with the text. +.Pp +The next and previous word keys skip over whitespace and treat consecutive +runs of either word separators or other letters as words. +Word separators can be customized with the +.Em word-separators +session option. +Next word moves to the start of the next word, next word end to the end of the +next word and previous word to the start of the previous word. +The three next and previous space keys work similarly but use a space alone as +the word separator. +Setting +.Em word-separators +to the empty string makes next/previous word equivalent to next/previous space. +.Pp +The jump commands enable quick movement within a line. +For instance, typing +.Ql f +followed by +.Ql / +will move the cursor to the next +.Ql / +character on the current line. +A +.Ql \&; +will then jump to the next occurrence. +.Pp +Commands in copy mode may be prefaced by an optional repeat count. +With vi key bindings, a prefix is entered using the number keys; with +emacs, the Alt (meta) key and a number begins prefix entry. +.Pp +The synopsis for the +.Ic copy-mode +command is: +.Bl -tag -width Ds +.It Xo Ic copy-mode +.Op Fl deHMqSu +.Op Fl s Ar src-pane +.Op Fl t Ar target-pane +.Xc +Enter copy mode. +.Pp +.Fl u +enters copy mode and scrolls one page up and +.Fl d +one page down. +.Fl H +hides the position indicator in the top right. +.Fl q +cancels copy mode and any other modes. +.Pp +.Fl M +begins a mouse drag (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Fl S +scrolls when bound to a mouse drag event; for example, +.Ic copy-mode -Se +is bound to +.Ar MouseDrag1ScrollbarSlider +by default. +.Pp +.Fl s +copies from +.Ar src-pane +instead of +.Ar target-pane . +.Pp +.Fl e +specifies that scrolling to the bottom of the history (to the visible screen) +should exit copy mode. +While in copy mode, pressing a key other than those used for scrolling will +disable this behaviour. +This is intended to allow fast scrolling through a pane's history, for +example with: +.Bd -literal -offset indent +bind PageUp copy-mode -eu +bind PageDown copy-mode -ed +.Ed +.El +.Pp +A number of preset arrangements of panes are available, these are called +layouts. +These may be selected with the +.Ic select-layout +command or cycled with +.Ic next-layout +(bound to +.Ql Space +by default); once a layout is chosen, panes within it may be moved and resized +as normal. +.Pp +The following layouts are supported: +.Bl -tag -width Ds +.It Ic even-horizontal +Panes are spread out evenly from left to right across the window. +.It Ic even-vertical +Panes are spread evenly from top to bottom. +.It Ic main-horizontal +A large (main) pane is shown at the top of the window and the remaining panes +are spread from left to right in the leftover space at the bottom. +Use the +.Em main-pane-height +window option to specify the height of the top pane. +.It Ic main-horizontal-mirrored +The same as +.Ic main-horizontal +but mirrored so the main pane is at the bottom of the window. +.It Ic main-vertical +A large (main) pane is shown on the left of the window and the remaining panes +are spread from top to bottom in the leftover space on the right. +Use the +.Em main-pane-width +window option to specify the width of the left pane. +.It Ic main-vertical-mirrored +The same as +.Ic main-vertical +but mirrored so the main pane is on the right of the window. +.It Ic tiled +Panes are spread out as evenly as possible over the window in both rows and +columns. +.El +.Pp +In addition, +.Ic select-layout +may be used to apply a previously used layout - the +.Ic list-windows +command displays the layout of each window in a form suitable for use with +.Ic select-layout . +For example: +.Bd -literal -offset indent +$ tmux list-windows +0: ksh [159x48] + layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmux select-layout \[aq]bb62,159x48,0,0{79x48,0,0,79x48,80,0}\[aq] +.Ed +.Pp +.Nm +automatically adjusts the size of the layout for the current window size. +Note that a layout cannot be applied to a window with more panes than that +from which the layout was originally defined. +.Pp +Commands related to windows and panes are as follows: +.Bl -tag -width Ds +.Tg breakp +.It Xo Ic break-pane +.Op Fl abdP +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl s Ar src-pane +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic breakp +Break +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . +With +.Fl a +or +.Fl b , +the window is moved to the next index after or before (existing windows are +moved if necessary). +If +.Fl d +is given, the new window does not become the current window. +The +.Fl P +option prints information about the new window after it has been created. +By default, it uses the format +.Ql #{session_name}:#{window_index}.#{pane_index} +but a different format may be specified with +.Fl F . +.Tg capturep +.It Xo Ic capture-pane +.Op Fl aepPqCJMN +.Op Fl b Ar buffer-name +.Op Fl E Ar end-line +.Op Fl S Ar start-line +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic capturep +Capture the contents of a pane. +If +.Fl p +is given, the output goes to stdout, otherwise to the buffer specified with +.Fl b +or a new buffer if omitted. +If +.Fl a +is given, the alternate screen is used, and the history is not accessible. +If no alternate screen exists, an error will be returned unless +.Fl q +is given. +Similarly, if the pane is in a mode, +.Fl M +uses the screen for the mode. +If +.Fl e +is given, the output includes escape sequences for text and background +attributes. +.Fl C +also escapes non-printable characters as octal \exxx. +.Fl T +ignores trailing positions that do not contain a character. +.Fl N +preserves trailing spaces at each line's end and +.Fl J +preserves trailing spaces and joins any wrapped lines; +.Fl J +implies +.Fl T . +.Fl P +captures only any output that the pane has received that is the beginning of an +as-yet incomplete escape sequence. +.Pp +.Fl S +and +.Fl E +specify the starting and ending line numbers, zero is the first line of the +visible pane and negative numbers are lines in the history. +.Ql - +to +.Fl S +is the start of the history and to +.Fl E +the end of the visible pane. +The default is to capture only the visible contents of the pane. +.It Xo +.Ic choose-client +.Op Fl NryZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into client mode, allowing a client to be selected interactively from +a list. +Each client is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in client mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected client" +.It Li "Up" Ta "Select previous client" +.It Li "Down" Ta "Select next client" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if client is tagged" +.It Li "T" Ta "Tag no clients" +.It Li "C-t" Ta "Tag all clients" +.It Li "d" Ta "Detach selected client" +.It Li "D" Ta "Detach tagged clients" +.It Li "x" Ta "Detach and HUP selected client" +.It Li "X" Ta "Detach and HUP tagged clients" +.It Li "z" Ta "Suspend selected client" +.It Li "Z" Ta "Suspend tagged clients" +.It Li "f" Ta "Enter a format to filter items" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a client is chosen, +.Ql %% +is replaced by the client name in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "detach-client -t \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql name , +.Ql size , +.Ql creation +(time), +or +.Ql activity +(time). +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview or if given twice with the larger preview. +This command works only if at least one client is attached. +.It Xo +.Ic choose-tree +.Op Fl GNrswyZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into tree mode, where a session, window or pane may be chosen +interactively from a tree. +Each session, window or pane is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the tree may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl s +starts with sessions collapsed and +.Fl w +with windows collapsed. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in tree mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected item" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "S-Up" Ta "Swap the current window with the previous one" +.It Li "S-Down" Ta "Swap the current window with the next one" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "x" Ta "Kill selected item" +.It Li "X" Ta "Kill tagged items" +.It Li "<" Ta "Scroll list of previews left" +.It Li ">" Ta "Scroll list of previews right" +.It Li "C-s" Ta "Search by name" +.It Li "m" Ta "Set the marked pane" +.It Li "M" Ta "Clear the marked pane" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "\&:" Ta "Run a command for each tagged item" +.It Li "f" Ta "Enter a format to filter items" +.It Li "H" Ta "Jump to the starting pane" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a session, window or pane is chosen, the first instance of +.Ql %% +and all instances of +.Ql %1 +are replaced by the target in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "switch-client -t \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql index , +.Ql name , +or +.Ql time +(activity). +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview or if given twice with the larger preview. +.Fl G +includes all sessions in any session groups in the tree rather than only the +first. +This command works only if at least one client is attached. +.It Xo +.Ic customize-mode +.Op Fl NZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into customize mode, where options and key bindings may be browsed +and modified from a list. +Option values in the list are shown for the active pane in the current window. +.Fl Z +zooms the pane. +The following keys may be used in customize mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Set pane, window, session or global option value" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "+" Ta "Expand selected item" +.It Li "-" Ta "Collapse selected item" +.It Li "M-+" Ta "Expand all items" +.It Li "M--" Ta "Collapse all items" +.It Li "s" Ta "Set option value or key attribute" +.It Li "S" Ta "Set global option value" +.It Li "w" Ta "Set window option value, if option is for pane and window" +.It Li "d" Ta "Set an option or key to the default" +.It Li "D" Ta "Set tagged options and tagged keys to the default" +.It Li "u" Ta "Unset an option (set to default value if global) or unbind a key" +.It Li "U" Ta "Unset tagged options and unbind tagged keys" +.It Li "C-s" Ta "Search by name" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if item is tagged" +.It Li "T" Ta "Tag no items" +.It Li "C-t" Ta "Tag all items" +.It Li "f" Ta "Enter a format to filter items" +.It Li "v" Ta "Toggle option information" +.It Li "q" Ta "Exit mode" +.El +.Pp +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the tree. +.Fl N +starts without the option information. +This command works only if at least one client is attached. +.It Xo +.Tg displayp +.Ic display-panes +.Op Fl bN +.Op Fl d Ar duration +.Op Fl t Ar target-client +.Op Ar template +.Xc +.D1 Pq alias: Ic displayp +Display a visible indicator of each pane shown by +.Ar target-client . +See the +.Ic display-panes-colour +and +.Ic display-panes-active-colour +session options. +The indicator is closed when a key is pressed (unless +.Fl N +is given) or +.Ar duration +milliseconds have passed. +If +.Fl d +is not given, +.Ic display-panes-time +is used. +A duration of zero means the indicator stays until a key is pressed. +While the indicator is on screen, a pane may be chosen with the +.Ql 0 +to +.Ql 9 +keys, which will cause +.Ar template +to be executed as a command with +.Ql %% +substituted by the pane ID. +The default +.Ar template +is "select-pane -t \[aq]%%\[aq]". +With +.Fl b , +other commands are not blocked from running until the indicator is closed. +.Tg findw +.It Xo Ic find-window +.Op Fl iCNrTZ +.Op Fl t Ar target-pane +.Ar match-string +.Xc +.D1 Pq alias: Ic findw +Search for a +.Xr glob 7 +pattern or, with +.Fl r , +regular expression +.Ar match-string +in window names, titles, and visible content (but not history). +The flags control matching behavior: +.Fl C +matches only visible window contents, +.Fl N +matches only the window name and +.Fl T +matches only the window title. +.Fl i +makes the search ignore case. +The default is +.Fl CNT . +.Fl Z +zooms the pane. +.Pp +This command works only if at least one client is attached. +.Tg joinp +.It Xo Ic join-pane +.Op Fl bdfhv +.Op Fl l Ar size +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic joinp +Like +.Ic split-window , +but instead of splitting +.Ar dst-pane +and creating a new pane, split it and move +.Ar src-pane +into the space. +This can be used to reverse +.Ic break-pane . +The +.Fl b +option causes +.Ar src-pane +to be joined to left of or above +.Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. +.Tg killp +.It Xo Ic kill-pane +.Op Fl a +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic killp +Destroy the given pane. +If no panes remain in the containing window, it is also destroyed. +The +.Fl a +option kills all but the pane given with +.Fl t . +.Tg killw +.It Xo Ic kill-window +.Op Fl a +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic killw +Kill the current window or the window at +.Ar target-window , +removing it from any sessions to which it is linked. +The +.Fl a +option kills all but the window given with +.Fl t . +.Tg lastp +.It Xo Ic last-pane +.Op Fl deZ +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic lastp +Select the last (previously selected) pane. +.Fl Z +keeps the window zoomed if it was zoomed. +.Fl e +enables or +.Fl d +disables input to the pane. +.Tg last +.It Ic last-window Op Fl t Ar target-session +.D1 Pq alias: Ic last +Select the last (previously selected) window. +If no +.Ar target-session +is specified, select the last window of the current session. +.Tg link +.It Xo Ic link-window +.Op Fl abdk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic linkw +Link the window at +.Ar src-window +to the specified +.Ar dst-window . +If +.Ar dst-window +is specified and no such window exists, the +.Ar src-window +is linked there. +With +.Fl a +or +.Fl b +the window is moved to the next index after or before +.Ar dst-window +(existing windows are moved if necessary). +If +.Fl k +is given and +.Ar dst-window +exists, it is killed, otherwise an error is generated. +If +.Fl d +is given, the newly linked window is not selected. +.Tg lsp +.It Xo Ic list-panes +.Op Fl as +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target +.Xc +.D1 Pq alias: Ic lsp +If +.Fl a +is given, +.Ar target +is ignored and all panes on the server are listed. +If +.Fl s +is given, +.Ar target +is a session (or the current session). +If neither is given, +.Ar target +is a window (or the current window). +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only panes for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg lsw +.It Xo Ic list-windows +.Op Fl a +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic lsw +If +.Fl a +is given, list all windows on the server. +Otherwise, list windows in the current session or in +.Ar target-session . +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only windows for which the filter is true are shown. +See the +.Sx FORMATS +section. +.Tg movep +.It Xo Ic move-pane +.Op Fl bdfhv +.Op Fl l Ar size +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic movep +Does the same as +.Ic join-pane . +.Tg movew +.It Xo Ic move-window +.Op Fl abrdk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic movew +This is similar to +.Ic link-window , +except the window at +.Ar src-window +is moved to +.Ar dst-window . +With +.Fl r , +all windows in the session are renumbered in sequential order, respecting +the +.Ic base-index +option. +.Tg neww +.It Xo Ic new-window +.Op Fl abdkPS +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl F Ar format +.Op Fl n Ar window-name +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic neww +Create a new window. +With +.Fl a +or +.Fl b , +the new window is inserted at the next index after or before the specified +.Ar target-window , +moving windows up if necessary; +otherwise +.Ar target-window +is the new window location. +.Pp +If +.Fl d +is given, the session does not make the new window the current window. +.Ar target-window +represents the window to be created; if the target already exists an error is +shown, unless the +.Fl k +flag is used, in which case it is destroyed. +If +.Fl S +is given and a window named +.Ar window-name +already exists, it is selected (unless +.Fl d +is also given in which case the command does nothing). +.Pp +.Ar shell-command +is the command to execute. +If +.Ar shell-command +is not specified, the value of the +.Ic default-command +option is used. +.Fl c +specifies the working directory in which the new window is created. +.Pp +When the shell command completes, the window closes. +See the +.Ic remain-on-exit +option to change this behaviour. +.Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the newly created window; it may be +specified multiple times. +.Pp +The +.Ev TERM +environment variable must be set to +.Ql screen +or +.Ql tmux +for all programs running +.Em inside +.Nm . +New windows will automatically have +.Ql TERM=screen +added to their environment, but care must be taken not to reset this in shell +start-up files or by the +.Fl e +option. +.Pp +The +.Fl P +option prints information about the new window after it has been created. +By default, it uses the format +.Ql #{session_name}:#{window_index} +but a different format may be specified with +.Fl F . +.Tg nextl +.It Ic next-layout Op Fl t Ar target-window +.D1 Pq alias: Ic nextl +Move a window to the next layout and rearrange the panes to fit. +.Tg next +.It Xo Ic next-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic next +Move to the next window in the session. +If +.Fl a +is used, move to the next window with an alert. +.Tg pipep +.It Xo Ic pipe-pane +.Op Fl IOo +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic pipep +Pipe output sent by the program in +.Ar target-pane +to a shell command or vice versa. +A pane may only be connected to one command at a time, any existing pipe is +closed before +.Ar shell-command +is executed. +The +.Ar shell-command +string may contain the special character sequences supported by the +.Ic status-left +option. +If no +.Ar shell-command +is given, the current pipe (if any) is closed. +.Pp +.Fl I +and +.Fl O +specify which of the +.Ar shell-command +output streams are connected to the pane: +with +.Fl I +stdout is connected (so anything +.Ar shell-command +prints is written to the pane as if it were typed); +with +.Fl O +stdin is connected (so any output in the pane is piped to +.Ar shell-command ) . +Both may be used together and if neither are specified, +.Fl O +is used. +.Pp +The +.Fl o +option only opens a new pipe if no previous pipe exists, allowing a pipe to +be toggled with a single key, for example: +.Bd -literal -offset indent +bind-key C-p pipe-pane -o \[aq]cat >>\[ti]/output.#I-#P\[aq] +.Ed +.Tg prevl +.It Xo Ic previous-layout +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic prevl +Move to the previous layout in the session. +.Tg prev +.It Xo Ic previous-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 Pq alias: Ic prev +Move to the previous window in the session. +With +.Fl a , +move to the previous window with an alert. +.Tg renamew +.It Xo Ic rename-window +.Op Fl t Ar target-window +.Ar new-name +.Xc +.D1 Pq alias: Ic renamew +Rename the current window, or the window at +.Ar target-window +if specified, to +.Ar new-name . +.Tg resizep +.It Xo Ic resize-pane +.Op Fl DLMRTUZ +.Op Fl t Ar target-pane +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic resizep +Resize a pane, up, down, left or right by +.Ar adjustment +with +.Fl U , +.Fl D , +.Fl L +or +.Fl R , +or +to an absolute size +with +.Fl x +or +.Fl y . +The +.Ar adjustment +is given in lines or columns (the default is 1); +.Fl x +and +.Fl y +may be a given as a number of lines or columns or followed by +.Ql % +for a percentage of the window size (for example +.Ql -x 10% ) . +With +.Fl Z , +the active pane is toggled between zoomed (occupying the whole of the window) +and unzoomed (its normal position in the layout). +.Pp +.Fl M +begins mouse resizing (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Pp +.Fl T +trims all lines below the current cursor position and moves lines out of the +history to replace them. +.Tg resizew +.It Xo Ic resize-window +.Op Fl aADLRU +.Op Fl t Ar target-window +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar adjustment +.Xc +.D1 Pq alias: Ic resizew +Resize a window, up, down, left or right by +.Ar adjustment +with +.Fl U , +.Fl D , +.Fl L +or +.Fl R , +or +to an absolute size +with +.Fl x +or +.Fl y . +The +.Ar adjustment +is given in lines or cells (the default is 1). +.Fl A +sets the size of the largest session containing the window; +.Fl a +the size of the smallest. +This command will automatically set +.Ic window-size +to manual in the window options. +.Tg respawnp +.It Xo Ic respawn-pane +.Op Fl k +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic respawnp +Reactivate a pane in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the pane was created or last respawned is +executed. +The pane must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.Fl c +specifies a new working directory for the pane. +The +.Fl e +option has the same meaning as for the +.Ic new-window +command. +.Tg respawnw +.It Xo Ic respawn-window +.Op Fl k +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic respawnw +Reactivate a window in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the window was created or last respawned is +executed. +The window must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.Fl c +specifies a new working directory for the window. +The +.Fl e +option has the same meaning as for the +.Ic new-window +command. +.Tg rotatew +.It Xo Ic rotate-window +.Op Fl DUZ +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic rotatew +Rotate the positions of the panes within a window, either upward (numerically +lower) with +.Fl U +or downward (numerically higher). +.Fl Z +keeps the window zoomed if it was zoomed. +.Tg selectl +.It Xo Ic select-layout +.Op Fl Enop +.Op Fl t Ar target-pane +.Op Ar layout-name +.Xc +.D1 Pq alias: Ic selectl +Choose a specific layout for a window. +If +.Ar layout-name +is not given, the last preset layout used (if any) is reapplied. +.Fl n +and +.Fl p +are equivalent to the +.Ic next-layout +and +.Ic previous-layout +commands. +.Fl o +applies the last set layout if possible (undoes the most recent layout change). +.Fl E +spreads the current pane and any panes next to it out evenly. +.Tg selectp +.It Xo Ic select-pane +.Op Fl DdeLlMmRUZ +.Op Fl T Ar title +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic selectp +Make pane +.Ar target-pane +the active pane in its window. +If one of +.Fl D , +.Fl L , +.Fl R , +or +.Fl U +is used, respectively the pane below, to the left, to the right, or above the +target pane is used. +.Fl Z +keeps the window zoomed if it was zoomed. +.Fl l +is the same as using the +.Ic last-pane +command. +.Fl e +enables or +.Fl d +disables input to the pane. +.Fl T +sets the pane title. +.Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic move-pane , +.Ic swap-pane +and +.Ic swap-window . +.Tg selectw +.It Xo Ic select-window +.Op Fl lnpT +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic selectw +Select the window at +.Ar target-window . +.Fl l , +.Fl n +and +.Fl p +are equivalent to the +.Ic last-window , +.Ic next-window +and +.Ic previous-window +commands. +If +.Fl T +is given and the selected window is already the current window, +the command behaves like +.Ic last-window . +.Tg splitw +.It Xo Ic split-window +.Op Fl bdfhIvPZ +.Op Fl c Ar start-directory +.Op Fl e Ar environment +.Op Fl l Ar size +.Op Fl t Ar target-pane +.Op Ar shell-command +.Op Fl F Ar format +.Xc +.D1 Pq alias: Ic splitw +Create a new pane by splitting +.Ar target-pane : +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target-pane . +The +.Fl f +option creates a new pane spanning the full window height (with +.Fl h ) +or full window width (with +.Fl v ) , +instead of splitting the active pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +.Pp +An empty +.Ar shell-command +(\[aq]\[aq]) will create a pane with no command running in it. +Output can be sent to such a pane with the +.Ic display-message +command. +The +.Fl I +flag (if +.Ar shell-command +is not specified or empty) +will create an empty pane and forward any output from stdin to it. +For example: +.Bd -literal -offset indent +$ make 2>&1|tmux splitw -dI & +.Ed +.Pp +All other options have the same meaning as for the +.Ic new-window +command. +.Tg swapp +.It Xo Ic swap-pane +.Op Fl dDUZ +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 Pq alias: Ic swapp +Swap two panes. +If +.Fl U +is used and no source pane is specified with +.Fl s , +.Ar dst-pane +is swapped with the previous pane (before it numerically); +.Fl D +swaps with the next pane (after it numerically). +.Fl d +instructs +.Nm +not to change the active pane and +.Fl Z +keeps the window zoomed if it was zoomed. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. +.Tg swapw +.It Xo Ic swap-window +.Op Fl d +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 Pq alias: Ic swapw +This is similar to +.Ic link-window , +except the source and destination windows are swapped. +It is an error if no window exists at +.Ar src-window . +If +.Fl d +is given, the new window does not become the current window. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. +.Tg unlinkw +.It Xo Ic unlink-window +.Op Fl k +.Op Fl t Ar target-window +.Xc +.D1 Pq alias: Ic unlinkw +Unlink +.Ar target-window . +Unless +.Fl k +is given, a window may be unlinked only if it is linked to multiple sessions - +windows may not be linked to no sessions; +if +.Fl k +is specified and the window is linked to only one session, it is unlinked and +destroyed. +.El +.Sh KEY BINDINGS +.Nm +allows a command to be bound to most keys, with or without a prefix key. +When specifying keys, most represent themselves (for example +.Ql A +to +.Ql Z ) . +Ctrl keys may be prefixed with +.Ql C- +or +.Ql ^ , +Shift keys with +.Ql S- +and Alt (meta) with +.Ql M- . +In addition, the following special key names are accepted: +.Em Up , +.Em Down , +.Em Left , +.Em Right , +.Em BSpace , +.Em BTab , +.Em DC +(Delete), +.Em End , +.Em Enter , +.Em Escape , +.Em F1 +to +.Em F12 , +.Em Home , +.Em IC +(Insert), +.Em NPage/PageDown/PgDn , +.Em PPage/PageUp/PgUp , +.Em Space , +and +.Em Tab . +Note that to bind the +.Ql \&" +or +.Ql \[aq] +keys, quotation marks are necessary, for example: +.Bd -literal -offset indent +bind-key \[aq]"\[aq] split-window +bind-key "\[aq]" new-window +.Ed +.Pp +A command bound to the +.Em Any +key will execute for all keys which do not have a more specific binding. +.Pp +Commands related to key bindings are as follows: +.Bl -tag -width Ds +.Tg bind +.It Xo Ic bind-key +.Op Fl nr +.Op Fl N Ar note +.Op Fl T Ar key-table +.Ar key command Op Ar argument ... +.Xc +.D1 Pq alias: Ic bind +Bind key +.Ar key +to +.Ar command . +Keys are bound in a key table. +By default (without -T), the key is bound in +the +.Em prefix +key table. +This table is used for keys pressed after the prefix key (for example, +by default +.Ql c +is bound to +.Ic new-window +in the +.Em prefix +table, so +.Ql C-b c +creates a new window). +The +.Em root +table is used for keys pressed without the prefix key: binding +.Ql c +to +.Ic new-window +in the +.Em root +table (not recommended) means a plain +.Ql c +will create a new window. +.Fl n +is an alias +for +.Fl T Ar root . +Keys may also be bound in custom key tables and the +.Ic switch-client +.Fl T +command used to switch to them from a key binding. +The +.Fl r +flag indicates this key may repeat, see the +.Ic initial-repeat-time +and +.Ic repeat-time +options. +.Fl N +attaches a note to the key (shown with +.Ic list-keys +.Fl N ) . +.Pp +To view the default bindings and possible commands, see the +.Ic list-keys +command. +.Tg lsk +.It Xo Ic list-keys +.Op Fl 1aN +.Op Fl P Ar prefix-string Fl T Ar key-table +.Op Ar key +.Xc +.D1 Pq alias: Ic lsk +List key bindings. +There are two forms: the default lists keys as +.Ic bind-key +commands; +.Fl N +lists only keys with attached notes and shows only the key and note for each +key. +.Pp +With the default form, all key tables are listed by default. +.Fl T +lists only keys in +.Ar key-table . +.Pp +With the +.Fl N +form, only keys in the +.Em root +and +.Em prefix +key tables are listed by default; +.Fl T +also lists only keys in +.Ar key-table . +.Fl P +specifies a prefix to print before each key and +.Fl 1 +lists only the first matching key. +.Fl a +lists the command for keys that do not have a note rather than skipping them. +.Tg send +.It Xo Ic send-keys +.Op Fl FHKlMRX +.Op Fl c Ar target-client +.Op Fl N Ar repeat-count +.Op Fl t Ar target-pane +.Ar key ... +.Xc +.D1 Pq alias: Ic send +Send a key or keys to a window or client. +Each argument +.Ar key +is the name of the key (such as +.Ql C-a +or +.Ql NPage ) +to send; if the string is not recognised as a key, it is sent as a series of +characters. +If +.Fl K +is given, keys are sent to +.Ar target-client , +so they are looked up in the client's key table, rather than to +.Ar target-pane . +All arguments are sent sequentially from first to last. +If no keys are given and the command is bound to a key, then that key is used. +.Pp +The +.Fl l +flag disables key name lookup and processes the keys as literal UTF-8 +characters. +The +.Fl H +flag expects each key to be a hexadecimal number for an ASCII character. +.Pp +The +.Fl R +flag causes the terminal state to be reset. +.Pp +.Fl M +passes through a mouse event (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Pp +.Fl X +is used to send a command into copy mode - see +the +.Sx WINDOWS AND PANES +section. +.Fl N +specifies a repeat count and +.Fl F +expands formats in arguments where appropriate. +.It Xo Ic send-prefix +.Op Fl 2 +.Op Fl t Ar target-pane +.Xc +Send the prefix key, or with +.Fl 2 +the secondary prefix key, to a window as if it was pressed. +.Tg unbind +.It Xo Ic unbind-key +.Op Fl anq +.Op Fl T Ar key-table +.Ar key +.Xc +.D1 Pq alias: Ic unbind +Unbind the command bound to +.Ar key . +.Fl n +and +.Fl T +are the same as for +.Ic bind-key . +If +.Fl a +is present, all key bindings are removed. +The +.Fl q +option prevents errors being returned. +.El +.Sh OPTIONS +The appearance and behaviour of +.Nm +may be modified by changing the value of various options. +There are four types of option: +.Em server options , +.Em session options , +.Em window options , +and +.Em pane options . +.Pp +The +.Nm +server has a set of global server options which do not apply to any particular +window or session or pane. +These are altered with the +.Ic set-option +.Fl s +command, or displayed with the +.Ic show-options +.Fl s +command. +.Pp +In addition, each individual session may have a set of session options, and +there is a separate set of global session options. +Sessions which do not have a particular option configured inherit the value +from the global session options. +Session options are set or unset with the +.Ic set-option +command and may be listed with the +.Ic show-options +command. +The available server and session options are listed under the +.Ic set-option +command. +.Pp +Similarly, a set of window options is attached to each window and a set of pane +options to each pane. +Pane options inherit from window options. +This means any pane option may be set as a window option to apply the option to +all panes in the window without the option set, for example these commands will +set the background colour to red for all panes except pane 0: +.Bd -literal -offset indent +set -w window-style bg=red +set -pt:.0 window-style bg=blue +.Ed +.Pp +There is also a set of global window options from which any unset window or +pane options are inherited. +Window and pane options are altered with +.Ic set-option +.Fl w +and +.Fl p +commands and displayed with +.Ic show-option +.Fl w +and +.Fl p . +.Pp +.Nm +also supports user options which are prefixed with a +.Ql \&@ . +User options may have any name, so long as they are prefixed with +.Ql \&@ , +and be set to any string. +For example: +.Bd -literal -offset indent +$ tmux set -wq @foo "abc123" +$ tmux show -wv @foo +abc123 +.Ed +.Pp +Commands which set options are as follows: +.Bl -tag -width Ds +.Tg set +.It Xo Ic set-option +.Op Fl aFgopqsuUw +.Op Fl t Ar target-pane +.Ar option Ar value +.Xc +.D1 Pq alias: Ic set +Set a pane option with +.Fl p , +a window option with +.Fl w , +a server option with +.Fl s , +otherwise a session option. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +If +.Fl g +is given, the global session or window option is set. +.Pp +.Fl F +expands formats in the option value. +The +.Fl u +flag unsets an option, so a session inherits the option from the global +options (or with +.Fl g , +restores a global option to the default). +.Fl U +unsets an option (like +.Fl u ) +but if the option is a pane option also unsets the option on any panes in the +window. +.Ar value +depends on the option and may be a number, a string, or a flag (on, off, or +omitted to toggle). +.Pp +The +.Fl o +flag prevents setting an option that is already set and the +.Fl q +flag suppresses errors about unknown or ambiguous options. +.Pp +With +.Fl a , +and if the option expects a string or a style, +.Ar value +is appended to the existing setting. +For example: +.Bd -literal -offset indent +set -g status-left "foo" +set -ag status-left "bar" +.Ed +.Pp +Will result in +.Ql foobar . +And: +.Bd -literal -offset indent +set -g status-style "bg=red" +set -ag status-style "fg=blue" +.Ed +.Pp +Will result in a red background +.Em and +blue foreground. +Without +.Fl a , +the result would be the default background and a blue foreground. +.Tg show +.It Xo Ic show-options +.Op Fl AgHpqsvw +.Op Fl t Ar target-pane +.Op Ar option +.Xc +.D1 Pq alias: Ic show +Show the pane options (or a single option if +.Ar option +is provided) with +.Fl p , +the window options with +.Fl w , +the server options with +.Fl s , +otherwise the session options. +If the option is not a user option, +.Fl w +or +.Fl s +may be unnecessary - +.Nm +will infer the type from the option name, assuming +.Fl w +for pane options. +Global session or window options are listed if +.Fl g +is used. +.Fl v +shows only the option value, not the name. +If +.Fl q +is set, no error will be returned if +.Ar option +is unset. +.Fl H +includes hooks (omitted by default). +.Fl A +includes options inherited from a parent set of options, such options are +marked with an asterisk. +.El +.Pp +Available server options are: +.Bl -tag -width Ds +.It Ic backspace Ar key +Set the key sent by +.Nm +for backspace. +.It Ic buffer-limit Ar number +Set the number of buffers; as new buffers are added to the top of the stack, +old ones are removed from the bottom if necessary to maintain this maximum +length. +.It Xo Ic command-alias[] +.Ar name=value +.Xc +This is an array of custom aliases for commands. +If an unknown command matches +.Ar name , +it is replaced with +.Ar value . +For example, after: +.Pp +.Dl set -s command-alias[100] zoom=\[aq]resize-pane -Z\[aq] +.Pp +Using: +.Pp +.Dl zoom -t:.1 +.Pp +Is equivalent to: +.Pp +.Dl resize-pane -Z -t:.1 +.Pp +Note that aliases are expanded when a command is parsed rather than when it is +executed, so binding an alias with +.Ic bind-key +will bind the expanded form. +.It Ic codepoint-widths[] Ar string +An array option allowing widths of Unicode codepoints to be overridden. +Note the new width applies to all clients. +Each entry is of the form +.Em codepoint=width , +where codepoint may be a UTF-8 character or an identifier of the form +.Ql U+number +where the number is a hexadecimal number. +.It Ic copy-command Ar shell-command +Give the command to pipe to if the +.Ic copy-pipe +copy mode command is used without arguments. +.It Ic default-client-command Ar command +Set the default command to run when tmux is called without a command. +The default is +.Ic new-session . +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen , +.Ql tmux +or a derivative of them. +.It Ic escape-time Ar time +Set the time in milliseconds for which +.Nm +waits after an escape is input to determine if it is part of a function or meta +key sequences. +.It Ic editor Ar shell-command +Set the command used when +.Nm +runs an editor. +.It Xo Ic exit-empty +.Op Ic on | off +.Xc +If enabled (the default), the server will exit when there are no active +sessions. +.It Xo Ic exit-unattached +.Op Ic on | off +.Xc +If enabled, the server will exit when there are no attached clients. +.It Xo Ic extended-keys +.Op Ic on | off | always +.Xc +Controls how modified keys (keys pressed together with Control, Meta, or Shift) +are reported. +This is the equivalent of the +.Ic modifyOtherKeys +.Xr xterm 1 +resource. +.Pp +When set to +.Ic on , +the program inside the pane can request one of two modes: mode 1 which changes +the sequence for only keys which lack an existing well-known representation; or +mode 2 which changes the sequence for all keys. +When set to +.Ic always , +modes 1 and 2 can still be requested by applications, but mode 1 will be forced +instead of the standard mode. +When set to +.Ic off , +this feature is disabled and only standard keys are reported. +.Pp +.Nm +will always request extended keys itself if the terminal supports them. +See also the +.Ic extkeys +feature for the +.Ic terminal-features +option, the +.Ic extended-keys-format +option and the +.Ic pane_key_mode +variable. +.It Xo Ic extended-keys-format +.Op Ic csi-u | xterm +.Xc +Selects one of the two possible formats for reporting modified keys to +applications. +This is the equivalent of the +.Ic formatOtherKeys +.Xr xterm 1 +resource. +For example, C-S-a will be reported as +.Ql ^[[27;6;65~ +when set to +.Ic xterm , +and as +.Ql ^[[65;6u +when set to +.Ic csi-u . +.It Xo Ic focus-events +.Op Ic on | off +.Xc +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. +.It Ic input-buffer-size Ar bytes +Maximum of bytes allowed to read in escape and control sequences. +Once reached, the sequence will be discarded. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +.It Ic prompt-history-limit Ar number +Set the number of history items to save in the history file for each type of +command prompt. +.It Xo Ic set-clipboard +.Op Ic on | external | off +.Xc +Attempt to set the terminal clipboard content using the +.Xr xterm 1 +escape sequence, if there is an +.Em \&Ms +entry in the +.Xr terminfo 5 +description (see the +.Sx TERMINFO EXTENSIONS +section). +.Pp +If set to +.Ic on , +.Nm +will both accept the escape sequence to create a buffer and attempt to set +the terminal clipboard. +If set to +.Ic external , +.Nm +will attempt to set the terminal clipboard but ignore attempts +by applications to set +.Nm +buffers. +If +.Ic off , +.Nm +will neither accept the clipboard escape sequence nor attempt to set the +clipboard. +.Pp +Note that this feature needs to be enabled in +.Xr xterm 1 +by setting the resource: +.Bd -literal -offset indent +disallowedWindowOps: 20,21,SetXprop +.Ed +.Pp +Or changing this property from the +.Xr xterm 1 +interactive menu when required. +.It Ic terminal-features[] Ar string +Set terminal features for terminal types read from +.Xr terminfo 5 . +.Nm +has a set of named terminal features. +Each will apply appropriate changes to the +.Xr terminfo 5 +entry in use. +.Pp +.Nm +can detect features for a few common terminals; this option can be used to +easily tell tmux about features supported by terminals it cannot detect. +The +.Ic terminal-overrides +option allows individual +.Xr terminfo 5 +capabilities to be set instead, +.Ic terminal-features +is intended for classes of functionality supported in a standard way but not +reported by +.Xr terminfo 5 . +Care must be taken to configure this only with features the terminal actually +supports. +.Pp +This is an array option where each entry is a colon-separated string made up +of a terminal type pattern (matched using +.Xr glob 7 +patterns) followed by a list of terminal features. +The available features are: +.Bl -tag -width Ds +.It 256 +Supports 256 colours with the SGR escape sequences. +.It clipboard +Allows setting the system clipboard. +.It ccolour +Allows setting the cursor colour. +.It cstyle +Allows setting the cursor style. +.It extkeys +Supports extended keys. +.It focus +Supports focus reporting. +.It hyperlinks +Supports OSC 8 hyperlinks. +.It ignorefkeys +Ignore function keys from +.Xr terminfo 5 +and use the +.Nm +internal set only. +.It margins +Supports DECSLRM margins. +.It mouse +Supports +.Xr xterm 1 +mouse sequences. +.It osc7 +Supports the OSC 7 working directory extension. +.It overline +Supports the overline SGR attribute. +.It rectfill +Supports the DECFRA rectangle fill escape sequence. +.It RGB +Supports RGB colour with the SGR escape sequences. +.It sixel +Supports SIXEL graphics. +.It strikethrough +Supports the strikethrough SGR escape sequence. +.It sync +Supports synchronized updates. +.It title +Supports +.Xr xterm 1 +title setting. +.It usstyle +Allows underscore style and colour to be set. +.El +.It Ic terminal-overrides[] Ar string +Allow terminal descriptions read using +.Xr terminfo 5 +to be overridden. +Each entry is a colon-separated string made up of a terminal type pattern +(matched using +.Xr glob 7 +patterns) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types matching +.Ql rxvt* : +.Pp +.Dl "rxvt*:clear=\ee[H\ee[2J" +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +.It Ic user-keys[] Ar key +Set list of user-defined key escape sequences. +Each item is associated with a key named +.Ql User0 , +.Ql User1 , +and so on. +.Pp +For example: +.Bd -literal -offset indent +set -s user-keys[0] "\ee[5;30012\[ti]" +bind User0 resize-pane -L 3 +.Ed +.El +.Pp +Available session options are: +.Bl -tag -width Ds +.It Xo Ic activity-action +.Op Ic any | none | current | other +.Xc +Set action on window activity when +.Ic monitor-activity +is on. +.Ic any +means activity in any window linked to a session causes a bell or message +(depending on +.Ic visual-activity ) +in the current window of that session, +.Ic none +means all activity is ignored (equivalent to +.Ic monitor-activity +being off), +.Ic current +means only activity in windows other than the current window are ignored and +.Ic other +means activity in the current window is ignored but not those in other windows. +.It Ic assume-paste-time Ar milliseconds +If keys are entered faster than one in +.Ar milliseconds , +they are assumed to have been pasted rather than typed and +.Nm +key bindings are not processed. +The default is one millisecond and zero disables. +.It Ic base-index Ar index +Set the base index from which an unused index should be searched when a new +window is created. +The default is zero. +.It Xo Ic bell-action +.Op Ic any | none | current | other +.Xc +Set action on a bell in a window when +.Ic monitor-bell +is on. +The values are the same as those for +.Ic activity-action . +.It Ic default-command Ar shell-command +Set the command used for new windows (if not specified when the window is +created) to +.Ar shell-command , +which may be any +.Xr sh 1 +command. +The default is an empty string, which instructs +.Nm +to create a login shell using the value of the +.Ic default-shell +option. +.It Ic default-shell Ar path +Specify the default shell. +This is used as the login shell for new windows when the +.Ic default-command +option is set to empty, and must be the full path of the executable. +When started +.Nm +tries to set a default value from the first suitable of the +.Ev SHELL +environment variable, the shell returned by +.Xr getpwuid 3 , +or +.Pa /bin/sh . +This option should be configured when +.Nm +is used as a login shell. +.It Ic default-size Ar XxY +Set the default size of new windows when the +.Ic window-size +option is set to manual or when a session is created with +.Ic new-session +.Fl d . +The value is the width and height separated by an +.Ql x +character. +The default is 80x24. +.It Xo Ic destroy-unattached +.Op Ic off | on | keep-last | keep-group +.Xc +If +.Ic on , +destroy the session after the last client has detached. +If +.Ic off +(the default), leave the session orphaned. +If +.Ic keep-last , +destroy the session only if it is in a group and has other sessions in that +group. +If +.Ic keep-group , +destroy the session unless it is in a group and is the only session in that +group. +.It Xo Ic detach-on-destroy +.Op Ic off | on | no-detached | previous | next +.Xc +If +.Ic on +(the default), the client is detached when the session it is attached to +is destroyed. +If +.Ic off , +the client is switched to the most recently active of the remaining +sessions. +If +.Ic no-detached , +the client is detached only if there are no detached sessions; if detached +sessions exist, the client is switched to the most recently active. +If +.Ic previous +or +.Ic next , +the client is switched to the previous or next session in alphabetical order. +.It Ic display-panes-active-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicator for the active pane. +.It Ic display-panes-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicators for inactive panes. +.It Ic display-panes-time Ar time +Set the time in milliseconds for which the indicators shown by the +.Ic display-panes +command appear. +.It Ic display-time Ar time +Set the amount of time for which status line messages and other on-screen +indicators are displayed. +If set to 0, messages and indicators are displayed until a key is pressed. +.Ar time +is in milliseconds. +.It Ic history-limit Ar lines +Set the maximum number of lines held in window history. +This setting applies only to new windows - existing window histories are not +resized and retain the limit at the point they were created. +.It Ic initial-repeat-time Ar time +Set the time in milliseconds for the initial repeat when a key is bound with the +.Fl r +flag. +This allows multiple commands to be entered without pressing the prefix key +again. +See also the +.Ic repeat-time +option. +If +.Ic initial-repeat-time +is zero, +.Ic repeat-time +is used for the first key press. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . +.It Ic lock-after-time Ar number +Lock the session (like the +.Ic lock-session +command) after +.Ar number +seconds of inactivity. +The default is not to lock (set to 0). +.It Ic lock-command Ar shell-command +Command to run when locking each client. +The default is to run +.Xr lock 1 +with +.Fl np . +.It Ic menu-style Ar style +Set the menu style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-selected-style Ar style +Set the selected menu item style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-border-style Ar style +Set the menu border style. +See the +.Sx STYLES +section on how to specify +.Ar style . +.It Ic menu-border-lines Ar type +Set the type of characters used for drawing menu borders. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.It Ic message-command-style Ar style +Set status line message command style. +This is used for the command prompt with +.Xr vi 1 +keys when in command mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic message-line +.Op Ic 0 | 1 | 2 | 3 | 4 +.Xc +Set line on which status line messages and the command prompt are shown. +.It Ic message-style Ar style +Set status line message style. +This is used for messages and for the command prompt. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic mouse +.Op Ic on | off +.Xc +If on, +.Nm +captures the mouse and allows mouse events to be bound as key bindings. +See the +.Sx MOUSE SUPPORT +section for details. +.It Ic prefix Ar key +Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. +.It Ic prefix2 Ar key +Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . +.It Ic prefix-timeout Ar time +Set the time in milliseconds for which +.Nm +waits after +.Ic prefix +is input before dismissing it. +Can be set to zero to disable any timeout. +.It Ic prompt-cursor-colour Ar colour +Set the colour of the cursor in the command prompt. +.It Ic prompt-cursor-style Ar style +Set the style of the cursor in the command prompt. +See the +.Ic cursor-style +options for available styles. +.It Xo Ic renumber-windows +.Op Ic on | off +.Xc +If on, when a window is closed in a session, automatically renumber the other +windows in numerical order. +This respects the +.Ic base-index +option if it has been set. +If off, do not renumber the windows. +.It Ic repeat-time Ar time +Allow multiple commands to be entered without pressing the prefix key again +in the specified +.Ar time +milliseconds (the default is 500). +Whether a key repeats may be set when it is bound using the +.Fl r +flag to +.Ic bind-key . +Repeat is enabled for the default keys bound to the +.Ic resize-pane +command. +See also the +.Ic initial-repeat-time +option. +.It Xo Ic set-titles +.Op Ic on | off +.Xc +Attempt to set the client terminal title using the +.Em tsl +and +.Em fsl +.Xr terminfo 5 +entries if they exist. +.Nm +automatically sets these to the \ee]0;...\e007 sequence if +the terminal appears to be +.Xr xterm 1 . +This option is off by default. +.It Ic set-titles-string Ar string +String used to set the client terminal title if +.Ic set-titles +is on. +Formats are expanded, see the +.Sx FORMATS +section. +.It Xo Ic silence-action +.Op Ic any | none | current | other +.Xc +Set action on window silence when +.Ic monitor-silence +is on. +The values are the same as those for +.Ic activity-action . +.It Xo Ic status +.Op Ic off | on | 2 | 3 | 4 | 5 +.Xc +Show or hide the status line or specify its size. +Using +.Ic on +gives a status line one row in height; +.Ic 2 , +.Ic 3 , +.Ic 4 +or +.Ic 5 +more rows. +.It Ic status-format[] Ar format +Specify the format to be used for each line of the status line. +The default builds the top status line from the various individual status +options below. +.It Ic status-interval Ar interval +Update the status line every +.Ar interval +seconds. +By default, updates will occur every 15 seconds. +A setting of zero disables redrawing at interval. +.It Xo Ic status-justify +.Op Ic left | centre | right | absolute-centre +.Xc +Set the position of the window list in the status line: left, centre or right. +centre puts the window list in the relative centre of the available free space; +absolute-centre uses the centre of the entire horizontal space. +.It Xo Ic status-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style +key bindings in the status line, for example at the command prompt. +The default is emacs, unless the +.Ev VISUAL +or +.Ev EDITOR +environment variables are set and contain the string +.Ql vi . +.It Ic status-left Ar string +Display +.Ar string +(by default the session name) to the left of the status line. +.Ar string +will be passed through +.Xr strftime 3 . +Also see the +.Sx FORMATS +and +.Sx STYLES +sections. +.Pp +For details on how the names and titles can be set see the +.Sx "NAMES AND TITLES" +section. +.Pp +Examples are: +.Bd -literal -offset indent +#(sysctl vm.loadavg) +#[fg=yellow,bold]#(apm -l)%%#[default] [#S] +.Ed +.Pp +The default is +.Ql "[#S] " . +.It Ic status-left-length Ar length +Set the maximum +.Ar length +of the left component of the status line. +The default is 10. +.It Ic status-left-style Ar style +Set the style of the left part of the status line. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Xo Ic status-position +.Op Ic top | bottom +.Xc +Set the position of the status line. +.It Ic status-right Ar string +Display +.Ar string +to the right of the status line. +By default, the current pane title in double quotes, the date and the time +are shown. +As with +.Ic status-left , +.Ar string +will be passed to +.Xr strftime 3 +and character pairs are replaced. +.It Ic status-right-length Ar length +Set the maximum +.Ar length +of the right component of the status line. +The default is 40. +.It Ic status-right-style Ar style +Set the style of the right part of the status line. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Ic status-style Ar style +Set status line style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.It Ic update-environment[] Ar variable +Set list of environment variables to be copied into the session environment +when a new session is created or an existing session is attached. +Any variables that do not exist in the source environment are set to be +removed from the session environment (as if +.Fl r +was given to the +.Ic set-environment +command). +.It Xo Ic visual-activity +.Op Ic on | off | both +.Xc +If on, display a message instead of sending a bell when activity occurs in a +window for which the +.Ic monitor-activity +window option is enabled. +If set to both, a bell and a message are produced. +.It Xo Ic visual-bell +.Op Ic on | off | both +.Xc +If on, a message is shown on a bell in a window for which the +.Ic monitor-bell +window option is enabled instead of it being passed through to the +terminal (which normally makes a sound). +If set to both, a bell and a message are produced. +Also see the +.Ic bell-action +option. +.It Xo Ic visual-silence +.Op Ic on | off | both +.Xc +If +.Ic monitor-silence +is enabled, prints a message after the interval has expired on a given window +instead of sending a bell. +If set to both, a bell and a message are produced. +.It Ic word-separators Ar string +Sets the session's conception of what characters are considered word +separators, for the purposes of the next and previous word commands in +copy mode. +.El +.Pp +Available window options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic aggressive-resize +.Op Ic on | off +.Xc +Aggressively resize the chosen window. +This means that +.Nm +will resize the window to the size of the smallest or largest session +(see the +.Ic window-size +option) for which it is the current window, rather than the session to +which it is attached. +The window may resize when the current window is changed on another +session; this option is good for full-screen programs which support +.Dv SIGWINCH +and poor for interactive programs such as shells. +.Pp +.It Xo Ic automatic-rename +.Op Ic on | off +.Xc +Control automatic window renaming. +When this setting is enabled, +.Nm +will rename the window automatically using the format specified by +.Ic automatic-rename-format . +This flag is automatically disabled for an individual window when a name +is specified at creation with +.Ic new-window +or +.Ic new-session , +or later with +.Ic rename-window , +or with a terminal escape sequence. +It may be switched off globally with: +.Bd -literal -offset indent +set-option -wg automatic-rename off +.Ed +.Pp +.It Ic automatic-rename-format Ar format +The format (see +.Sx FORMATS ) +used when the +.Ic automatic-rename +option is enabled. +.Pp +.It Ic clock-mode-colour Ar colour +Set clock colour. +.Pp +.It Xo Ic clock-mode-style +.Op Ic 12 | 24 +.Xc +Set clock hour format. +.Pp +.It Ic fill-character Ar character +Set the character used to fill areas of the terminal unused by a window. +.Pp +.It Ic main-pane-height Ar height +.It Ic main-pane-width Ar width +Set the width or height of the main (left or top) pane in the +.Ic main-horizontal , +.Ic main-horizontal-mirrored , +.Ic main-vertical , +or +.Ic main-vertical-mirrored +layouts. +If suffixed by +.Ql % , +this is a percentage of the window size. +.Pp +.It Ic copy-mode-match-style Ar style +Set the style of search matches in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-mark-style Ar style +Set the style of the line containing the mark in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-current-match-style Ar style +Set the style of the current search match in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-position-format Ar format +Format of the position indicator in copy mode. +.Pp +.It Xo Ic mode-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style key bindings in copy mode. +The default is emacs, unless +.Ev VISUAL +or +.Ev EDITOR +contains +.Ql vi . +.Pp +.It Ic copy-mode-position-style Ar style +Set the style of the position indicator in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy-mode-selection-style Ar style +Set the style of the selection in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic mode-style Ar style +Set window modes style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Xo Ic monitor-activity +.Op Ic on | off +.Xc +Monitor for activity in the window. +Windows with activity are highlighted in the status line. +.Pp +.It Xo Ic monitor-bell +.Op Ic on | off +.Xc +Monitor for a bell in the window. +Windows with a bell are highlighted in the status line. +.Pp +.It Xo Ic monitor-silence +.Op Ic interval +.Xc +Monitor for silence (no activity) in the window within +.Ic interval +seconds. +Windows that have been silent for the interval are highlighted in the +status line. +An interval of zero disables the monitoring. +.Pp +.It Ic other-pane-height Ar height +Set the height of the other panes (not the main pane) in the +.Ic main-horizontal +and +.Ic main-horizontal-mirrored +layouts. +If this option is set to 0 (the default), it will have no effect. +If both the +.Ic main-pane-height +and +.Ic other-pane-height +options are set, the main pane will grow taller to make the other panes the +specified height, but will never shrink to do so. +If suffixed by +.Ql % , +this is a percentage of the window size. +.Pp +.It Ic other-pane-width Ar width +Like +.Ic other-pane-height , +but set the width of other panes in the +.Ic main-vertical +and +.Ic main-vertical-mirrored +layouts. +.Pp +.It Ic pane-active-border-style Ar style +Set the pane border style for the currently active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic pane-base-index Ar index +Like +.Ic base-index , +but set the starting index for pane numbers. +.Pp +.It Ic pane-border-format Ar format +Set the text shown in pane border status lines. +.Pp +.It Xo Ic pane-border-indicators +.Op Ic off | colour | arrows | both +.Xc +Indicate active pane by colouring only half of the border in windows with +exactly two panes, by displaying arrow markers, by drawing both or neither. +.Pp +.It Ic pane-border-lines Ar type +Set the type of characters used for drawing pane borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It number +the pane number +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp +.It Xo Ic pane-border-status +.Op Ic off | top | bottom +.Xc +Turn pane border status lines off or set their position. +.Pp +.It Ic pane-border-style Ar style +Set the pane border style for panes aside from the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +Attributes are ignored. +.Pp +.It Ic popup-style Ar style +Set the popup style. +See the +.Sx STYLES +section on how to specify +.Ar style . +Attributes are ignored. +.Pp +.It Ic popup-border-style Ar style +Set the popup border style. +See the +.Sx STYLES +section on how to specify +.Ar style . +Attributes are ignored. +.Pp +.It Ic popup-border-lines Ar type +Set the type of characters used for drawing popup borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters (default) +.It rounded +variation of single with rounded corners using UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It padded +simple ASCII space character +.It none +no border +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp +.It Xo Ic pane-scrollbars +.Op Ic off | modal | on +.Xc +When enabled, a character based scrollbar appears on the left or right +of each pane. +A filled section of the scrollbar, known as the +.Ql slider , +represents the position and size of the visible part of the pane content. +.Pp +If set to +.Ic on +the scrollbar is visible all the time. +If set to +.Ic modal +the scrollbar only appears when the pane is in copy mode or view mode. +When the scrollbar is visible, the pane is narrowed by the width of the +scrollbar and the text in the pane is reflowed. +If set to +.Ic modal , +the pane is narrowed only when the scrollbar is visible. +.Pp +See also +.Ic pane-scrollbars-style . +.Pp +.It Ic pane-scrollbars-style Ar style +Set the scrollbars style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +The foreground colour is used for the slider, the background for the rest of the +scrollbar. +The +.Ar width +attribute sets the width of the scrollbar and the +.Ar pad +attribute the padding between the scrollbar and the pane. +Other attributes are ignored. +.Pp +.It Xo Ic pane-scrollbars-position +.Op Ic left | right +.Xc +Sets which side of the pane to display pane scrollbars on. +.Pp +.It Ic window-status-activity-style Ar style +Set status line style for windows with an activity alert. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-bell-style Ar style +Set status line style for windows with a bell alert. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-current-format Ar string +Like +.Ar window-status-format , +but is the format used when the window is the current window. +.Pp +.It Ic window-status-current-style Ar style +Set status line style for the currently active window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-format Ar string +Set the format in which the window is displayed in the status line window list. +See the +.Sx FORMATS +and +.Sx STYLES +sections. +.Pp +.It Ic window-status-last-style Ar style +Set status line style for the last active window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-status-separator Ar string +Sets the separator drawn between windows in the status line. +The default is a single space character. +.Pp +.It Ic window-status-style Ar style +Set status line style for a single window. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Xo Ic window-size +.Ar largest | Ar smallest | Ar manual | Ar latest +.Xc +Configure how +.Nm +determines the window size. +If set to +.Ar largest , +the size of the largest attached session is used; if +.Ar smallest , +the size of the smallest. +If +.Ar manual , +the size of a new window is set from the +.Ic default-size +option and windows are resized automatically. +With +.Ar latest , +.Nm +uses the size of the client that had the most recent activity. +See also the +.Ic resize-window +command and the +.Ic aggressive-resize +option. +.Pp +.It Xo Ic wrap-search +.Op Ic on | off +.Xc +If this option is set, searches will wrap around the end of the pane contents. +The default is on. +.El +.Pp +Available pane options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic allow-passthrough +.Op Ic on | off | all +.Xc +Allow programs in the pane to bypass +.Nm +using a terminal escape sequence (\eePtmux;...\ee\e\e). +If set to +.Ic on , +passthrough sequences will be allowed only if the pane is visible. +If set to +.Ic all , +they will be allowed even if the pane is invisible. +.Pp +.It Xo Ic allow-rename +.Op Ic on | off +.Xc +Allow programs in the pane to change the window name using a terminal escape +sequence (\eek...\ee\e\e). +.Pp +.It Xo Ic allow-set-title +.Op Ic on | off +.Xc +Allow programs in the pane to change the title using the terminal escape +sequences (\ee]2;...\ee\e\e or \ee]0;...\ee\e\e). +.Pp +.It Xo Ic alternate-screen +.Op Ic on | off +.Xc +This option configures whether programs running inside the pane may use the +terminal alternate screen feature, which allows the +.Em smcup +and +.Em rmcup +.Xr terminfo 5 +capabilities. +The alternate screen feature preserves the contents of the window when an +interactive application starts and restores it on exit, so that any output +visible before the application starts reappears unchanged after it exits. +.Pp +.It Ic cursor-colour Ar colour +Set the colour of the cursor. +.Pp +.It Ic cursor-style Ar style +Set the style of the cursor. +Available styles are: +.Ic default , +.Ic blinking-block , +.Ic block , +.Ic blinking-underline , +.Ic underline , +.Ic blinking-bar , +.Ic bar . +.Pp +.It Ic pane-colours[] Ar colour +The default colour palette. +Each entry in the array defines the colour +.Nm +uses when the colour with that index is requested. +The index may be from zero to 255. +.Pp +.It Xo Ic remain-on-exit +.Op Ic on | off | failed +.Xc +A pane with this flag set is not destroyed when the program running in it +exits. +If set to +.Ic failed , +then only when the program exit status is not zero. +The pane may be reactivated with the +.Ic respawn-pane +command. +.Pp +.It Ic remain-on-exit-format Ar string +Set the text shown at the bottom of exited panes when +.Ic remain-on-exit +is enabled. +.Pp +.It Xo Ic scroll-on-clear +.Op Ic on | off +.Xc +When the entire screen is cleared and this option is on, scroll the contents of +the screen into history before clearing it. +.Pp +.It Xo Ic synchronize-panes +.Op Ic on | off +.Xc +Duplicate input to all other panes in the same window where this option is also +on (only for panes that are not in any mode). +.Pp +.It Ic window-active-style Ar style +Set the pane style when it is the active pane. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic window-style Ar style +Set the pane style. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Most +.Nm +commands have an +.Em after +hook and there are a number of hooks not associated with commands. +.Pp +Hooks are stored as array options, members of the array are executed in +order when the hook is triggered. +Like options different hooks may be global or belong to a session, window or +pane. +Hooks may be configured with the +.Ic set-hook +or +.Ic set-option +commands and displayed with +.Ic show-hooks +or +.Ic show-options +.Fl H . +The following two commands are equivalent: +.Bd -literal -offset indent. +set-hook -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq] +set-option -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq] +.Ed +.Pp +Setting a hook without specifying an array index clears the hook and sets the +first member of the array. +.Pp +A command's after +hook is run after it completes, except when the command is run as part of a hook +itself. +They are named with an +.Ql after- +prefix. +For example, the following command adds a hook to select the even-vertical +layout after every +.Ic split-window : +.Bd -literal -offset indent +set-hook -g after-split-window "selectl even-vertical" +.Ed +.Pp +If a command fails, the +.Ql command-error +hook will be fired. +For example, this could be used to write to a log file: +.Bd -literal -offset indent +set-hook -g command-error "run-shell \\"echo 'a tmux command failed' >>/tmp/log\\"" +.Ed +.Pp +All the notifications listed in the +.Sx CONTROL MODE +section are hooks (without any arguments), except +.Ic %exit . +The following additional hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX" +.It alert-activity +Run when a window has activity. +See +.Ic monitor-activity . +.It alert-bell +Run when a window has received a bell. +See +.Ic monitor-bell . +.It alert-silence +Run when a window has been silent. +See +.Ic monitor-silence . +.It client-active +Run when a client becomes the latest active client of its session. +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-focus-in +Run when focus enters a client +.It client-focus-out +Run when focus exits a client +.It client-resized +Run when a client is resized. +.It client-session-changed +Run when a client's attached session is changed. +.It command-error +Run when a command fails. +.It pane-died +Run when the program running in a pane exits, but +.Ic remain-on-exit +is on so the pane has not closed. +.It pane-exited +Run when the program running in a pane exits. +.It pane-focus-in +Run when the focus enters a pane, if the +.Ic focus-events +option is on. +.It pane-focus-out +Run when the focus exits a pane, if the +.Ic focus-events +option is on. +.It pane-set-clipboard +Run when the terminal clipboard is set using the +.Xr xterm 1 +escape sequence. +.It session-created +Run when a new session created. +.It session-closed +Run when a session closed. +.It session-renamed +Run when a session is renamed. +.It window-layout-changed +Run when a window layout is changed. +.It window-linked +Run when a window is linked into a session. +.It window-renamed +Run when a window is renamed. +.It window-resized +Run when a window is resized. +This may be after the +.Ar client-resized +hook is run. +.It window-unlinked +Run when a window is unlinked from a session. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl agpRuw +.Op Fl t Ar target-pane +.Ar hook-name +.Ar command +.Xc +Without +.Fl R , +sets (or with +.Fl u +unsets) hook +.Ar hook-name +to +.Ar command . +The flags are the same as for +.Ic set-option . +.Pp +With +.Fl R , +run +.Ar hook-name +immediately. +.It Xo Ic show-hooks +.Op Fl gpw +.Op Fl t Ar target-pane +.Xc +Shows hooks. +The flags are the same as for +.Ic show-options . +.El +.Sh MOUSE SUPPORT +If the +.Ic mouse +option is on (the default is off), +.Nm +allows mouse events to be bound as keys. +The name of each key is made up of a mouse event (such as +.Ql MouseUp1 ) +and a location suffix, one of the following: +.Bl -column "XXXXXXXXXXXXX" -offset indent +.It Li "Pane" Ta "the contents of a pane" +.It Li "Border" Ta "a pane border" +.It Li "Status" Ta "the status line window list" +.It Li "StatusLeft" Ta "the left part of the status line" +.It Li "StatusRight" Ta "the right part of the status line" +.It Li "StatusDefault" Ta "any other part of the status line" +.It Li "ScrollbarSlider" Ta "the scrollbar slider" +.It Li "ScrollbarUp" Ta "above the scrollbar slider" +.It Li "ScrollbarDown" Ta "below the scrollbar slider" +.El +.Pp +The following mouse events are available: +.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent +.It Li "WheelUp" Ta "WheelDown" Ta "" +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "SecondClick1" Ta "SecondClick2" Ta "SecondClick3" +.It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" +.It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" +.El +.Pp +The +.Ql SecondClick +events are fired for the second click of a double click, even if there may be a +third click which will fire +.Ql TripleClick +instead of +.Ql DoubleClick . +.Pp +Each should be suffixed with a location, for example +.Ql MouseDown1Status . +.Pp +The special token +.Ql {mouse} +or +.Ql = +may be used as +.Ar target-window +or +.Ar target-pane +in commands bound to mouse key bindings. +It resolves to the window or pane over which the mouse event took place +(for example, the window in the status line over which button 1 was released +for a +.Ql MouseUp1Status +binding, or the pane over which the wheel was scrolled for a +.Ql WheelDownPane +binding). +.Pp +The +.Ic send-keys +.Fl M +flag may be used to forward a mouse event to a pane. +.Pp +The default key bindings allow the mouse to be used to select and resize panes, +to copy text and to change window using the status line. +These take effect if the +.Ic mouse +option is turned on. +.Sh FORMATS +Certain commands accept the +.Fl F +flag with a +.Ar format +argument. +This is a string which controls the output format of the command. +Format variables are enclosed in +.Ql #{ +and +.Ql } , +for example +.Ql #{session_name} . +The possible variables are listed in the table below, or the name of a +.Nm +option may be used for an option's value. +Some variables have a shorter alias such as +.Ql #S ; +.Ql ## +is replaced by a single +.Ql # , +.Ql #, +by a +.Ql \&, +and +.Ql #} +by a +.Ql } . +.Pp +Conditionals are available by prefixing with +.Ql \&? +and separating two alternatives with a comma; +if the specified variable exists and is not zero, the first alternative +is chosen, otherwise the second is used. +For example +.Ql #{?session_attached,attached,not attached} +will include the string +.Ql attached +if the session is attached and the string +.Ql not attached +if it is unattached, or +.Ql #{?automatic-rename,yes,no} +will include +.Ql yes +if +.Ic automatic-rename +is enabled, or +.Ql no +if not. +Conditionals can be nested arbitrarily. +Inside a conditional, +.Ql \&, +and +.Ql } +must be escaped as +.Ql #, +and +.Ql #} , +unless they are part of a +.Ql #{...} +replacement. +For example: +.Bd -literal -offset indent +#{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W . +.Ed +.Pp +String comparisons may be expressed by prefixing two comma-separated +alternatives by +.Ql == , +.Ql != , +.Ql < , +.Ql > , +.Ql <= +or +.Ql >= +and a colon. +For example +.Ql #{==:#{host},myhost} +will be replaced by +.Ql 1 +if running on +.Ql myhost , +otherwise by +.Ql 0 . +.Ql || +and +.Ql && +evaluate to true if either or both of two comma-separated alternatives are +true, for example +.Ql #{||:#{pane_in_mode},#{alternate_on}} . +.Pp +An +.Ql m +specifies a +.Xr glob 7 +pattern or regular expression comparison. +The first argument is the pattern and the second the string to compare. +An optional argument specifies flags: +.Ql r +means the pattern is a regular expression instead of the default +.Xr glob 7 +pattern, and +.Ql i +means to ignore case. +For example: +.Ql #{m:*foo*,#{host}} +or +.Ql #{m/ri:^A,MYVAR} . +A +.Ql C +performs a search for a +.Xr glob 7 +pattern or regular expression in the pane content and evaluates to zero if not +found, or a line number if found. +Like +.Ql m , +an +.Ql r +flag means search for a regular expression and +.Ql i +ignores case. +For example: +.Ql #{C/r:^Start} +.Pp +Numeric operators may be performed by prefixing two comma-separated alternatives +with an +.Ql e +and an operator. +An optional +.Ql f +flag may be given after the operator to use floating point numbers, otherwise +integers are used. +This may be followed by a number giving the number of decimal places to use for +the result. +The available operators are: +addition +.Ql + , +subtraction +.Ql - , +multiplication +.Ql * , +division +.Ql / , +modulus +.Ql m +or +.Ql % +(note that +.Ql % +must be escaped as +.Ql %% +in formats which are also expanded by +.Xr strftime 3 ) +and numeric comparison operators +.Ql == , +.Ql != , +.Ql < , +.Ql <= , +.Ql > +and +.Ql >= . +For example, +.Ql #{e|*|f|4:5.5,3} +multiplies 5.5 by 3 for a result with four decimal places and +.Ql #{e|%%:7,3} +returns the modulus of 7 and 3. +.Ql a +replaces a numeric argument by its ASCII equivalent, so +.Ql #{a:98} +results in +.Ql b . +.Ql c +replaces a +.Nm +colour by its six-digit hexadecimal RGB value. +.Pp +A limit may be placed on the length of the resultant string by prefixing it +by an +.Ql = , +a number and a colon. +Positive numbers count from the start of the string and negative from the end, +so +.Ql #{=5:pane_title} +will include at most the first five characters of the pane title, or +.Ql #{=-5:pane_title} +the last five characters. +A suffix or prefix may be given as a second argument - if provided then it is +appended or prepended to the string if the length has been trimmed, for example +.Ql #{=/5/...:pane_title} +will append +.Ql ... +if the pane title is more than five characters. +Similarly, +.Ql p +pads the string to a given width, for example +.Ql #{p10:pane_title} +will result in a width of at least 10 characters. +A positive width pads on the left, a negative on the right. +.Ql n +expands to the length of the variable and +.Ql w +to its width when displayed, for example +.Ql #{n:window_name} . +.Pp +Prefixing a time variable with +.Ql t:\& +will convert it to a string, so if +.Ql #{window_activity} +gives +.Ql 1445765102 , +.Ql #{t:window_activity} +gives +.Ql Sun Oct 25 09:25:02 2015 . +Adding +.Ql p ( +.Ql `t/p` ) +will use shorter but less accurate time format for times in the past. +A custom format may be given using an +.Ql f +suffix (note that +.Ql % +must be escaped as +.Ql %% +if the format is separately being passed through +.Xr strftime 3 , +for example in the +.Ic status-left +option): +.Ql #{t/f/%%H#:%%M:window_activity} , +see +.Xr strftime 3 . +.Pp +The +.Ql b:\& +and +.Ql d:\& +prefixes are +.Xr basename 3 +and +.Xr dirname 3 +of the variable respectively. +.Ql q:\& +will escape +.Xr sh 1 +special characters or with a +.Ql h +suffix, escape hash characters (so +.Ql # +becomes +.Ql ## ) . +.Ql E:\& +will expand the format twice, for example +.Ql #{E:status-left} +is the result of expanding the content of the +.Ic status-left +option rather than the option itself. +.Ql T:\& +is like +.Ql E:\& +but also expands +.Xr strftime 3 +specifiers. +.Ql S:\& , +.Ql W:\& , +.Ql P:\& +or +.Ql L:\& +will loop over each session, window, pane or client and insert the format once +for each. +For windows and panes, two comma-separated formats may be given: +the second is used for the current window or active pane. +For example, to get a list of windows formatted like the status line: +.Bd -literal -offset indent +#{W:#{E:window-status-format} ,#{E:window-status-current-format} } +.Ed +.Pp +.Ql N:\& +checks if a window (without any suffix or with the +.Ql w +suffix) or a session (with the +.Ql s +suffix) name exists, for example +.Ql `N/w:foo` +is replaced with 1 if a window named +.Ql foo +exists. +.Pp +A prefix of the form +.Ql s/foo/bar/:\& +will substitute +.Ql foo +with +.Ql bar +throughout. +The first argument may be an extended regular expression and a final argument +may be +.Ql i +to ignore case, for example +.Ql s/a(.)/\e1x/i:\& +would change +.Ql abABab +into +.Ql bxBxbx . +A different delimiter character may also be used, to avoid collisions with +literal slashes in the pattern. +For example, +.Ql s|foo/|bar/|:\& +will substitute +.Ql foo/ +with +.Ql bar/ +throughout. +.Pp +Multiple modifiers may be separated with a semicolon (;) as in +.Ql #{T;=10:status-left} , +which limits the resulting +.Xr strftime 3 -expanded +string to at most 10 characters. +.Pp +In addition, the last line of a shell command's output may be inserted using +.Ql #() . +For example, +.Ql #(uptime) +will insert the system's uptime. +When constructing formats, +.Nm +does not wait for +.Ql #() +commands to finish; instead, the previous result from running the same command +is used, or a placeholder if the command has not been run before. +If the command hasn't exited, the most recent line of output will be used, but +the status line will not be updated more than once a second. +Commands are executed using +.Pa /bin/sh +and with the +.Nm +global environment set (see the +.Sx GLOBAL AND SESSION ENVIRONMENT +section). +.Pp +An +.Ql l +specifies that a string should be interpreted literally and not expanded. +For example +.Ql #{l:#{?pane_in_mode,yes,no}} +will be replaced by +.Ql #{?pane_in_mode,yes,no} . +.Pp +The following variables are available, where appropriate: +.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" +.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" +.It Li "active_window_index" Ta "" Ta "Index of active window in session" +.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" +.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" +.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" +.It Li "buffer_created" Ta "" Ta "Time buffer created" +.It Li "buffer_name" Ta "" Ta "Name of buffer" +.It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" +.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" +.It Li "client_activity" Ta "" Ta "Time client last had activity" +.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels" +.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels" +.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" +.It Li "client_created" Ta "" Ta "Time client created" +.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" +.It Li "client_flags" Ta "" Ta "List of client flags" +.It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_key_table" Ta "" Ta "Current key table" +.It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_name" Ta "" Ta "Name of client" +.It Li "client_pid" Ta "" Ta "PID of client process" +.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" +.It Li "client_readonly" Ta "" Ta "1 if client is read-only" +.It Li "client_session" Ta "" Ta "Name of the client's session" +.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" +.It Li "client_termname" Ta "" Ta "Terminal name of client" +.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" +.It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_uid" Ta "" Ta "UID of client process" +.It Li "client_user" Ta "" Ta "User of client process" +.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" +.It Li "client_width" Ta "" Ta "Width of client" +.It Li "client_written" Ta "" Ta "Bytes written to client" +.It Li "command" Ta "" Ta "Name of command in use, if any" +.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" +.It Li "command_list_name" Ta "" Ta "Command name if listing commands" +.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" +.It Li "config_files" Ta "" Ta "List of configuration files loaded" +.It Li "cursor_blinking" Ta "" Ta "1 if the cursor is blinking" +.It Li "copy_cursor_hyperlink" Ta "" Ta "Hyperlink under cursor in copy mode" +.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" +.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" +.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" +.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" +.It Li "current_file" Ta "" Ta "Current configuration file" +.It Li "cursor_character" Ta "" Ta "Character at cursor in pane" +.It Li "cursor_colour" Ta "" Ta "Cursor colour in pane" +.It Li "cursor_flag" Ta "" Ta "Pane cursor flag" +.It Li "cursor_shape" Ta "" Ta "Cursor shape in pane" +.It Li "cursor_very_visible" Ta "" Ta "1 if the cursor is in very visible mode" +.It Li "cursor_x" Ta "" Ta "Cursor X position in pane" +.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" +.It Li "history_bytes" Ta "" Ta "Number of bytes in window history" +.It Li "history_limit" Ta "" Ta "Maximum window history lines" +.It Li "history_size" Ta "" Ta "Size of history in lines" +.It Li "hook" Ta "" Ta "Name of running hook, if any" +.It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any" +.It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any" +.It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any" +.It Li "hook_session_name" Ta "" Ta "Name of session where hook was run, if any" +.It Li "hook_window" Ta "" Ta "ID of window where hook was run, if any" +.It Li "hook_window_name" Ta "" Ta "Name of window where hook was run, if any" +.It Li "host" Ta "#H" Ta "Hostname of local host" +.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" +.It Li "insert_flag" Ta "" Ta "Pane insert flag" +.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" +.It Li "keypad_flag" Ta "" Ta "Pane keypad flag" +.It Li "last_window_index" Ta "" Ta "Index of last window in session" +.It Li "line" Ta "" Ta "Line number in the list" +.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" +.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" +.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any" +.It Li "mouse_line" Ta "" Ta "Line under mouse, if any" +.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" +.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "mouse_status_line" Ta "" Ta "Status line on which mouse event took place" +.It Li "mouse_status_range" Ta "" Ta "Range type or argument of mouse event on status line" +.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" +.It Li "mouse_word" Ta "" Ta "Word under mouse, if any" +.It Li "mouse_x" Ta "" Ta "Mouse X position, if any" +.It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" +.It Li "next_session_id" Ta "" Ta "Unique session ID for next new session" +.It Li "origin_flag" Ta "" Ta "Pane origin flag" +.It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" +.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" +.It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window" +.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" +.It Li "pane_bg" Ta "" Ta "Pane background colour" +.It Li "pane_bottom" Ta "" Ta "Bottom of pane" +.It Li "pane_current_command" Ta "" Ta "Current command if available" +.It Li "pane_current_path" Ta "" Ta "Current path if available" +.It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_signal" Ta "" Ta "Exit signal of process in dead pane" +.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_dead_time" Ta "" Ta "Exit time of process in dead pane" +.It Li "pane_fg" Ta "" Ta "Pane foreground colour" +.It Li "pane_format" Ta "" Ta "1 if format is for a pane" +.It Li "pane_height" Ta "" Ta "Height of pane" +.It Li "pane_id" Ta "#D" Ta "Unique pane ID" +.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" +.It Li "pane_index" Ta "#P" Ta "Index of pane" +.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" +.It Li "pane_key_mode" Ta "" Ta "Extended key reporting mode in this pane" +.It Li "pane_last" Ta "" Ta "1 if last pane" +.It Li "pane_left" Ta "" Ta "Left of pane" +.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" +.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" +.It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" +.It Li "pane_path" Ta "" Ta "Path of pane (can be set by application)" +.It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" +.It Li "pane_right" Ta "" Ta "Right of pane" +.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" +.It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_path" Ta "" Ta "Path pane started with" +.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" +.It Li "pane_tabs" Ta "" Ta "Pane tab positions" +.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)" +.It Li "pane_top" Ta "" Ta "Top of pane" +.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" +.It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode" +.It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pid" Ta "" Ta "Server PID" +.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" +.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" +.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" +.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "search_count" Ta "" Ta "Count of search results" +.It Li "search_count_partial" Ta "" Ta "1 if search count is partial count" +.It Li "search_match" Ta "" Ta "Search match if any" +.It Li "search_present" Ta "" Ta "1 if search started in copy mode" +.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" +.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" +.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" +.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" +.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection" +.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection" +.It Li "server_sessions" Ta "" Ta "Number of sessions" +.It Li "session_activity" Ta "" Ta "Time of session last activity" +.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" +.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_attached_list" Ta "" Ta "List of clients session is attached to" +.It Li "session_created" Ta "" Ta "Time session created" +.It Li "session_format" Ta "" Ta "1 if format is for a session" +.It Li "session_group" Ta "" Ta "Name of session group" +.It Li "session_group_attached" Ta "" Ta "Number of clients sessions in group are attached to" +.It Li "session_group_attached_list" Ta "" Ta "List of clients sessions in group are attached to" +.It Li "session_group_list" Ta "" Ta "List of sessions in group" +.It Li "session_group_many_attached" Ta "" Ta "1 if multiple clients attached to sessions in group" +.It Li "session_group_size" Ta "" Ta "Size of session group" +.It Li "session_grouped" Ta "" Ta "1 if session in a group" +.It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_last_attached" Ta "" Ta "Time session last attached" +.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" +.It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane" +.It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_path" Ta "" Ta "Working directory of session" +.It Li "session_stack" Ta "" Ta "Window indexes in most recent order" +.It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "socket_path" Ta "" Ta "Server socket path" +.It Li "sixel_support" Ta "" Ta "1 if server has support for SIXEL" +.It Li "start_time" Ta "" Ta "Server start time" +.It Li "uid" Ta "" Ta "Server UID" +.It Li "user" Ta "" Ta "Server user" +.It Li "version" Ta "" Ta "Server version" +.It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" +.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window" +.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active" +.It Li "window_active_sessions_list" Ta "" Ta "List of sessions on which this window is active" +.It Li "window_activity" Ta "" Ta "Time of window last activity" +.It Li "window_activity_flag" Ta "" Ta "1 if window has activity" +.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_bigger" Ta "" Ta "1 if window is larger than client" +.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" +.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" +.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" +.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" +.It Li "window_format" Ta "" Ta "1 if format is for a window" +.It Li "window_height" Ta "" Ta "Height of window" +.It Li "window_id" Ta "" Ta "Unique window ID" +.It Li "window_index" Ta "#I" Ta "Index of window" +.It Li "window_last_flag" Ta "" Ta "1 if window is the last used" +.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" +.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to" +.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to" +.It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane" +.It Li "window_name" Ta "#W" Ta "Name of window" +.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" +.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" +.It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" +.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" +.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" +.It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" +.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" +.It Li "window_width" Ta "" Ta "Width of window" +.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" +.It Li "wrap_flag" Ta "" Ta "Pane wrap flag" +.El +.Sh STYLES +.Nm +offers various options to specify the colour and attributes of aspects of the +interface, for example +.Ic status-style +for the status line. +In addition, embedded styles may be specified in format options, such as +.Ic status-left , +by enclosing them in +.Ql #[ +and +.Ql \&] . +.Pp +A style may be the single term +.Ql default +to specify the default style (which may come from an option, for example +.Ic status-style +in the status line) or a space +or comma separated list of the following: +.Bl -tag -width Ds +.It Ic fg=colour +Set the foreground colour. +The colour is one of: +.Ic black , +.Ic red , +.Ic green , +.Ic yellow , +.Ic blue , +.Ic magenta , +.Ic cyan , +.Ic white ; +if supported the bright variants +.Ic brightred , +.Ic brightgreen , +.Ic brightyellow ; +.Ic colour0 +to +.Ic colour255 +from the 256-colour set; +.Ic default +for the default colour; +.Ic terminal +for the terminal default colour; or a hexadecimal RGB string such as +.Ql #ffffff . +.It Ic bg=colour +Set the background colour. +.It Ic us=colour +Set the underscore colour. +.It Ic none +Set no attributes (turn off any active attributes). +.It Xo Ic acs , +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +.Ic italics , +.Ic overline , +.Ic strikethrough , +.Ic double-underscore , +.Ic curly-underscore , +.Ic dotted-underscore , +.Ic dashed-underscore +.Xc +Set an attribute. +Any of the attributes may be prefixed with +.Ql no +to unset. +.Ic acs +is the terminal alternate character set. +.It Xo Ic align=left +(or +.Ic noalign ) , +.Ic align=centre , +.Ic align=right +.Xc +Align text to the left, centre or right of the available space if appropriate. +.It Ic fill=colour +Fill the available space with a background colour if appropriate. +.It Xo Ic list=on , +.Ic list=focus , +.Ic list=left-marker , +.Ic list=right-marker , +.Ic nolist +.Xc +Mark the position of the various window list components in the +.Ic status-format +option: +.Ic list=on +marks the start of the list; +.Ic list=focus +is the part of the list that should be kept in focus if the entire list won't +fit in the available space (typically the current window); +.Ic list=left-marker +and +.Ic list=right-marker +mark the text to be used to mark that text has been trimmed from the left or +right of the list if there is not enough space. +.It Xo Ic push-default , +.Ic pop-default +.Xc +Store the current colours and attributes as the default or reset to the previous +default. +A +.Ic push-default +affects any subsequent use of the +.Ic default +term until a +.Ic pop-default . +Only one default may be pushed (each +.Ic push-default +replaces the previous saved default). +.It Xo Ic range=left , +.Ic range=right , +.Ic range=session|X , +.Ic range=window|X , +.Ic range=pane|X , +.Ic range=user|X , +.Ic norange +.Xc +Mark a range for mouse events in the +.Ic status-format +option. +When a mouse event occurs in the +.Ic range=left +or +.Ic range=right +range, the +.Ql StatusLeft +and +.Ql StatusRight +key bindings are triggered. +.Pp +.Ic range=session|X , +.Ic range=window|X +and +.Ic range=pane|X +are ranges for a session, window or pane. +These trigger the +.Ql Status +mouse key with the target session, window or pane given by the +.Ql X +argument. +.Ql X +is a session ID, window index in the current session or a pane ID. +For these, the +.Ic mouse_status_range +format variable will be set to +.Ql session , +.Ql window +or +.Ql pane . +.Pp +.Ic range=user|X +is a user-defined range; it triggers the +.Ql Status +mouse key. +The argument +.Ql X +will be available in the +.Ic mouse_status_range +format variable. +.Ql X +must be at most 15 bytes in length. +.El +.Pp +Examples are: +.Bd -literal -offset indent +fg=yellow bold underscore blink +bg=black,fg=default,noreverse +.Ed +.Sh NAMES AND TITLES +.Nm +distinguishes between names and titles. +Windows and sessions have names, which may be used to specify them in targets +and are displayed in the status line and various lists: the name is the +.Nm +identifier for a window or session. +Only panes have titles. +A pane's title is typically set by the program running inside the pane using +an escape sequence (like it would set the +.Xr xterm 1 +window title in +.Xr X 7 ) . +Windows themselves do not have titles - a window's title is the title of its +active pane. +.Nm +itself may set the title of the terminal in which the client is running, see +the +.Ic set-titles +option. +.Pp +A session's name is set with the +.Ic new-session +and +.Ic rename-session +commands. +A window's name is set with one of: +.Bl -enum -width Ds +.It +A command argument (such as +.Fl n +for +.Ic new-window +or +.Ic new-session ) . +.It +An escape sequence (if the +.Ic allow-rename +option is turned on): +.Bd -literal -offset indent +$ printf \[aq]\e033kWINDOW_NAME\e033\e\e\[aq] +.Ed +.It +Automatic renaming, which sets the name to the active command in the window's +active pane. +See the +.Ic automatic-rename +option. +.El +.Pp +When a pane is first created, its title is the hostname. +A pane's title can be set via the title setting escape sequence, for example: +.Bd -literal -offset indent +$ printf \[aq]\e033]2;My Title\e033\e\e\[aq] +.Ed +.Pp +It can also be modified with the +.Ic select-pane +.Fl T +command. +.Sh GLOBAL AND SESSION ENVIRONMENT +When the server is started, +.Nm +copies the environment into the +.Em global environment ; +in addition, each session has a +.Em session environment . +When a window is created, the session and global environments are merged. +If a variable exists in both, the value from the session environment is used. +The result is the initial environment passed to the new process. +.Pp +The +.Ic update-environment +session option may be used to update the session environment from the client +when a new session is created or an old reattached. +.Nm +also initialises the +.Ev TMUX +variable with some internal information to allow commands to be executed +from inside, and the +.Ev TERM +variable with the correct terminal setting of +.Ql screen . +.Pp +Variables in both session and global environments may be marked as hidden. +Hidden variables are not passed into the environment of new processes and +instead can only be used by tmux itself (for example in formats, see the +.Sx FORMATS +section). +.Pp +Commands to alter and view the environment are: +.Bl -tag -width Ds +.Tg setenv +.It Xo Ic set-environment +.Op Fl Fhgru +.Op Fl t Ar target-session +.Ar name Op Ar value +.Xc +.D1 Pq alias: Ic setenv +Set or unset an environment variable. +If +.Fl g +is used, the change is made in the global environment; otherwise, it is applied +to the session environment for +.Ar target-session . +If +.Fl F +is present, then +.Ar value +is expanded as a format. +The +.Fl u +flag unsets a variable. +.Fl r +indicates the variable is to be removed from the environment before starting a +new process. +.Fl h +marks the variable as hidden. +.Tg showenv +.It Xo Ic show-environment +.Op Fl hgs +.Op Fl t Ar target-session +.Op Ar variable +.Xc +.D1 Pq alias: Ic showenv +Display the environment for +.Ar target-session +or the global environment with +.Fl g . +If +.Ar variable +is omitted, all variables are shown. +Variables removed from the environment are prefixed with +.Ql - . +If +.Fl s +is used, the output is formatted as a set of Bourne shell commands. +.Fl h +shows hidden variables (omitted by default). +.El +.Sh STATUS LINE +.Nm +includes an optional status line which is displayed in the bottom line of each +terminal. +.Pp +By default, the status line is enabled and one line in height (it may be +disabled or made multiple lines with the +.Ic status +session option) and contains, from left-to-right: the name of the current +session in square brackets; the window list; the title of the active pane +in double quotes; and the time and date. +.Pp +Each line of the status line is configured with the +.Ic status-format +option. +The default is made of three parts: configurable left and right sections (which +may contain dynamic content such as the time or output from a shell command, +see the +.Ic status-left , +.Ic status-left-length , +.Ic status-right , +and +.Ic status-right-length +options below), and a central window list. +By default, the window list shows the index, name and (if any) flag of the +windows present in the current session in ascending numerical order. +It may be customised with the +.Ar window-status-format +and +.Ar window-status-current-format +options. +The flag is one of the following symbols appended to the window name: +.Bl -column "Symbol" "Meaning" -offset indent +.It Sy "Symbol" Ta Sy "Meaning" +.It Li "*" Ta "Denotes the current window." +.It Li "-" Ta "Marks the last window (previously selected)." +.It Li "#" Ta "Window activity is monitored and activity has been detected." +.It Li "\&!" Ta "Window bells are monitored and a bell has occurred in the window." +.It Li "\[ti]" Ta "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." +.It Li "Z" Ta "The window's active pane is zoomed." +.El +.Pp +The # symbol relates to the +.Ic monitor-activity +window option. +The window name is printed in inverted colours if an alert (bell, activity or +silence) is present. +.Pp +The colour and attributes of the status line may be configured, the entire +status line using the +.Ic status-style +session option and individual windows using the +.Ic window-status-style +window option. +.Pp +The status line is automatically refreshed at interval if it has changed, the +interval may be controlled with the +.Ic status-interval +session option. +.Pp +Commands related to the status line are as follows: +.Bl -tag -width Ds +.Tg clearphist +.It Xo Ic clear-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 Pq alias: Ic clearphist +Clear status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then clear history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . +.It Xo Ic command-prompt +.Op Fl 1bFikN +.Op Fl I Ar inputs +.Op Fl p Ar prompts +.Op Fl t Ar target-client +.Op Fl T Ar prompt-type +.Op Ar template +.Xc +Open the command prompt in a client. +This may be used from inside +.Nm +to execute commands interactively. +.Pp +If +.Ar template +is specified, it is used as the command. +With +.Fl F , +.Ar template +is expanded as a format. +.Pp +If present, +.Fl I +is a comma-separated list of the initial text for each prompt. +If +.Fl p +is given, +.Ar prompts +is a comma-separated list of prompts which are displayed in order; otherwise +a single prompt is displayed, constructed from +.Ar template +if it is present, or +.Ql \&: +if not. +.Pp +Before the command is executed, the first occurrence of the string +.Ql %% +and all occurrences of +.Ql %1 +are replaced by the response to the first prompt, all +.Ql %2 +are replaced with the response to the second prompt, and so on for further +prompts. +Up to nine prompt responses may be replaced +.Po +.Ql %1 +to +.Ql %9 +.Pc . +.Ql %%% +is like +.Ql %% +but any quotation marks are escaped. +.Pp +.Fl 1 +makes the prompt only accept one key press, in this case the resulting input +is a single character. +.Fl k +is like +.Fl 1 +but the key press is translated to a key name. +.Fl N +makes the prompt only accept numeric key presses. +.Fl i +executes the command every time the prompt input changes instead of when the +user exits the command prompt. +.Pp +.Fl T +tells +.Nm +the prompt type. +This affects what completions are offered when +.Em Tab +is pressed. +Available types are: +.Ql command , +.Ql search , +.Ql target +and +.Ql window-target . +.Pp +The following keys have a special meaning in the command prompt, depending +on the value of the +.Ic status-keys +option: +.Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent +.It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Cancel command prompt" Ta "q" Ta "Escape" +.It Li "Delete from cursor to start of word" Ta "" Ta "C-w" +.It Li "Delete entire command" Ta "d" Ta "C-u" +.It Li "Delete from cursor to end" Ta "D" Ta "C-k" +.It Li "Execute command" Ta "Enter" Ta "Enter" +.It Li "Get next command from history" Ta "" Ta "Down" +.It Li "Get previous command from history" Ta "" Ta "Up" +.It Li "Insert top paste buffer" Ta "p" Ta "C-y" +.It Li "Look for completions" Ta "Tab" Ta "Tab" +.It Li "Move cursor left" Ta "h" Ta "Left" +.It Li "Move cursor right" Ta "l" Ta "Right" +.It Li "Move cursor to end" Ta "$" Ta "C-e" +.It Li "Move cursor to next word" Ta "w" Ta "M-f" +.It Li "Move cursor to previous word" Ta "b" Ta "M-b" +.It Li "Move cursor to start" Ta "0" Ta "C-a" +.It Li "Transpose characters" Ta "" Ta "C-t" +.El +.Pp +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. +.Tg confirm +.It Xo Ic confirm-before +.Op Fl by +.Op Fl c Ar confirm-key +.Op Fl p Ar prompt +.Op Fl t Ar target-client +.Ar command +.Xc +.D1 Pq alias: Ic confirm +Ask for confirmation before executing +.Ar command . +If +.Fl p +is given, +.Ar prompt +is the prompt to display; otherwise a prompt is constructed from +.Ar command . +It may contain the special character sequences supported by the +.Ic status-left +option. +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. +.Fl y +changes the default behaviour (if Enter alone is pressed) of the prompt to +run the command. +.Fl c +changes the confirmation key to +.Ar confirm-key ; +the default is +.Ql y . +.Tg menu +.It Xo Ic display-menu +.Op Fl OM +.Op Fl b Ar border-lines +.Op Fl c Ar target-client +.Op Fl C Ar starting-choice +.Op Fl H Ar selected-style +.Op Fl s Ar style +.Op Fl S Ar border-style +.Op Fl t Ar target-pane +.Op Fl T Ar title +.Op Fl x Ar position +.Op Fl y Ar position +.Ar name +.Ar key +.Ar command Op Ar argument ... +.Xc +.D1 Pq alias: Ic menu +Display a menu on +.Ar target-client . +.Ar target-pane +gives the target for any commands run from the menu. +.Pp +A menu is passed as a series of arguments: first the menu item name, +second the key shortcut (or empty for none) and third the command +to run when the menu item is chosen. +The name and command are formats, see the +.Sx FORMATS +and +.Sx STYLES +sections. +If the name begins with a hyphen (-), then the item is disabled (shown dim) and +may not be chosen. +The name may be empty for a separator line, in which case both the key and +command should be omitted. +.Pp +.Fl b +sets the type of characters used for drawing menu borders. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.Pp +.Fl H +sets the style for the selected menu item (see +.Sx STYLES ) . +.Pp +.Fl s +sets the style for the menu and +.Fl S +sets the style for the menu border (see +.Sx STYLES ) . +.Pp +.Fl T +is a format for the menu title (see +.Sx FORMATS ) . +.Pp +.Fl C +sets the menu item selected by default, if the menu is not bound to a mouse key +binding. +.Pp +.Fl x +and +.Fl y +give the position of the menu. +Both may be a row or column number, or one of the following special values: +.Bl -column "XXXXX" "XXXX" -offset indent +.It Sy "Value" Ta Sy "Flag" Ta Sy "Meaning" +.It Li "C" Ta "Both" Ta "The centre of the terminal" +.It Li "R" Ta Fl x Ta "The right side of the terminal" +.It Li "P" Ta "Both" Ta "The bottom left of the pane" +.It Li "M" Ta "Both" Ta "The mouse position" +.It Li "W" Ta "Both" Ta "The window position on the status line" +.It Li "S" Ta Fl y Ta "The line above or below the status line" +.El +.Pp +Or a format, which is expanded including the following additional variables: +.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent +.It Sy "Variable name" Ta Sy "Replaced with" +.It Li "popup_centre_x" Ta "Centered in the client" +.It Li "popup_centre_y" Ta "Centered in the client" +.It Li "popup_height" Ta "Height of menu or popup" +.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse" +.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse" +.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse" +.It Li "popup_mouse_top" Ta "Top at the mouse" +.It Li "popup_mouse_x" Ta "Mouse X position" +.It Li "popup_mouse_y" Ta "Mouse Y position" +.It Li "popup_pane_bottom" Ta "Bottom of the pane" +.It Li "popup_pane_left" Ta "Left of the pane" +.It Li "popup_pane_right" Ta "Right of the pane" +.It Li "popup_pane_top" Ta "Top of the pane" +.It Li "popup_status_line_y" Ta "Above or below the status line" +.It Li "popup_width" Ta "Width of menu or popup" +.It Li "popup_window_status_line_x" Ta "At the window position in status line" +.It Li "popup_window_status_line_y" Ta "At the status line showing the window" +.El +.Pp +Each menu consists of items followed by a key shortcut shown in brackets. +If the menu is too large to fit on the terminal, it is not displayed. +Pressing the key shortcut chooses the corresponding item. +If the mouse is enabled and the menu is opened from a mouse key binding, +releasing the mouse button with an item selected chooses that item and +releasing the mouse button without an item selected closes the menu. +.Fl O +changes this behaviour so that the menu does not close when the mouse button is +released without an item selected the menu is not closed and a mouse button +must be clicked to choose an item. +.Pp +.Fl M +tells +.Nm +the menu should handle mouse events; by default only menus opened from mouse +key bindings do so. +.Pp +The following keys are available in menus: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Choose selected item" +.It Li "Up" Ta "Select previous item" +.It Li "Down" Ta "Select next item" +.It Li "q" Ta "Exit menu" +.El +.Tg display +.It Xo Ic display-message +.Op Fl aCIlNpv +.Op Fl c Ar target-client +.Op Fl d Ar delay +.Op Fl t Ar target-pane +.Op Ar message +.Xc +.D1 Pq alias: Ic display +Display a message. +If +.Fl p +is given, the output is printed to stdout, otherwise it is displayed in the +.Ar target-client +status line for up to +.Ar delay +milliseconds. +If +.Ar delay +is not given, the +.Ic display-time +option is used; a delay of zero waits for a key press. +.Ql N +ignores key presses and closes only after the delay expires. +If +.Fl C +is given, the pane will continue to be updated while the message is displayed. +If +.Fl l +is given, +.Ar message +is printed unchanged. +Otherwise, the format of +.Ar message +is described in the +.Sx FORMATS +section; information is taken from +.Ar target-pane +if +.Fl t +is given, otherwise the active pane. +.Pp +.Fl v +prints verbose logging as the format is parsed and +.Fl a +lists the format variables and their values. +.Pp +.Fl I +forwards any input read from stdin to the empty pane given by +.Ar target-pane . +.Tg popup +.It Xo Ic display-popup +.Op Fl BCE +.Op Fl b Ar border-lines +.Op Fl c Ar target-client +.Op Fl d Ar start-directory +.Op Fl e Ar environment +.Op Fl h Ar height +.Op Fl s Ar border-style +.Op Fl S Ar style +.Op Fl t Ar target-pane +.Op Fl T Ar title +.Op Fl w Ar width +.Op Fl x Ar position +.Op Fl y Ar position +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic popup +Display a popup running +.Ar shell-command +on +.Ar target-client . +A popup is a rectangular box drawn over the top of any panes. +Panes are not updated while a popup is present. +.Pp +.Fl E +closes the popup automatically when +.Ar shell-command +exits. +Two +.Fl E +closes the popup only if +.Ar shell-command +exited with success. +.Pp +.Fl x +and +.Fl y +give the position of the popup, they have the same meaning as for the +.Ic display-menu +command. +.Fl w +and +.Fl h +give the width and height - both may be a percentage (followed by +.Ql % ) . +If omitted, half of the terminal size is used. +.Pp +.Fl B +does not surround the popup by a border. +.Pp +.Fl b +sets the type of characters used for drawing popup borders. +When +.Fl B +is specified, the +.Fl b +option is ignored. +See +.Ic popup-border-lines +for possible values for +.Ar border-lines . +.Pp +.Fl s +sets the style for the popup and +.Fl S +sets the style for the popup border (see +.Sx STYLES ) . +.Pp +.Fl e +takes the form +.Ql VARIABLE=value +and sets an environment variable for the popup; it may be specified multiple +times. +.Pp +.Fl T +is a format for the popup title (see +.Sx FORMATS ) . +.Pp +The +.Fl C +flag closes any popup on the client. +.Tg showphist +.It Xo Ic show-prompt-history +.Op Fl T Ar prompt-type +.Xc +.D1 Pq alias: Ic showphist +Display status prompt history for prompt type +.Ar prompt-type . +If +.Fl T +is omitted, then show history for all types. +See +.Ic command-prompt +for possible values for +.Ar prompt-type . +.El +.Sh BUFFERS +.Nm +maintains a set of named +.Em paste buffers . +Each buffer may be either explicitly or automatically named. +Explicitly named buffers are named when created with the +.Ic set-buffer +or +.Ic load-buffer +commands, or by renaming an automatically named buffer with +.Ic set-buffer +.Fl n . +Automatically named buffers are given a name such as +.Ql buffer0001 , +.Ql buffer0002 +and so on. +When the +.Ic buffer-limit +option is reached, the oldest automatically named buffer is deleted. +Explicitly named buffers are not subject to +.Ic buffer-limit +and may be deleted with the +.Ic delete-buffer +command. +.Pp +Buffers may be added using +.Ic copy-mode +or the +.Ic set-buffer +and +.Ic load-buffer +commands, and pasted into a window using the +.Ic paste-buffer +command. +If a buffer command is used and no buffer is specified, the most +recently added automatically named buffer is assumed. +.Pp +A configurable history buffer is also maintained for each window. +By default, up to 2000 lines are kept; this can be altered with the +.Ic history-limit +option (see the +.Ic set-option +command above). +.Pp +The buffer commands are as follows: +.Bl -tag -width Ds +.It Xo +.Ic choose-buffer +.Op Fl NryZ +.Op Fl F Ar format +.Op Fl f Ar filter +.Op Fl K Ar key-format +.Op Fl O Ar sort-order +.Op Fl t Ar target-pane +.Op Ar template +.Xc +Put a pane into buffer mode, where a buffer may be chosen interactively from +a list. +Each buffer is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. +.Fl Z +zooms the pane. +.Fl y +disables any confirmation prompts. +The following keys may be used in buffer mode: +.Bl -column "Key" "Function" -offset indent +.It Sy "Key" Ta Sy "Function" +.It Li "Enter" Ta "Paste selected buffer" +.It Li "Up" Ta "Select previous buffer" +.It Li "Down" Ta "Select next buffer" +.It Li "C-s" Ta "Search by name or content" +.It Li "n" Ta "Repeat last search forwards" +.It Li "N" Ta "Repeat last search backwards" +.It Li "t" Ta "Toggle if buffer is tagged" +.It Li "T" Ta "Tag no buffers" +.It Li "C-t" Ta "Tag all buffers" +.It Li "p" Ta "Paste selected buffer" +.It Li "P" Ta "Paste tagged buffers" +.It Li "d" Ta "Delete selected buffer" +.It Li "D" Ta "Delete tagged buffers" +.It Li "e" Ta "Open the buffer in an editor" +.It Li "f" Ta "Enter a format to filter items" +.It Li "O" Ta "Change sort field" +.It Li "r" Ta "Reverse sort order" +.It Li "v" Ta "Toggle preview" +.It Li "q" Ta "Exit mode" +.El +.Pp +After a buffer is chosen, +.Ql %% +is replaced by the buffer name in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "paste-buffer -p -b \[aq]%%\[aq]" is used. +.Pp +.Fl O +specifies the initial sort field: one of +.Ql time +(creation), +.Ql name +or +.Ql size . +.Fl r +reverses the sort order. +.Fl f +specifies an initial filter: the filter is a format - if it evaluates to zero, +the item in the list is not shown, otherwise it is shown. +If a filter would lead to an empty list, it is ignored. +.Fl F +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. +.Fl N +starts without the preview. +This command works only if at least one client is attached. +.Tg clearhist +.It Xo Ic clear-history +.Op Fl H +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic clearhist +Remove and free the history for the specified pane. +.Fl H +also removes all hyperlinks. +.Tg deleteb +.It Ic delete-buffer Op Fl b Ar buffer-name +.D1 Pq alias: Ic deleteb +Delete the buffer named +.Ar buffer-name , +or the most recently added automatically named buffer if not specified. +.Tg lsb +.It Xo Ic list-buffers +.Op Fl F Ar format +.Op Fl f Ar filter +.Xc +.D1 Pq alias: Ic lsb +List the global buffers. +.Fl F +specifies the format of each line and +.Fl f +a filter. +Only buffers for which the filter is true are shown. +See the +.Sx FORMATS +section. +.It Xo Ic load-buffer +.Op Fl w +.Op Fl b Ar buffer-name +.Op Fl t Ar target-client +.Ar path +.Xc +.Tg loadb +.D1 Pq alias: Ic loadb +Load the contents of the specified paste buffer from +.Ar path . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. +If +.Ar path +is +.Ql - , +the contents are read from stdin. +.Tg pasteb +.It Xo Ic paste-buffer +.Op Fl dpr +.Op Fl b Ar buffer-name +.Op Fl s Ar separator +.Op Fl t Ar target-pane +.Xc +.D1 Pq alias: Ic pasteb +Insert the contents of a paste buffer into the specified pane. +If not specified, paste into the current one. +With +.Fl d , +also delete the paste buffer. +When output, any linefeed (LF) characters in the paste buffer are replaced with +a separator, by default carriage return (CR). +A custom separator may be specified using the +.Fl s +flag. +The +.Fl r +flag means to do no replacement (equivalent to a separator of LF). +If +.Fl p +is specified, paste bracket control codes are inserted around the +buffer if the application has requested bracketed paste mode. +.Tg saveb +.It Xo Ic save-buffer +.Op Fl a +.Op Fl b Ar buffer-name +.Ar path +.Xc +.D1 Pq alias: Ic saveb +Save the contents of the specified paste buffer to +.Ar path . +The +.Fl a +option appends to rather than overwriting the file. +If +.Ar path +is +.Ql - , +the contents are written to stdout. +.It Xo Ic set-buffer +.Op Fl aw +.Op Fl b Ar buffer-name +.Op Fl t Ar target-client +.Tg setb +.Op Fl n Ar new-buffer-name +.Ar data +.Xc +.D1 Pq alias: Ic setb +Set the contents of the specified buffer to +.Ar data . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. +The +.Fl a +option appends to rather than overwriting the buffer. +The +.Fl n +option renames the buffer to +.Ar new-buffer-name . +.Tg showb +.It Xo Ic show-buffer +.Op Fl b Ar buffer-name +.Xc +.D1 Pq alias: Ic showb +Display the contents of the specified buffer. +.El +.Sh MISCELLANEOUS +Miscellaneous commands are as follows: +.Bl -tag -width Ds +.It Ic clock-mode Op Fl t Ar target-pane +Display a large clock. +.Tg if +.It Xo Ic if-shell +.Op Fl bF +.Op Fl t Ar target-pane +.Ar shell-command command +.Op Ar command +.Xc +.D1 Pq alias: Ic if +Execute the first +.Ar command +if +.Ar shell-command +(run with +.Pa /bin/sh ) +returns success or the second +.Ar command +otherwise. +Before being executed, +.Ar shell-command +is expanded using the rules specified in the +.Sx FORMATS +section, including those relevant to +.Ar target-pane . +With +.Fl b , +.Ar shell-command +is run in the background. +.Pp +If +.Fl F +is given, +.Ar shell-command +is not executed but considered success if neither empty nor zero (after formats +are expanded). +.Tg lock +.It Ic lock-server +.D1 Pq alias: Ic lock +Lock each client individually by running the command specified by the +.Ic lock-command +option. +.Tg run +.It Xo Ic run-shell +.Op Fl bC +.Op Fl c Ar start-directory +.Op Fl d Ar delay +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 Pq alias: Ic run +Execute +.Ar shell-command +using +.Pa /bin/sh +or (with +.Fl C ) +a +.Nm +command in the background without creating a window. +Before being executed, +.Ar shell-command +is expanded using the rules specified in the +.Sx FORMATS +section. +With +.Fl b , +the command is run in the background. +.Fl d +waits for +.Ar delay +seconds before starting the command. +If +.Fl c +is given, the current working directory is set to +.Ar start-directory . +If +.Fl C +is not given, any output to stdout is displayed in view mode (in the pane +specified by +.Fl t +or the current pane if omitted) after the command finishes. +If the command fails, the exit status is also displayed. +.Tg wait +.It Xo Ic wait-for +.Op Fl L | S | U +.Ar channel +.Xc +.D1 Pq alias: Ic wait +When used without options, prevents the client from exiting until woken using +.Ic wait-for +.Fl S +with the same channel. +When +.Fl L +is used, the channel is locked and any clients that try to lock the same +channel are made to wait until the channel is unlocked with +.Ic wait-for +.Fl U . +.El +.Sh EXIT MESSAGES +When a +.Nm +client detaches, it prints a message. +This may be one of: +.Bl -tag -width Ds +.It detached (from session ...) +The client was detached normally. +.It detached and SIGHUP +The client was detached and its parent sent the +.Dv SIGHUP +signal (for example with +.Ic detach-client +.Fl P ) . +.It lost tty +The client's +.Xr tty 4 +or +.Xr pty 4 +was unexpectedly destroyed. +.It terminated +The client was killed with +.Dv SIGTERM . +.It too far behind +The client is in control mode and became unable to keep up with the data from +.Nm . +.It exited +The server exited when it had no sessions. +.It server exited +The server exited when it received +.Dv SIGTERM . +.It server exited unexpectedly +The server crashed or otherwise exited without telling the client the reason. +.El +.Sh TERMINFO EXTENSIONS +.Nm +understands some unofficial extensions to +.Xr terminfo 5 . +It is not normally necessary to set these manually, instead the +.Ic terminal-features +option should be used. +.Bl -tag -width Ds +.It Em \&AX +An existing extension that tells +.Nm +the terminal supports default colours. +.It Em \&Bidi +Tell +.Nm +that the terminal supports the VTE bidirectional text extensions. +.It Em \&Cs , Cr +Set the cursor colour. +The first takes a single string argument and is used to set the colour; +the second takes no arguments and restores the default cursor colour. +If set, a sequence such as this may be used +to change the cursor colour from inside +.Nm : +.Bd -literal -offset indent +$ printf \[aq]\e033]12;red\e033\e\e\[aq] +.Ed +.Pp +The colour is an +.Xr X 7 +colour, see +.Xr XParseColor 3 . +.It Em \&Cmg, \&Clmg, \&Dsmg , \&Enmg +Set, clear, disable or enable DECSLRM margins. +These are set automatically if the terminal reports it is +.Em VT420 +compatible. +.It Em \&Dsbp , \&Enbp +Disable and enable bracketed paste. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Dseks , \&Eneks +Disable and enable extended keys. +.It Em \&Dsfcs , \&Enfcs +Disable and enable focus reporting. +These are set automatically if the +.Em XT +capability is present. +.It Em \&Hls +Set or clear a hyperlink annotation. +.It Em \&Nobr +Tell +.Nm +that the terminal does not use bright colors for bold display. +.It Em \&Rect +Tell +.Nm +that the terminal supports rectangle operations. +.It Em \&Smol +Enable the overline attribute. +.It Em \&Smulx +Set a styled underscore. +The single parameter is one of: 0 for no underscore, 1 for normal +underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted +underscore and 5 for dashed underscore. +.It Em \&Setulc , \&Setulc1, \&ol +Set the underscore colour or reset to the default. +.Em Setulc +is for RGB colours and +.Em Setulc1 +for ANSI or 256 colours. +The +.Em Setulc +argument is (red * 65536) + (green * 256) + blue where each is between 0 +and 255. +.It Em \&Ss , Se +Set or reset the cursor style. +If set, a sequence such as this may be used +to change the cursor to an underline: +.Bd -literal -offset indent +$ printf \[aq]\e033[4 q\[aq] +.Ed +.Pp +If +.Em Se +is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Swd +Set the opening sequence for the working directory notification. +The sequence is terminated using the standard +.Em fsl +capability. +.It Em \&Sxl +Indicates that the terminal supports SIXEL. +.It Em \&Sync +Start (parameter is 1) or end (parameter is 2) a synchronized update. +.It Em \&Tc +Indicate that the terminal supports the +.Ql direct colour +RGB escape sequence (for example, \ee[38;2;255;255;255m). +.Pp +If supported, this is used for the initialize colour escape sequence (which +may be enabled by adding the +.Ql initc +and +.Ql ccc +capabilities to the +.Nm +.Xr terminfo 5 +entry). +.Pp +This is equivalent to the +.Em RGB +.Xr terminfo 5 +capability. +.It Em \&Ms +Store the current buffer in the host terminal's selection (clipboard). +See the +.Em set-clipboard +option above and the +.Xr xterm 1 +man page. +.It Em \&XT +This is an existing extension capability that tmux uses to mean that the +terminal supports the +.Xr xterm 1 +title set sequences and to automatically set some of the capabilities above. +.El +.Sh CONTROL MODE +.Nm +offers a textual interface called +.Em control mode . +This allows applications to communicate with +.Nm +using a simple text-only protocol. +.Pp +In control mode, a client sends +.Nm +commands or command sequences terminated by newlines on standard input. +Each command will produce one block of output on standard output. +An output block consists of a +.Em %begin +line followed by the output (which may be empty). +The output block ends with a +.Em %end +or +.Em %error . +.Em %begin +and matching +.Em %end +or +.Em %error +have three arguments: an integer time (as seconds from epoch), command number +and flags (currently not used). +For example: +.Bd -literal -offset indent +%begin 1363006971 2 1 +0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) +%end 1363006971 2 1 +.Ed +.Pp +The +.Ic refresh-client +.Fl C +command may be used to set the size of a client in control mode. +.Pp +In control mode, +.Nm +outputs notifications. +A notification will never occur inside an output block. +.Pp +The following notifications are defined: +.Bl -tag -width Ds +.It Ic %client-detached Ar client +The client has detached. +.It Ic %client-session-changed Ar client session-id name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %config-error Ar error +An error has happened in a configuration file. +.It Ic %continue Ar pane-id +The pane has been continued after being paused (if the +.Ar pause-after +flag is set, see +.Ic refresh-client +.Fl A ) . +.It Ic %exit Op Ar reason +The +.Nm +client is exiting immediately, either because it is not attached to any session +or an error occurred. +If present, +.Ar reason +describes why the client exited. +.It Ic %extended-output Ar pane-id Ar age Ar ... \& : Ar value +New form of +.Ic %output +sent when the +.Ar pause-after +flag is set. +.Ar age +is the time in milliseconds for which tmux had buffered the output before it +was sent. +Any subsequent arguments up until a single +.Ql \&: +are for future use and should be ignored. +.It Xo Ic %layout-change +.Ar window-id +.Ar window-layout +.Ar window-visible-layout +.Ar window-flags +.Xc +The layout of a window with ID +.Ar window-id +changed. +The new layout is +.Ar window-layout . +The window's visible layout is +.Ar window-visible-layout +and the window flags are +.Ar window-flags . +.It Ic %message Ar message +A message sent with the +.Ic display-message +command. +.It Ic %output Ar pane-id Ar value +A window pane produced output. +.Ar value +escapes non-printable characters and backslash as octal \\xxx. +.It Ic %pane-mode-changed Ar pane-id +The pane with ID +.Ar pane-id +has changed mode. +.It Ic %paste-buffer-changed Ar name +Paste buffer +.Ar name +has been changed. +.It Ic %paste-buffer-deleted Ar name +Paste buffer +.Ar name +has been deleted. +.It Ic %pause Ar pane-id +The pane has been paused (if the +.Ar pause-after +flag is set). +.It Ic %session-changed Ar session-id Ar name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %session-renamed Ar name +The current session was renamed to +.Ar name . +.It Ic %session-window-changed Ar session-id Ar window-id +The session with ID +.Ar session-id +changed its active window to the window with ID +.Ar window-id . +.It Ic %sessions-changed +A session was created or destroyed. +.It Xo Ic %subscription-changed +.Ar name +.Ar session-id +.Ar window-id +.Ar window-index +.Ar pane-id ... \& : +.Ar value +.Xc +The value of the format associated with subscription +.Ar name +has changed to +.Ar value . +See +.Ic refresh-client +.Fl B . +Any arguments after +.Ar pane-id +up until a single +.Ql \&: +are for future use and should be ignored. +.It Ic %unlinked-window-add Ar window-id +The window with ID +.Ar window-id +was created but is not linked to the current session. +.It Ic %unlinked-window-close Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was closed. +.It Ic %unlinked-window-renamed Ar window-id +The window with ID +.Ar window-id , +which is not linked to the current session, was renamed. +.It Ic %window-add Ar window-id +The window with ID +.Ar window-id +was linked to the current session. +.It Ic %window-close Ar window-id +The window with ID +.Ar window-id +closed. +.It Ic %window-pane-changed Ar window-id Ar pane-id +The active pane in the window with ID +.Ar window-id +changed to the pane with ID +.Ar pane-id . +.It Ic %window-renamed Ar window-id Ar name +The window with ID +.Ar window-id +was renamed to +.Ar name . +.El +.Sh ENVIRONMENT +When +.Nm +is started, it inspects the following environment variables: +.Bl -tag -width LC_CTYPE +.It Ev EDITOR +If the command specified in this variable contains the string +.Ql vi +and +.Ev VISUAL +is unset, use vi-style key bindings. +Overridden by the +.Ic mode-keys +and +.Ic status-keys +options. +.It Ev HOME +The user's login directory. +If unset, the +.Xr passwd 5 +database is consulted. +.It Ev LC_CTYPE +The character encoding +.Xr locale 1 . +It is used for two separate purposes. +For output to the terminal, UTF-8 is used if the +.Fl u +option is given or if +.Ev LC_CTYPE +contains +.Qq UTF-8 +or +.Qq UTF8 . +Otherwise, only ASCII characters are written and non-ASCII characters +are replaced with underscores +.Pq Ql _ . +For input, +.Nm +always runs with a UTF-8 locale. +If en_US.UTF-8 is provided by the operating system, it is used and +.Ev LC_CTYPE +is ignored for input. +Otherwise, +.Ev LC_CTYPE +tells +.Nm +what the UTF-8 locale is called on the current system. +If the locale specified by +.Ev LC_CTYPE +is not available or is not a UTF-8 locale, +.Nm +exits with an error message. +.It Ev LC_TIME +The date and time format +.Xr locale 1 . +It is used for locale-dependent +.Xr strftime 3 +format specifiers. +.It Ev PWD +The current working directory to be set in the global environment. +This may be useful if it contains symbolic links. +If the value of the variable does not match the current working +directory, the variable is ignored and the result of +.Xr getcwd 3 +is used instead. +.It Ev SHELL +The absolute path to the default shell for new windows. +See the +.Ic default-shell +option for details. +.It Ev TMUX_TMPDIR +The parent directory of the directory containing the server sockets. +See the +.Fl L +option for details. +.It Ev VISUAL +If the command specified in this variable contains the string +.Ql vi , +use vi-style key bindings. +Overridden by the +.Ic mode-keys +and +.Ic status-keys +options. +.El +.Sh FILES +.Bl -tag -width "/etc/tmux.confXXX" -compact +.It Pa \[ti]/.tmux.conf +Default +.Nm +configuration file. +.It Pa /etc/tmux.conf +System-wide configuration file. +.El +.Sh EXAMPLES +To create a new +.Nm +session running +.Xr vi 1 : +.Pp +.Dl $ tmux new-session vi +.Pp +Most commands have a shorter form, known as an alias. +For new-session, this is +.Ic new : +.Pp +.Dl $ tmux new vi +.Pp +Alternatively, the shortest unambiguous form of a command is accepted. +If there are several options, they are listed: +.Bd -literal -offset indent +$ tmux n +ambiguous command: n, could be: new-session, new-window, next-window +.Ed +.Pp +Within an active session, a new window may be created by typing +.Ql C-b c +(Ctrl +followed by the +.Ql b +key +followed by the +.Ql c +key). +.Pp +Windows may be navigated with: +.Ql C-b 0 +(to select window 0), +.Ql C-b 1 +(to select window 1), and so on; +.Ql C-b n +to select the next window; and +.Ql C-b p +to select the previous window. +.Pp +A session may be detached using +.Ql C-b d +(or by an external event such as +.Xr ssh 1 +disconnection) and reattached with: +.Pp +.Dl $ tmux attach-session +.Pp +Typing +.Ql C-b \&? +lists the current key bindings in the current window; up and down may be used +to navigate the list or +.Ql q +to exit from it. +.Pp +Commands to be run when the +.Nm +server is started may be placed in the +.Pa \[ti]/.tmux.conf +configuration file. +Common examples include: +.Pp +Changing the default prefix key: +.Bd -literal -offset indent +set-option -g prefix C-a +unbind-key C-b +bind-key C-a send-prefix +.Ed +.Pp +Turning the status line off, or changing its colour: +.Bd -literal -offset indent +set-option -g status off +set-option -g status-style bg=blue +.Ed +.Pp +Setting other options, such as the default command, +or locking after 30 minutes of inactivity: +.Bd -literal -offset indent +set-option -g default-command "exec /bin/ksh" +set-option -g lock-after-time 1800 +.Ed +.Pp +Creating new key bindings: +.Bd -literal -offset indent +bind-key b set-option status +bind-key / command-prompt "split-window \[aq]exec man %%\[aq]" +bind-key S command-prompt "new-window -n %1 \[aq]ssh %1\[aq]" +.Ed +.Sh SEE ALSO +.Xr pty 4 +.Sh AUTHORS +.An Nicholas Marriott Aq Mt nicholas.marriott@gmail.com diff --git a/man/test_files/mdoc/top.1 b/man/test_files/mdoc/top.1 new file mode 100644 index 00000000..3f5aa686 --- /dev/null +++ b/man/test_files/mdoc/top.1 @@ -0,0 +1,520 @@ +.\" $OpenBSD: top.1,v 1.81 2022/03/31 17:27:28 naddy Exp $ +.\" +.\" Copyright (c) 1997, Jason Downs. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt TOP 1 +.Os +.Sh NAME +.Nm top +.Nd display and update information about the top CPU processes +.Sh SYNOPSIS +.Nm top +.Bk -words +.Op Fl 1bCHIinqStu +.Op Fl d Ar count +.Op Fl g Ar string +.Op Fl o Oo - Oc Ns Ar field +.Op Fl p Ar pid +.Op Fl s Ar time +.Op Fl T Oo - Oc Ns Ar rtable +.Op Fl U Oo - Oc Ns Ar user +.Op Ar number +.Ek +.Sh DESCRIPTION +.Nm +displays the top processes on the system and periodically updates this +information. +If standard output is an intelligent terminal (see below) then +as many processes as will fit on the terminal screen are displayed +by default. +Otherwise, a good number of them are shown (around 20). +Raw CPU percentage is used to rank the processes. +If +.Ar number +is given, then the top +.Ar number +processes will be displayed instead of the default. +.Pp +.Nm +makes a distinction between terminals that support advanced capabilities +and those that do not. +This distinction affects the choice of defaults for certain options. +In the remainder of this document, an +.Em intelligent +terminal is one that supports cursor addressing, clear screen, and clear +to end of line. +Conversely, a +.Em dumb +terminal is one that does not support such features. +If the output of +.Nm +is redirected to a file, it acts as if it were being run on a dumb +terminal. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 1 +Display combined CPU statistics for all processors on a single line +instead of one line per CPU. +If there are more than 8 CPUs detected in the system, this option +is automatically enabled. +.It Fl b +Use +.Em batch +mode. +In this mode, all input from the terminal is ignored. +Interrupt characters (such as +.Ql ^C +and +.Ql ^\e ) +still have an effect. +This is the default on a dumb terminal, or when the output is not a terminal. +.It Fl C +Show command line arguments +as well as the process itself. +.It Fl d Ar count +Show only +.Ar count +displays, then exit. +A display is considered to be one update of the screen. +This option allows the user to select the number of displays +to be shown before +.Nm +automatically exits. +For intelligent terminals, no upper limit is set. +The default is 1 for dumb terminals. +.It Fl g Ar string +Display only processes that contain +.Ar string +in their command name. +If displaying of arguments is enabled, the arguments are searched too. +.It Fl H +Show process threads in the display. +Normally, only the main process is shown. +This option makes all threads visible. +.It Fl I +Do not display idle processes. +By default, +.Nm +displays both active and idle processes. +.It Fl i +Use +.Em interactive +mode. +In this mode, any input is immediately read for processing. +See the section on +.Sx INTERACTIVE MODE +for an explanation of which keys perform what functions. +After the command +is processed, the screen will immediately be updated, even if the command was +not understood. +This mode is the default when standard output is an intelligent terminal. +.It Fl n +Use +.Em non-interactive +mode. +This is identical to +.Em batch +mode. +.It Fl o Oo - Oc Ns Ar field +Sort the process display area using the specified +.Ar field +as the primary key. +The field name is the name of the column as seen in the output, +but in lower case. +The +.Sq - +prefix reverses the order. +The +.Ox +version of +.Nm +supports +.Ar cpu , +.Ar size , +.Ar res , +.Ar time , +.Ar pri , +.Ar pid , +and +.Ar command . +.It Fl p Ar pid +Show only the process +.Ar pid . +.It Fl q +Renice +.Nm +to \-20 so that it will run faster. +This can be used when the system is +being very sluggish to improve the possibility of discovering the problem. +This option can only be used by root. +.It Fl S +Show system processes in the display. +Normally, system processes such as the pager and the swapper are not shown. +This option makes them visible. +.It Fl s Ar time +Set the delay between screen updates to +.Ar time +seconds. +The value may be fractional, to permit delays of less than 1 second. +The default delay between updates is 5 seconds. +.It Fl T Oo - Oc Ns Ar rtable +Display only processes associated with the specified routing table +.Ar rtable . +.Sq T+ +shows processes associated with all routing tables. +The +.Sq - +prefix hides processes associated with a single routing table. +.It Fl t +Display routing tables. +By default, routing tables are not shown. +.It Fl U Oo - Oc Ns Ar user +Show only those processes owned by username or UID +.Ar user . +The prefix +.Sq - +hides processes owned by that user. +.It Fl u +Do not take the time to map UID numbers to usernames. +Normally, +.Nm +will read as much of the password database as is necessary to map +all the user ID numbers it encounters into login names. +This option +disables all that, while possibly decreasing execution time. +The UID numbers are displayed instead of the names. +.El +.Pp +Both +.Ar count +and +.Ar number +fields can be specified as +.Li infinite , +indicating that they can stretch as far as possible. +This is accomplished by using any proper prefix of the keywords +.Li infinity , +.Li maximum , +or +.Li all . +The default for +.Ar count +on an intelligent terminal is, in fact, +.Li infinity . +.Pp +The environment variable +.Ev TOP +is examined for options before the command line is scanned. +This enables users to set their own defaults. +The number of processes to display +can also be specified in the environment variable +.Ev TOP . +.Pp +The options +.Fl I , +.Fl S , +and +.Fl u +are actually toggles. +A second specification of any of these options +will negate the first. +Thus a user who has the environment variable +.Ev TOP +set to +.Dq -I +may use the command +.Dq top -I +to see idle processes. +.Sh INTERACTIVE MODE +When +.Nm +is running in +.Em interactive mode , +it reads commands from the terminal and acts upon them accordingly. +In this mode, the terminal is put in +.Dv CBREAK , +so that a character will be processed as soon as it is typed. +Almost always, a key will be pressed when +.Nm +is between displays; that is, while it is waiting for +.Ar time +seconds to elapse. +If this is the case, the command will be +processed and the display will be updated immediately thereafter +(reflecting any changes that the command may have specified). +This happens even if the command was incorrect. +If a key is pressed while +.Nm +is in the middle of updating the display, it will finish the update and +then process the command. +Some commands require additional information, +and the user will be prompted accordingly. +While typing this information +in, the user's erase and kill keys (as set up by the command +.Xr stty 1 ) +are recognized, and a newline terminates the input. +.Pp +These commands are currently recognized (^L refers to control-L): +.Bl -tag -width XxXXXX +.It h | \&? +Display a summary of the commands (help screen). +.It ^L +Redraw the screen. +.It +Update the screen. +.It q +Quit +.Nm . +.El +.Bl -tag -width XxXXXX +.It + +Reset any filters put in place by the +.Sq g , +.Sq p , +and +.Sq u +interactive commands, +or their command line equivalents, +or any process highlighting put in place by the +.Sq P +interactive command. +.It 1 +Toggle the display of per CPU or combined CPU statistics. +.It 9 | 0 +Scroll up/down the process list by one line. +.It \&( | \&) +Scroll up/down the process list by screen half. +.It C +Toggle the display of process command line arguments. +.It d Ar count +Show only +.Ar count +displays, +then exit. +.It e +Display a list of system errors (if any) generated by the last +.Li kill +or +.Li renice +command. +.It g|/ Ar string +Display only processes that contain +.Ar string +in their command name. +If displaying of arguments is enabled, the arguments are searched too. +.Sq g+ +or +.Sq /+ +shows all processes. +.It H +Toggle the display of process threads. +.It I | i +Toggle the display of idle processes. +.It Xo k +.Op - Ns Ar sig +.Ar pid +.Xc +Send signal +.No - Ns Ar sig +.Pf ( Dv TERM +by default) to process +.Ar pid . +This acts similarly to the command +.Xr kill 1 . +.It n|# Ar count +Show +.Ar count +processes. +.It o Oo - Oc Ns Ar field +Sort the process display area using the specified +.Ar field +as the primary key. +The +.Sq - +prefix reverses the order. +Values are the same as for the +.Fl o +flag, as detailed above. +.It P Ar pid +Highlight a specific process, selected by +.Ar pid . +.Sq P+ +removes process highlighting. +.It p Ar pid +Show only the process +.Ar pid . +.Sq p+ +shows all processes. +.It r Ar count pid +Change the priority (the +.Em nice ) +of a list of processes to +.Ar count +for process +.Ar pid . +This acts similarly to the command +.Xr renice 8 . +.It S +Toggle the display of system processes. +.It s Ar time +Set the delay between screen updates to +.Ar time +seconds. +.It T Oo - Oc Ns Ar rtable +Display only processes associated with the specified routing table +.Ar rtable . +.Sq T+ +shows processes associated with all routing tables. +The +.Sq - +prefix hides processes associated with a single routing table. +.It t +Toggle the display of routing tables. +.It u Oo - Oc Ns Ar user +Show only those processes owned by username or UID +.Ar user . +.Sq u+ +shows processes belonging to all users. +The +.Sq - +prefix hides processes belonging to a single +.Ar user . +.El +.Sh THE DISPLAY +.\" The actual display varies depending on the specific variant of Unix +.\" that the machine is running. This description may not exactly match +.\" what is seen by top running on this particular machine. Differences +.\" are listed at the end of this manual entry. +.\" .Pp +The top few lines of the display show general information +about the state of the system, including +.\" the last process ID assigned to a process, +.\" (on most systems), +the three load average numbers, +the hostname, +the current time, +the number of existing processes, +the number of processes in each state +(starting, running, idle, stopped, zombie, dead, and on processor), +and a percentage of time spent in each of the processor states +(user, nice, system, spinning, interrupt, and idle). +It also includes information about physical and virtual memory allocation. +The load average numbers give the number of jobs in the run queue averaged +over 1, 5, and 15 minutes. +.Pp +The remainder of the screen displays information about individual +processes. +This display is similar in spirit to +.Xr ps 1 +but it is not exactly the same. +The following fields are displayed: +.Bl -tag -width USERNAME -offset indent +.It PID +The process ID. +.It USERNAME +The name of the process's owner. +.It TID +The thread ID, used instead of USERNAME if +.Fl H +is specified. +.It UID +Used instead of USERNAME if +.Fl u +is specified. +.It PRI +The current priority of the process. +.It NICE +The nice amount (in the range \-20 to 20). +.It SIZE +The total size of the process (the text, data, and stack segments). +.It RES +The current amount of resident memory. +.It STATE +The current state (one of +.Li start , +.Li run , +.Li sleep , +.Li stop , +.Li idle , +.Li zomb , +.Li dead , +or +.Li onproc ) . +On multiprocessor systems, this is followed by a slash and the CPU +number on which the process is bound. +.It WAIT +A description of the wait channel the process is sleeping on if it's +asleep. +.It RTABLE +The routing table, used instead of WAIT if +.Fl t +is specified. +.It TIME +The number of system and user CPU seconds that the process has used. +.It CPU +The raw percentage of CPU usage and the default field on which the +display is sorted. +.It COMMAND +The name (and arguments if +.Fl C +is specified) of the command that the process is currently running. +.El +.Sh ENVIRONMENT +.Bl -tag -width Ev +.It Ev TOP +User-configurable defaults for options. +.El +.Sh FILES +.Bl -tag -width "/etc/passwdXXX" -compact +.It Pa /dev/kmem +kernel memory +.It Pa /dev/mem +physical memory +.It Pa /etc/passwd +used to map user ID to user +.It Pa /bsd +kernel image +.El +.Sh SEE ALSO +.Xr fstat 1 , +.Xr kill 1 , +.Xr netstat 1 , +.Xr ps 1 , +.Xr stty 1 , +.Xr systat 1 , +.Xr mem 4 , +.Xr iostat 8 , +.Xr pstat 8 , +.Xr renice 8 , +.Xr vmstat 8 +.Sh AUTHORS +.An William LeFebvre , +EECS Department, Northwestern University +.Sh CAVEATS +As with +.Xr ps 1 , +.Nm +only provides snapshots of a constantly changing system. diff --git a/man/test_files/mdoc/truncate.2 b/man/test_files/mdoc/truncate.2 new file mode 100644 index 00000000..63568410 --- /dev/null +++ b/man/test_files/mdoc/truncate.2 @@ -0,0 +1,152 @@ +.\" $OpenBSD: truncate.2,v 1.21 2022/05/23 15:17:11 millert Exp $ +.\" $NetBSD: truncate.2,v 1.7 1995/02/27 12:39:00 cgd Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)truncate.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: May 23 2022 $ +.Dt TRUNCATE 2 +.Os +.Sh NAME +.Nm truncate , +.Nm ftruncate +.Nd truncate or extend a file to a specified length +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn truncate "const char *path" "off_t length" +.Ft int +.Fn ftruncate "int fd" "off_t length" +.Sh DESCRIPTION +.Fn truncate +causes the file named by +.Fa path +or referenced by +.Fa fd +to be truncated or extended to +.Fa length +bytes in size. +If the file was larger than this size, the extra data is lost. +If the file was smaller than this size, it will be extended as if by +writing bytes with the value zero. +With +.Fn ftruncate , +the file must be open for writing. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +.Fn truncate +and +.Fn ftruncate +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa length +is a negative value. +.It Bq Er EFBIG +The +.Fa length +exceeds the process's file size limit or the maximum file size +of the underlying filesystem. +.It Bq Er EIO +An I/O error occurred updating the inode. +.El +.Pp +In addition, +.Fn truncate +may return the following errors: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire pathname (including the terminating NUL) +exceeded +.Dv PATH_MAX +bytes. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er EACCES +The named file is not writable by the user. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er EISDIR +The named file is a directory. +.It Bq Er EROFS +The named file resides on a read-only file system. +.It Bq Er ETXTBSY +The file is a pure procedure (shared text) file that is being executed. +.It Bq Er EFAULT +.Fa path +points outside the process's allocated address space. +.El +.Pp +.Fn ftruncate +may return the following errors: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa fd +is not a valid descriptor. +.It Bq Er EINVAL +The +.Fa fd +references a socket, not a file. +.It Bq Er EINVAL +The +.Fa fd +is not open for writing. +.El +.Sh SEE ALSO +.Xr open 2 +.Sh STANDARDS +The +.Fn truncate +and +.Fn ftruncate +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn truncate +and +.Fn ftruncate +system calls first appeared in +.Bx 4.1c . +.Sh BUGS +These calls should be generalized to allow ranges of bytes in a file +to be discarded. +.Pp +Use of +.Fn truncate +to extend a file is not portable. diff --git a/man/test_files/mdoc/umask.2 b/man/test_files/mdoc/umask.2 new file mode 100644 index 00000000..61d77997 --- /dev/null +++ b/man/test_files/mdoc/umask.2 @@ -0,0 +1,94 @@ +.\" $OpenBSD: umask.2,v 1.13 2022/02/18 23:17:14 jsg Exp $ +.\" $NetBSD: umask.2,v 1.6 1995/02/27 12:39:06 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)umask.2 8.1 (Berkeley) 6/4/93 +.\" +.Dd $Mdocdate: February 18 2022 $ +.Dt UMASK 2 +.Os +.Sh NAME +.Nm umask +.Nd set file creation mode mask +.Sh SYNOPSIS +.In sys/types.h +.In sys/stat.h +.Ft mode_t +.Fn umask "mode_t numask" +.Sh DESCRIPTION +The +.Fn umask +routine sets the process's file mode creation mask to +.Fa numask +and returns the previous value of the mask. +Only the read, write, and execute file permission bits of +.Fa numask +are honored, all others are ignored. +.Pp +The file mode creation mask is used by the +.Xr bind 2 , +.Xr mkdir 2 , +.Xr mkdirat 2 , +.Xr mkfifo 2 , +.Xr mkfifoat 2 , +.Xr mknod 2 , +.Xr mknodat 2 , +.Xr open 2 +and +.Xr openat 2 +system calls to turn off corresponding bits requested in the file mode +(see +.Xr chmod 2 ) . +This clearing allows users to restrict the default access to their files. +.Pp +The default mask value is S_IWGRP|S_IWOTH (022, write access for the +owner only). +Child processes inherit the mask of the calling process. +.Sh RETURN VALUES +The previous value of the file mode mask is returned by the call. +.Sh ERRORS +The +.Fn umask +function is always successful. +.Sh SEE ALSO +.Xr chmod 2 , +.Xr mkdir 2 , +.Xr mkfifo 2 , +.Xr mknod 2 , +.Xr open 2 +.Sh STANDARDS +The +.Fn umask +function conforms to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn umask +system call first appeared in +.At v7 . diff --git a/man/test_files/mdoc/w.1 b/man/test_files/mdoc/w.1 new file mode 100644 index 00000000..cebc2845 --- /dev/null +++ b/man/test_files/mdoc/w.1 @@ -0,0 +1,134 @@ +.\" $OpenBSD: w.1,v 1.23 2018/07/13 16:59:46 cheloha Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)w.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: July 13 2018 $ +.Dt W 1 +.Os +.Sh NAME +.Nm w +.Nd display users who are logged on and what they are doing +.Sh SYNOPSIS +.Nm w +.Op Fl ahi +.Op Fl M Ar core +.Op Fl N Ar system +.Op Ar user +.Sh DESCRIPTION +The +.Nm +utility prints a summary of the current activity on the system, +including what each user is doing. +The first line displays the current time of day, how long the system has +been running, the number of users logged into the system, and the load +averages. +The load average numbers give the number of jobs in the run queue averaged +over 1, 5 and 15 minutes. +.Pp +The fields output are the user's login name, the name of the terminal the +user is on, the host from which the user is logged in, the time the user +logged on, the time since the user last typed anything, +and the name and arguments of the current process. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Attempt to translate network addresses into names. +.It Fl h +Suppress the heading. +.It Fl i +Output is sorted by idle time. +.It Fl M Ar core +Extract values associated with the name list from the specified +.Ar core +instead of the running kernel. +.It Fl N Ar system +Extract the name list from the specified +.Ar system +instead of the running kernel. +.El +.Pp +If a +.Ar user +name is specified, the output is restricted to that user. +.Sh FILES +.Bl -tag -width /var/run/utmp -compact +.It Pa /var/run/utmp +list of users on the system +.El +.Sh SEE ALSO +.Xr finger 1 , +.Xr ps 1 , +.Xr uptime 1 , +.Xr who 1 , +.Xr utmp 5 +.Sh STANDARDS +The +.Fl f , +.Fl l , +.Fl s , +.Fl u , +and +.Fl w +flags are no longer supported. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 2 . +.Sh AUTHORS +.An Mary Ann Horton . +.Sh BUGS +The notion of the +.Dq current process +is muddy. +Currently, the highest numbered process on the terminal that is not ignoring +interrupts is used or, if there is none, the highest numbered process on the +terminal. +This fails, for example, in critical sections of programs like the shell +and editor, or when faulty programs running in the background fork and fail +to ignore interrupts. +(In cases where no process can be found, +.Nm +prints +.Dq \- . ) +.Pp +Background processes are not shown, even though they account for +much of the load on the system. +.Pp +Sometimes processes, typically those in the background, are printed with +null or garbaged arguments. +In these cases, the name of the command is printed in parentheses. +.Pp +The +.Nm +utility does not know about the new conventions for detection of background +jobs. +It will sometimes find a background job instead of the right one. diff --git a/man/test_files/mdoc/wall.1 b/man/test_files/mdoc/wall.1 new file mode 100644 index 00000000..3ecbb5a9 --- /dev/null +++ b/man/test_files/mdoc/wall.1 @@ -0,0 +1,83 @@ +.\" $OpenBSD: wall.1,v 1.13 2020/02/08 01:09:58 jsg Exp $ +.\" $NetBSD: wall.1,v 1.3 1994/11/17 07:17:57 jtc Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)wall.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd $Mdocdate: February 8 2020 $ +.Dt WALL 1 +.Os +.Sh NAME +.Nm wall +.Nd write a message to users +.Sh SYNOPSIS +.Nm wall +.Op Fl g Ar group +.Op Ar file +.Sh DESCRIPTION +.Nm +displays the contents of +.Ar file , +or, by default, its standard input, on the terminals of all +currently logged in users. +.Pp +Only the superuser can write on the +terminals of users who have chosen +to deny messages or are using a program which +automatically denies messages. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl g Ar group +Send messages to users in this group. +This option may be specified +multiple times, and any user in any of the specified groups will +receive the message. +.El +.Pp +Since the sender's +.Xr locale 1 +need not match the receivers' locales, +.Nm +deliberately ignores the +.Ev LC_CTYPE +environment variable and allows ASCII printable and space characters +only. +Non-printable characters and UTF-8 characters are replaced with +question marks. +.Sh SEE ALSO +.Xr mesg 1 , +.Xr talk 1 , +.Xr write 1 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/man/test_files/mdoc/write.2 b/man/test_files/mdoc/write.2 new file mode 100644 index 00000000..cf7a6fba --- /dev/null +++ b/man/test_files/mdoc/write.2 @@ -0,0 +1,329 @@ +.\" $OpenBSD: write.2,v 1.45 2023/02/05 12:33:17 jsg Exp $ +.\" $NetBSD: write.2,v 1.6 1995/02/27 12:39:43 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)write.2 8.5 (Berkeley) 4/2/94 +.\" +.Dd $Mdocdate: February 5 2023 $ +.Dt WRITE 2 +.Os +.Sh NAME +.Nm write , +.Nm writev , +.Nm pwrite , +.Nm pwritev +.Nd write output +.Sh SYNOPSIS +.In unistd.h +.Ft ssize_t +.Fn write "int d" "const void *buf" "size_t nbytes" +.Ft ssize_t +.Fn pwrite "int d" "const void *buf" "size_t nbytes" "off_t offset" +.Pp +.In sys/uio.h +.Ft ssize_t +.Fn writev "int d" "const struct iovec *iov" "int iovcnt" +.In sys/types.h +.In sys/uio.h +.Ft ssize_t +.Fn pwritev "int d" "const struct iovec *iov" "int iovcnt" "off_t offset" +.Sh DESCRIPTION +.Fn write +attempts to write +.Fa nbytes +of data to the object referenced by the descriptor +.Fa d +from the buffer pointed to by +.Fa buf . +.Fn writev +performs the same action, but gathers the output data from the +.Fa iovcnt +buffers specified by the members of the +.Fa iov +array: iov[0], iov[1], ..., iov[iovcnt-1]. +.Fn pwrite +and +.Fn pwritev +perform the same functions, but write to the specified position +.Fa offset +in the file without modifying the file pointer. +.Pp +For +.Fn writev +and +.Fn pwritev , +the +.Vt iovec +structure is defined as: +.Bd -literal -offset indent +struct iovec { + void *iov_base; + size_t iov_len; +}; +.Ed +.Pp +Each +.Vt iovec +entry specifies the base address and length of an area +in memory from which data should be written. +.Fn writev +and +.Fn pwritev +will always write a complete area before proceeding to the next. +.Pp +On objects capable of seeking, the +.Fn write +starts at a position given by the pointer associated with +.Fa d +(see +.Xr lseek 2 ) . +Upon return from +.Fn write , +the pointer is incremented by the number of bytes which were written. +If a file was opened with the +.Dv O_APPEND +flag (see +.Xr open 2 ) , +calls to +.Fn write +or +.Fn writev +will automatically set the pointer to the end of the file before writing. +.Pp +Objects that are not capable of seeking always write from the current +position. +The value of the pointer associated with such an object is undefined. +.Pp +If the real user is not the superuser, then +.Fn write +clears the set-user-ID bit on a file. +This prevents penetration of system security by a user who +.Dq captures +a writable set-user-ID file owned by the superuser. +.Pp +If +.Fn write +succeeds, it will update the st_ctime and st_mtime fields of the file's +meta-data (see +.Xr stat 2 ) . +.Pp +When using non-blocking I/O on objects such as sockets that are subject +to flow control, +.Fn write +and +.Fn writev +may write fewer bytes than requested; the return value must be noted, +and the remainder of the operation should be retried when possible. +.Pp +Note that +.Fn writev +and +.Fn pwritev +will fail if the value of +.Fa iovcnt +exceeds the constant +.Dv IOV_MAX . +.Sh RETURN VALUES +Upon successful completion the number of bytes which were written +is returned. +Otherwise, a \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh EXAMPLES +A typical loop allowing partial writes looks like this: +.Bd -literal +const char *buf; +size_t bsz, off; +ssize_t nw; +int d; + +for (off = 0; off < bsz; off += nw) + if ((nw = write(d, buf + off, bsz - off)) == 0 || nw == -1) + err(1, "write"); +.Ed +.Sh ERRORS +.Fn write , +.Fn pwrite , +.Fn writev , +and +.Fn pwritev +will fail and the file pointer will remain unchanged if: +.Bl -tag -width Er +.It Bq Er EBADF +.Fa d +is not a valid descriptor open for writing. +.It Bq Er EFBIG +An attempt was made to write a file that exceeds the process's +file size limit or the maximum file size. +.It Bq Er ENOSPC +There is no free space remaining on the file system containing the file. +.It Bq Er EDQUOT +The user's quota of disk blocks on the file system containing the file +has been exhausted. +.It Bq Er EINTR +A write to a slow device +(i.e. one that might block for an arbitrary amount of time) +was interrupted by the delivery of a signal +before any data could be written. +.It Bq Er EIO +An I/O error occurred while reading from or writing to the file system. +.It Bq Er EFAULT +Part of +.Fa buf +points outside the process's allocated address space. +.El +.Pp +In addition, +.Fn write +and +.Fn writev +may return the following errors: +.Bl -tag -width Er +.It Bq Er EPIPE +An attempt is made to write to a pipe that is not open +for reading by any process. +.It Bq Er EPIPE +An attempt is made to write to a socket of type +.Dv SOCK_STREAM +that is not connected to a peer socket. +.It Bq Er EAGAIN +The file was marked for non-blocking I/O, and no data could be +written immediately. +.It Bq Er ENETDOWN +The destination address specified a network that is down. +.It Bq Er EDESTADDRREQ +The destination is no longer available when writing to a +.Ux Ns -domain +datagram socket on which +.Xr connect 2 +had been used to set a destination address. +.It Bq Er EIO +The process is a member of a background process attempting to write +to its controlling terminal, +.Dv TOSTOP +is set on the terminal, +the process isn't ignoring the +.Dv SIGTTOUT +signal and the thread isn't blocking the +.Dv SIGTTOUT +signal, +and either the process was created with +.Xr vfork 2 +and hasn't successfully executed one of the exec functions or +the process group is orphaned. +.El +.Pp +.Fn write +and +.Fn pwrite +may return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa nbytes +was larger than +.Dv SSIZE_MAX . +.El +.Pp +.Fn pwrite +and +.Fn pwritev +may return the following error: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa offset +was negative. +.It Bq Er ESPIPE +.Fa d +is associated with a pipe, socket, FIFO, or tty. +.El +.Pp +.Fn writev +and +.Fn pwritev +may return one of the following errors: +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa iovcnt +was less than or equal to 0, or greater than +.Dv IOV_MAX . +.It Bq Er EINVAL +The sum of the +.Fa iov_len +values in the +.Fa iov +array overflowed an +.Vt ssize_t . +.It Bq Er EFAULT +Part of +.Fa iov +points outside the process's allocated address space. +.It Bq Er ENOBUFS +The system lacked sufficient buffer space or a queue was full. +.El +.Sh SEE ALSO +.Xr fcntl 2 , +.Xr lseek 2 , +.Xr open 2 , +.Xr pipe 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr termios 4 +.Sh STANDARDS +The +.Fn write , +.Fn writev , +and +.Fn pwrite +functions conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn write +function call appeared in +.At v1 , +.Fn pwrite +in +.At V.4 , +.Fn writev +in +.Bx 4.1c , +and +.Fn pwritev +in +.Ox 2.7 . +.Sh CAVEATS +Error checks should explicitly test for \-1. +On some platforms, if +.Fa nbytes +is larger than +.Dv SSIZE_MAX +but smaller than +.Dv SIZE_MAX +\- 2, the return value of an error-free call +may appear as a negative number distinct from \-1. diff --git a/man/test_files/mdoc/ypconnect.2 b/man/test_files/mdoc/ypconnect.2 new file mode 100644 index 00000000..8ca33ace --- /dev/null +++ b/man/test_files/mdoc/ypconnect.2 @@ -0,0 +1,80 @@ +.\" $OpenBSD: ypconnect.2,v 1.3 2022/07/21 22:45:06 deraadt Exp $ +.\" +.\" Copyright (c) 2022 Theo de Raadt +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: July 21 2022 $ +.Dt YPCONNECT 2 +.Os +.Sh NAME +.Nm ypconnect +.Nd create connected socket to a YP server +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn ypconnect "int type" +.Sh DESCRIPTION +The +.Fn ypconnect +system call creates a pre-connected +.Va SOCK_STREAM +or +.Va SOCK_DGRAM +socket to a YP server (either the original +.Xr ypserv 8 +or +.Xr ypldap 8 ) +for use by internal library routines. +It verifies that the domainname is set, that +.Xr ypbind 8 +has found a server and created an advisory locked binding file, +and then creates the connected socket based upon the binding file. +This type of socket is restricted in various ways and is not +general purpose. +.Nm +is only intended for use by internal libc YP functions. +.Sh RETURN VALUES +If successful, +.Fn ypconnect +returns a non-negative integer, the socket file descriptor. +Otherwise, a value of \-1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn ypconnect +will fail if: +.Bl -tag -width Er +.It Bq Er EAFNOSUPPORT +The YP subsystem is not active. +.It Bq Er EFTYPE +The YP binding file is strange. +.It Bq Er EOWNERDEAD +The YP binding file is not locked. +YP subsystem is not active. +.It Bq Er EMFILE +The per-process descriptor table is full. +.It Bq Er ENFILE +The system file table is full. +.It Bq Er ENOBUFS +Insufficient resources were available in the system to perform the operation. +.El +.Sh SEE ALSO +.Xr connect 2 , +.Xr socket 2 , +.Xr ypbind 8 +.Sh HISTORY +The +.Fn ypconnect +function first appeared in +.Ox 7.2 . diff --git a/man/tests/man-tests.rs b/man/tests/man-tests.rs new file mode 100644 index 00000000..f9abe8da --- /dev/null +++ b/man/tests/man-tests.rs @@ -0,0 +1,10 @@ +// +// Copyright (c) 2024 Jeff Garzik +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +mod man; diff --git a/man/tests/man/mod.rs b/man/tests/man/mod.rs new file mode 100644 index 00000000..a0ef8ae1 --- /dev/null +++ b/man/tests/man/mod.rs @@ -0,0 +1,464 @@ +// +// Copyright (c) 2024 Hemi Labs, Inc. +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +#[cfg(test)] +mod tests { + use std::process::Command; + + // ------------------------------------------------------------------------- + // -k / --apropos + // ------------------------------------------------------------------------- + #[test] + fn apropos_no_keywords() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-k", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -k"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn apropos_with_keywords() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-k", "printf", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -k printf"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -f / --whatis + // ------------------------------------------------------------------------- + #[test] + fn whatis_no_arguments() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-f", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -f"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn whatis_one_argument() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-f", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -f ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -a / --all + // ------------------------------------------------------------------------- + #[test] + fn all_flag_without_names() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-a", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -a"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn all_flag_with_names() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-a", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -a ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -C / --config-file + // ------------------------------------------------------------------------- + #[test] + fn config_file_invalid() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-C", "non_existent.conf", "ls"]) + .output() + .expect("Failed to run man -C non_existent.conf ls"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("configuration file was not found"), + "Expected 'configuration file was not found' error, got:\n{stderr}" + ); + } + + #[test] + fn config_file_without_names() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-C", "man.test.conf"]) + .output() + .expect("Failed to run man -C /etc/man.conf"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + // ------------------------------------------------------------------------- + // -c / --copy + // ------------------------------------------------------------------------- + #[test] + fn copy_flag_without_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-c", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -c"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn copy_flag_with_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-c", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -c ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -h / --synopsis + // ------------------------------------------------------------------------- + #[test] + fn synopsis_without_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-h", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -h"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn synopsis_with_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-h", "printf", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -h printf"); + + println!("Output: \"{}\"", String::from_utf8(output.stdout).unwrap()); + println!("Error: \"{}\"", String::from_utf8(output.stderr).unwrap()); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -l / --local-file + // ------------------------------------------------------------------------- + #[test] + fn local_file_not_found() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-l", "fake/path.1", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -l fake/path.1"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("was not found"), + "Expected 'file: fake/path.1 was not found' error, got:\n{stderr}" + ); + } + + #[test] + fn local_file_without_other_args() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-l", "test.mdoc", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -l tests/test_data.1"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -M / --override_paths + // ------------------------------------------------------------------------- + #[test] + fn override_paths_single() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-M", "/tmp", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -M /tmp ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + #[test] + fn override_paths_multiple() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args([ + "-M", + "/tmp:/nonexistent:/usr/local/man", + "ls", + "-C", + "man.test.conf", + ]) + .output() + .expect("Failed to run man -M with multiple paths ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -m / --augment_paths + // ------------------------------------------------------------------------- + #[test] + fn augment_paths_single() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-m", "/opt/mylocalman", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -m /opt/mylocalman ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + #[test] + fn augment_paths_multiple() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args([ + "-m", + "/first/path:/second/path", + "ls", + "-C", + "man.test.conf", + ]) + .output() + .expect("Failed to run man -m /first/path:/second/path ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -S / --subsection + // ------------------------------------------------------------------------- + #[test] + fn subsection_flag_no_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-S", "amd64", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -S amd64"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("no names specified"), + "Expected 'no names specified' error, got:\n{stderr}" + ); + } + + #[test] + fn subsection_flag_with_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-S", "amd64", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -S amd64 ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -s / --section + // ------------------------------------------------------------------------- + #[test] + fn section_invalid() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-s", "99", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -s 99 ls"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("invalid value '99' for '-s
'"), + "Expected 'Invalid section: 99', got:\n{stderr}" + ); + } + + #[test] + fn section_valid() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-s", "s1", "ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -s 1 ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + // ------------------------------------------------------------------------- + // -w / --list_pathnames + // ------------------------------------------------------------------------- + #[test] + fn list_pathnames_flag_no_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-w", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -w"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + #[test] + fn list_pathnames_flag_with_name() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["-w", "nonexistent_cmd", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man -w nonexistent_cmd"); + + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("system documentation for \"nonexistent_cmd\" not found"), + "Expected 'system documentation for \"nonexistent_cmd\" not found', got:\n{stderr}" + ); + } + + // ------------------------------------------------------------------------- + // --help + // ------------------------------------------------------------------------- + #[test] + fn help_flag() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["--help", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man --help"); + + assert!(output.status.success()); + let stdout = String::from_utf8_lossy(&output.stdout); + assert!( + stdout.contains("Usage:"), + "Expected help text containing 'Usage:', got:\n{stdout}" + ); + assert!( + stdout.contains("-k, --apropos"), + "Expected help text mentioning '-k, --apropos', got:\n{stdout}" + ); + } + + // ------------------------------------------------------------------------- + // Basic check for "names" + // ------------------------------------------------------------------------- + #[test] + fn single_name_argument() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["ls", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man ls"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } + + #[test] + fn multiple_name_arguments() { + let output = Command::new(env!("CARGO_BIN_EXE_man")) + .args(["ls", "cat", "nonexistent", "-C", "man.test.conf"]) + .output() + .expect("Failed to run man ls cat nonexistent"); + + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } +}