From db0cbda75c8f42413195dda2a48afb7ba06e03f7 Mon Sep 17 00:00:00 2001 From: wandalen Date: Fri, 4 Oct 2024 19:49:46 +0300 Subject: [PATCH 1/4] [man] full implementation --- Cargo.lock | 2 + display/Cargo.toml | 5 + display/man.rs | 434 +++++++++++++++++++++++++++++++++ display/tests/display-tests.rs | 1 + display/tests/man/mod.rs | 105 ++++++++ 5 files changed, 547 insertions(+) create mode 100644 display/man.rs create mode 100644 display/tests/man/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 51c569a1..df29cdb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1318,7 +1318,9 @@ version = "0.2.1" dependencies = [ "clap", "gettext-rs", + "libc", "plib", + "thiserror", ] [[package]] diff --git a/display/Cargo.toml b/display/Cargo.toml index 41b419ee..f5334280 100644 --- a/display/Cargo.toml +++ b/display/Cargo.toml @@ -10,6 +10,8 @@ edition.workspace = true plib = { path = "../plib" } clap.workspace = true gettext-rs.workspace = true +libc.workspace = true +thiserror = "1.0" [lints] workspace = true @@ -22,3 +24,6 @@ path = "./echo.rs" name = "printf" path = "./printf.rs" +[[bin]] +name = "man" +path = "./man.rs" diff --git a/display/man.rs b/display/man.rs new file mode 100644 index 00000000..560ef10b --- /dev/null +++ b/display/man.rs @@ -0,0 +1,434 @@ +// +// 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::Parser; +use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleCategory}; +use plib::PROJECT_NAME; +use std::ffi::OsStr; +use std::io::{self, IsTerminal, Write}; +use std::path::PathBuf; +use std::process::{Command, Output, Stdio}; +use thiserror::Error; + +// `/usr/share/man` - system provided directory with system documentation. +// `/usr/local/share/man` - user programs provided directory with system documentation. +const MAN_PATHS: [&str; 2] = ["/usr/share/man", "/usr/local/share/man"]; +// Prioritized order of sections. +const MAN_SECTIONS: [i8; 9] = [1, 8, 2, 3, 4, 5, 6, 7, 9]; + +#[derive(Parser)] +#[command(version, about = gettext("man - display system documentation"))] +struct Args { + #[arg(short, help = gettext("Interpret name operands as keywords for searching the summary database."))] + keyword: bool, + + #[arg(help = gettext("Names of the utilities or keywords to display documentation for."))] + names: Vec, +} + +#[derive(Error, Debug)] +enum ManError { + #[error("man paths to man pages doesn't exist")] + ManPaths, + #[error("no names specified")] + NoNames, + #[error("system documentation for \"{0}\" not found")] + PageNotFound(String), + #[error("failed to get terminal size")] + GetTerminalSize, + #[error("neither groff(1), nor nroff(1), nor mandoc(1) are installed")] + NoFormatters, + #[error("{0} command not found")] + CommandNotFound(String), + #[error("failed to execute command: {0}")] + Io(#[from] io::Error), +} + +/// Gets system documentation path by passed name. +/// +/// # Arguments +/// +/// `name` - [str] name of necessary system documentation. +/// +/// # Returns +/// +/// [PathBuf] of found system documentation. +/// +/// # Errors +/// +/// [ManError] if file not found. +fn get_man_page_path(name: &str) -> Result { + MAN_PATHS + .iter() + .flat_map(|path| { + MAN_SECTIONS.iter().flat_map(move |section| { + let base_path = format!("{path}/man{section}/{name}.{section}"); + vec![format!("{base_path}.gz"), base_path] + }) + }) + .find(|path| PathBuf::from(path).exists()) + .map(PathBuf::from) + .ok_or_else(|| ManError::PageNotFound(name.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 system documentation content by passed name. +/// +/// # Arguments +/// +/// `name` - [str] name of necessary system documentation. +/// +/// # Returns +/// +/// [Vec] output of called `*cat` command. +/// +/// # Errors +/// +/// [ManError] if file not found or failed to execute `*cat` command. +fn get_man_page(name: &str) -> Result, ManError> { + let man_page_path = get_man_page_path(name)?; + + let cat_process_name = if man_page_path.extension().and_then(|ext| ext.to_str()) == Some("gz") { + "zcat" + } else { + "cat" + }; + + let output = spawn(cat_process_name, &[man_page_path], None, Stdio::piped())?; + Ok(output.stdout) +} + +/// 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_page_width() -> Result, ManError> { + if !std::io::stdout().is_terminal() { + return Ok(None); + } + let mut winsize = libc::winsize { + ws_row: 0, + ws_col: 0, + ws_xpixel: 0, + ws_ypixel: 0, + }; + let result = unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) }; + if result != 0 { + return Err(ManError::GetTerminalSize); + } + let result_width = if winsize.ws_col >= 80 { + winsize.ws_col - 2 + } else { + winsize.ws_col + }; + Ok(Some(result_width)) +} + +/// Gets formated by `groff(1)` system documentation. +/// +/// # Arguments +/// +/// `man_page` - [&[u8]] with content that needs to be formatted. +/// `width` - [Option] width value of current terminal. +/// +/// # Returns +/// +/// [Vec] STDOUT of called `groff(1)` formatter. +/// +/// # Errors +/// +/// [ManError] if file failed to execute `groff(1)` formatter. +fn groff_format(man_page: &[u8], width: Option) -> Result, ManError> { + let mut args = vec![ + "-Tutf8", + "-S", + "-P-h", + "-Wall", + "-mtty-char", + "-t", + "-mandoc", + ]; + let width = width.map(|w| (format!("-rLL={w}n"), format!("-rLR={w}n"))); + if let Some((rll, rlr)) = width.as_ref() { + args.push(rll); + args.push(rlr); + } + + spawn("groff", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) +} + +/// Gets formated by `nroff(1)` system documentation. +/// +/// # Arguments +/// +/// `man_page` - [&[u8]] with content that needs to be formatted. +/// `width` - [Option] width value of current terminal. +/// +/// # Returns +/// +/// [Vec] STDOUT of called `nroff(1)` formatter. +/// +/// # Errors +/// +/// [ManError] if file failed to execute `nroff(1)` formatter. +fn nroff_format(man_page: &[u8], width: Option) -> Result, ManError> { + let mut args = vec!["-Tutf8", "-S", "-Wall", "-mtty-char", "-t", "-mandoc"]; + let width = width.map(|w| (format!("-rLL={w}n"), format!("-rLR={w}n"))); + if let Some((rll, rlr)) = width.as_ref() { + args.push(rll); + args.push(rlr); + } + + spawn("nroff", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) +} + +/// Gets formatted by `mandoc(1)` system documentation. +/// +/// # Arguments +/// +/// `man_page` - [&[u8]] with content that needs to be formatted. +/// `width` - [Option] width value of current terminal. +/// +/// # Returns +/// +/// [Vec] STDOUT of called `mandoc(1)` formatter. +/// +/// # Errors +/// +/// [ManError] if file failed to execute `mandoc(1)` formatter. +fn mandoc_format(man_page: &[u8], width: Option) -> Result, ManError> { + let mut args = vec![]; + let width = width.map(|w| format!("width={w}")); + if let Some(width) = width.as_ref() { + args.push("-O"); + args.push(width); + } + + spawn("mandoc", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) +} + +/// Formats man page content into appropriate format. +/// +/// # 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_page: Vec) -> Result, ManError> { + let width = get_page_width()?; + + let formatters = [groff_format, nroff_format, mandoc_format]; + + for formatter in &formatters { + match formatter(&man_page, width) { + Ok(formatted_man_page) => return Ok(formatted_man_page), + Err(ManError::CommandNotFound(_)) => continue, + Err(err) => return Err(err), + } + } + + Err(ManError::NoFormatters) +} + +/// Formats man page content into appropriate format. +/// +/// # 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) -> Result<(), ManError> { + 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 +/// +/// # Arguments +/// +/// `name` - [str] name of system documentation. +/// +/// # Errors +/// +/// [ManError] if man page not found, or any display error happened. +fn display_man_page(name: &str) -> Result<(), ManError> { + let cat_output = get_man_page(name)?; + let formatter_output = format_man_page(cat_output)?; + display_pager(formatter_output)?; + + 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(keyword: &str) -> Result { + let exit_status = Command::new("apropos").arg(keyword).spawn()?.wait()?; + + if exit_status.success() { + Ok(true) + } else { + Ok(false) + } +} + +/// 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(args: Args) -> Result { + let any_path_exists = MAN_PATHS.iter().any(|path| PathBuf::from(path).exists()); + + if !any_path_exists { + return Err(ManError::ManPaths); + } + + if args.names.is_empty() { + return Err(ManError::NoNames); + } + + let mut no_errors = true; + if args.keyword { + for name in &args.names { + if !display_summary_database(name)? { + no_errors = false; + } + } + } else { + for name in &args.names { + if let Err(err) = display_man_page(name) { + no_errors = false; + eprintln!("man: {err}"); + } + } + }; + + Ok(no_errors) +} + +// Exit code: +// 0 - Successful completion. +// >0 - An error occurred. +fn main() -> Result<(), Box> { + setlocale(LocaleCategory::LcAll, ""); + textdomain(PROJECT_NAME)?; + bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?; + + // parse command line arguments + let args = Args::parse(); + + let exit_code = match man(args) { + 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/display/tests/display-tests.rs b/display/tests/display-tests.rs index 859a66e8..73b6c367 100644 --- a/display/tests/display-tests.rs +++ b/display/tests/display-tests.rs @@ -8,4 +8,5 @@ // mod echo; +mod man; mod printf; diff --git a/display/tests/man/mod.rs b/display/tests/man/mod.rs new file mode 100644 index 00000000..78e72000 --- /dev/null +++ b/display/tests/man/mod.rs @@ -0,0 +1,105 @@ +// +// 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::process::Output; + +use plib::{run_test_with_checker, TestPlan}; + +fn test_checker_man(plan: &TestPlan, output: &Output) { + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains(&plan.expected_out)); + + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains(&plan.expected_err)); + + assert_eq!(output.status.code(), Some(plan.expected_exit_code)); + if plan.expected_exit_code == 0 { + assert!(output.status.success()); + } +} + +fn run_test_man(args: &[&str], expected_out: &str, expected_err: &str, expected_exit_code: i32) { + let str_args: Vec = args.iter().map(|s| String::from(*s)).collect(); + + run_test_with_checker( + TestPlan { + cmd: String::from("man"), + args: str_args, + stdin_data: String::new(), + expected_out: String::from(expected_out), + expected_err: String::from(expected_err), + expected_exit_code, + }, + test_checker_man, + ); +} + +const LS: &'static str = "ls"; +const MAN: &'static str = "man"; +const INVALID_NAME: &'static str = "invalid_name"; +const INVALID_NAME_MAN_ERROR: &'static str = + "man: system documentation for \"invalid_name\" not found\n"; +const INVALID_NAME_APROPOS_ERROR: &'static str = "invalid_name: nothing appropriate"; + +#[test] +fn test_one_manpage() { + run_test_man(&[LS], "LS(1)", "", 0); +} + +#[test] +fn test_one_page_not_found() { + run_test_man(&[INVALID_NAME], "", INVALID_NAME_MAN_ERROR, 1); +} + +#[test] +fn test_multiple_nampages() { + run_test_man(&[LS, MAN], "LS(1)", "", 0); + run_test_man(&[LS, MAN], "MAN(1)", "", 0); +} + +#[test] +fn test_multiple_nampages_one_not_found() { + run_test_man(&[LS, INVALID_NAME], "LS(1)", INVALID_NAME_MAN_ERROR, 1); +} + +#[test] +fn test_empty_names() { + run_test_man(&[], "", "man: no names specified\n", 1); +} + +#[test] +fn test_k() { + run_test_man(&["-k", "user"], "ls", "", 0); +} + +#[test] +fn test_k_invalid_name() { + run_test_man(&["-k", "invalid_name"], "", &INVALID_NAME_APROPOS_ERROR, 1); +} + +#[test] +fn test_k_multiple_nampages() { + run_test_man(&["-k", LS, MAN], "ls", "", 0); + run_test_man(&["-k", LS, MAN], "man", "", 0); +} + +#[test] +fn test_k_multiple_nampages_one_not_found() { + run_test_man( + &["-k", LS, INVALID_NAME], + "ls", + INVALID_NAME_APROPOS_ERROR, + 1, + ); +} + +#[test] +fn test_k_empty_names() { + run_test_man(&["-k"], "", "man: no names specified\n", 1); +} From 4f170456cc9edb5c20a9e99c377423785b004fbd Mon Sep 17 00:00:00 2001 From: wandalen Date: Fri, 18 Apr 2025 09:20:23 +0300 Subject: [PATCH 2/4] [man] full implementation --- Cargo.lock | 178 +- display/Cargo.toml | 11 + display/man.rs | 827 +- display/man.test.conf | 53 + display/man_util/config.rs | 69 + display/man_util/formatter.rs | 7635 ++++++++++ display/man_util/mdoc.pest | 747 + display/man_util/mdoc_macro/mod.rs | 193 + .../man_util/mdoc_macro/text_production.rs | 331 + display/man_util/mdoc_macro/types.rs | 142 + display/man_util/mod.rs | 17 + display/man_util/parser.rs | 12052 ++++++++++++++++ display/test.mdoc | 5 + display/test_files/mdoc/access.2 | 240 + display/test_files/mdoc/adjfreq.2 | 76 + display/test_files/mdoc/atq.1 | 103 + display/test_files/mdoc/bc.1 | 409 + display/test_files/mdoc/brk.2 | 154 + display/test_files/mdoc/cal.1 | 132 + display/test_files/mdoc/cat.1 | 185 + display/test_files/mdoc/chdir.2 | 130 + display/test_files/mdoc/chflags.2 | 228 + display/test_files/mdoc/chmod.2 | 275 + display/test_files/mdoc/closefrom.2 | 67 + display/test_files/mdoc/cu.1 | 224 + display/test_files/mdoc/cut.1 | 184 + display/test_files/mdoc/cvs.1 | 1987 +++ display/test_files/mdoc/dc.1 | 550 + display/test_files/mdoc/diff.1 | 475 + display/test_files/mdoc/dup.2 | 214 + display/test_files/mdoc/execve.2 | 348 + display/test_files/mdoc/fgen.1 | 71 + display/test_files/mdoc/file.1 | 130 + display/test_files/mdoc/flex.1 | 4440 ++++++ display/test_files/mdoc/flock.2 | 151 + display/test_files/mdoc/fork.2 | 145 + display/test_files/mdoc/fsync.2 | 121 + display/test_files/mdoc/futex.2 | 153 + display/test_files/mdoc/getdents.2 | 195 + display/test_files/mdoc/getfh.2 | 102 + display/test_files/mdoc/getgroups.2 | 99 + display/test_files/mdoc/getitimer.2 | 176 + display/test_files/mdoc/getpeername.2 | 143 + display/test_files/mdoc/getpriority.2 | 158 + display/test_files/mdoc/getrtable.2 | 66 + display/test_files/mdoc/getrusage.2 | 192 + display/test_files/mdoc/getsid.2 | 83 + display/test_files/mdoc/getsockname.2 | 162 + display/test_files/mdoc/getsockopt.2 | 541 + display/test_files/mdoc/gettimeofday.2 | 205 + display/test_files/mdoc/grep.1 | 395 + display/test_files/mdoc/id.1 | 164 + display/test_files/mdoc/ioctl.2 | 171 + display/test_files/mdoc/ipcs.1 | 149 + display/test_files/mdoc/ktrace.2 | 232 + display/test_files/mdoc/lpq.1 | 141 + display/test_files/mdoc/mg.1 | 1203 ++ display/test_files/mdoc/minherit.2 | 108 + display/test_files/mdoc/mkdir.1 | 121 + display/test_files/mdoc/mkfifo.2 | 181 + display/test_files/mdoc/mlockall.2 | 124 + display/test_files/mdoc/mopa.out.1 | 49 + display/test_files/mdoc/moptrace.1 | 74 + display/test_files/mdoc/msgrcv.2 | 207 + display/test_files/mdoc/msgsnd.2 | 168 + display/test_files/mdoc/munmap.2 | 104 + display/test_files/mdoc/mv.1 | 203 + display/test_files/mdoc/nl.1 | 231 + display/test_files/mdoc/nm.1 | 166 + display/test_files/mdoc/open.2 | 465 + display/test_files/mdoc/poll.2 | 364 + display/test_files/mdoc/profil.2 | 127 + display/test_files/mdoc/quotactl.2 | 212 + display/test_files/mdoc/rcs.1 | 485 + display/test_files/mdoc/rdist.1 | 866 ++ display/test_files/mdoc/read.2 | 282 + display/test_files/mdoc/reboot.2 | 163 + display/test_files/mdoc/rename.2 | 296 + display/test_files/mdoc/rev.1 | 59 + display/test_files/mdoc/rlog.1 | 208 + display/test_files/mdoc/rup.1 | 101 + display/test_files/mdoc/sched_yield.2 | 49 + display/test_files/mdoc/scp.1 | 364 + display/test_files/mdoc/select.2 | 248 + display/test_files/mdoc/semget.2 | 154 + display/test_files/mdoc/send.2 | 289 + display/test_files/mdoc/setuid.2 | 152 + display/test_files/mdoc/sftp.1 | 767 + display/test_files/mdoc/shar.1 | 102 + display/test_files/mdoc/shmctl.2 | 198 + display/test_files/mdoc/shmget.2 | 142 + display/test_files/mdoc/shutdown.2 | 154 + display/test_files/mdoc/signify.1 | 206 + display/test_files/mdoc/sigreturn.2 | 86 + display/test_files/mdoc/sigsuspend.2 | 78 + display/test_files/mdoc/size.1 | 78 + display/test_files/mdoc/snmp.1 | 568 + display/test_files/mdoc/socket.2 | 311 + display/test_files/mdoc/socketpair.2 | 132 + display/test_files/mdoc/statfs.2 | 158 + display/test_files/mdoc/symlink.2 | 204 + display/test_files/mdoc/sync.2 | 73 + display/test_files/mdoc/sysarch.2 | 68 + display/test_files/mdoc/t11.2 | 908 ++ display/test_files/mdoc/talk.1 | 160 + display/test_files/mdoc/test.1 | 7799 ++++++++++ display/test_files/mdoc/tmux.1 | 7799 ++++++++++ display/test_files/mdoc/top.1 | 520 + display/test_files/mdoc/truncate.2 | 152 + display/test_files/mdoc/umask.2 | 94 + display/test_files/mdoc/w.1 | 134 + display/test_files/mdoc/wall.1 | 83 + display/test_files/mdoc/write.2 | 329 + display/test_files/mdoc/ypconnect.2 | 80 + display/tests/display-tests.rs | 1 + display/tests/man/mod.rs | 524 +- 116 files changed, 66241 insertions(+), 316 deletions(-) create mode 100644 display/man.test.conf create mode 100644 display/man_util/config.rs create mode 100644 display/man_util/formatter.rs create mode 100644 display/man_util/mdoc.pest create mode 100644 display/man_util/mdoc_macro/mod.rs create mode 100644 display/man_util/mdoc_macro/text_production.rs create mode 100644 display/man_util/mdoc_macro/types.rs create mode 100644 display/man_util/mod.rs create mode 100644 display/man_util/parser.rs create mode 100644 display/test.mdoc create mode 100644 display/test_files/mdoc/access.2 create mode 100644 display/test_files/mdoc/adjfreq.2 create mode 100644 display/test_files/mdoc/atq.1 create mode 100644 display/test_files/mdoc/bc.1 create mode 100644 display/test_files/mdoc/brk.2 create mode 100644 display/test_files/mdoc/cal.1 create mode 100644 display/test_files/mdoc/cat.1 create mode 100644 display/test_files/mdoc/chdir.2 create mode 100644 display/test_files/mdoc/chflags.2 create mode 100644 display/test_files/mdoc/chmod.2 create mode 100644 display/test_files/mdoc/closefrom.2 create mode 100644 display/test_files/mdoc/cu.1 create mode 100644 display/test_files/mdoc/cut.1 create mode 100644 display/test_files/mdoc/cvs.1 create mode 100644 display/test_files/mdoc/dc.1 create mode 100644 display/test_files/mdoc/diff.1 create mode 100644 display/test_files/mdoc/dup.2 create mode 100644 display/test_files/mdoc/execve.2 create mode 100644 display/test_files/mdoc/fgen.1 create mode 100644 display/test_files/mdoc/file.1 create mode 100644 display/test_files/mdoc/flex.1 create mode 100644 display/test_files/mdoc/flock.2 create mode 100644 display/test_files/mdoc/fork.2 create mode 100644 display/test_files/mdoc/fsync.2 create mode 100644 display/test_files/mdoc/futex.2 create mode 100644 display/test_files/mdoc/getdents.2 create mode 100644 display/test_files/mdoc/getfh.2 create mode 100644 display/test_files/mdoc/getgroups.2 create mode 100644 display/test_files/mdoc/getitimer.2 create mode 100644 display/test_files/mdoc/getpeername.2 create mode 100644 display/test_files/mdoc/getpriority.2 create mode 100644 display/test_files/mdoc/getrtable.2 create mode 100644 display/test_files/mdoc/getrusage.2 create mode 100644 display/test_files/mdoc/getsid.2 create mode 100644 display/test_files/mdoc/getsockname.2 create mode 100644 display/test_files/mdoc/getsockopt.2 create mode 100644 display/test_files/mdoc/gettimeofday.2 create mode 100644 display/test_files/mdoc/grep.1 create mode 100644 display/test_files/mdoc/id.1 create mode 100644 display/test_files/mdoc/ioctl.2 create mode 100644 display/test_files/mdoc/ipcs.1 create mode 100644 display/test_files/mdoc/ktrace.2 create mode 100644 display/test_files/mdoc/lpq.1 create mode 100644 display/test_files/mdoc/mg.1 create mode 100644 display/test_files/mdoc/minherit.2 create mode 100644 display/test_files/mdoc/mkdir.1 create mode 100644 display/test_files/mdoc/mkfifo.2 create mode 100644 display/test_files/mdoc/mlockall.2 create mode 100644 display/test_files/mdoc/mopa.out.1 create mode 100644 display/test_files/mdoc/moptrace.1 create mode 100644 display/test_files/mdoc/msgrcv.2 create mode 100644 display/test_files/mdoc/msgsnd.2 create mode 100644 display/test_files/mdoc/munmap.2 create mode 100644 display/test_files/mdoc/mv.1 create mode 100644 display/test_files/mdoc/nl.1 create mode 100644 display/test_files/mdoc/nm.1 create mode 100644 display/test_files/mdoc/open.2 create mode 100644 display/test_files/mdoc/poll.2 create mode 100644 display/test_files/mdoc/profil.2 create mode 100644 display/test_files/mdoc/quotactl.2 create mode 100644 display/test_files/mdoc/rcs.1 create mode 100644 display/test_files/mdoc/rdist.1 create mode 100644 display/test_files/mdoc/read.2 create mode 100644 display/test_files/mdoc/reboot.2 create mode 100644 display/test_files/mdoc/rename.2 create mode 100644 display/test_files/mdoc/rev.1 create mode 100644 display/test_files/mdoc/rlog.1 create mode 100644 display/test_files/mdoc/rup.1 create mode 100644 display/test_files/mdoc/sched_yield.2 create mode 100644 display/test_files/mdoc/scp.1 create mode 100644 display/test_files/mdoc/select.2 create mode 100644 display/test_files/mdoc/semget.2 create mode 100644 display/test_files/mdoc/send.2 create mode 100644 display/test_files/mdoc/setuid.2 create mode 100644 display/test_files/mdoc/sftp.1 create mode 100644 display/test_files/mdoc/shar.1 create mode 100644 display/test_files/mdoc/shmctl.2 create mode 100644 display/test_files/mdoc/shmget.2 create mode 100644 display/test_files/mdoc/shutdown.2 create mode 100644 display/test_files/mdoc/signify.1 create mode 100644 display/test_files/mdoc/sigreturn.2 create mode 100644 display/test_files/mdoc/sigsuspend.2 create mode 100644 display/test_files/mdoc/size.1 create mode 100644 display/test_files/mdoc/snmp.1 create mode 100644 display/test_files/mdoc/socket.2 create mode 100644 display/test_files/mdoc/socketpair.2 create mode 100644 display/test_files/mdoc/statfs.2 create mode 100644 display/test_files/mdoc/symlink.2 create mode 100644 display/test_files/mdoc/sync.2 create mode 100644 display/test_files/mdoc/sysarch.2 create mode 100644 display/test_files/mdoc/t11.2 create mode 100644 display/test_files/mdoc/talk.1 create mode 100644 display/test_files/mdoc/test.1 create mode 100644 display/test_files/mdoc/tmux.1 create mode 100644 display/test_files/mdoc/top.1 create mode 100644 display/test_files/mdoc/truncate.2 create mode 100644 display/test_files/mdoc/umask.2 create mode 100644 display/test_files/mdoc/w.1 create mode 100644 display/test_files/mdoc/wall.1 create mode 100644 display/test_files/mdoc/write.2 create mode 100644 display/test_files/mdoc/ypconnect.2 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/display/Cargo.toml b/display/Cargo.toml index a5acb137..d17739d0 100644 --- a/display/Cargo.toml +++ b/display/Cargo.toml @@ -12,11 +12,18 @@ clap.workspace = true clap.features = ["env"] gettext-rs.workspace = true libc.workspace = true +pest = "2.7" +pest_derive = "2.7" termion = "4.0" 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 @@ -29,6 +36,10 @@ path = "./echo.rs" name = "printf" path = "./printf.rs" +[[bin]] +name = "man" +path = "./man.rs" + [[bin]] name = "more" path = "./more.rs" diff --git a/display/man.rs b/display/man.rs index 560ef10b..5d62cc6c 100644 --- a/display/man.rs +++ b/display/man.rs @@ -7,74 +7,317 @@ // SPDX-License-Identifier: MIT // -use clap::Parser; +use clap::{ArgAction, Parser, ValueEnum}; use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleCategory}; -use plib::PROJECT_NAME; +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; 2] = ["/usr/share/man", "/usr/local/share/man"]; -// Prioritized order of sections. -const MAN_SECTIONS: [i8; 9] = [1, 8, 2, 3, 4, 5, 6, 7, 9]; +const MAN_PATHS: [&str; 3] = ["/usr/share/man", "/usr/X11R6/man", "/usr/local/share/man"]; -#[derive(Parser)] -#[command(version, about = gettext("man - display system documentation"))] +// 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 { - #[arg(short, help = gettext("Interpret name operands as keywords for searching the summary database."))] - keyword: bool, - - #[arg(help = gettext("Names of the utilities or keywords to display documentation for."))] + /// 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, - #[error("neither groff(1), nor nroff(1), nor mandoc(1) are installed")] - NoFormatters, + + /// 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), } -/// Gets system documentation path by passed name. -/// -/// # Arguments -/// -/// `name` - [str] name of necessary system documentation. -/// -/// # Returns -/// -/// [PathBuf] of found system documentation. -/// -/// # Errors -/// -/// [ManError] if file not found. -fn get_man_page_path(name: &str) -> Result { - MAN_PATHS - .iter() - .flat_map(|path| { - MAN_SECTIONS.iter().flat_map(move |section| { - let base_path = format!("{path}/man{section}/{name}.{section}"); - vec![format!("{base_path}.gz"), base_path] - }) - }) - .find(|path| PathBuf::from(path).exists()) - .map(PathBuf::from) - .ok_or_else(|| ManError::PageNotFound(name.to_string())) +/// 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. @@ -132,147 +375,76 @@ where } } -/// Gets system documentation content by passed name. -/// -/// # Arguments -/// -/// `name` - [str] name of necessary system documentation. +/// Gets page width. /// /// # Returns /// -/// [Vec] output of called `*cat` command. +/// [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 /// -/// [ManError] if file not found or failed to execute `*cat` command. -fn get_man_page(name: &str) -> Result, ManError> { - let man_page_path = get_man_page_path(name)?; +/// Returns [ManError] if working on terminal and failed to get terminal size. +fn get_pager_settings(config: &ManConfig) -> Result { + let mut settings = FormattingSettings::default(); - let cat_process_name = if man_page_path.extension().and_then(|ext| ext.to_str()) == Some("gz") { - "zcat" - } else { - "cat" - }; + if let Some(Some(val_str)) = config.output_options.get("indent") { + settings.indent = val_str + .parse::() + .map_err(|err| ManError::ParseError(ParseError::ParseIntError(err)))?; + } - let output = spawn(cat_process_name, &[man_page_path], None, Stdio::piped())?; - Ok(output.stdout) -} + if let Some(Some(val_str)) = config.output_options.get("width") { + settings.width = val_str + .parse::() + .map_err(|err| ManError::ParseError(ParseError::ParseIntError(err)))?; + } -/// 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_page_width() -> Result, ManError> { - if !std::io::stdout().is_terminal() { - return Ok(None); + // 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 result = unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) }; - if result != 0 { - return Err(ManError::GetTerminalSize); - } - let result_width = if winsize.ws_col >= 80 { - winsize.ws_col - 2 - } else { - winsize.ws_col - }; - Ok(Some(result_width)) -} -/// Gets formated by `groff(1)` system documentation. -/// -/// # Arguments -/// -/// `man_page` - [&[u8]] with content that needs to be formatted. -/// `width` - [Option] width value of current terminal. -/// -/// # Returns -/// -/// [Vec] STDOUT of called `groff(1)` formatter. -/// -/// # Errors -/// -/// [ManError] if file failed to execute `groff(1)` formatter. -fn groff_format(man_page: &[u8], width: Option) -> Result, ManError> { - let mut args = vec![ - "-Tutf8", - "-S", - "-P-h", - "-Wall", - "-mtty-char", - "-t", - "-mandoc", - ]; - let width = width.map(|w| (format!("-rLL={w}n"), format!("-rLR={w}n"))); - if let Some((rll, rlr)) = width.as_ref() { - args.push(rll); - args.push(rlr); + let ret = unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) }; + if ret != 0 { + return Err(ManError::GetTerminalSize); } - spawn("groff", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) -} - -/// Gets formated by `nroff(1)` system documentation. -/// -/// # Arguments -/// -/// `man_page` - [&[u8]] with content that needs to be formatted. -/// `width` - [Option] width value of current terminal. -/// -/// # Returns -/// -/// [Vec] STDOUT of called `nroff(1)` formatter. -/// -/// # Errors -/// -/// [ManError] if file failed to execute `nroff(1)` formatter. -fn nroff_format(man_page: &[u8], width: Option) -> Result, ManError> { - let mut args = vec!["-Tutf8", "-S", "-Wall", "-mtty-char", "-t", "-mandoc"]; - let width = width.map(|w| (format!("-rLL={w}n"), format!("-rLR={w}n"))); - if let Some((rll, rlr)) = width.as_ref() { - args.push(rll); - args.push(rlr); + // 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; + } } - spawn("nroff", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) + Ok(settings) } -/// Gets formatted by `mandoc(1)` system documentation. -/// -/// # Arguments -/// -/// `man_page` - [&[u8]] with content that needs to be formatted. -/// `width` - [Option] width value of current terminal. -/// -/// # Returns -/// -/// [Vec] STDOUT of called `mandoc(1)` formatter. -/// -/// # Errors -/// -/// [ManError] if file failed to execute `mandoc(1)` formatter. -fn mandoc_format(man_page: &[u8], width: Option) -> Result, ManError> { - let mut args = vec![]; - let width = width.map(|w| format!("width={w}")); - if let Some(width) = width.as_ref() { - args.push("-O"); - args.push(width); - } +/// 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", + }; - spawn("mandoc", &args, Some(man_page), Stdio::piped()).map(|output| output.stdout) + let output = spawn(cat_cmd, [path], None, Stdio::piped())?; + Ok(output.stdout) } -/// Formats man page content into appropriate format. +/// Parse and format a man page’s raw content into text suitable for display. /// /// # Arguments /// @@ -285,23 +457,26 @@ fn mandoc_format(man_page: &[u8], width: Option) -> Result, ManErro /// # Errors /// /// [ManError] if failed to execute formatter. -fn format_man_page(man_page: Vec) -> Result, ManError> { - let width = get_page_width()?; - - let formatters = [groff_format, nroff_format, mandoc_format]; - - for formatter in &formatters { - match formatter(&man_page, width) { - Ok(formatted_man_page) => return Ok(formatted_man_page), - Err(ManError::CommandNotFound(_)) => continue, - Err(err) => return Err(err), - } - } +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), + }; - Err(ManError::NoFormatters) + Ok(formatted_document) } -/// Formats man page content into appropriate format. +/// Write formatted output to either a pager or directly to stdout if `copy = true`. /// /// # Arguments /// @@ -310,9 +485,14 @@ fn format_man_page(man_page: Vec) -> Result, ManError> { /// # Errors /// /// [ManError] if failed to execute pager or failed write to its STDIN. -fn display_pager(man_page: Vec) -> Result<(), ManError> { - let pager = std::env::var("PAGER").unwrap_or_else(|_| "more".to_string()); +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 { @@ -324,23 +504,6 @@ fn display_pager(man_page: Vec) -> Result<(), ManError> { Ok(()) } -/// Displays man page -/// -/// # Arguments -/// -/// `name` - [str] name of system documentation. -/// -/// # Errors -/// -/// [ManError] if man page not found, or any display error happened. -fn display_man_page(name: &str) -> Result<(), ManError> { - let cat_output = get_man_page(name)?; - let formatter_output = format_man_page(cat_output)?; - display_pager(formatter_output)?; - - Ok(()) -} - /// Displays man page summaries for the given keyword. /// /// # Arguments @@ -354,72 +517,280 @@ fn display_man_page(name: &str) -> Result<(), ManError> { /// # Errors /// /// [ManError] if call of `apropros` utility failed. -fn display_summary_database(keyword: &str) -> Result { - let exit_status = Command::new("apropos").arg(keyword).spawn()?.wait()?; +fn display_summary_database(command: &str, keyword: &str) -> Result { + let status = Command::new(command).arg(keyword).spawn()?.wait()?; - if exit_status.success() { - Ok(true) - } else { - Ok(false) - } + Ok(status.success()) } -/// 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(args: Args) -> Result { - let any_path_exists = MAN_PATHS.iter().any(|path| PathBuf::from(path).exists()); +/// 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 !any_path_exists { - return Err(ManError::ManPaths); + 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(()) } - if args.names.is_empty() { - return Err(ManError::NoNames); + /// 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(()) } - let mut no_errors = true; - if args.keyword { - for name in &args.names { - if !display_summary_database(name)? { - no_errors = false; + 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)); + } } } - } else { - for name in &args.names { - if let Err(err) = display_man_page(name) { - no_errors = false; - eprintln!("man: {err}"); + + 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); } - }; - 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(PROJECT_NAME)?; - bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?; + textdomain("posixutils-rs")?; + bind_textdomain_codeset("posixutils-rs", "UTF-8")?; // parse command line arguments let args = Args::parse(); - let exit_code = match man(args) { + 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, diff --git a/display/man.test.conf b/display/man.test.conf new file mode 100644 index 00000000..8dbb42e9 --- /dev/null +++ b/display/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/display/man_util/config.rs b/display/man_util/config.rs new file mode 100644 index 00000000..6ca67d01 --- /dev/null +++ b/display/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/display/man_util/formatter.rs b/display/man_util/formatter.rs new file mode 100644 index 00000000..d3bea089 --- /dev/null +++ b/display/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/display/man_util/mdoc.pest b/display/man_util/mdoc.pest new file mode 100644 index 00000000..c6a395d8 --- /dev/null +++ b/display/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/display/man_util/mdoc_macro/mod.rs b/display/man_util/mdoc_macro/mod.rs new file mode 100644 index 00000000..26b91374 --- /dev/null +++ b/display/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/display/man_util/mdoc_macro/text_production.rs b/display/man_util/mdoc_macro/text_production.rs new file mode 100644 index 00000000..f1725404 --- /dev/null +++ b/display/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/display/man_util/mdoc_macro/types.rs b/display/man_util/mdoc_macro/types.rs new file mode 100644 index 00000000..a9f85f66 --- /dev/null +++ b/display/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/display/man_util/mod.rs b/display/man_util/mod.rs new file mode 100644 index 00000000..2d7a9be3 --- /dev/null +++ b/display/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/display/man_util/parser.rs b/display/man_util/parser.rs new file mode 100644 index 00000000..69a4a83f --- /dev/null +++ b/display/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/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/display/test_files/mdoc/access.2 b/display/test_files/mdoc/access.2 new file mode 100644 index 00000000..756449a8 --- /dev/null +++ b/display/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/display/test_files/mdoc/adjfreq.2 b/display/test_files/mdoc/adjfreq.2 new file mode 100644 index 00000000..b3903024 --- /dev/null +++ b/display/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/display/test_files/mdoc/atq.1 b/display/test_files/mdoc/atq.1 new file mode 100644 index 00000000..1e43804f --- /dev/null +++ b/display/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/display/test_files/mdoc/bc.1 b/display/test_files/mdoc/bc.1 new file mode 100644 index 00000000..62043c46 --- /dev/null +++ b/display/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/display/test_files/mdoc/brk.2 b/display/test_files/mdoc/brk.2 new file mode 100644 index 00000000..77826c91 --- /dev/null +++ b/display/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/display/test_files/mdoc/cal.1 b/display/test_files/mdoc/cal.1 new file mode 100644 index 00000000..1d275943 --- /dev/null +++ b/display/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/display/test_files/mdoc/cat.1 b/display/test_files/mdoc/cat.1 new file mode 100644 index 00000000..2eeaa0d6 --- /dev/null +++ b/display/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/display/test_files/mdoc/chdir.2 b/display/test_files/mdoc/chdir.2 new file mode 100644 index 00000000..b4530895 --- /dev/null +++ b/display/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/display/test_files/mdoc/chflags.2 b/display/test_files/mdoc/chflags.2 new file mode 100644 index 00000000..8836cc82 --- /dev/null +++ b/display/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/display/test_files/mdoc/chmod.2 b/display/test_files/mdoc/chmod.2 new file mode 100644 index 00000000..110470f8 --- /dev/null +++ b/display/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/display/test_files/mdoc/closefrom.2 b/display/test_files/mdoc/closefrom.2 new file mode 100644 index 00000000..a6087df6 --- /dev/null +++ b/display/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/display/test_files/mdoc/cu.1 b/display/test_files/mdoc/cu.1 new file mode 100644 index 00000000..e2afc254 --- /dev/null +++ b/display/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/display/test_files/mdoc/cut.1 b/display/test_files/mdoc/cut.1 new file mode 100644 index 00000000..e1718775 --- /dev/null +++ b/display/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/display/test_files/mdoc/cvs.1 b/display/test_files/mdoc/cvs.1 new file mode 100644 index 00000000..97dccddf --- /dev/null +++ b/display/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/display/test_files/mdoc/dc.1 b/display/test_files/mdoc/dc.1 new file mode 100644 index 00000000..c8f39233 --- /dev/null +++ b/display/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/display/test_files/mdoc/diff.1 b/display/test_files/mdoc/diff.1 new file mode 100644 index 00000000..7935075c --- /dev/null +++ b/display/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/display/test_files/mdoc/dup.2 b/display/test_files/mdoc/dup.2 new file mode 100644 index 00000000..4da4bed3 --- /dev/null +++ b/display/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/display/test_files/mdoc/execve.2 b/display/test_files/mdoc/execve.2 new file mode 100644 index 00000000..dc814441 --- /dev/null +++ b/display/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/display/test_files/mdoc/fgen.1 b/display/test_files/mdoc/fgen.1 new file mode 100644 index 00000000..9f2ad296 --- /dev/null +++ b/display/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/display/test_files/mdoc/file.1 b/display/test_files/mdoc/file.1 new file mode 100644 index 00000000..36930bad --- /dev/null +++ b/display/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/display/test_files/mdoc/flex.1 b/display/test_files/mdoc/flex.1 new file mode 100644 index 00000000..05236c88 --- /dev/null +++ b/display/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/display/test_files/mdoc/flock.2 b/display/test_files/mdoc/flock.2 new file mode 100644 index 00000000..e063baac --- /dev/null +++ b/display/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/display/test_files/mdoc/fork.2 b/display/test_files/mdoc/fork.2 new file mode 100644 index 00000000..a72213b9 --- /dev/null +++ b/display/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/display/test_files/mdoc/fsync.2 b/display/test_files/mdoc/fsync.2 new file mode 100644 index 00000000..319ff75e --- /dev/null +++ b/display/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/display/test_files/mdoc/futex.2 b/display/test_files/mdoc/futex.2 new file mode 100644 index 00000000..9c6da666 --- /dev/null +++ b/display/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/display/test_files/mdoc/getdents.2 b/display/test_files/mdoc/getdents.2 new file mode 100644 index 00000000..25848b82 --- /dev/null +++ b/display/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/display/test_files/mdoc/getfh.2 b/display/test_files/mdoc/getfh.2 new file mode 100644 index 00000000..305642dd --- /dev/null +++ b/display/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/display/test_files/mdoc/getgroups.2 b/display/test_files/mdoc/getgroups.2 new file mode 100644 index 00000000..1acf6018 --- /dev/null +++ b/display/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/display/test_files/mdoc/getitimer.2 b/display/test_files/mdoc/getitimer.2 new file mode 100644 index 00000000..bbf13d92 --- /dev/null +++ b/display/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/display/test_files/mdoc/getpeername.2 b/display/test_files/mdoc/getpeername.2 new file mode 100644 index 00000000..28cffd53 --- /dev/null +++ b/display/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/display/test_files/mdoc/getpriority.2 b/display/test_files/mdoc/getpriority.2 new file mode 100644 index 00000000..a31f0f0b --- /dev/null +++ b/display/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/display/test_files/mdoc/getrtable.2 b/display/test_files/mdoc/getrtable.2 new file mode 100644 index 00000000..2f2bdf64 --- /dev/null +++ b/display/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/display/test_files/mdoc/getrusage.2 b/display/test_files/mdoc/getrusage.2 new file mode 100644 index 00000000..5b541955 --- /dev/null +++ b/display/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/display/test_files/mdoc/getsid.2 b/display/test_files/mdoc/getsid.2 new file mode 100644 index 00000000..96d3dd24 --- /dev/null +++ b/display/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/display/test_files/mdoc/getsockname.2 b/display/test_files/mdoc/getsockname.2 new file mode 100644 index 00000000..41d79fdb --- /dev/null +++ b/display/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/display/test_files/mdoc/getsockopt.2 b/display/test_files/mdoc/getsockopt.2 new file mode 100644 index 00000000..b5603a80 --- /dev/null +++ b/display/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/display/test_files/mdoc/gettimeofday.2 b/display/test_files/mdoc/gettimeofday.2 new file mode 100644 index 00000000..30105491 --- /dev/null +++ b/display/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/display/test_files/mdoc/grep.1 b/display/test_files/mdoc/grep.1 new file mode 100644 index 00000000..d4c013f4 --- /dev/null +++ b/display/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/display/test_files/mdoc/id.1 b/display/test_files/mdoc/id.1 new file mode 100644 index 00000000..5931b301 --- /dev/null +++ b/display/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/display/test_files/mdoc/ioctl.2 b/display/test_files/mdoc/ioctl.2 new file mode 100644 index 00000000..fde3420f --- /dev/null +++ b/display/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/display/test_files/mdoc/ipcs.1 b/display/test_files/mdoc/ipcs.1 new file mode 100644 index 00000000..5e88e44c --- /dev/null +++ b/display/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/display/test_files/mdoc/ktrace.2 b/display/test_files/mdoc/ktrace.2 new file mode 100644 index 00000000..790e4682 --- /dev/null +++ b/display/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/display/test_files/mdoc/lpq.1 b/display/test_files/mdoc/lpq.1 new file mode 100644 index 00000000..bbceb0dd --- /dev/null +++ b/display/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/display/test_files/mdoc/mg.1 b/display/test_files/mdoc/mg.1 new file mode 100644 index 00000000..1b32e4fb --- /dev/null +++ b/display/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/display/test_files/mdoc/minherit.2 b/display/test_files/mdoc/minherit.2 new file mode 100644 index 00000000..211a3ecd --- /dev/null +++ b/display/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/display/test_files/mdoc/mkdir.1 b/display/test_files/mdoc/mkdir.1 new file mode 100644 index 00000000..76b07e85 --- /dev/null +++ b/display/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/display/test_files/mdoc/mkfifo.2 b/display/test_files/mdoc/mkfifo.2 new file mode 100644 index 00000000..bf75f8f7 --- /dev/null +++ b/display/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/display/test_files/mdoc/mlockall.2 b/display/test_files/mdoc/mlockall.2 new file mode 100644 index 00000000..de0bf39f --- /dev/null +++ b/display/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/display/test_files/mdoc/mopa.out.1 b/display/test_files/mdoc/mopa.out.1 new file mode 100644 index 00000000..cb6bf3b1 --- /dev/null +++ b/display/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/display/test_files/mdoc/moptrace.1 b/display/test_files/mdoc/moptrace.1 new file mode 100644 index 00000000..45b2d035 --- /dev/null +++ b/display/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/display/test_files/mdoc/msgrcv.2 b/display/test_files/mdoc/msgrcv.2 new file mode 100644 index 00000000..078cae3c --- /dev/null +++ b/display/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/display/test_files/mdoc/msgsnd.2 b/display/test_files/mdoc/msgsnd.2 new file mode 100644 index 00000000..99abd3a8 --- /dev/null +++ b/display/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/display/test_files/mdoc/munmap.2 b/display/test_files/mdoc/munmap.2 new file mode 100644 index 00000000..7376a50c --- /dev/null +++ b/display/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/display/test_files/mdoc/mv.1 b/display/test_files/mdoc/mv.1 new file mode 100644 index 00000000..bb6925e6 --- /dev/null +++ b/display/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/display/test_files/mdoc/nl.1 b/display/test_files/mdoc/nl.1 new file mode 100644 index 00000000..3375ac54 --- /dev/null +++ b/display/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/display/test_files/mdoc/nm.1 b/display/test_files/mdoc/nm.1 new file mode 100644 index 00000000..273402c4 --- /dev/null +++ b/display/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/display/test_files/mdoc/open.2 b/display/test_files/mdoc/open.2 new file mode 100644 index 00000000..96d9a087 --- /dev/null +++ b/display/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/display/test_files/mdoc/poll.2 b/display/test_files/mdoc/poll.2 new file mode 100644 index 00000000..c7d3d609 --- /dev/null +++ b/display/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/display/test_files/mdoc/profil.2 b/display/test_files/mdoc/profil.2 new file mode 100644 index 00000000..960d2109 --- /dev/null +++ b/display/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/display/test_files/mdoc/quotactl.2 b/display/test_files/mdoc/quotactl.2 new file mode 100644 index 00000000..72fbed3a --- /dev/null +++ b/display/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/display/test_files/mdoc/rcs.1 b/display/test_files/mdoc/rcs.1 new file mode 100644 index 00000000..220f1ab9 --- /dev/null +++ b/display/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/display/test_files/mdoc/rdist.1 b/display/test_files/mdoc/rdist.1 new file mode 100644 index 00000000..e6616fbf --- /dev/null +++ b/display/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/display/test_files/mdoc/read.2 b/display/test_files/mdoc/read.2 new file mode 100644 index 00000000..1cdacd72 --- /dev/null +++ b/display/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/display/test_files/mdoc/reboot.2 b/display/test_files/mdoc/reboot.2 new file mode 100644 index 00000000..6f0fa887 --- /dev/null +++ b/display/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/display/test_files/mdoc/rename.2 b/display/test_files/mdoc/rename.2 new file mode 100644 index 00000000..5380cc77 --- /dev/null +++ b/display/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/display/test_files/mdoc/rev.1 b/display/test_files/mdoc/rev.1 new file mode 100644 index 00000000..9dcf7ac7 --- /dev/null +++ b/display/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/display/test_files/mdoc/rlog.1 b/display/test_files/mdoc/rlog.1 new file mode 100644 index 00000000..807510ab --- /dev/null +++ b/display/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/display/test_files/mdoc/rup.1 b/display/test_files/mdoc/rup.1 new file mode 100644 index 00000000..ba6e4901 --- /dev/null +++ b/display/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/display/test_files/mdoc/sched_yield.2 b/display/test_files/mdoc/sched_yield.2 new file mode 100644 index 00000000..7b50e74e --- /dev/null +++ b/display/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/display/test_files/mdoc/scp.1 b/display/test_files/mdoc/scp.1 new file mode 100644 index 00000000..34130ac4 --- /dev/null +++ b/display/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/display/test_files/mdoc/select.2 b/display/test_files/mdoc/select.2 new file mode 100644 index 00000000..c3f7da2c --- /dev/null +++ b/display/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/display/test_files/mdoc/semget.2 b/display/test_files/mdoc/semget.2 new file mode 100644 index 00000000..2a38933b --- /dev/null +++ b/display/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/display/test_files/mdoc/send.2 b/display/test_files/mdoc/send.2 new file mode 100644 index 00000000..97d62652 --- /dev/null +++ b/display/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/display/test_files/mdoc/setuid.2 b/display/test_files/mdoc/setuid.2 new file mode 100644 index 00000000..e96fcff9 --- /dev/null +++ b/display/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/display/test_files/mdoc/sftp.1 b/display/test_files/mdoc/sftp.1 new file mode 100644 index 00000000..ae9ff21a --- /dev/null +++ b/display/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/display/test_files/mdoc/shar.1 b/display/test_files/mdoc/shar.1 new file mode 100644 index 00000000..01fafa55 --- /dev/null +++ b/display/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/display/test_files/mdoc/shmctl.2 b/display/test_files/mdoc/shmctl.2 new file mode 100644 index 00000000..1dbe90ba --- /dev/null +++ b/display/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/display/test_files/mdoc/shmget.2 b/display/test_files/mdoc/shmget.2 new file mode 100644 index 00000000..3bf3b4c1 --- /dev/null +++ b/display/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/display/test_files/mdoc/shutdown.2 b/display/test_files/mdoc/shutdown.2 new file mode 100644 index 00000000..76305429 --- /dev/null +++ b/display/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/display/test_files/mdoc/signify.1 b/display/test_files/mdoc/signify.1 new file mode 100644 index 00000000..b2cacfa1 --- /dev/null +++ b/display/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/display/test_files/mdoc/sigreturn.2 b/display/test_files/mdoc/sigreturn.2 new file mode 100644 index 00000000..fc52f7f0 --- /dev/null +++ b/display/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/display/test_files/mdoc/sigsuspend.2 b/display/test_files/mdoc/sigsuspend.2 new file mode 100644 index 00000000..e26f3d94 --- /dev/null +++ b/display/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/display/test_files/mdoc/size.1 b/display/test_files/mdoc/size.1 new file mode 100644 index 00000000..1e0e0ba7 --- /dev/null +++ b/display/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/display/test_files/mdoc/snmp.1 b/display/test_files/mdoc/snmp.1 new file mode 100644 index 00000000..55452d39 --- /dev/null +++ b/display/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/display/test_files/mdoc/socket.2 b/display/test_files/mdoc/socket.2 new file mode 100644 index 00000000..6d0ccf73 --- /dev/null +++ b/display/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/display/test_files/mdoc/socketpair.2 b/display/test_files/mdoc/socketpair.2 new file mode 100644 index 00000000..675e6b97 --- /dev/null +++ b/display/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/display/test_files/mdoc/statfs.2 b/display/test_files/mdoc/statfs.2 new file mode 100644 index 00000000..48a44fb5 --- /dev/null +++ b/display/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/display/test_files/mdoc/symlink.2 b/display/test_files/mdoc/symlink.2 new file mode 100644 index 00000000..48cd5957 --- /dev/null +++ b/display/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/display/test_files/mdoc/sync.2 b/display/test_files/mdoc/sync.2 new file mode 100644 index 00000000..9f67d53b --- /dev/null +++ b/display/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/display/test_files/mdoc/sysarch.2 b/display/test_files/mdoc/sysarch.2 new file mode 100644 index 00000000..3d6fbc2b --- /dev/null +++ b/display/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/display/test_files/mdoc/t11.2 b/display/test_files/mdoc/t11.2 new file mode 100644 index 00000000..f744068b --- /dev/null +++ b/display/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/display/test_files/mdoc/talk.1 b/display/test_files/mdoc/talk.1 new file mode 100644 index 00000000..804445ae --- /dev/null +++ b/display/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/display/test_files/mdoc/test.1 b/display/test_files/mdoc/test.1 new file mode 100644 index 00000000..cb7b35bb --- /dev/null +++ b/display/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/display/test_files/mdoc/tmux.1 b/display/test_files/mdoc/tmux.1 new file mode 100644 index 00000000..33950ba1 --- /dev/null +++ b/display/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/display/test_files/mdoc/top.1 b/display/test_files/mdoc/top.1 new file mode 100644 index 00000000..937d8064 --- /dev/null +++ b/display/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/display/test_files/mdoc/truncate.2 b/display/test_files/mdoc/truncate.2 new file mode 100644 index 00000000..497aa231 --- /dev/null +++ b/display/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/display/test_files/mdoc/umask.2 b/display/test_files/mdoc/umask.2 new file mode 100644 index 00000000..b490c51f --- /dev/null +++ b/display/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/display/test_files/mdoc/w.1 b/display/test_files/mdoc/w.1 new file mode 100644 index 00000000..faf59f70 --- /dev/null +++ b/display/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/display/test_files/mdoc/wall.1 b/display/test_files/mdoc/wall.1 new file mode 100644 index 00000000..045a9978 --- /dev/null +++ b/display/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/display/test_files/mdoc/write.2 b/display/test_files/mdoc/write.2 new file mode 100644 index 00000000..a5769325 --- /dev/null +++ b/display/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/display/test_files/mdoc/ypconnect.2 b/display/test_files/mdoc/ypconnect.2 new file mode 100644 index 00000000..95c0a2f4 --- /dev/null +++ b/display/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/display/tests/display-tests.rs b/display/tests/display-tests.rs index 8f3b3d00..2e2f4504 100644 --- a/display/tests/display-tests.rs +++ b/display/tests/display-tests.rs @@ -8,5 +8,6 @@ // mod echo; +mod man; mod more; mod printf; diff --git a/display/tests/man/mod.rs b/display/tests/man/mod.rs index 78e72000..0c6a7d59 100644 --- a/display/tests/man/mod.rs +++ b/display/tests/man/mod.rs @@ -1,105 +1,457 @@ -// -// 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 -// +// tests/man.rs -use std::process::Output; +#[cfg(test)] +mod tests { + use std::process::Command; -use plib::{run_test_with_checker, TestPlan}; + // ------------------------------------------------------------------------- + // -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"); -fn test_checker_man(plan: &TestPlan, output: &Output) { - let stdout = String::from_utf8_lossy(&output.stdout); - assert!(stdout.contains(&plan.expected_out)); + 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}" + ); + } - let stderr = String::from_utf8_lossy(&output.stderr); - assert!(stderr.contains(&plan.expected_err)); + #[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_eq!(output.status.code(), Some(plan.expected_exit_code)); - if plan.expected_exit_code == 0 { - assert!(output.status.success()); + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); } -} -fn run_test_man(args: &[&str], expected_out: &str, expected_err: &str, expected_exit_code: i32) { - let str_args: Vec = args.iter().map(|s| String::from(*s)).collect(); - - run_test_with_checker( - TestPlan { - cmd: String::from("man"), - args: str_args, - stdin_data: String::new(), - expected_out: String::from(expected_out), - expected_err: String::from(expected_err), - expected_exit_code, - }, - test_checker_man, - ); -} + // ------------------------------------------------------------------------- + // -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"); -const LS: &'static str = "ls"; -const MAN: &'static str = "man"; -const INVALID_NAME: &'static str = "invalid_name"; -const INVALID_NAME_MAN_ERROR: &'static str = - "man: system documentation for \"invalid_name\" not found\n"; -const INVALID_NAME_APROPOS_ERROR: &'static str = "invalid_name: nothing appropriate"; + 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 test_one_manpage() { - run_test_man(&[LS], "LS(1)", "", 0); -} + #[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"); -#[test] -fn test_one_page_not_found() { - run_test_man(&[INVALID_NAME], "", INVALID_NAME_MAN_ERROR, 1); -} + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } -#[test] -fn test_multiple_nampages() { - run_test_man(&[LS, MAN], "LS(1)", "", 0); - run_test_man(&[LS, MAN], "MAN(1)", "", 0); -} + // ------------------------------------------------------------------------- + // -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"); -#[test] -fn test_multiple_nampages_one_not_found() { - run_test_man(&[LS, INVALID_NAME], "LS(1)", INVALID_NAME_MAN_ERROR, 1); -} + 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 test_empty_names() { - run_test_man(&[], "", "man: no names specified\n", 1); -} + #[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"); -#[test] -fn test_k() { - run_test_man(&["-k", "user"], "ls", "", 0); -} + assert!( + output.status.success() || output.status.code() == Some(1), + "Expected exit code 0 or 1, got: {:?}", + output.status.code() + ); + } -#[test] -fn test_k_invalid_name() { - run_test_man(&["-k", "invalid_name"], "", &INVALID_NAME_APROPOS_ERROR, 1); -} + // ------------------------------------------------------------------------- + // -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"); -#[test] -fn test_k_multiple_nampages() { - run_test_man(&["-k", LS, MAN], "ls", "", 0); - run_test_man(&["-k", LS, MAN], "man", "", 0); -} + 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 test_k_multiple_nampages_one_not_found() { - run_test_man( - &["-k", LS, INVALID_NAME], - "ls", - INVALID_NAME_APROPOS_ERROR, - 1, - ); -} + #[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() + ); + } -#[test] -fn test_k_empty_names() { - run_test_man(&["-k"], "", "man: no names specified\n", 1); + // ------------------------------------------------------------------------- + // -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() + ); + } } From 82d01ebf1b949dca2c0607ce266d74c6672716e4 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 10 May 2025 12:41:54 +0300 Subject: [PATCH 3/4] [man] move man outside display as independed package --- Cargo.toml | 1 + display/Cargo.toml | 10 - display/tests/display-tests.rs | 1 - man/Cargo.toml | 32 + man/man.rs | 805 ++ man/man.test.conf | 53 + man/man_util/config.rs | 69 + man/man_util/formatter.rs | 7635 ++++++++++++ man/man_util/mdoc.pest | 747 ++ man/man_util/mdoc_macro/mod.rs | 193 + man/man_util/mdoc_macro/text_production.rs | 331 + man/man_util/mdoc_macro/types.rs | 142 + man/man_util/mod.rs | 17 + man/man_util/parser.rs | 12052 +++++++++++++++++++ man/test_files/mdoc/access.2 | 240 + man/test_files/mdoc/adjfreq.2 | 76 + man/test_files/mdoc/atq.1 | 103 + man/test_files/mdoc/bc.1 | 409 + man/test_files/mdoc/brk.2 | 154 + man/test_files/mdoc/cal.1 | 132 + man/test_files/mdoc/cat.1 | 185 + man/test_files/mdoc/chdir.2 | 130 + man/test_files/mdoc/chflags.2 | 228 + man/test_files/mdoc/chmod.2 | 275 + man/test_files/mdoc/closefrom.2 | 67 + man/test_files/mdoc/cu.1 | 224 + man/test_files/mdoc/cut.1 | 184 + man/test_files/mdoc/cvs.1 | 1987 +++ man/test_files/mdoc/dc.1 | 550 + man/test_files/mdoc/diff.1 | 475 + man/test_files/mdoc/dup.2 | 214 + man/test_files/mdoc/execve.2 | 348 + man/test_files/mdoc/fgen.1 | 71 + man/test_files/mdoc/file.1 | 130 + man/test_files/mdoc/flex.1 | 4440 +++++++ man/test_files/mdoc/flock.2 | 151 + man/test_files/mdoc/fork.2 | 145 + man/test_files/mdoc/fsync.2 | 121 + man/test_files/mdoc/futex.2 | 153 + man/test_files/mdoc/getdents.2 | 195 + man/test_files/mdoc/getfh.2 | 102 + man/test_files/mdoc/getgroups.2 | 99 + man/test_files/mdoc/getitimer.2 | 176 + man/test_files/mdoc/getpeername.2 | 143 + man/test_files/mdoc/getpriority.2 | 158 + man/test_files/mdoc/getrtable.2 | 66 + man/test_files/mdoc/getrusage.2 | 192 + man/test_files/mdoc/getsid.2 | 83 + man/test_files/mdoc/getsockname.2 | 162 + man/test_files/mdoc/getsockopt.2 | 541 + man/test_files/mdoc/gettimeofday.2 | 205 + man/test_files/mdoc/grep.1 | 395 + man/test_files/mdoc/id.1 | 164 + man/test_files/mdoc/ioctl.2 | 171 + man/test_files/mdoc/ipcs.1 | 149 + man/test_files/mdoc/ktrace.2 | 232 + man/test_files/mdoc/lpq.1 | 141 + man/test_files/mdoc/mg.1 | 1203 ++ man/test_files/mdoc/minherit.2 | 108 + man/test_files/mdoc/mkdir.1 | 121 + man/test_files/mdoc/mkfifo.2 | 181 + man/test_files/mdoc/mlockall.2 | 124 + man/test_files/mdoc/mopa.out.1 | 49 + man/test_files/mdoc/moptrace.1 | 74 + man/test_files/mdoc/msgrcv.2 | 207 + man/test_files/mdoc/msgsnd.2 | 168 + man/test_files/mdoc/munmap.2 | 104 + man/test_files/mdoc/mv.1 | 203 + man/test_files/mdoc/nl.1 | 231 + man/test_files/mdoc/nm.1 | 166 + man/test_files/mdoc/open.2 | 465 + man/test_files/mdoc/poll.2 | 364 + man/test_files/mdoc/profil.2 | 127 + man/test_files/mdoc/quotactl.2 | 212 + man/test_files/mdoc/rcs.1 | 485 + man/test_files/mdoc/rdist.1 | 866 ++ man/test_files/mdoc/read.2 | 282 + man/test_files/mdoc/reboot.2 | 163 + man/test_files/mdoc/rename.2 | 296 + man/test_files/mdoc/rev.1 | 59 + man/test_files/mdoc/rlog.1 | 208 + man/test_files/mdoc/rup.1 | 101 + man/test_files/mdoc/sched_yield.2 | 49 + man/test_files/mdoc/scp.1 | 364 + man/test_files/mdoc/select.2 | 248 + man/test_files/mdoc/semget.2 | 154 + man/test_files/mdoc/send.2 | 289 + man/test_files/mdoc/setuid.2 | 152 + man/test_files/mdoc/sftp.1 | 767 ++ man/test_files/mdoc/shar.1 | 102 + man/test_files/mdoc/shmctl.2 | 198 + man/test_files/mdoc/shmget.2 | 142 + man/test_files/mdoc/shutdown.2 | 154 + man/test_files/mdoc/signify.1 | 206 + man/test_files/mdoc/sigreturn.2 | 86 + man/test_files/mdoc/sigsuspend.2 | 78 + man/test_files/mdoc/size.1 | 78 + man/test_files/mdoc/snmp.1 | 568 + man/test_files/mdoc/socket.2 | 311 + man/test_files/mdoc/socketpair.2 | 132 + man/test_files/mdoc/statfs.2 | 158 + man/test_files/mdoc/symlink.2 | 204 + man/test_files/mdoc/sync.2 | 73 + man/test_files/mdoc/sysarch.2 | 68 + man/test_files/mdoc/t11.2 | 908 ++ man/test_files/mdoc/talk.1 | 160 + man/test_files/mdoc/test.1 | 7799 ++++++++++++ man/test_files/mdoc/tmux.1 | 7799 ++++++++++++ man/test_files/mdoc/top.1 | 520 + man/test_files/mdoc/truncate.2 | 152 + man/test_files/mdoc/umask.2 | 94 + man/test_files/mdoc/w.1 | 134 + man/test_files/mdoc/wall.1 | 83 + man/test_files/mdoc/write.2 | 329 + man/test_files/mdoc/ypconnect.2 | 80 + man/tests/man-tests.rs | 10 + man/tests/man/mod.rs | 464 + 117 files changed, 66323 insertions(+), 11 deletions(-) create mode 100644 man/Cargo.toml create mode 100644 man/man.rs create mode 100644 man/man.test.conf create mode 100644 man/man_util/config.rs create mode 100644 man/man_util/formatter.rs create mode 100644 man/man_util/mdoc.pest create mode 100644 man/man_util/mdoc_macro/mod.rs create mode 100644 man/man_util/mdoc_macro/text_production.rs create mode 100644 man/man_util/mdoc_macro/types.rs create mode 100644 man/man_util/mod.rs create mode 100644 man/man_util/parser.rs create mode 100644 man/test_files/mdoc/access.2 create mode 100644 man/test_files/mdoc/adjfreq.2 create mode 100644 man/test_files/mdoc/atq.1 create mode 100644 man/test_files/mdoc/bc.1 create mode 100644 man/test_files/mdoc/brk.2 create mode 100644 man/test_files/mdoc/cal.1 create mode 100644 man/test_files/mdoc/cat.1 create mode 100644 man/test_files/mdoc/chdir.2 create mode 100644 man/test_files/mdoc/chflags.2 create mode 100644 man/test_files/mdoc/chmod.2 create mode 100644 man/test_files/mdoc/closefrom.2 create mode 100644 man/test_files/mdoc/cu.1 create mode 100644 man/test_files/mdoc/cut.1 create mode 100644 man/test_files/mdoc/cvs.1 create mode 100644 man/test_files/mdoc/dc.1 create mode 100644 man/test_files/mdoc/diff.1 create mode 100644 man/test_files/mdoc/dup.2 create mode 100644 man/test_files/mdoc/execve.2 create mode 100644 man/test_files/mdoc/fgen.1 create mode 100644 man/test_files/mdoc/file.1 create mode 100644 man/test_files/mdoc/flex.1 create mode 100644 man/test_files/mdoc/flock.2 create mode 100644 man/test_files/mdoc/fork.2 create mode 100644 man/test_files/mdoc/fsync.2 create mode 100644 man/test_files/mdoc/futex.2 create mode 100644 man/test_files/mdoc/getdents.2 create mode 100644 man/test_files/mdoc/getfh.2 create mode 100644 man/test_files/mdoc/getgroups.2 create mode 100644 man/test_files/mdoc/getitimer.2 create mode 100644 man/test_files/mdoc/getpeername.2 create mode 100644 man/test_files/mdoc/getpriority.2 create mode 100644 man/test_files/mdoc/getrtable.2 create mode 100644 man/test_files/mdoc/getrusage.2 create mode 100644 man/test_files/mdoc/getsid.2 create mode 100644 man/test_files/mdoc/getsockname.2 create mode 100644 man/test_files/mdoc/getsockopt.2 create mode 100644 man/test_files/mdoc/gettimeofday.2 create mode 100644 man/test_files/mdoc/grep.1 create mode 100644 man/test_files/mdoc/id.1 create mode 100644 man/test_files/mdoc/ioctl.2 create mode 100644 man/test_files/mdoc/ipcs.1 create mode 100644 man/test_files/mdoc/ktrace.2 create mode 100644 man/test_files/mdoc/lpq.1 create mode 100644 man/test_files/mdoc/mg.1 create mode 100644 man/test_files/mdoc/minherit.2 create mode 100644 man/test_files/mdoc/mkdir.1 create mode 100644 man/test_files/mdoc/mkfifo.2 create mode 100644 man/test_files/mdoc/mlockall.2 create mode 100644 man/test_files/mdoc/mopa.out.1 create mode 100644 man/test_files/mdoc/moptrace.1 create mode 100644 man/test_files/mdoc/msgrcv.2 create mode 100644 man/test_files/mdoc/msgsnd.2 create mode 100644 man/test_files/mdoc/munmap.2 create mode 100644 man/test_files/mdoc/mv.1 create mode 100644 man/test_files/mdoc/nl.1 create mode 100644 man/test_files/mdoc/nm.1 create mode 100644 man/test_files/mdoc/open.2 create mode 100644 man/test_files/mdoc/poll.2 create mode 100644 man/test_files/mdoc/profil.2 create mode 100644 man/test_files/mdoc/quotactl.2 create mode 100644 man/test_files/mdoc/rcs.1 create mode 100644 man/test_files/mdoc/rdist.1 create mode 100644 man/test_files/mdoc/read.2 create mode 100644 man/test_files/mdoc/reboot.2 create mode 100644 man/test_files/mdoc/rename.2 create mode 100644 man/test_files/mdoc/rev.1 create mode 100644 man/test_files/mdoc/rlog.1 create mode 100644 man/test_files/mdoc/rup.1 create mode 100644 man/test_files/mdoc/sched_yield.2 create mode 100644 man/test_files/mdoc/scp.1 create mode 100644 man/test_files/mdoc/select.2 create mode 100644 man/test_files/mdoc/semget.2 create mode 100644 man/test_files/mdoc/send.2 create mode 100644 man/test_files/mdoc/setuid.2 create mode 100644 man/test_files/mdoc/sftp.1 create mode 100644 man/test_files/mdoc/shar.1 create mode 100644 man/test_files/mdoc/shmctl.2 create mode 100644 man/test_files/mdoc/shmget.2 create mode 100644 man/test_files/mdoc/shutdown.2 create mode 100644 man/test_files/mdoc/signify.1 create mode 100644 man/test_files/mdoc/sigreturn.2 create mode 100644 man/test_files/mdoc/sigsuspend.2 create mode 100644 man/test_files/mdoc/size.1 create mode 100644 man/test_files/mdoc/snmp.1 create mode 100644 man/test_files/mdoc/socket.2 create mode 100644 man/test_files/mdoc/socketpair.2 create mode 100644 man/test_files/mdoc/statfs.2 create mode 100644 man/test_files/mdoc/symlink.2 create mode 100644 man/test_files/mdoc/sync.2 create mode 100644 man/test_files/mdoc/sysarch.2 create mode 100644 man/test_files/mdoc/t11.2 create mode 100644 man/test_files/mdoc/talk.1 create mode 100644 man/test_files/mdoc/test.1 create mode 100644 man/test_files/mdoc/tmux.1 create mode 100644 man/test_files/mdoc/top.1 create mode 100644 man/test_files/mdoc/truncate.2 create mode 100644 man/test_files/mdoc/umask.2 create mode 100644 man/test_files/mdoc/w.1 create mode 100644 man/test_files/mdoc/wall.1 create mode 100644 man/test_files/mdoc/write.2 create mode 100644 man/test_files/mdoc/ypconnect.2 create mode 100644 man/tests/man-tests.rs create mode 100644 man/tests/man/mod.rs 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/Cargo.toml b/display/Cargo.toml index d17739d0..0886aa1e 100644 --- a/display/Cargo.toml +++ b/display/Cargo.toml @@ -12,18 +12,12 @@ clap.workspace = true clap.features = ["env"] gettext-rs.workspace = true libc.workspace = true -pest = "2.7" -pest_derive = "2.7" termion = "4.0" 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 @@ -36,10 +30,6 @@ path = "./echo.rs" name = "printf" path = "./printf.rs" -[[bin]] -name = "man" -path = "./man.rs" - [[bin]] name = "more" path = "./more.rs" diff --git a/display/tests/display-tests.rs b/display/tests/display-tests.rs index 2e2f4504..8f3b3d00 100644 --- a/display/tests/display-tests.rs +++ b/display/tests/display-tests.rs @@ -8,6 +8,5 @@ // mod echo; -mod man; mod more; mod printf; 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() + ); + } +} From ea351700329f67ad3f2398d00c026946ce7cd919 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 10 May 2025 14:11:03 +0300 Subject: [PATCH 4/4] [man] cleaning --- display/Cargo.toml | 1 - display/man.rs | 805 -- display/man.test.conf | 53 - display/man_util/config.rs | 69 - display/man_util/formatter.rs | 7635 ---------- display/man_util/mdoc.pest | 747 - display/man_util/mdoc_macro/mod.rs | 193 - .../man_util/mdoc_macro/text_production.rs | 331 - display/man_util/mdoc_macro/types.rs | 142 - display/man_util/mod.rs | 17 - display/man_util/parser.rs | 12052 ---------------- display/test_files/mdoc/access.2 | 240 - display/test_files/mdoc/adjfreq.2 | 76 - display/test_files/mdoc/atq.1 | 103 - display/test_files/mdoc/bc.1 | 409 - display/test_files/mdoc/brk.2 | 154 - display/test_files/mdoc/cal.1 | 132 - display/test_files/mdoc/cat.1 | 185 - display/test_files/mdoc/chdir.2 | 130 - display/test_files/mdoc/chflags.2 | 228 - display/test_files/mdoc/chmod.2 | 275 - display/test_files/mdoc/closefrom.2 | 67 - display/test_files/mdoc/cu.1 | 224 - display/test_files/mdoc/cut.1 | 184 - display/test_files/mdoc/cvs.1 | 1987 --- display/test_files/mdoc/dc.1 | 550 - display/test_files/mdoc/diff.1 | 475 - display/test_files/mdoc/dup.2 | 214 - display/test_files/mdoc/execve.2 | 348 - display/test_files/mdoc/fgen.1 | 71 - display/test_files/mdoc/file.1 | 130 - display/test_files/mdoc/flex.1 | 4440 ------ display/test_files/mdoc/flock.2 | 151 - display/test_files/mdoc/fork.2 | 145 - display/test_files/mdoc/fsync.2 | 121 - display/test_files/mdoc/futex.2 | 153 - display/test_files/mdoc/getdents.2 | 195 - display/test_files/mdoc/getfh.2 | 102 - display/test_files/mdoc/getgroups.2 | 99 - display/test_files/mdoc/getitimer.2 | 176 - display/test_files/mdoc/getpeername.2 | 143 - display/test_files/mdoc/getpriority.2 | 158 - display/test_files/mdoc/getrtable.2 | 66 - display/test_files/mdoc/getrusage.2 | 192 - display/test_files/mdoc/getsid.2 | 83 - display/test_files/mdoc/getsockname.2 | 162 - display/test_files/mdoc/getsockopt.2 | 541 - display/test_files/mdoc/gettimeofday.2 | 205 - display/test_files/mdoc/grep.1 | 395 - display/test_files/mdoc/id.1 | 164 - display/test_files/mdoc/ioctl.2 | 171 - display/test_files/mdoc/ipcs.1 | 149 - display/test_files/mdoc/ktrace.2 | 232 - display/test_files/mdoc/lpq.1 | 141 - display/test_files/mdoc/mg.1 | 1203 -- display/test_files/mdoc/minherit.2 | 108 - display/test_files/mdoc/mkdir.1 | 121 - display/test_files/mdoc/mkfifo.2 | 181 - display/test_files/mdoc/mlockall.2 | 124 - display/test_files/mdoc/mopa.out.1 | 49 - display/test_files/mdoc/moptrace.1 | 74 - display/test_files/mdoc/msgrcv.2 | 207 - display/test_files/mdoc/msgsnd.2 | 168 - display/test_files/mdoc/munmap.2 | 104 - display/test_files/mdoc/mv.1 | 203 - display/test_files/mdoc/nl.1 | 231 - display/test_files/mdoc/nm.1 | 166 - display/test_files/mdoc/open.2 | 465 - display/test_files/mdoc/poll.2 | 364 - display/test_files/mdoc/profil.2 | 127 - display/test_files/mdoc/quotactl.2 | 212 - display/test_files/mdoc/rcs.1 | 485 - display/test_files/mdoc/rdist.1 | 866 -- display/test_files/mdoc/read.2 | 282 - display/test_files/mdoc/reboot.2 | 163 - display/test_files/mdoc/rename.2 | 296 - display/test_files/mdoc/rev.1 | 59 - display/test_files/mdoc/rlog.1 | 208 - display/test_files/mdoc/rup.1 | 101 - display/test_files/mdoc/sched_yield.2 | 49 - display/test_files/mdoc/scp.1 | 364 - display/test_files/mdoc/select.2 | 248 - display/test_files/mdoc/semget.2 | 154 - display/test_files/mdoc/send.2 | 289 - display/test_files/mdoc/setuid.2 | 152 - display/test_files/mdoc/sftp.1 | 767 - display/test_files/mdoc/shar.1 | 102 - display/test_files/mdoc/shmctl.2 | 198 - display/test_files/mdoc/shmget.2 | 142 - display/test_files/mdoc/shutdown.2 | 154 - display/test_files/mdoc/signify.1 | 206 - display/test_files/mdoc/sigreturn.2 | 86 - display/test_files/mdoc/sigsuspend.2 | 78 - display/test_files/mdoc/size.1 | 78 - display/test_files/mdoc/snmp.1 | 568 - display/test_files/mdoc/socket.2 | 311 - display/test_files/mdoc/socketpair.2 | 132 - display/test_files/mdoc/statfs.2 | 158 - display/test_files/mdoc/symlink.2 | 204 - display/test_files/mdoc/sync.2 | 73 - display/test_files/mdoc/sysarch.2 | 68 - display/test_files/mdoc/t11.2 | 908 -- display/test_files/mdoc/talk.1 | 160 - display/test_files/mdoc/test.1 | 7799 ---------- display/test_files/mdoc/tmux.1 | 7799 ---------- display/test_files/mdoc/top.1 | 520 - display/test_files/mdoc/truncate.2 | 152 - display/test_files/mdoc/umask.2 | 94 - display/test_files/mdoc/w.1 | 134 - display/test_files/mdoc/wall.1 | 83 - display/test_files/mdoc/write.2 | 329 - display/test_files/mdoc/ypconnect.2 | 80 - display/tests/man/mod.rs | 457 - 113 files changed, 66274 deletions(-) delete mode 100644 display/man.rs delete mode 100644 display/man.test.conf delete mode 100644 display/man_util/config.rs delete mode 100644 display/man_util/formatter.rs delete mode 100644 display/man_util/mdoc.pest delete mode 100644 display/man_util/mdoc_macro/mod.rs delete mode 100644 display/man_util/mdoc_macro/text_production.rs delete mode 100644 display/man_util/mdoc_macro/types.rs delete mode 100644 display/man_util/mod.rs delete mode 100644 display/man_util/parser.rs delete mode 100644 display/test_files/mdoc/access.2 delete mode 100644 display/test_files/mdoc/adjfreq.2 delete mode 100644 display/test_files/mdoc/atq.1 delete mode 100644 display/test_files/mdoc/bc.1 delete mode 100644 display/test_files/mdoc/brk.2 delete mode 100644 display/test_files/mdoc/cal.1 delete mode 100644 display/test_files/mdoc/cat.1 delete mode 100644 display/test_files/mdoc/chdir.2 delete mode 100644 display/test_files/mdoc/chflags.2 delete mode 100644 display/test_files/mdoc/chmod.2 delete mode 100644 display/test_files/mdoc/closefrom.2 delete mode 100644 display/test_files/mdoc/cu.1 delete mode 100644 display/test_files/mdoc/cut.1 delete mode 100644 display/test_files/mdoc/cvs.1 delete mode 100644 display/test_files/mdoc/dc.1 delete mode 100644 display/test_files/mdoc/diff.1 delete mode 100644 display/test_files/mdoc/dup.2 delete mode 100644 display/test_files/mdoc/execve.2 delete mode 100644 display/test_files/mdoc/fgen.1 delete mode 100644 display/test_files/mdoc/file.1 delete mode 100644 display/test_files/mdoc/flex.1 delete mode 100644 display/test_files/mdoc/flock.2 delete mode 100644 display/test_files/mdoc/fork.2 delete mode 100644 display/test_files/mdoc/fsync.2 delete mode 100644 display/test_files/mdoc/futex.2 delete mode 100644 display/test_files/mdoc/getdents.2 delete mode 100644 display/test_files/mdoc/getfh.2 delete mode 100644 display/test_files/mdoc/getgroups.2 delete mode 100644 display/test_files/mdoc/getitimer.2 delete mode 100644 display/test_files/mdoc/getpeername.2 delete mode 100644 display/test_files/mdoc/getpriority.2 delete mode 100644 display/test_files/mdoc/getrtable.2 delete mode 100644 display/test_files/mdoc/getrusage.2 delete mode 100644 display/test_files/mdoc/getsid.2 delete mode 100644 display/test_files/mdoc/getsockname.2 delete mode 100644 display/test_files/mdoc/getsockopt.2 delete mode 100644 display/test_files/mdoc/gettimeofday.2 delete mode 100644 display/test_files/mdoc/grep.1 delete mode 100644 display/test_files/mdoc/id.1 delete mode 100644 display/test_files/mdoc/ioctl.2 delete mode 100644 display/test_files/mdoc/ipcs.1 delete mode 100644 display/test_files/mdoc/ktrace.2 delete mode 100644 display/test_files/mdoc/lpq.1 delete mode 100644 display/test_files/mdoc/mg.1 delete mode 100644 display/test_files/mdoc/minherit.2 delete mode 100644 display/test_files/mdoc/mkdir.1 delete mode 100644 display/test_files/mdoc/mkfifo.2 delete mode 100644 display/test_files/mdoc/mlockall.2 delete mode 100644 display/test_files/mdoc/mopa.out.1 delete mode 100644 display/test_files/mdoc/moptrace.1 delete mode 100644 display/test_files/mdoc/msgrcv.2 delete mode 100644 display/test_files/mdoc/msgsnd.2 delete mode 100644 display/test_files/mdoc/munmap.2 delete mode 100644 display/test_files/mdoc/mv.1 delete mode 100644 display/test_files/mdoc/nl.1 delete mode 100644 display/test_files/mdoc/nm.1 delete mode 100644 display/test_files/mdoc/open.2 delete mode 100644 display/test_files/mdoc/poll.2 delete mode 100644 display/test_files/mdoc/profil.2 delete mode 100644 display/test_files/mdoc/quotactl.2 delete mode 100644 display/test_files/mdoc/rcs.1 delete mode 100644 display/test_files/mdoc/rdist.1 delete mode 100644 display/test_files/mdoc/read.2 delete mode 100644 display/test_files/mdoc/reboot.2 delete mode 100644 display/test_files/mdoc/rename.2 delete mode 100644 display/test_files/mdoc/rev.1 delete mode 100644 display/test_files/mdoc/rlog.1 delete mode 100644 display/test_files/mdoc/rup.1 delete mode 100644 display/test_files/mdoc/sched_yield.2 delete mode 100644 display/test_files/mdoc/scp.1 delete mode 100644 display/test_files/mdoc/select.2 delete mode 100644 display/test_files/mdoc/semget.2 delete mode 100644 display/test_files/mdoc/send.2 delete mode 100644 display/test_files/mdoc/setuid.2 delete mode 100644 display/test_files/mdoc/sftp.1 delete mode 100644 display/test_files/mdoc/shar.1 delete mode 100644 display/test_files/mdoc/shmctl.2 delete mode 100644 display/test_files/mdoc/shmget.2 delete mode 100644 display/test_files/mdoc/shutdown.2 delete mode 100644 display/test_files/mdoc/signify.1 delete mode 100644 display/test_files/mdoc/sigreturn.2 delete mode 100644 display/test_files/mdoc/sigsuspend.2 delete mode 100644 display/test_files/mdoc/size.1 delete mode 100644 display/test_files/mdoc/snmp.1 delete mode 100644 display/test_files/mdoc/socket.2 delete mode 100644 display/test_files/mdoc/socketpair.2 delete mode 100644 display/test_files/mdoc/statfs.2 delete mode 100644 display/test_files/mdoc/symlink.2 delete mode 100644 display/test_files/mdoc/sync.2 delete mode 100644 display/test_files/mdoc/sysarch.2 delete mode 100644 display/test_files/mdoc/t11.2 delete mode 100644 display/test_files/mdoc/talk.1 delete mode 100644 display/test_files/mdoc/test.1 delete mode 100644 display/test_files/mdoc/tmux.1 delete mode 100644 display/test_files/mdoc/top.1 delete mode 100644 display/test_files/mdoc/truncate.2 delete mode 100644 display/test_files/mdoc/umask.2 delete mode 100644 display/test_files/mdoc/w.1 delete mode 100644 display/test_files/mdoc/wall.1 delete mode 100644 display/test_files/mdoc/write.2 delete mode 100644 display/test_files/mdoc/ypconnect.2 delete mode 100644 display/tests/man/mod.rs diff --git a/display/Cargo.toml b/display/Cargo.toml index 0886aa1e..a5acb137 100644 --- a/display/Cargo.toml +++ b/display/Cargo.toml @@ -14,7 +14,6 @@ gettext-rs.workspace = true libc.workspace = true termion = "4.0" thiserror = "1.0" -regex.workspace = true [dev-dependencies] plib = { path = "../plib" } diff --git a/display/man.rs b/display/man.rs deleted file mode 100644 index 5d62cc6c..00000000 --- a/display/man.rs +++ /dev/null @@ -1,805 +0,0 @@ -// -// 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/display/man.test.conf b/display/man.test.conf deleted file mode 100644 index 8dbb42e9..00000000 --- a/display/man.test.conf +++ /dev/null @@ -1,53 +0,0 @@ -# $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/display/man_util/config.rs b/display/man_util/config.rs deleted file mode 100644 index 6ca67d01..00000000 --- a/display/man_util/config.rs +++ /dev/null @@ -1,69 +0,0 @@ -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/display/man_util/formatter.rs b/display/man_util/formatter.rs deleted file mode 100644 index d3bea089..00000000 --- a/display/man_util/formatter.rs +++ /dev/null @@ -1,7635 +0,0 @@ -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/display/man_util/mdoc.pest b/display/man_util/mdoc.pest deleted file mode 100644 index c6a395d8..00000000 --- a/display/man_util/mdoc.pest +++ /dev/null @@ -1,747 +0,0 @@ -// -// 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/display/man_util/mdoc_macro/mod.rs b/display/man_util/mdoc_macro/mod.rs deleted file mode 100644 index 26b91374..00000000 --- a/display/man_util/mdoc_macro/mod.rs +++ /dev/null @@ -1,193 +0,0 @@ -// -// 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/display/man_util/mdoc_macro/text_production.rs b/display/man_util/mdoc_macro/text_production.rs deleted file mode 100644 index f1725404..00000000 --- a/display/man_util/mdoc_macro/text_production.rs +++ /dev/null @@ -1,331 +0,0 @@ -// -// 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/display/man_util/mdoc_macro/types.rs b/display/man_util/mdoc_macro/types.rs deleted file mode 100644 index a9f85f66..00000000 --- a/display/man_util/mdoc_macro/types.rs +++ /dev/null @@ -1,142 +0,0 @@ -// -// 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/display/man_util/mod.rs b/display/man_util/mod.rs deleted file mode 100644 index 2d7a9be3..00000000 --- a/display/man_util/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// -// 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/display/man_util/parser.rs b/display/man_util/parser.rs deleted file mode 100644 index 69a4a83f..00000000 --- a/display/man_util/parser.rs +++ /dev/null @@ -1,12052 +0,0 @@ -// -// 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/display/test_files/mdoc/access.2 b/display/test_files/mdoc/access.2 deleted file mode 100644 index 756449a8..00000000 --- a/display/test_files/mdoc/access.2 +++ /dev/null @@ -1,240 +0,0 @@ -.\" $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/display/test_files/mdoc/adjfreq.2 b/display/test_files/mdoc/adjfreq.2 deleted file mode 100644 index b3903024..00000000 --- a/display/test_files/mdoc/adjfreq.2 +++ /dev/null @@ -1,76 +0,0 @@ -.\" $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/display/test_files/mdoc/atq.1 b/display/test_files/mdoc/atq.1 deleted file mode 100644 index 1e43804f..00000000 --- a/display/test_files/mdoc/atq.1 +++ /dev/null @@ -1,103 +0,0 @@ -.\" $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/display/test_files/mdoc/bc.1 b/display/test_files/mdoc/bc.1 deleted file mode 100644 index 62043c46..00000000 --- a/display/test_files/mdoc/bc.1 +++ /dev/null @@ -1,409 +0,0 @@ -.\" $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/display/test_files/mdoc/brk.2 b/display/test_files/mdoc/brk.2 deleted file mode 100644 index 77826c91..00000000 --- a/display/test_files/mdoc/brk.2 +++ /dev/null @@ -1,154 +0,0 @@ -.\" $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/display/test_files/mdoc/cal.1 b/display/test_files/mdoc/cal.1 deleted file mode 100644 index 1d275943..00000000 --- a/display/test_files/mdoc/cal.1 +++ /dev/null @@ -1,132 +0,0 @@ -.\" $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/display/test_files/mdoc/cat.1 b/display/test_files/mdoc/cat.1 deleted file mode 100644 index 2eeaa0d6..00000000 --- a/display/test_files/mdoc/cat.1 +++ /dev/null @@ -1,185 +0,0 @@ -.\" $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/display/test_files/mdoc/chdir.2 b/display/test_files/mdoc/chdir.2 deleted file mode 100644 index b4530895..00000000 --- a/display/test_files/mdoc/chdir.2 +++ /dev/null @@ -1,130 +0,0 @@ -.\" $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/display/test_files/mdoc/chflags.2 b/display/test_files/mdoc/chflags.2 deleted file mode 100644 index 8836cc82..00000000 --- a/display/test_files/mdoc/chflags.2 +++ /dev/null @@ -1,228 +0,0 @@ -.\" $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/display/test_files/mdoc/chmod.2 b/display/test_files/mdoc/chmod.2 deleted file mode 100644 index 110470f8..00000000 --- a/display/test_files/mdoc/chmod.2 +++ /dev/null @@ -1,275 +0,0 @@ -.\" $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/display/test_files/mdoc/closefrom.2 b/display/test_files/mdoc/closefrom.2 deleted file mode 100644 index a6087df6..00000000 --- a/display/test_files/mdoc/closefrom.2 +++ /dev/null @@ -1,67 +0,0 @@ -.\" $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/display/test_files/mdoc/cu.1 b/display/test_files/mdoc/cu.1 deleted file mode 100644 index e2afc254..00000000 --- a/display/test_files/mdoc/cu.1 +++ /dev/null @@ -1,224 +0,0 @@ -.\" $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/display/test_files/mdoc/cut.1 b/display/test_files/mdoc/cut.1 deleted file mode 100644 index e1718775..00000000 --- a/display/test_files/mdoc/cut.1 +++ /dev/null @@ -1,184 +0,0 @@ -.\" $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/display/test_files/mdoc/cvs.1 b/display/test_files/mdoc/cvs.1 deleted file mode 100644 index 97dccddf..00000000 --- a/display/test_files/mdoc/cvs.1 +++ /dev/null @@ -1,1987 +0,0 @@ -.\" $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/display/test_files/mdoc/dc.1 b/display/test_files/mdoc/dc.1 deleted file mode 100644 index c8f39233..00000000 --- a/display/test_files/mdoc/dc.1 +++ /dev/null @@ -1,550 +0,0 @@ -.\" $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/display/test_files/mdoc/diff.1 b/display/test_files/mdoc/diff.1 deleted file mode 100644 index 7935075c..00000000 --- a/display/test_files/mdoc/diff.1 +++ /dev/null @@ -1,475 +0,0 @@ -.\" $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/display/test_files/mdoc/dup.2 b/display/test_files/mdoc/dup.2 deleted file mode 100644 index 4da4bed3..00000000 --- a/display/test_files/mdoc/dup.2 +++ /dev/null @@ -1,214 +0,0 @@ -.\" $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/display/test_files/mdoc/execve.2 b/display/test_files/mdoc/execve.2 deleted file mode 100644 index dc814441..00000000 --- a/display/test_files/mdoc/execve.2 +++ /dev/null @@ -1,348 +0,0 @@ -.\" $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/display/test_files/mdoc/fgen.1 b/display/test_files/mdoc/fgen.1 deleted file mode 100644 index 9f2ad296..00000000 --- a/display/test_files/mdoc/fgen.1 +++ /dev/null @@ -1,71 +0,0 @@ -.\" $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/display/test_files/mdoc/file.1 b/display/test_files/mdoc/file.1 deleted file mode 100644 index 36930bad..00000000 --- a/display/test_files/mdoc/file.1 +++ /dev/null @@ -1,130 +0,0 @@ -.\" $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/display/test_files/mdoc/flex.1 b/display/test_files/mdoc/flex.1 deleted file mode 100644 index 05236c88..00000000 --- a/display/test_files/mdoc/flex.1 +++ /dev/null @@ -1,4440 +0,0 @@ -.\" $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/display/test_files/mdoc/flock.2 b/display/test_files/mdoc/flock.2 deleted file mode 100644 index e063baac..00000000 --- a/display/test_files/mdoc/flock.2 +++ /dev/null @@ -1,151 +0,0 @@ -.\" $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/display/test_files/mdoc/fork.2 b/display/test_files/mdoc/fork.2 deleted file mode 100644 index a72213b9..00000000 --- a/display/test_files/mdoc/fork.2 +++ /dev/null @@ -1,145 +0,0 @@ -.\" $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/display/test_files/mdoc/fsync.2 b/display/test_files/mdoc/fsync.2 deleted file mode 100644 index 319ff75e..00000000 --- a/display/test_files/mdoc/fsync.2 +++ /dev/null @@ -1,121 +0,0 @@ -.\" $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/display/test_files/mdoc/futex.2 b/display/test_files/mdoc/futex.2 deleted file mode 100644 index 9c6da666..00000000 --- a/display/test_files/mdoc/futex.2 +++ /dev/null @@ -1,153 +0,0 @@ -.\" $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/display/test_files/mdoc/getdents.2 b/display/test_files/mdoc/getdents.2 deleted file mode 100644 index 25848b82..00000000 --- a/display/test_files/mdoc/getdents.2 +++ /dev/null @@ -1,195 +0,0 @@ -.\" $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/display/test_files/mdoc/getfh.2 b/display/test_files/mdoc/getfh.2 deleted file mode 100644 index 305642dd..00000000 --- a/display/test_files/mdoc/getfh.2 +++ /dev/null @@ -1,102 +0,0 @@ -.\" $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/display/test_files/mdoc/getgroups.2 b/display/test_files/mdoc/getgroups.2 deleted file mode 100644 index 1acf6018..00000000 --- a/display/test_files/mdoc/getgroups.2 +++ /dev/null @@ -1,99 +0,0 @@ -.\" $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/display/test_files/mdoc/getitimer.2 b/display/test_files/mdoc/getitimer.2 deleted file mode 100644 index bbf13d92..00000000 --- a/display/test_files/mdoc/getitimer.2 +++ /dev/null @@ -1,176 +0,0 @@ -.\" $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/display/test_files/mdoc/getpeername.2 b/display/test_files/mdoc/getpeername.2 deleted file mode 100644 index 28cffd53..00000000 --- a/display/test_files/mdoc/getpeername.2 +++ /dev/null @@ -1,143 +0,0 @@ -.\" $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/display/test_files/mdoc/getpriority.2 b/display/test_files/mdoc/getpriority.2 deleted file mode 100644 index a31f0f0b..00000000 --- a/display/test_files/mdoc/getpriority.2 +++ /dev/null @@ -1,158 +0,0 @@ -.\" $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/display/test_files/mdoc/getrtable.2 b/display/test_files/mdoc/getrtable.2 deleted file mode 100644 index 2f2bdf64..00000000 --- a/display/test_files/mdoc/getrtable.2 +++ /dev/null @@ -1,66 +0,0 @@ -.\" $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/display/test_files/mdoc/getrusage.2 b/display/test_files/mdoc/getrusage.2 deleted file mode 100644 index 5b541955..00000000 --- a/display/test_files/mdoc/getrusage.2 +++ /dev/null @@ -1,192 +0,0 @@ -.\" $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/display/test_files/mdoc/getsid.2 b/display/test_files/mdoc/getsid.2 deleted file mode 100644 index 96d3dd24..00000000 --- a/display/test_files/mdoc/getsid.2 +++ /dev/null @@ -1,83 +0,0 @@ -.\" $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/display/test_files/mdoc/getsockname.2 b/display/test_files/mdoc/getsockname.2 deleted file mode 100644 index 41d79fdb..00000000 --- a/display/test_files/mdoc/getsockname.2 +++ /dev/null @@ -1,162 +0,0 @@ -.\" $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/display/test_files/mdoc/getsockopt.2 b/display/test_files/mdoc/getsockopt.2 deleted file mode 100644 index b5603a80..00000000 --- a/display/test_files/mdoc/getsockopt.2 +++ /dev/null @@ -1,541 +0,0 @@ -.\" $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/display/test_files/mdoc/gettimeofday.2 b/display/test_files/mdoc/gettimeofday.2 deleted file mode 100644 index 30105491..00000000 --- a/display/test_files/mdoc/gettimeofday.2 +++ /dev/null @@ -1,205 +0,0 @@ -.\" $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/display/test_files/mdoc/grep.1 b/display/test_files/mdoc/grep.1 deleted file mode 100644 index d4c013f4..00000000 --- a/display/test_files/mdoc/grep.1 +++ /dev/null @@ -1,395 +0,0 @@ -.\" $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/display/test_files/mdoc/id.1 b/display/test_files/mdoc/id.1 deleted file mode 100644 index 5931b301..00000000 --- a/display/test_files/mdoc/id.1 +++ /dev/null @@ -1,164 +0,0 @@ -.\" $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/display/test_files/mdoc/ioctl.2 b/display/test_files/mdoc/ioctl.2 deleted file mode 100644 index fde3420f..00000000 --- a/display/test_files/mdoc/ioctl.2 +++ /dev/null @@ -1,171 +0,0 @@ -.\" $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/display/test_files/mdoc/ipcs.1 b/display/test_files/mdoc/ipcs.1 deleted file mode 100644 index 5e88e44c..00000000 --- a/display/test_files/mdoc/ipcs.1 +++ /dev/null @@ -1,149 +0,0 @@ -.\" $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/display/test_files/mdoc/ktrace.2 b/display/test_files/mdoc/ktrace.2 deleted file mode 100644 index 790e4682..00000000 --- a/display/test_files/mdoc/ktrace.2 +++ /dev/null @@ -1,232 +0,0 @@ -.\" $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/display/test_files/mdoc/lpq.1 b/display/test_files/mdoc/lpq.1 deleted file mode 100644 index bbceb0dd..00000000 --- a/display/test_files/mdoc/lpq.1 +++ /dev/null @@ -1,141 +0,0 @@ -.\" $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/display/test_files/mdoc/mg.1 b/display/test_files/mdoc/mg.1 deleted file mode 100644 index 1b32e4fb..00000000 --- a/display/test_files/mdoc/mg.1 +++ /dev/null @@ -1,1203 +0,0 @@ -.\" $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/display/test_files/mdoc/minherit.2 b/display/test_files/mdoc/minherit.2 deleted file mode 100644 index 211a3ecd..00000000 --- a/display/test_files/mdoc/minherit.2 +++ /dev/null @@ -1,108 +0,0 @@ -.\" $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/display/test_files/mdoc/mkdir.1 b/display/test_files/mdoc/mkdir.1 deleted file mode 100644 index 76b07e85..00000000 --- a/display/test_files/mdoc/mkdir.1 +++ /dev/null @@ -1,121 +0,0 @@ -.\" $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/display/test_files/mdoc/mkfifo.2 b/display/test_files/mdoc/mkfifo.2 deleted file mode 100644 index bf75f8f7..00000000 --- a/display/test_files/mdoc/mkfifo.2 +++ /dev/null @@ -1,181 +0,0 @@ -.\" $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/display/test_files/mdoc/mlockall.2 b/display/test_files/mdoc/mlockall.2 deleted file mode 100644 index de0bf39f..00000000 --- a/display/test_files/mdoc/mlockall.2 +++ /dev/null @@ -1,124 +0,0 @@ -.\" $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/display/test_files/mdoc/mopa.out.1 b/display/test_files/mdoc/mopa.out.1 deleted file mode 100644 index cb6bf3b1..00000000 --- a/display/test_files/mdoc/mopa.out.1 +++ /dev/null @@ -1,49 +0,0 @@ -.\" $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/display/test_files/mdoc/moptrace.1 b/display/test_files/mdoc/moptrace.1 deleted file mode 100644 index 45b2d035..00000000 --- a/display/test_files/mdoc/moptrace.1 +++ /dev/null @@ -1,74 +0,0 @@ -.\" $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/display/test_files/mdoc/msgrcv.2 b/display/test_files/mdoc/msgrcv.2 deleted file mode 100644 index 078cae3c..00000000 --- a/display/test_files/mdoc/msgrcv.2 +++ /dev/null @@ -1,207 +0,0 @@ -.\" $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/display/test_files/mdoc/msgsnd.2 b/display/test_files/mdoc/msgsnd.2 deleted file mode 100644 index 99abd3a8..00000000 --- a/display/test_files/mdoc/msgsnd.2 +++ /dev/null @@ -1,168 +0,0 @@ -.\" $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/display/test_files/mdoc/munmap.2 b/display/test_files/mdoc/munmap.2 deleted file mode 100644 index 7376a50c..00000000 --- a/display/test_files/mdoc/munmap.2 +++ /dev/null @@ -1,104 +0,0 @@ -.\" $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/display/test_files/mdoc/mv.1 b/display/test_files/mdoc/mv.1 deleted file mode 100644 index bb6925e6..00000000 --- a/display/test_files/mdoc/mv.1 +++ /dev/null @@ -1,203 +0,0 @@ -.\" $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/display/test_files/mdoc/nl.1 b/display/test_files/mdoc/nl.1 deleted file mode 100644 index 3375ac54..00000000 --- a/display/test_files/mdoc/nl.1 +++ /dev/null @@ -1,231 +0,0 @@ -.\" $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/display/test_files/mdoc/nm.1 b/display/test_files/mdoc/nm.1 deleted file mode 100644 index 273402c4..00000000 --- a/display/test_files/mdoc/nm.1 +++ /dev/null @@ -1,166 +0,0 @@ -.\" $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/display/test_files/mdoc/open.2 b/display/test_files/mdoc/open.2 deleted file mode 100644 index 96d9a087..00000000 --- a/display/test_files/mdoc/open.2 +++ /dev/null @@ -1,465 +0,0 @@ -.\" $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/display/test_files/mdoc/poll.2 b/display/test_files/mdoc/poll.2 deleted file mode 100644 index c7d3d609..00000000 --- a/display/test_files/mdoc/poll.2 +++ /dev/null @@ -1,364 +0,0 @@ -.\" $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/display/test_files/mdoc/profil.2 b/display/test_files/mdoc/profil.2 deleted file mode 100644 index 960d2109..00000000 --- a/display/test_files/mdoc/profil.2 +++ /dev/null @@ -1,127 +0,0 @@ -.\" $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/display/test_files/mdoc/quotactl.2 b/display/test_files/mdoc/quotactl.2 deleted file mode 100644 index 72fbed3a..00000000 --- a/display/test_files/mdoc/quotactl.2 +++ /dev/null @@ -1,212 +0,0 @@ -.\" $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/display/test_files/mdoc/rcs.1 b/display/test_files/mdoc/rcs.1 deleted file mode 100644 index 220f1ab9..00000000 --- a/display/test_files/mdoc/rcs.1 +++ /dev/null @@ -1,485 +0,0 @@ -.\" $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/display/test_files/mdoc/rdist.1 b/display/test_files/mdoc/rdist.1 deleted file mode 100644 index e6616fbf..00000000 --- a/display/test_files/mdoc/rdist.1 +++ /dev/null @@ -1,866 +0,0 @@ -.\" $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/display/test_files/mdoc/read.2 b/display/test_files/mdoc/read.2 deleted file mode 100644 index 1cdacd72..00000000 --- a/display/test_files/mdoc/read.2 +++ /dev/null @@ -1,282 +0,0 @@ -.\" $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/display/test_files/mdoc/reboot.2 b/display/test_files/mdoc/reboot.2 deleted file mode 100644 index 6f0fa887..00000000 --- a/display/test_files/mdoc/reboot.2 +++ /dev/null @@ -1,163 +0,0 @@ -.\" $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/display/test_files/mdoc/rename.2 b/display/test_files/mdoc/rename.2 deleted file mode 100644 index 5380cc77..00000000 --- a/display/test_files/mdoc/rename.2 +++ /dev/null @@ -1,296 +0,0 @@ -.\" $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/display/test_files/mdoc/rev.1 b/display/test_files/mdoc/rev.1 deleted file mode 100644 index 9dcf7ac7..00000000 --- a/display/test_files/mdoc/rev.1 +++ /dev/null @@ -1,59 +0,0 @@ -.\" $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/display/test_files/mdoc/rlog.1 b/display/test_files/mdoc/rlog.1 deleted file mode 100644 index 807510ab..00000000 --- a/display/test_files/mdoc/rlog.1 +++ /dev/null @@ -1,208 +0,0 @@ -.\" $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/display/test_files/mdoc/rup.1 b/display/test_files/mdoc/rup.1 deleted file mode 100644 index ba6e4901..00000000 --- a/display/test_files/mdoc/rup.1 +++ /dev/null @@ -1,101 +0,0 @@ -.\" $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/display/test_files/mdoc/sched_yield.2 b/display/test_files/mdoc/sched_yield.2 deleted file mode 100644 index 7b50e74e..00000000 --- a/display/test_files/mdoc/sched_yield.2 +++ /dev/null @@ -1,49 +0,0 @@ -.\" $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/display/test_files/mdoc/scp.1 b/display/test_files/mdoc/scp.1 deleted file mode 100644 index 34130ac4..00000000 --- a/display/test_files/mdoc/scp.1 +++ /dev/null @@ -1,364 +0,0 @@ -.\" -.\" 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/display/test_files/mdoc/select.2 b/display/test_files/mdoc/select.2 deleted file mode 100644 index c3f7da2c..00000000 --- a/display/test_files/mdoc/select.2 +++ /dev/null @@ -1,248 +0,0 @@ -.\" $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/display/test_files/mdoc/semget.2 b/display/test_files/mdoc/semget.2 deleted file mode 100644 index 2a38933b..00000000 --- a/display/test_files/mdoc/semget.2 +++ /dev/null @@ -1,154 +0,0 @@ -.\" $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/display/test_files/mdoc/send.2 b/display/test_files/mdoc/send.2 deleted file mode 100644 index 97d62652..00000000 --- a/display/test_files/mdoc/send.2 +++ /dev/null @@ -1,289 +0,0 @@ -.\" $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/display/test_files/mdoc/setuid.2 b/display/test_files/mdoc/setuid.2 deleted file mode 100644 index e96fcff9..00000000 --- a/display/test_files/mdoc/setuid.2 +++ /dev/null @@ -1,152 +0,0 @@ -.\" $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/display/test_files/mdoc/sftp.1 b/display/test_files/mdoc/sftp.1 deleted file mode 100644 index ae9ff21a..00000000 --- a/display/test_files/mdoc/sftp.1 +++ /dev/null @@ -1,767 +0,0 @@ -.\" $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/display/test_files/mdoc/shar.1 b/display/test_files/mdoc/shar.1 deleted file mode 100644 index 01fafa55..00000000 --- a/display/test_files/mdoc/shar.1 +++ /dev/null @@ -1,102 +0,0 @@ -.\" $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/display/test_files/mdoc/shmctl.2 b/display/test_files/mdoc/shmctl.2 deleted file mode 100644 index 1dbe90ba..00000000 --- a/display/test_files/mdoc/shmctl.2 +++ /dev/null @@ -1,198 +0,0 @@ -.\" $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/display/test_files/mdoc/shmget.2 b/display/test_files/mdoc/shmget.2 deleted file mode 100644 index 3bf3b4c1..00000000 --- a/display/test_files/mdoc/shmget.2 +++ /dev/null @@ -1,142 +0,0 @@ -.\" $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/display/test_files/mdoc/shutdown.2 b/display/test_files/mdoc/shutdown.2 deleted file mode 100644 index 76305429..00000000 --- a/display/test_files/mdoc/shutdown.2 +++ /dev/null @@ -1,154 +0,0 @@ -.\" $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/display/test_files/mdoc/signify.1 b/display/test_files/mdoc/signify.1 deleted file mode 100644 index b2cacfa1..00000000 --- a/display/test_files/mdoc/signify.1 +++ /dev/null @@ -1,206 +0,0 @@ -.\" $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/display/test_files/mdoc/sigreturn.2 b/display/test_files/mdoc/sigreturn.2 deleted file mode 100644 index fc52f7f0..00000000 --- a/display/test_files/mdoc/sigreturn.2 +++ /dev/null @@ -1,86 +0,0 @@ -.\" $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/display/test_files/mdoc/sigsuspend.2 b/display/test_files/mdoc/sigsuspend.2 deleted file mode 100644 index e26f3d94..00000000 --- a/display/test_files/mdoc/sigsuspend.2 +++ /dev/null @@ -1,78 +0,0 @@ -.\" $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/display/test_files/mdoc/size.1 b/display/test_files/mdoc/size.1 deleted file mode 100644 index 1e0e0ba7..00000000 --- a/display/test_files/mdoc/size.1 +++ /dev/null @@ -1,78 +0,0 @@ -.\" $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/display/test_files/mdoc/snmp.1 b/display/test_files/mdoc/snmp.1 deleted file mode 100644 index 55452d39..00000000 --- a/display/test_files/mdoc/snmp.1 +++ /dev/null @@ -1,568 +0,0 @@ -.\" $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/display/test_files/mdoc/socket.2 b/display/test_files/mdoc/socket.2 deleted file mode 100644 index 6d0ccf73..00000000 --- a/display/test_files/mdoc/socket.2 +++ /dev/null @@ -1,311 +0,0 @@ -.\" $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/display/test_files/mdoc/socketpair.2 b/display/test_files/mdoc/socketpair.2 deleted file mode 100644 index 675e6b97..00000000 --- a/display/test_files/mdoc/socketpair.2 +++ /dev/null @@ -1,132 +0,0 @@ -.\" $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/display/test_files/mdoc/statfs.2 b/display/test_files/mdoc/statfs.2 deleted file mode 100644 index 48a44fb5..00000000 --- a/display/test_files/mdoc/statfs.2 +++ /dev/null @@ -1,158 +0,0 @@ -.\" $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/display/test_files/mdoc/symlink.2 b/display/test_files/mdoc/symlink.2 deleted file mode 100644 index 48cd5957..00000000 --- a/display/test_files/mdoc/symlink.2 +++ /dev/null @@ -1,204 +0,0 @@ -.\" $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/display/test_files/mdoc/sync.2 b/display/test_files/mdoc/sync.2 deleted file mode 100644 index 9f67d53b..00000000 --- a/display/test_files/mdoc/sync.2 +++ /dev/null @@ -1,73 +0,0 @@ -.\" $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/display/test_files/mdoc/sysarch.2 b/display/test_files/mdoc/sysarch.2 deleted file mode 100644 index 3d6fbc2b..00000000 --- a/display/test_files/mdoc/sysarch.2 +++ /dev/null @@ -1,68 +0,0 @@ -.\" $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/display/test_files/mdoc/t11.2 b/display/test_files/mdoc/t11.2 deleted file mode 100644 index f744068b..00000000 --- a/display/test_files/mdoc/t11.2 +++ /dev/null @@ -1,908 +0,0 @@ -.\" $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/display/test_files/mdoc/talk.1 b/display/test_files/mdoc/talk.1 deleted file mode 100644 index 804445ae..00000000 --- a/display/test_files/mdoc/talk.1 +++ /dev/null @@ -1,160 +0,0 @@ -.\" $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/display/test_files/mdoc/test.1 b/display/test_files/mdoc/test.1 deleted file mode 100644 index cb7b35bb..00000000 --- a/display/test_files/mdoc/test.1 +++ /dev/null @@ -1,7799 +0,0 @@ -.\" $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/display/test_files/mdoc/tmux.1 b/display/test_files/mdoc/tmux.1 deleted file mode 100644 index 33950ba1..00000000 --- a/display/test_files/mdoc/tmux.1 +++ /dev/null @@ -1,7799 +0,0 @@ -.\" $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/display/test_files/mdoc/top.1 b/display/test_files/mdoc/top.1 deleted file mode 100644 index 937d8064..00000000 --- a/display/test_files/mdoc/top.1 +++ /dev/null @@ -1,520 +0,0 @@ -.\" $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/display/test_files/mdoc/truncate.2 b/display/test_files/mdoc/truncate.2 deleted file mode 100644 index 497aa231..00000000 --- a/display/test_files/mdoc/truncate.2 +++ /dev/null @@ -1,152 +0,0 @@ -.\" $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/display/test_files/mdoc/umask.2 b/display/test_files/mdoc/umask.2 deleted file mode 100644 index b490c51f..00000000 --- a/display/test_files/mdoc/umask.2 +++ /dev/null @@ -1,94 +0,0 @@ -.\" $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/display/test_files/mdoc/w.1 b/display/test_files/mdoc/w.1 deleted file mode 100644 index faf59f70..00000000 --- a/display/test_files/mdoc/w.1 +++ /dev/null @@ -1,134 +0,0 @@ -.\" $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/display/test_files/mdoc/wall.1 b/display/test_files/mdoc/wall.1 deleted file mode 100644 index 045a9978..00000000 --- a/display/test_files/mdoc/wall.1 +++ /dev/null @@ -1,83 +0,0 @@ -.\" $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/display/test_files/mdoc/write.2 b/display/test_files/mdoc/write.2 deleted file mode 100644 index a5769325..00000000 --- a/display/test_files/mdoc/write.2 +++ /dev/null @@ -1,329 +0,0 @@ -.\" $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/display/test_files/mdoc/ypconnect.2 b/display/test_files/mdoc/ypconnect.2 deleted file mode 100644 index 95c0a2f4..00000000 --- a/display/test_files/mdoc/ypconnect.2 +++ /dev/null @@ -1,80 +0,0 @@ -.\" $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/display/tests/man/mod.rs b/display/tests/man/mod.rs deleted file mode 100644 index 0c6a7d59..00000000 --- a/display/tests/man/mod.rs +++ /dev/null @@ -1,457 +0,0 @@ -// tests/man.rs - -#[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() - ); - } -}