Compare commits
6 Commits
0.4.0
...
wincolor-0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79e5e6671f | ||
|
|
b04a68a782 | ||
|
|
e573ab5c60 | ||
|
|
f5a2d022ec | ||
|
|
b1d1cd2366 | ||
|
|
f26e0f088f |
@@ -39,6 +39,9 @@ Bug fixes:
|
|||||||
Fix bug that caused ripgrep's parallel iterator to spin and burn CPU.
|
Fix bug that caused ripgrep's parallel iterator to spin and burn CPU.
|
||||||
* [BUG #262](https://github.com/BurntSushi/ripgrep/issues/262):
|
* [BUG #262](https://github.com/BurntSushi/ripgrep/issues/262):
|
||||||
Document how to install shell completion files.
|
Document how to install shell completion files.
|
||||||
|
* [BUG #266](https://github.com/BurntSushi/ripgrep/issues/266),
|
||||||
|
[BUG #293](https://github.com/BurntSushi/ripgrep/issues/293):
|
||||||
|
Fix handling of bold styling and change the default colors.
|
||||||
* [BUG #268](https://github.com/BurntSushi/ripgrep/issues/268):
|
* [BUG #268](https://github.com/BurntSushi/ripgrep/issues/268):
|
||||||
Make lack of backreference support more explicit.
|
Make lack of backreference support more explicit.
|
||||||
* [BUG #271](https://github.com/BurntSushi/ripgrep/issues/271):
|
* [BUG #271](https://github.com/BurntSushi/ripgrep/issues/271):
|
||||||
|
|||||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -2,6 +2,7 @@
|
|||||||
name = "ripgrep"
|
name = "ripgrep"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -33,6 +34,16 @@ name = "ansi_term"
|
|||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -313,6 +324,7 @@ dependencies = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
"checksum aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f660b942762979b56c9f07b4b36bb559776fbad102f05d6771e1b629e8fd5bf"
|
"checksum aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f660b942762979b56c9f07b4b36bb559776fbad102f05d6771e1b629e8fd5bf"
|
||||||
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
||||||
|
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a"
|
"checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a"
|
||||||
"checksum clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95b78f3fe0fc94c13c731714363260e04b557a637166f33a4570d3189d642374"
|
"checksum clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95b78f3fe0fc94c13c731714363260e04b557a637166f33a4570d3189d642374"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ name = "integration"
|
|||||||
path = "tests/tests.rs"
|
path = "tests/tests.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
atty = "0.2.2"
|
||||||
bytecount = "0.1.4"
|
bytecount = "0.1.4"
|
||||||
clap = "2.19.0"
|
clap = "2.19.0"
|
||||||
env_logger = { version = "0.3", default-features = false }
|
env_logger = { version = "0.3", default-features = false }
|
||||||
|
|||||||
@@ -201,7 +201,8 @@ $ nix-env --install ripgrep
|
|||||||
$ # (Or using the attribute name, which is also `ripgrep`.)
|
$ # (Or using the attribute name, which is also `ripgrep`.)
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're a **Rust programmer**, `ripgrep` can be installed with `cargo`:
|
If you're a **Rust programmer**, `ripgrep` can be installed with `cargo`. Note
|
||||||
|
that this requires you to have **Rust 1.12 or newer** installed.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cargo install ripgrep
|
$ cargo install ripgrep
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
class RipgrepBin < Formula
|
class RipgrepBin < Formula
|
||||||
version '0.3.2'
|
version '0.4.0'
|
||||||
desc "Search tool like grep and The Silver Searcher."
|
desc "Search tool like grep and The Silver Searcher."
|
||||||
homepage "https://github.com/BurntSushi/ripgrep"
|
homepage "https://github.com/BurntSushi/ripgrep"
|
||||||
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
|
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
|
||||||
sha256 "05869abe67104822d29081f12e31e3e90c29cac60ee50546387b17e9be45739c"
|
sha256 "6ac71251909227f8ef7eda27d3080c954843f3665b81e455362c90b2a9c4734a"
|
||||||
|
|
||||||
conflicts_with "ripgrep"
|
conflicts_with "ripgrep"
|
||||||
|
|
||||||
|
|||||||
33
src/args.rs
33
src/args.rs
@@ -394,8 +394,8 @@ impl<'a> ArgMatches<'a> {
|
|||||||
self.values_of_os("file").map_or(false, |mut files| {
|
self.values_of_os("file").map_or(false, |mut files| {
|
||||||
files.any(|f| f == "-")
|
files.any(|f| f == "-")
|
||||||
});
|
});
|
||||||
let search_cwd = atty::on_stdin()
|
let search_cwd = atty::is(atty::Stream::Stdin)
|
||||||
|| !atty::stdin_is_readable()
|
|| !stdin_is_readable()
|
||||||
|| (self.is_present("file") && file_is_stdin)
|
|| (self.is_present("file") && file_is_stdin)
|
||||||
|| self.is_present("files")
|
|| self.is_present("files")
|
||||||
|| self.is_present("type-list");
|
|| self.is_present("type-list");
|
||||||
@@ -584,7 +584,7 @@ impl<'a> ArgMatches<'a> {
|
|||||||
} else {
|
} else {
|
||||||
self.is_present("line-number")
|
self.is_present("line-number")
|
||||||
|| self.is_present("column")
|
|| self.is_present("column")
|
||||||
|| atty::on_stdout()
|
|| atty::is(atty::Stream::Stdout)
|
||||||
|| self.is_present("pretty")
|
|| self.is_present("pretty")
|
||||||
|| self.is_present("vimgrep")
|
|| self.is_present("vimgrep")
|
||||||
}
|
}
|
||||||
@@ -602,7 +602,7 @@ impl<'a> ArgMatches<'a> {
|
|||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
self.is_present("heading")
|
self.is_present("heading")
|
||||||
|| atty::on_stdout()
|
|| atty::is(atty::Stream::Stdout)
|
||||||
|| self.is_present("pretty")
|
|| self.is_present("pretty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,7 +667,7 @@ impl<'a> ArgMatches<'a> {
|
|||||||
} else if self.is_present("vimgrep") {
|
} else if self.is_present("vimgrep") {
|
||||||
false
|
false
|
||||||
} else if preference == "auto" {
|
} else if preference == "auto" {
|
||||||
atty::on_stdout() || self.is_present("pretty")
|
atty::is(atty::Stream::Stdout) || self.is_present("pretty")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -687,7 +687,7 @@ impl<'a> ArgMatches<'a> {
|
|||||||
} else if self.is_present("vimgrep") {
|
} else if self.is_present("vimgrep") {
|
||||||
termcolor::ColorChoice::Never
|
termcolor::ColorChoice::Never
|
||||||
} else if preference == "auto" {
|
} else if preference == "auto" {
|
||||||
if atty::on_stdout() || self.is_present("pretty") {
|
if atty::is(atty::Stream::Stdout) || self.is_present("pretty") {
|
||||||
termcolor::ColorChoice::Auto
|
termcolor::ColorChoice::Auto
|
||||||
} else {
|
} else {
|
||||||
termcolor::ColorChoice::Never
|
termcolor::ColorChoice::Never
|
||||||
@@ -869,3 +869,24 @@ impl QuietMatched {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if stdin is deemed searchable.
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn stdin_is_readable() -> bool {
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
use same_file::Handle;
|
||||||
|
|
||||||
|
let ft = match Handle::stdin().and_then(|h| h.as_file().metadata()) {
|
||||||
|
Err(_) => return false,
|
||||||
|
Ok(md) => md.file_type(),
|
||||||
|
};
|
||||||
|
ft.is_file() || ft.is_fifo()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if stdin is deemed searchable.
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn stdin_is_readable() -> bool {
|
||||||
|
// On Windows, it's not clear what the possibilities are to me, so just
|
||||||
|
// always return true.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|||||||
145
src/atty.rs
145
src/atty.rs
@@ -1,145 +0,0 @@
|
|||||||
/*!
|
|
||||||
This atty module contains functions for detecting whether ripgrep is being fed
|
|
||||||
from (or to) a terminal. Windows and Unix do this differently, so implement
|
|
||||||
both here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use winapi::minwindef::DWORD;
|
|
||||||
#[cfg(windows)]
|
|
||||||
use winapi::winnt::HANDLE;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn stdin_is_readable() -> bool {
|
|
||||||
use std::os::unix::fs::FileTypeExt;
|
|
||||||
use same_file::Handle;
|
|
||||||
|
|
||||||
let ft = match Handle::stdin().and_then(|h| h.as_file().metadata()) {
|
|
||||||
Err(_) => return false,
|
|
||||||
Ok(md) => md.file_type(),
|
|
||||||
};
|
|
||||||
ft.is_file() || ft.is_fifo()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn stdin_is_readable() -> bool {
|
|
||||||
// ???
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a tty on stdin.
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn on_stdin() -> bool {
|
|
||||||
use libc;
|
|
||||||
0 < unsafe { libc::isatty(libc::STDIN_FILENO) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a tty on stdout.
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn on_stdout() -> bool {
|
|
||||||
use libc;
|
|
||||||
0 < unsafe { libc::isatty(libc::STDOUT_FILENO) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a tty on stdin.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn on_stdin() -> bool {
|
|
||||||
use kernel32::GetStdHandle;
|
|
||||||
use winapi::winbase::{
|
|
||||||
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let stdin = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
if console_on_handle(stdin) {
|
|
||||||
// False positives aren't possible. If we got a console then
|
|
||||||
// we definitely have a tty on stdin.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Otherwise, it's possible to get a false negative. If we know that
|
|
||||||
// there's a console on stdout or stderr however, then this is a true
|
|
||||||
// negative.
|
|
||||||
if console_on_fd(STD_OUTPUT_HANDLE)
|
|
||||||
|| console_on_fd(STD_ERROR_HANDLE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Otherwise, we can't really tell, so we do a weird hack.
|
|
||||||
msys_tty_on_handle(stdin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a tty on stdout.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn on_stdout() -> bool {
|
|
||||||
use kernel32::GetStdHandle;
|
|
||||||
use winapi::winbase::{
|
|
||||||
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
if console_on_handle(stdout) {
|
|
||||||
// False positives aren't possible. If we got a console then
|
|
||||||
// we definitely have a tty on stdout.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Otherwise, it's possible to get a false negative. If we know that
|
|
||||||
// there's a console on stdin or stderr however, then this is a true
|
|
||||||
// negative.
|
|
||||||
if console_on_fd(STD_INPUT_HANDLE) || console_on_fd(STD_ERROR_HANDLE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Otherwise, we can't really tell, so we do a weird hack.
|
|
||||||
msys_tty_on_handle(stdout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is an MSYS tty on the given handle.
|
|
||||||
#[cfg(windows)]
|
|
||||||
unsafe fn msys_tty_on_handle(handle: HANDLE) -> bool {
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::os::windows::ffi::OsStringExt;
|
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
use kernel32::{GetFileInformationByHandleEx};
|
|
||||||
use winapi::fileapi::FILE_NAME_INFO;
|
|
||||||
use winapi::minwinbase::FileNameInfo;
|
|
||||||
use winapi::minwindef::MAX_PATH;
|
|
||||||
|
|
||||||
let size = mem::size_of::<FILE_NAME_INFO>();
|
|
||||||
let mut name_info_bytes = vec![0u8; size + MAX_PATH];
|
|
||||||
let res = GetFileInformationByHandleEx(
|
|
||||||
handle,
|
|
||||||
FileNameInfo,
|
|
||||||
&mut *name_info_bytes as *mut _ as *mut c_void,
|
|
||||||
name_info_bytes.len() as u32);
|
|
||||||
if res == 0 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let name_info: FILE_NAME_INFO =
|
|
||||||
*(name_info_bytes[0..size].as_ptr() as *const FILE_NAME_INFO);
|
|
||||||
let name_bytes =
|
|
||||||
&name_info_bytes[size..size + name_info.FileNameLength as usize];
|
|
||||||
let name_u16 = slice::from_raw_parts(
|
|
||||||
name_bytes.as_ptr() as *const u16, name_bytes.len() / 2);
|
|
||||||
let name = OsString::from_wide(name_u16)
|
|
||||||
.as_os_str().to_string_lossy().into_owned();
|
|
||||||
name.contains("msys-") || name.contains("-pty")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a console on the given file descriptor.
|
|
||||||
#[cfg(windows)]
|
|
||||||
unsafe fn console_on_fd(fd: DWORD) -> bool {
|
|
||||||
use kernel32::GetStdHandle;
|
|
||||||
console_on_handle(GetStdHandle(fd))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if there is a console on the given handle.
|
|
||||||
#[cfg(windows)]
|
|
||||||
unsafe fn console_on_handle(handle: HANDLE) -> bool {
|
|
||||||
use kernel32::GetConsoleMode;
|
|
||||||
let mut out = 0;
|
|
||||||
GetConsoleMode(handle, &mut out) != 0
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
extern crate atty;
|
||||||
extern crate bytecount;
|
extern crate bytecount;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
@@ -46,7 +47,6 @@ macro_rules! eprintln {
|
|||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod args;
|
mod args;
|
||||||
mod atty;
|
|
||||||
mod pathutil;
|
mod pathutil;
|
||||||
mod printer;
|
mod printer;
|
||||||
mod search_buffer;
|
mod search_buffer;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wincolor"
|
name = "wincolor"
|
||||||
version = "0.1.1" #:version
|
version = "0.1.2" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
A simple Windows specific API for controlling text color in a Windows console.
|
A simple Windows specific API for controlling text color in a Windows console.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::mem;
|
|||||||
|
|
||||||
use kernel32;
|
use kernel32;
|
||||||
use winapi::{DWORD, HANDLE, WORD};
|
use winapi::{DWORD, HANDLE, WORD};
|
||||||
use winapi::winbase::STD_OUTPUT_HANDLE;
|
use winapi::winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE};
|
||||||
use winapi::wincon::{
|
use winapi::wincon::{
|
||||||
FOREGROUND_BLUE as FG_BLUE,
|
FOREGROUND_BLUE as FG_BLUE,
|
||||||
FOREGROUND_GREEN as FG_GREEN,
|
FOREGROUND_GREEN as FG_GREEN,
|
||||||
@@ -44,13 +44,11 @@ impl Drop for Console {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Console {
|
impl Console {
|
||||||
/// Create a new Console to stdout.
|
/// Get a console for a standard I/O stream.
|
||||||
///
|
fn create_for_stream(handle_id: DWORD) -> io::Result<Console> {
|
||||||
/// If there was a problem creating the console, then an error is returned.
|
|
||||||
pub fn stdout() -> io::Result<Console> {
|
|
||||||
let mut info = unsafe { mem::zeroed() };
|
let mut info = unsafe { mem::zeroed() };
|
||||||
let (handle, res) = unsafe {
|
let (handle, res) = unsafe {
|
||||||
let handle = kernel32::GetStdHandle(STD_OUTPUT_HANDLE);
|
let handle = kernel32::GetStdHandle(handle_id);
|
||||||
(handle, kernel32::GetConsoleScreenBufferInfo(handle, &mut info))
|
(handle, kernel32::GetConsoleScreenBufferInfo(handle, &mut info))
|
||||||
};
|
};
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
@@ -64,6 +62,20 @@ impl Console {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new Console to stdout.
|
||||||
|
///
|
||||||
|
/// If there was a problem creating the console, then an error is returned.
|
||||||
|
pub fn stdout() -> io::Result<Console> {
|
||||||
|
Self::create_for_stream(STD_OUTPUT_HANDLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new Console to stderr.
|
||||||
|
///
|
||||||
|
/// If there was a problem creating the console, then an error is returned.
|
||||||
|
pub fn stderr() -> io::Result<Console> {
|
||||||
|
Self::create_for_stream(STD_ERROR_HANDLE)
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies the current text attributes.
|
/// Applies the current text attributes.
|
||||||
fn set(&mut self) -> io::Result<()> {
|
fn set(&mut self) -> io::Result<()> {
|
||||||
let attr = self.cur_attr.to_word();
|
let attr = self.cur_attr.to_word();
|
||||||
|
|||||||
Reference in New Issue
Block a user