mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-09 19:30:29 +00:00
Paginate by writing data to pager via stdin
On Windows, `more` is unable to open the file generated by `mkstemp()` due to not recognizing the first argument as a path, but as a parameter. In contrast to *nix, arguments on Windows are, by convention, prefixed with `/`, which `mkntcmdline()` happily transforms into path-like strings. This commit sidesteps this issue entirely by writing data to the pager in question via stdin, dumping text to the console as a last resort.
This commit is contained in:
parent
aca2261cda
commit
2bb9db87c7
5 changed files with 68 additions and 72 deletions
|
@ -90,26 +90,13 @@ textwindows int mkntcmdline(char16_t cmdline[32767], char *const argv[]) {
|
|||
}
|
||||
}
|
||||
if (!x) break;
|
||||
if (x == '/' || x == '\\') {
|
||||
if (!i) {
|
||||
// turn / into \ for first argv[i]
|
||||
x = '\\';
|
||||
// turn \c\... into c:\ for first argv[i]
|
||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||
cmdline[0] = cmdline[1];
|
||||
cmdline[1] = ':';
|
||||
}
|
||||
} else {
|
||||
// turn stuff like `less /c/...`
|
||||
// into `less c:/...`
|
||||
// turn stuff like `more <"/c/..."`
|
||||
// into `more <"c:/..."`
|
||||
if (k > 3 && IsAlpha(cmdline[k - 1]) &&
|
||||
(cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') &&
|
||||
(cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) {
|
||||
cmdline[k - 2] = cmdline[k - 1];
|
||||
cmdline[k - 1] = ':';
|
||||
}
|
||||
if (!i && (x == '/' || x == '\\')) {
|
||||
// turn / into \ for first argv[i]
|
||||
x = '\\';
|
||||
// turn \c\... into c:\ for first argv[i]
|
||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||
cmdline[0] = cmdline[1];
|
||||
cmdline[1] = ':';
|
||||
}
|
||||
}
|
||||
if (x == '\\') {
|
||||
|
|
|
@ -19,39 +19,54 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/temp.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
#define RUN(fd, ofd, pfd, func) \
|
||||
do { \
|
||||
int i; \
|
||||
for (i = 0; i < 2; i++) { \
|
||||
if (i == fd) { \
|
||||
ofd = dup(fd); \
|
||||
dup2(pfd[i], fd); \
|
||||
} \
|
||||
close(pfd[i]); \
|
||||
} \
|
||||
func; \
|
||||
dup2(ofd, fd); \
|
||||
close(ofd); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Displays wall of text in terminal with pagination.
|
||||
*/
|
||||
void __paginate(int fd, const char *s) {
|
||||
int tfd, pid;
|
||||
char *args[3] = {0};
|
||||
char tmppath[] = "/tmp/paginate.XXXXXX";
|
||||
char progpath[PATH_MAX];
|
||||
if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) &&
|
||||
((args[0] = commandv("less", progpath, sizeof(progpath))) ||
|
||||
(args[0] = commandv("more", progpath, sizeof(progpath))))) {
|
||||
if ((tfd = mkstemp(tmppath)) != -1) {
|
||||
write(tfd, s, strlen(s));
|
||||
close(tfd);
|
||||
args[1] = tmppath;
|
||||
if ((pid = fork()) != -1) {
|
||||
putenv("LC_ALL=C.UTF-8");
|
||||
putenv("LESSCHARSET=utf-8");
|
||||
putenv("LESS=-RS");
|
||||
if (!pid) {
|
||||
execv(args[0], args);
|
||||
_Exit(127);
|
||||
}
|
||||
waitpid(pid, 0, 0);
|
||||
unlink(tmppath);
|
||||
return;
|
||||
int ofd, pid, pfd[2];
|
||||
char *prog;
|
||||
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) &&
|
||||
strcmp(nulltoempty(getenv("TERM")), "dumb") &&
|
||||
((prog = commandv("less", _gc(malloc(PATH_MAX)), PATH_MAX)) ||
|
||||
(prog = commandv("more", _gc(malloc(PATH_MAX)), PATH_MAX))) &&
|
||||
!pipe2(pfd, O_CLOEXEC)) {
|
||||
if ((pid = fork()) != -1) {
|
||||
putenv("LC_ALL=C.UTF-8");
|
||||
putenv("LESSCHARSET=utf-8");
|
||||
putenv("LESS=-RS");
|
||||
if (!pid) {
|
||||
RUN(STDIN_FILENO, ofd, pfd, execv(prog, (char *const[]){prog, NULL}));
|
||||
_Exit(127);
|
||||
} else {
|
||||
RUN(STDOUT_FILENO, ofd, pfd, write(STDOUT_FILENO, s, strlen(s)));
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
unlink(tmppath);
|
||||
return;
|
||||
}
|
||||
close(pfd[0]);
|
||||
close(pfd[1]);
|
||||
}
|
||||
write(fd, s, strlen(s));
|
||||
}
|
||||
|
|
|
@ -64,39 +64,27 @@ TEST(mkntcmdline, justSlash) {
|
|||
}
|
||||
|
||||
TEST(mkntcmdline, testUnicode) {
|
||||
char *argv1[] = {
|
||||
char *argv[] = {
|
||||
gc(strdup("(╯°□°)╯")),
|
||||
gc(strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非")),
|
||||
NULL,
|
||||
};
|
||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
|
||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
|
||||
EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"",
|
||||
cmdline);
|
||||
}
|
||||
|
||||
TEST(mkntcmdline, fixAsBestAsWeCanForNow1) {
|
||||
char *argv1[] = {
|
||||
TEST(mkntcmdline, testPathlikeArgv0) {
|
||||
char *argv[] = {
|
||||
"/C/WINDOWS/system32/cmd.exe",
|
||||
"/C",
|
||||
"more <\"/C/Users/jart/AppData/Local/Temp/tmplquaa_d6\"",
|
||||
"/S",
|
||||
"/D/c",
|
||||
"less C:\\Users\\jart\\AppData\\Local\\Temp\\tmplquaa_d6",
|
||||
NULL,
|
||||
};
|
||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
|
||||
EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"more <"
|
||||
u"\"\"\"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\"\"\"\"",
|
||||
cmdline);
|
||||
}
|
||||
|
||||
TEST(mkntcmdline, fixAsBestAsWeCanForNow2) {
|
||||
char *argv1[] = {
|
||||
"/C/WINDOWS/system32/cmd.exe",
|
||||
"/C",
|
||||
"less /C/Users/jart/AppData/Local/Temp/tmplquaa_d6",
|
||||
NULL,
|
||||
};
|
||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
|
||||
EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"less "
|
||||
u"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\"",
|
||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
|
||||
EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /S /D/c \"less "
|
||||
u"C:\\Users\\jart\\AppData\\Local\\Temp\\tmplquaa_d6\"",
|
||||
cmdline);
|
||||
}
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ SECURITY
|
|||
redbean provides hardened ASAN (Address Sanitizer) builds that
|
||||
proactively guard against any potential memory weaknesses that may be
|
||||
discovered, such as buffer overruns, use after free, etc. MDOE=asan is
|
||||
recomended when serving on the public Internet.
|
||||
recommended when serving on the public Internet.
|
||||
|
||||
redbean also supports robust sandboxing on Linux Kernel 5.13+ and
|
||||
OpenBSD. The recommended way to harden your redbean is to call the
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/hwcap.h"
|
||||
#include "libc/sysv/consts/inaddr.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
|
@ -215,7 +216,7 @@ __static_yoink("blink_xnu_aarch64"); // is apple silicon
|
|||
// digits not used: 0123456789
|
||||
// puncts not used: !"#$&'()+,-./;<=>@[\]^_`{|}~
|
||||
#define GETOPTS \
|
||||
"*%BEJSVXZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:W:c:e:l:p:r:t:w:"
|
||||
"%*?BEJSVXZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:W:c:e:l:p:r:t:w:"
|
||||
|
||||
static const uint8_t kGzipHeader[] = {
|
||||
0x1F, // MAGNUM
|
||||
|
@ -7294,7 +7295,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
break;
|
||||
CASE('r', ProgramRedirectArg(307, optarg));
|
||||
CASE('t', ProgramTimeout(ParseInt(optarg)));
|
||||
CASE('h', PrintUsage(1, EXIT_SUCCESS));
|
||||
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
|
||||
#if !IsTiny()
|
||||
CASE('W', monitortty = optarg);
|
||||
|
@ -7331,8 +7331,14 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('C', ProgramFile(optarg, ProgramCertificate));
|
||||
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
|
||||
#endif
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
PrintUsage(2, EX_USAGE);
|
||||
if (opt == optopt) {
|
||||
PrintUsage(STDOUT_FILENO, EXIT_SUCCESS);
|
||||
} else {
|
||||
PrintUsage(STDERR_FILENO, EX_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if storing asset(s) is requested, don't need to continue
|
||||
|
@ -7481,5 +7487,5 @@ int main(int argc, char *argv[]) {
|
|||
CheckForMemoryLeaks();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue