mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
l10n: port tail for translation + add french
This commit is contained in:
parent
4128efad0f
commit
ea9f8f88d0
7 changed files with 490 additions and 189 deletions
|
@ -1,6 +1,72 @@
|
|||
tail-about = Print the last 10 lines of each FILE to standard output.
|
||||
With more than one FILE, precede each with a header giving the file name.
|
||||
With no FILE, or when FILE is -, read standard input.
|
||||
|
||||
Mandatory arguments to long flags are mandatory for short flags too.
|
||||
tail-usage = tail [FLAG]... [FILE]...
|
||||
|
||||
# Help messages
|
||||
tail-help-bytes = Number of bytes to print
|
||||
tail-help-follow = Print the file as it grows
|
||||
tail-help-lines = Number of lines to print
|
||||
tail-help-pid = With -f, terminate after process ID, PID dies
|
||||
tail-help-quiet = Never output headers giving file names
|
||||
tail-help-sleep-interval = Number of seconds to sleep between polling the file when running with -f
|
||||
tail-help-max-unchanged-stats = Reopen a FILE which has not changed size after N (default 5) iterations to see if it has been unlinked or renamed (this is the usual case of rotated log files); This option is meaningful only when polling (i.e., with --use-polling) and when --follow=name
|
||||
tail-help-verbose = Always output headers giving file names
|
||||
tail-help-zero-terminated = Line delimiter is NUL, not newline
|
||||
tail-help-retry = Keep trying to open a file if it is inaccessible
|
||||
tail-help-follow-retry = Same as --follow=name --retry
|
||||
tail-help-polling-linux = Disable 'inotify' support and use polling instead
|
||||
tail-help-polling-unix = Disable 'kqueue' support and use polling instead
|
||||
tail-help-polling-windows = Disable 'ReadDirectoryChanges' support and use polling instead
|
||||
|
||||
# Error messages
|
||||
tail-error-cannot-follow-stdin-by-name = cannot follow { $stdin } by name
|
||||
tail-error-cannot-open-no-such-file = cannot open '{ $file }' for reading: { $error }
|
||||
tail-error-reading-file = error reading '{ $file }': { $error }
|
||||
tail-error-cannot-follow-file-type = { $file }: cannot follow end of this type of file{ $msg }
|
||||
tail-error-cannot-open-for-reading = cannot open '{ $file }' for reading
|
||||
tail-error-cannot-fstat = cannot fstat { $file }: { $error }
|
||||
tail-error-invalid-number-of-bytes = invalid number of bytes: { $arg }
|
||||
tail-error-invalid-number-of-lines = invalid number of lines: { $arg }
|
||||
tail-error-invalid-number-of-seconds = invalid number of seconds: '{ $source }'
|
||||
tail-error-invalid-max-unchanged-stats = invalid maximum number of unchanged stats between opens: { $value }
|
||||
tail-error-invalid-pid = invalid PID: { $pid }
|
||||
tail-error-invalid-pid-with-error = invalid PID: { $pid }: { $error }
|
||||
tail-error-invalid-number-out-of-range = invalid number: { $arg }: Numerical result out of range
|
||||
tail-error-invalid-number-overflow = invalid number: { $arg }
|
||||
tail-error-option-used-in-invalid-context = option used in invalid context -- { $option }
|
||||
tail-error-bad-argument-encoding = bad argument encoding: '{ $arg }'
|
||||
tail-error-cannot-watch-parent-directory = cannot watch parent directory of { $path }
|
||||
tail-error-backend-cannot-be-used-too-many-files = { $backend } cannot be used, reverting to polling: Too many open files
|
||||
tail-error-backend-resources-exhausted = { $backend } resources exhausted
|
||||
tail-error-notify-error = NotifyError: { $error }
|
||||
tail-error-recv-timeout-error = RecvTimeoutError: { $error }
|
||||
|
||||
# Warning messages
|
||||
tail-warning-retry-ignored = --retry ignored; --retry is useful only when following
|
||||
tail-warning-retry-only-effective = --retry only effective for the initial open
|
||||
tail-warning-pid-ignored = PID ignored; --pid=PID is useful only when following
|
||||
tail-warning-pid-not-supported = --pid=PID is not supported on this system
|
||||
tail-warning-following-stdin-ineffective = following standard input indefinitely is ineffective
|
||||
|
||||
# Status messages
|
||||
tail-status-has-become-accessible = { $file } has become accessible
|
||||
tail-status-has-appeared-following-new-file = { $file } has appeared; following new file
|
||||
tail-status-has-been-replaced-following-new-file = { $file } has been replaced; following new file
|
||||
tail-status-file-truncated = { $file }: file truncated
|
||||
tail-status-replaced-with-untailable-file = { $file } has been replaced with an untailable file
|
||||
tail-status-replaced-with-untailable-file-giving-up = { $file } has been replaced with an untailable file; giving up on this name
|
||||
tail-status-file-became-inaccessible = { $file } { $become_inaccessible }: { $no_such_file }
|
||||
tail-status-directory-containing-watched-file-removed = directory containing watched file was removed
|
||||
tail-status-backend-cannot-be-used-reverting-to-polling = { $backend } cannot be used, reverting to polling
|
||||
tail-status-file-no-such-file = { $file }: { $no_such_file }
|
||||
|
||||
# Text constants
|
||||
tail-bad-fd = Bad file descriptor
|
||||
tail-no-such-file-or-directory = No such file or directory
|
||||
tail-is-a-directory = Is a directory
|
||||
tail-giving-up-on-this-name = ; giving up on this name
|
||||
tail-stdin-header = standard input
|
||||
tail-no-files-remaining = no files remaining
|
||||
tail-become-inaccessible = has become inaccessible
|
||||
|
|
72
src/uu/tail/locales/fr-FR.ftl
Normal file
72
src/uu/tail/locales/fr-FR.ftl
Normal file
|
@ -0,0 +1,72 @@
|
|||
tail-about = Afficher les 10 dernières lignes de chaque FICHIER sur la sortie standard.
|
||||
Avec plus d'un FICHIER, précéder chacun d'un en-tête donnant le nom du fichier.
|
||||
Sans FICHIER, ou quand FICHIER est -, lire l'entrée standard.
|
||||
Les arguments obligatoires pour les drapeaux longs sont également obligatoires pour les drapeaux courts.
|
||||
tail-usage = tail [DRAPEAU]... [FICHIER]...
|
||||
|
||||
# Messages d'aide
|
||||
tail-help-bytes = Nombre d'octets à afficher
|
||||
tail-help-follow = Afficher le fichier au fur et à mesure de sa croissance
|
||||
tail-help-lines = Nombre de lignes à afficher
|
||||
tail-help-pid = Avec -f, terminer après que l'ID de processus, PID meure
|
||||
tail-help-quiet = Ne jamais afficher d'en-têtes donnant les noms de fichiers
|
||||
tail-help-sleep-interval = Nombre de secondes à attendre entre les sondages du fichier lors de l'exécution avec -f
|
||||
tail-help-max-unchanged-stats = Rouvrir un FICHIER qui n'a pas changé de taille après N (par défaut 5) itérations pour voir s'il a été supprimé ou renommé (c'est le cas habituel des fichiers journaux pivotés) ; Cette option n'a de sens que lors du sondage (c'est-à-dire avec --use-polling) et quand --follow=name
|
||||
tail-help-verbose = Toujours afficher des en-têtes donnant les noms de fichiers
|
||||
tail-help-zero-terminated = Le délimiteur de ligne est NUL, pas newline
|
||||
tail-help-retry = Continuer d'essayer d'ouvrir un fichier s'il est inaccessible
|
||||
tail-help-follow-retry = Identique à --follow=name --retry
|
||||
tail-help-polling-linux = Désactiver le support 'inotify' et utiliser le sondage à la place
|
||||
tail-help-polling-unix = Désactiver le support 'kqueue' et utiliser le sondage à la place
|
||||
tail-help-polling-windows = Désactiver le support 'ReadDirectoryChanges' et utiliser le sondage à la place
|
||||
|
||||
# Messages d'erreur
|
||||
tail-error-cannot-follow-stdin-by-name = impossible de suivre { $stdin } par nom
|
||||
tail-error-cannot-open-no-such-file = impossible d'ouvrir '{ $file }' en lecture : { $error }
|
||||
tail-error-reading-file = erreur de lecture de '{ $file }' : { $error }
|
||||
tail-error-cannot-follow-file-type = { $file } : impossible de suivre la fin de ce type de fichier{ $msg }
|
||||
tail-error-cannot-open-for-reading = impossible d'ouvrir '{ $file }' en lecture
|
||||
tail-error-cannot-fstat = impossible de faire fstat { $file } : { $error }
|
||||
tail-error-invalid-number-of-bytes = nombre d'octets invalide : { $arg }
|
||||
tail-error-invalid-number-of-lines = nombre de lignes invalide : { $arg }
|
||||
tail-error-invalid-number-of-seconds = nombre de secondes invalide : '{ $source }'
|
||||
tail-error-invalid-max-unchanged-stats = nombre maximum invalide de statistiques inchangées entre les ouvertures : { $value }
|
||||
tail-error-invalid-pid = PID invalide : { $pid }
|
||||
tail-error-invalid-pid-with-error = PID invalide : { $pid } : { $error }
|
||||
tail-error-invalid-number-out-of-range = nombre invalide : { $arg } : Résultat numérique hors limites
|
||||
tail-error-invalid-number-overflow = nombre invalide : { $arg }
|
||||
tail-error-option-used-in-invalid-context = option utilisée dans un contexte invalide -- { $option }
|
||||
tail-error-bad-argument-encoding = encodage d'argument incorrect : '{ $arg }'
|
||||
tail-error-cannot-watch-parent-directory = impossible de surveiller le répertoire parent de { $path }
|
||||
tail-error-backend-cannot-be-used-too-many-files = { $backend } ne peut pas être utilisé, retour au sondage : Trop de fichiers ouverts
|
||||
tail-error-backend-resources-exhausted = ressources { $backend } épuisées
|
||||
tail-error-notify-error = Erreur de notification : { $error }
|
||||
tail-error-recv-timeout-error = Erreur de délai de réception : { $error }
|
||||
|
||||
# Messages d'avertissement
|
||||
tail-warning-retry-ignored = --retry ignoré ; --retry n'est utile que lors du suivi
|
||||
tail-warning-retry-only-effective = --retry n'est effectif que pour l'ouverture initiale
|
||||
tail-warning-pid-ignored = PID ignoré ; --pid=PID n'est utile que lors du suivi
|
||||
tail-warning-pid-not-supported = --pid=PID n'est pas pris en charge sur ce système
|
||||
tail-warning-following-stdin-ineffective = suivre l'entrée standard indéfiniment est inefficace
|
||||
|
||||
# Messages de statut
|
||||
tail-status-has-become-accessible = { $file } est devenu accessible
|
||||
tail-status-has-appeared-following-new-file = { $file } est apparu ; suivi du nouveau fichier
|
||||
tail-status-has-been-replaced-following-new-file = { $file } a été remplacé ; suivi du nouveau fichier
|
||||
tail-status-file-truncated = { $file } : fichier tronqué
|
||||
tail-status-replaced-with-untailable-file = { $file } a été remplacé par un fichier non suivable
|
||||
tail-status-replaced-with-untailable-file-giving-up = { $file } a été remplacé par un fichier non suivable ; abandon de ce nom
|
||||
tail-status-file-became-inaccessible = { $file } { $become_inaccessible } : { $no_such_file }
|
||||
tail-status-directory-containing-watched-file-removed = le répertoire contenant le fichier surveillé a été supprimé
|
||||
tail-status-backend-cannot-be-used-reverting-to-polling = { $backend } ne peut pas être utilisé, retour au sondage
|
||||
tail-status-file-no-such-file = { $file } : { $no_such_file }
|
||||
|
||||
# Constantes de texte
|
||||
tail-bad-fd = Descripteur de fichier incorrect
|
||||
tail-no-such-file-or-directory = Aucun fichier ou répertoire de ce type
|
||||
tail-is-a-directory = Est un répertoire
|
||||
tail-giving-up-on-this-name = ; abandon de ce nom
|
||||
tail-stdin-header = entrée standard
|
||||
tail-no-files-remaining = aucun fichier restant
|
||||
tail-become-inaccessible = est devenu inaccessible
|
|
@ -9,6 +9,7 @@ use crate::paths::Input;
|
|||
use crate::{Quotable, parse, platform};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
|
||||
use same_file::Handle;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::io::IsTerminal;
|
||||
use std::time::Duration;
|
||||
|
@ -18,7 +19,7 @@ use uucore::parser::parse_time;
|
|||
use uucore::parser::shortcut_value_parser::ShortcutValueParser;
|
||||
use uucore::{format_usage, show_warning};
|
||||
|
||||
use uucore::locale::get_message;
|
||||
use uucore::locale::{get_message, get_message_with_args};
|
||||
|
||||
pub mod options {
|
||||
pub mod verbosity {
|
||||
|
@ -78,7 +79,10 @@ impl FilterMode {
|
|||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid number of bytes: '{e}'"),
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-number-of-bytes",
|
||||
HashMap::from([("arg".to_string(), format!("'{}'", e))]),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +92,13 @@ impl FilterMode {
|
|||
let delimiter = if zero_term { 0 } else { b'\n' };
|
||||
Self::Lines(signum, delimiter)
|
||||
}
|
||||
Err(e) => {
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid number of lines: {e}"),
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-number-of-lines",
|
||||
HashMap::from([("arg".to_string(), arg.quote().to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +235,13 @@ impl Settings {
|
|||
|
||||
if let Some(source) = matches.get_one::<String>(options::SLEEP_INT) {
|
||||
settings.sleep_sec = parse_time::from_str(source, false).map_err(|_| {
|
||||
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
|
||||
UUsageError::new(
|
||||
1,
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-number-of-seconds",
|
||||
HashMap::from([("source".to_string(), source.clone())]),
|
||||
),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
|
@ -238,9 +251,9 @@ impl Settings {
|
|||
Err(_) => {
|
||||
return Err(UUsageError::new(
|
||||
1,
|
||||
format!(
|
||||
"invalid maximum number of unchanged stats between opens: {}",
|
||||
s.quote()
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-max-unchanged-stats",
|
||||
HashMap::from([("value".to_string(), s.quote().to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -256,7 +269,10 @@ impl Settings {
|
|||
// NOTE: tail only accepts an unsigned pid
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid PID: {}", pid_str.quote()),
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-pid",
|
||||
HashMap::from([("pid".to_string(), pid_str.quote().to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -265,7 +281,13 @@ impl Settings {
|
|||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid PID: {}: {e}", pid_str.quote()),
|
||||
get_message_with_args(
|
||||
"tail-error-invalid-pid-with-error",
|
||||
HashMap::from([
|
||||
("pid".to_string(), pid_str.quote().to_string()),
|
||||
("error".to_string(), e.to_string()),
|
||||
]),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -300,17 +322,17 @@ impl Settings {
|
|||
pub fn check_warnings(&self) {
|
||||
if self.retry {
|
||||
if self.follow.is_none() {
|
||||
show_warning!("--retry ignored; --retry is useful only when following");
|
||||
show_warning!("{}", get_message("tail-warning-retry-ignored"));
|
||||
} else if self.follow == Some(FollowMode::Descriptor) {
|
||||
show_warning!("--retry only effective for the initial open");
|
||||
show_warning!("{}", get_message("tail-warning-retry-only-effective"));
|
||||
}
|
||||
}
|
||||
|
||||
if self.pid != 0 {
|
||||
if self.follow.is_none() {
|
||||
show_warning!("PID ignored; --pid=PID is useful only when following");
|
||||
show_warning!("{}", get_message("tail-warning-pid-ignored"));
|
||||
} else if !platform::supports_pid_checks(self.pid) {
|
||||
show_warning!("--pid=PID is not supported on this system");
|
||||
show_warning!("{}", get_message("tail-warning-pid-not-supported"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,7 +352,10 @@ impl Settings {
|
|||
});
|
||||
|
||||
if !blocking_stdin && std::io::stdin().is_terminal() {
|
||||
show_warning!("following standard input indefinitely is ineffective");
|
||||
show_warning!(
|
||||
"{}",
|
||||
get_message("tail-warning-following-stdin-ineffective")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,19 +393,26 @@ pub fn parse_obsolete(arg: &OsString, input: Option<&OsString>) -> UResult<Optio
|
|||
Err(USimpleError::new(
|
||||
1,
|
||||
match e {
|
||||
parse::ParseError::OutOfRange => format!(
|
||||
"invalid number: {}: Numerical result out of range",
|
||||
arg_str.quote()
|
||||
parse::ParseError::OutOfRange => get_message_with_args(
|
||||
"tail-error-invalid-number-out-of-range",
|
||||
HashMap::from([("arg".to_string(), arg_str.quote().to_string())]),
|
||||
),
|
||||
parse::ParseError::Overflow => get_message_with_args(
|
||||
"tail-error-invalid-number-overflow",
|
||||
HashMap::from([("arg".to_string(), arg_str.quote().to_string())]),
|
||||
),
|
||||
parse::ParseError::Overflow => format!("invalid number: {}", arg_str.quote()),
|
||||
// this ensures compatibility to GNU's error message (as tested in misc/tail)
|
||||
parse::ParseError::Context => format!(
|
||||
"option used in invalid context -- {}",
|
||||
arg_str.chars().nth(1).unwrap_or_default()
|
||||
parse::ParseError::Context => get_message_with_args(
|
||||
"tail-error-option-used-in-invalid-context",
|
||||
HashMap::from([(
|
||||
"option".to_string(),
|
||||
arg_str.chars().nth(1).unwrap_or_default().to_string(),
|
||||
)]),
|
||||
),
|
||||
parse::ParseError::InvalidEncoding => get_message_with_args(
|
||||
"tail-error-bad-argument-encoding",
|
||||
HashMap::from([("arg".to_string(), arg_str.to_string())]),
|
||||
),
|
||||
parse::ParseError::InvalidEncoding => {
|
||||
format!("bad argument encoding: '{arg_str}'")
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -455,11 +487,11 @@ pub fn parse_args(args: impl uucore::Args) -> UResult<Settings> {
|
|||
|
||||
pub fn uu_app() -> Command {
|
||||
#[cfg(target_os = "linux")]
|
||||
const POLLING_HELP: &str = "Disable 'inotify' support and use polling instead";
|
||||
let polling_help = get_message("tail-help-polling-linux");
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
const POLLING_HELP: &str = "Disable 'kqueue' support and use polling instead";
|
||||
let polling_help = get_message("tail-help-polling-unix");
|
||||
#[cfg(target_os = "windows")]
|
||||
const POLLING_HELP: &str = "Disable 'ReadDirectoryChanges' support and use polling instead";
|
||||
let polling_help = get_message("tail-help-polling-windows");
|
||||
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
|
@ -472,7 +504,7 @@ pub fn uu_app() -> Command {
|
|||
.long(options::BYTES)
|
||||
.allow_hyphen_values(true)
|
||||
.overrides_with_all([options::BYTES, options::LINES])
|
||||
.help("Number of bytes to print"),
|
||||
.help(get_message("tail-help-bytes")),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FOLLOW)
|
||||
|
@ -483,7 +515,7 @@ pub fn uu_app() -> Command {
|
|||
.require_equals(true)
|
||||
.value_parser(ShortcutValueParser::new(["descriptor", "name"]))
|
||||
.overrides_with(options::FOLLOW)
|
||||
.help("Print the file as it grows"),
|
||||
.help(get_message("tail-help-follow")),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::LINES)
|
||||
|
@ -491,13 +523,13 @@ pub fn uu_app() -> Command {
|
|||
.long(options::LINES)
|
||||
.allow_hyphen_values(true)
|
||||
.overrides_with_all([options::BYTES, options::LINES])
|
||||
.help("Number of lines to print"),
|
||||
.help(get_message("tail-help-lines")),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PID)
|
||||
.long(options::PID)
|
||||
.value_name("PID")
|
||||
.help("With -f, terminate after process ID, PID dies")
|
||||
.help(get_message("tail-help-pid"))
|
||||
.overrides_with(options::PID),
|
||||
)
|
||||
.arg(
|
||||
|
@ -506,7 +538,7 @@ pub fn uu_app() -> Command {
|
|||
.long(options::verbosity::QUIET)
|
||||
.visible_alias("silent")
|
||||
.overrides_with_all([options::verbosity::QUIET, options::verbosity::VERBOSE])
|
||||
.help("Never output headers giving file names")
|
||||
.help(get_message("tail-help-quiet"))
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
|
@ -514,32 +546,27 @@ pub fn uu_app() -> Command {
|
|||
.short('s')
|
||||
.value_name("N")
|
||||
.long(options::SLEEP_INT)
|
||||
.help("Number of seconds to sleep between polling the file when running with -f"),
|
||||
.help(get_message("tail-help-sleep-interval")),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::MAX_UNCHANGED_STATS)
|
||||
.value_name("N")
|
||||
.long(options::MAX_UNCHANGED_STATS)
|
||||
.help(
|
||||
"Reopen a FILE which has not changed size after N (default 5) iterations \
|
||||
to see if it has been unlinked or renamed (this is the usual case of rotated \
|
||||
log files); This option is meaningful only when polling \
|
||||
(i.e., with --use-polling) and when --follow=name",
|
||||
),
|
||||
.help(get_message("tail-help-max-unchanged-stats")),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::verbosity::VERBOSE)
|
||||
.short('v')
|
||||
.long(options::verbosity::VERBOSE)
|
||||
.overrides_with_all([options::verbosity::QUIET, options::verbosity::VERBOSE])
|
||||
.help("Always output headers giving file names")
|
||||
.help(get_message("tail-help-verbose"))
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::ZERO_TERM)
|
||||
.short('z')
|
||||
.long(options::ZERO_TERM)
|
||||
.help("Line delimiter is NUL, not newline")
|
||||
.help(get_message("tail-help-zero-terminated"))
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
|
@ -547,20 +574,20 @@ pub fn uu_app() -> Command {
|
|||
.alias(options::DISABLE_INOTIFY_TERM) // NOTE: Used by GNU's test suite
|
||||
.alias("dis") // NOTE: Used by GNU's test suite
|
||||
.long(options::USE_POLLING)
|
||||
.help(POLLING_HELP)
|
||||
.help(polling_help)
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::RETRY)
|
||||
.long(options::RETRY)
|
||||
.help("Keep trying to open a file if it is inaccessible")
|
||||
.help(get_message("tail-help-retry"))
|
||||
.overrides_with(options::RETRY)
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FOLLOW_RETRY)
|
||||
.short('F')
|
||||
.help("Same as --follow=name --retry")
|
||||
.help(get_message("tail-help-follow-retry"))
|
||||
.overrides_with(options::FOLLOW_RETRY)
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
|
|
|
@ -10,11 +10,13 @@ use crate::follow::files::{FileHandling, PathData};
|
|||
use crate::paths::{Input, InputKind, MetadataExtTail, PathExtTail};
|
||||
use crate::{platform, text};
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher, WatcherKind};
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufRead;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{self, Receiver, channel};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError, set_exit_code};
|
||||
use uucore::locale::{get_message, get_message_with_args};
|
||||
use uucore::show_error;
|
||||
|
||||
pub struct WatcherRx {
|
||||
|
@ -56,7 +58,10 @@ impl WatcherRx {
|
|||
} else {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("cannot watch parent directory of {}", path.display()),
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-watch-parent-directory",
|
||||
HashMap::from([("path".to_string(), path.display().to_string())]),
|
||||
),
|
||||
));
|
||||
};
|
||||
}
|
||||
|
@ -239,8 +244,11 @@ impl Observer {
|
|||
`sudo sysctl fs.inotify.max_user_instances=64`
|
||||
*/
|
||||
show_error!(
|
||||
"{} cannot be used, reverting to polling: Too many open files",
|
||||
text::BACKEND
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-backend-cannot-be-used-too-many-files",
|
||||
HashMap::from([("backend".to_string(), text::BACKEND.to_string())])
|
||||
)
|
||||
);
|
||||
set_exit_code(1);
|
||||
self.use_polling = true;
|
||||
|
@ -318,32 +326,52 @@ impl Observer {
|
|||
let display_name = self.files.get(event_path).display_name.clone();
|
||||
|
||||
match event.kind {
|
||||
EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any |
|
||||
MetadataKind::WriteTime) | ModifyKind::Data(DataChange::Any) |
|
||||
ModifyKind::Name(RenameMode::To)) |
|
||||
EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
|
||||
EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any | MetadataKind::WriteTime) | ModifyKind::Data(DataChange::Any) | ModifyKind::Name(RenameMode::To)) |
|
||||
EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
|
||||
if let Ok(new_md) = event_path.metadata() {
|
||||
|
||||
let is_tailable = new_md.is_tailable();
|
||||
let pd = self.files.get(event_path);
|
||||
if let Some(old_md) = &pd.metadata {
|
||||
if is_tailable {
|
||||
// We resume tracking from the start of the file,
|
||||
// assuming it has been truncated to 0. This mimics GNU's `tail`
|
||||
// behavior and is the usual truncation operation for log self.files.
|
||||
// behavior and is the usual truncation operation for log files.
|
||||
if !old_md.is_tailable() {
|
||||
show_error!( "{} has become accessible", display_name.quote());
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-has-become-accessible",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
self.files.update_reader(event_path)?;
|
||||
} else if pd.reader.is_none() {
|
||||
show_error!( "{} has appeared; following new file", display_name.quote());
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-has-appeared-following-new-file",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
self.files.update_reader(event_path)?;
|
||||
} else if event.kind == EventKind::Modify(ModifyKind::Name(RenameMode::To))
|
||||
|| (self.use_polling
|
||||
&& !old_md.file_id_eq(&new_md)) {
|
||||
show_error!( "{} has been replaced; following new file", display_name.quote());
|
||||
|| (self.use_polling && !old_md.file_id_eq(&new_md)) {
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-has-been-replaced-following-new-file",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
self.files.update_reader(event_path)?;
|
||||
} else if old_md.got_truncated(&new_md)? {
|
||||
show_error!("{display_name}: file truncated");
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-file-truncated",
|
||||
HashMap::from([("file".to_string(), display_name)])
|
||||
)
|
||||
);
|
||||
self.files.update_reader(event_path)?;
|
||||
}
|
||||
paths.push(event_path.clone());
|
||||
|
@ -352,30 +380,45 @@ EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
|
|||
self.files.reset_reader(event_path);
|
||||
} else {
|
||||
show_error!(
|
||||
"{} has been replaced with an untailable file",
|
||||
display_name.quote()
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-replaced-with-untailable-file",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if is_tailable {
|
||||
show_error!( "{} has appeared; following new file", display_name.quote());
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-has-appeared-following-new-file",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
self.files.update_reader(event_path)?;
|
||||
paths.push(event_path.clone());
|
||||
} else if settings.retry {
|
||||
if self.follow_descriptor() {
|
||||
show_error!(
|
||||
"{} has been replaced with an untailable file; giving up on this name",
|
||||
display_name.quote()
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-replaced-with-untailable-file-giving-up",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
let _ = self.watcher_rx.as_mut().unwrap().watcher.unwatch(event_path);
|
||||
self.files.remove(event_path);
|
||||
if self.files.no_files_remaining(settings) {
|
||||
return Err(USimpleError::new(1, text::NO_FILES_REMAINING));
|
||||
return Err(USimpleError::new(1, get_message("tail-no-files-remaining")));
|
||||
}
|
||||
} else {
|
||||
show_error!(
|
||||
"{} has been replaced with an untailable file",
|
||||
display_name.quote()
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-replaced-with-untailable-file",
|
||||
HashMap::from([("file".to_string(), display_name.quote().to_string())])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -391,27 +434,44 @@ EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
|
|||
if let Some(old_md) = self.files.get_mut_metadata(event_path) {
|
||||
if old_md.is_tailable() && self.files.get(event_path).reader.is_some() {
|
||||
show_error!(
|
||||
"{} {}: {}",
|
||||
display_name.quote(),
|
||||
text::BECOME_INACCESSIBLE,
|
||||
text::NO_SUCH_FILE
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-file-became-inaccessible",
|
||||
HashMap::from([
|
||||
("file".to_string(), display_name.quote().to_string()),
|
||||
("become_inaccessible".to_string(), get_message("tail-become-inaccessible")),
|
||||
("no_such_file".to_string(), get_message("tail-no-such-file-or-directory"))
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if event_path.is_orphan() && !self.orphans.contains(event_path) {
|
||||
show_error!("directory containing watched file was removed");
|
||||
show_error!("{}", get_message("tail-status-directory-containing-watched-file-removed"));
|
||||
show_error!(
|
||||
"{} cannot be used, reverting to polling",
|
||||
text::BACKEND
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-backend-cannot-be-used-reverting-to-polling",
|
||||
HashMap::from([("backend".to_string(), text::BACKEND.to_string())])
|
||||
)
|
||||
);
|
||||
self.orphans.push(event_path.clone());
|
||||
let _ = self.watcher_rx.as_mut().unwrap().unwatch(event_path);
|
||||
}
|
||||
} else {
|
||||
show_error!("{display_name}: {}", text::NO_SUCH_FILE);
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-file-no-such-file",
|
||||
HashMap::from([
|
||||
("file".to_string(), display_name),
|
||||
("no_such_file".to_string(), get_message("tail-no-such-file-or-directory"))
|
||||
])
|
||||
)
|
||||
);
|
||||
if !self.files.files_remaining() && self.use_polling {
|
||||
// NOTE: GNU's tail exits here for `---disable-inotify`
|
||||
return Err(USimpleError::new(1, text::NO_FILES_REMAINING));
|
||||
return Err(USimpleError::new(1, get_message("tail-no-files-remaining")));
|
||||
}
|
||||
}
|
||||
self.files.reset_reader(event_path);
|
||||
|
@ -475,7 +535,7 @@ EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
|
|||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
||||
if observer.files.no_files_remaining(settings) && !observer.files.only_stdin_remaining() {
|
||||
return Err(USimpleError::new(1, text::NO_FILES_REMAINING.to_string()));
|
||||
return Err(USimpleError::new(1, get_message("tail-no-files-remaining")));
|
||||
}
|
||||
|
||||
let mut process = platform::ProcessChecker::new(observer.pid);
|
||||
|
@ -504,8 +564,14 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
|||
let md = new_path.metadata().unwrap();
|
||||
if md.is_tailable() && pd.reader.is_none() {
|
||||
show_error!(
|
||||
"{} has appeared; following new file",
|
||||
pd.display_name.quote()
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-status-has-appeared-following-new-file",
|
||||
HashMap::from([(
|
||||
"file".to_string(),
|
||||
pd.display_name.quote().to_string()
|
||||
)])
|
||||
)
|
||||
);
|
||||
observer.files.update_metadata(new_path, Some(md));
|
||||
observer.files.update_reader(new_path)?;
|
||||
|
@ -528,6 +594,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
|||
.unwrap()
|
||||
.receiver
|
||||
.recv_timeout(settings.sleep_sec);
|
||||
|
||||
if rx_result.is_ok() {
|
||||
timeout_counter = 0;
|
||||
}
|
||||
|
@ -563,14 +630,33 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
|||
})) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("{} resources exhausted", text::BACKEND),
|
||||
get_message_with_args(
|
||||
"tail-error-backend-resources-exhausted",
|
||||
HashMap::from([("backend".to_string(), text::BACKEND.to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
get_message_with_args(
|
||||
"tail-error-notify-error",
|
||||
HashMap::from([("error".to_string(), e.to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
Ok(Err(e)) => return Err(USimpleError::new(1, format!("NotifyError: {e}"))),
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => {
|
||||
timeout_counter += 1;
|
||||
}
|
||||
Err(e) => return Err(USimpleError::new(1, format!("RecvTimeoutError: {e}"))),
|
||||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
get_message_with_args(
|
||||
"tail-error-recv-timeout-error",
|
||||
HashMap::from([("error".to_string(), e.to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if observer.use_polling && settings.follow.is_some() {
|
||||
|
@ -588,7 +674,7 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
|||
if timeout_counter == settings.max_unchanged_stats {
|
||||
/*
|
||||
TODO: [2021-10; jhscheer] implement timeout_counter for each file.
|
||||
‘--max-unchanged-stats=n’
|
||||
'--max-unchanged-stats=n'
|
||||
When tailing a file by name, if there have been n (default n=5) consecutive iterations
|
||||
for which the file has not changed, then open/fstat the file to determine if that file
|
||||
name is still associated with the same device/inode-number pair as before. When
|
||||
|
@ -599,5 +685,6 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
|
|||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use std::io::{Seek, SeekFrom};
|
|||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::error::UResult;
|
||||
use uucore::locale::get_message;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InputKind {
|
||||
|
@ -55,7 +56,7 @@ impl Input {
|
|||
let kind = string.into();
|
||||
let display_name = match kind {
|
||||
InputKind::File(_) => string.to_string_lossy().to_string(),
|
||||
InputKind::Stdin => text::STDIN_HEADER.to_string(),
|
||||
InputKind::Stdin => get_message("tail-stdin-header"),
|
||||
};
|
||||
|
||||
Self { kind, display_name }
|
||||
|
@ -106,7 +107,7 @@ impl Default for Input {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
kind: InputKind::Stdin,
|
||||
display_name: String::from(text::STDIN_HEADER),
|
||||
display_name: get_message("tail-stdin-header"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +222,7 @@ impl PathExtTail for Path {
|
|||
fn is_stdin(&self) -> bool {
|
||||
self.eq(Self::new(text::DASH))
|
||||
|| self.eq(Self::new(text::DEV_STDIN))
|
||||
|| self.eq(Self::new(text::STDIN_HEADER))
|
||||
|| self.eq(Self::new(&get_message("tail-stdin-header")))
|
||||
}
|
||||
|
||||
/// Return true if `path` does not have an existing parent directory
|
||||
|
|
|
@ -29,11 +29,13 @@ use memchr::{memchr_iter, memrchr_iter};
|
|||
use paths::{FileExtTail, HeaderPrinter, Input, InputKind, MetadataExtTail};
|
||||
use same_file::Handle;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufReader, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write, stdin, stdout};
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError, get_exit_code, set_exit_code};
|
||||
use uucore::locale::{get_message, get_message_with_args};
|
||||
use uucore::{show, show_error};
|
||||
|
||||
#[uucore::main]
|
||||
|
@ -46,7 +48,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
args::VerificationResult::CannotFollowStdinByName => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("cannot follow {} by name", text::DASH.quote()),
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-follow-stdin-by-name",
|
||||
HashMap::from([("stdin".to_string(), text::DASH.quote().to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
// Exit early if we do not output anything. Note, that this may break a pipe
|
||||
|
@ -95,7 +100,7 @@ fn uu_tail(settings: &Settings) -> UResult<()> {
|
|||
}
|
||||
|
||||
if get_exit_code() > 0 && paths::stdin_is_bad_fd() {
|
||||
show_error!("-: {}", text::BAD_FD);
|
||||
show_error!("{}: {}", text::DASH, get_message("tail-bad-fd"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -112,27 +117,50 @@ fn tail_file(
|
|||
if !path.exists() {
|
||||
set_exit_code(1);
|
||||
show_error!(
|
||||
"cannot open '{}' for reading: {}",
|
||||
input.display_name,
|
||||
text::NO_SUCH_FILE
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-open-no-such-file",
|
||||
HashMap::from([
|
||||
("file".to_string(), input.display_name.clone()),
|
||||
(
|
||||
"error".to_string(),
|
||||
get_message("tail-no-such-file-or-directory")
|
||||
)
|
||||
])
|
||||
)
|
||||
);
|
||||
observer.add_bad_path(path, input.display_name.as_str(), false)?;
|
||||
} else if path.is_dir() {
|
||||
set_exit_code(1);
|
||||
|
||||
header_printer.print_input(input);
|
||||
let err_msg = "Is a directory".to_string();
|
||||
let err_msg = get_message("tail-is-a-directory");
|
||||
|
||||
show_error!("error reading '{}': {err_msg}", input.display_name);
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-reading-file",
|
||||
HashMap::from([
|
||||
("file".to_string(), input.display_name.clone()),
|
||||
("error".to_string(), err_msg)
|
||||
])
|
||||
)
|
||||
);
|
||||
if settings.follow.is_some() {
|
||||
let msg = if settings.retry {
|
||||
""
|
||||
} else {
|
||||
"; giving up on this name"
|
||||
&get_message("tail-giving-up-on-this-name")
|
||||
};
|
||||
show_error!(
|
||||
"{}: cannot follow end of this type of file{msg}",
|
||||
input.display_name,
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-follow-file-type",
|
||||
HashMap::from([
|
||||
("file".to_string(), input.display_name.clone()),
|
||||
("msg".to_string(), msg.to_string())
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
if !observer.follow_name_retry() {
|
||||
|
@ -166,13 +194,19 @@ fn tail_file(
|
|||
Err(e) if e.kind() == ErrorKind::PermissionDenied => {
|
||||
observer.add_bad_path(path, input.display_name.as_str(), false)?;
|
||||
show!(e.map_err_context(|| {
|
||||
format!("cannot open '{}' for reading", input.display_name)
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-open-for-reading",
|
||||
HashMap::from([("file".to_string(), input.display_name.clone())]),
|
||||
)
|
||||
}));
|
||||
}
|
||||
Err(e) => {
|
||||
observer.add_bad_path(path, input.display_name.as_str(), false)?;
|
||||
return Err(e.map_err_context(|| {
|
||||
format!("cannot open '{}' for reading", input.display_name)
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-open-for-reading",
|
||||
HashMap::from([("file".to_string(), input.display_name.clone())]),
|
||||
)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -202,9 +236,17 @@ fn tail_stdin(
|
|||
if meta.file_type().is_dir() {
|
||||
set_exit_code(1);
|
||||
show_error!(
|
||||
"cannot open '{}' for reading: {}",
|
||||
input.display_name,
|
||||
text::NO_SUCH_FILE
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-open-no-such-file",
|
||||
HashMap::from([
|
||||
("file".to_string(), input.display_name.clone()),
|
||||
(
|
||||
"error".to_string(),
|
||||
get_message("tail-no-such-file-or-directory")
|
||||
)
|
||||
])
|
||||
)
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -240,15 +282,25 @@ fn tail_stdin(
|
|||
if paths::stdin_is_bad_fd() {
|
||||
set_exit_code(1);
|
||||
show_error!(
|
||||
"cannot fstat {}: {}",
|
||||
text::STDIN_HEADER.quote(),
|
||||
text::BAD_FD
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-cannot-fstat",
|
||||
HashMap::from([
|
||||
("file".to_string(), get_message("tail-stdin-header")),
|
||||
("error".to_string(), get_message("tail-bad-fd"))
|
||||
])
|
||||
)
|
||||
);
|
||||
if settings.follow.is_some() {
|
||||
show_error!(
|
||||
"error reading {}: {}",
|
||||
text::STDIN_HEADER.quote(),
|
||||
text::BAD_FD
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"tail-error-reading-file",
|
||||
HashMap::from([
|
||||
("file".to_string(), get_message("tail-stdin-header")),
|
||||
("error".to_string(), get_message("tail-bad-fd"))
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -5,20 +5,16 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) kqueue
|
||||
|
||||
// Non-localized constants (system paths and technical identifiers)
|
||||
pub const DASH: &str = "-";
|
||||
pub const DEV_STDIN: &str = "/dev/stdin";
|
||||
pub const STDIN_HEADER: &str = "standard input";
|
||||
pub const NO_FILES_REMAINING: &str = "no files remaining";
|
||||
pub const NO_SUCH_FILE: &str = "No such file or directory";
|
||||
pub const BECOME_INACCESSIBLE: &str = "has become inaccessible";
|
||||
pub const BAD_FD: &str = "Bad file descriptor";
|
||||
pub const FD0: &str = "/dev/fd/0";
|
||||
pub const DEV_TTY: &str = "/dev/tty";
|
||||
pub const DEV_PTMX: &str = "/dev/ptmx";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const BACKEND: &str = "inotify";
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
pub const BACKEND: &str = "kqueue";
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const BACKEND: &str = "ReadDirectoryChanges";
|
||||
pub const FD0: &str = "/dev/fd/0";
|
||||
pub const IS_A_DIRECTORY: &str = "Is a directory";
|
||||
pub const DEV_TTY: &str = "/dev/tty";
|
||||
pub const DEV_PTMX: &str = "/dev/ptmx";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue