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) break;
|
||||||
if (x == '/' || x == '\\') {
|
if (!i && (x == '/' || x == '\\')) {
|
||||||
if (!i) {
|
// turn / into \ for first argv[i]
|
||||||
// turn / into \ for first argv[i]
|
x = '\\';
|
||||||
x = '\\';
|
// turn \c\... into c:\ for first argv[i]
|
||||||
// turn \c\... into c:\ for first argv[i]
|
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
cmdline[0] = cmdline[1];
|
||||||
cmdline[0] = cmdline[1];
|
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 (x == '\\') {
|
if (x == '\\') {
|
||||||
|
|
|
@ -19,39 +19,54 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
#include "libc/limits.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/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.
|
* Displays wall of text in terminal with pagination.
|
||||||
*/
|
*/
|
||||||
void __paginate(int fd, const char *s) {
|
void __paginate(int fd, const char *s) {
|
||||||
int tfd, pid;
|
int ofd, pid, pfd[2];
|
||||||
char *args[3] = {0};
|
char *prog;
|
||||||
char tmppath[] = "/tmp/paginate.XXXXXX";
|
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) &&
|
||||||
char progpath[PATH_MAX];
|
strcmp(nulltoempty(getenv("TERM")), "dumb") &&
|
||||||
if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) &&
|
((prog = commandv("less", _gc(malloc(PATH_MAX)), PATH_MAX)) ||
|
||||||
((args[0] = commandv("less", progpath, sizeof(progpath))) ||
|
(prog = commandv("more", _gc(malloc(PATH_MAX)), PATH_MAX))) &&
|
||||||
(args[0] = commandv("more", progpath, sizeof(progpath))))) {
|
!pipe2(pfd, O_CLOEXEC)) {
|
||||||
if ((tfd = mkstemp(tmppath)) != -1) {
|
if ((pid = fork()) != -1) {
|
||||||
write(tfd, s, strlen(s));
|
putenv("LC_ALL=C.UTF-8");
|
||||||
close(tfd);
|
putenv("LESSCHARSET=utf-8");
|
||||||
args[1] = tmppath;
|
putenv("LESS=-RS");
|
||||||
if ((pid = fork()) != -1) {
|
if (!pid) {
|
||||||
putenv("LC_ALL=C.UTF-8");
|
RUN(STDIN_FILENO, ofd, pfd, execv(prog, (char *const[]){prog, NULL}));
|
||||||
putenv("LESSCHARSET=utf-8");
|
_Exit(127);
|
||||||
putenv("LESS=-RS");
|
} else {
|
||||||
if (!pid) {
|
RUN(STDOUT_FILENO, ofd, pfd, write(STDOUT_FILENO, s, strlen(s)));
|
||||||
execv(args[0], args);
|
waitpid(pid, NULL, 0);
|
||||||
_Exit(127);
|
|
||||||
}
|
|
||||||
waitpid(pid, 0, 0);
|
|
||||||
unlink(tmppath);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
unlink(tmppath);
|
return;
|
||||||
}
|
}
|
||||||
|
close(pfd[0]);
|
||||||
|
close(pfd[1]);
|
||||||
}
|
}
|
||||||
write(fd, s, strlen(s));
|
write(fd, s, strlen(s));
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,39 +64,27 @@ TEST(mkntcmdline, justSlash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(mkntcmdline, testUnicode) {
|
TEST(mkntcmdline, testUnicode) {
|
||||||
char *argv1[] = {
|
char *argv[] = {
|
||||||
gc(strdup("(╯°□°)╯")),
|
gc(strdup("(╯°□°)╯")),
|
||||||
gc(strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非")),
|
gc(strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非")),
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
|
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
|
||||||
EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"",
|
EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"",
|
||||||
cmdline);
|
cmdline);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(mkntcmdline, fixAsBestAsWeCanForNow1) {
|
TEST(mkntcmdline, testPathlikeArgv0) {
|
||||||
char *argv1[] = {
|
char *argv[] = {
|
||||||
"/C/WINDOWS/system32/cmd.exe",
|
"/C/WINDOWS/system32/cmd.exe",
|
||||||
"/C",
|
"/S",
|
||||||
"more <\"/C/Users/jart/AppData/Local/Temp/tmplquaa_d6\"",
|
"/D/c",
|
||||||
|
"less C:\\Users\\jart\\AppData\\Local\\Temp\\tmplquaa_d6",
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
|
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
|
||||||
EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"more <"
|
EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /S /D/c \"less "
|
||||||
u"\"\"\"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\"\"\"\"",
|
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\"",
|
|
||||||
cmdline);
|
cmdline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,7 @@ SECURITY
|
||||||
redbean provides hardened ASAN (Address Sanitizer) builds that
|
redbean provides hardened ASAN (Address Sanitizer) builds that
|
||||||
proactively guard against any potential memory weaknesses that may be
|
proactively guard against any potential memory weaknesses that may be
|
||||||
discovered, such as buffer overruns, use after free, etc. MDOE=asan is
|
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
|
redbean also supports robust sandboxing on Linux Kernel 5.13+ and
|
||||||
OpenBSD. The recommended way to harden your redbean is to call the
|
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/ex.h"
|
||||||
#include "libc/sysv/consts/exit.h"
|
#include "libc/sysv/consts/exit.h"
|
||||||
#include "libc/sysv/consts/f.h"
|
#include "libc/sysv/consts/f.h"
|
||||||
|
#include "libc/sysv/consts/fileno.h"
|
||||||
#include "libc/sysv/consts/hwcap.h"
|
#include "libc/sysv/consts/hwcap.h"
|
||||||
#include "libc/sysv/consts/inaddr.h"
|
#include "libc/sysv/consts/inaddr.h"
|
||||||
#include "libc/sysv/consts/ipproto.h"
|
#include "libc/sysv/consts/ipproto.h"
|
||||||
|
@ -215,7 +216,7 @@ __static_yoink("blink_xnu_aarch64"); // is apple silicon
|
||||||
// digits not used: 0123456789
|
// digits not used: 0123456789
|
||||||
// puncts not used: !"#$&'()+,-./;<=>@[\]^_`{|}~
|
// puncts not used: !"#$&'()+,-./;<=>@[\]^_`{|}~
|
||||||
#define GETOPTS \
|
#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[] = {
|
static const uint8_t kGzipHeader[] = {
|
||||||
0x1F, // MAGNUM
|
0x1F, // MAGNUM
|
||||||
|
@ -7294,7 +7295,6 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
break;
|
break;
|
||||||
CASE('r', ProgramRedirectArg(307, optarg));
|
CASE('r', ProgramRedirectArg(307, optarg));
|
||||||
CASE('t', ProgramTimeout(ParseInt(optarg)));
|
CASE('t', ProgramTimeout(ParseInt(optarg)));
|
||||||
CASE('h', PrintUsage(1, EXIT_SUCCESS));
|
|
||||||
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
|
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
|
||||||
#if !IsTiny()
|
#if !IsTiny()
|
||||||
CASE('W', monitortty = optarg);
|
CASE('W', monitortty = optarg);
|
||||||
|
@ -7331,8 +7331,14 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
CASE('C', ProgramFile(optarg, ProgramCertificate));
|
CASE('C', ProgramFile(optarg, ProgramCertificate));
|
||||||
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
|
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
|
||||||
#endif
|
#endif
|
||||||
|
case 'h':
|
||||||
|
case '?':
|
||||||
default:
|
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
|
// if storing asset(s) is requested, don't need to continue
|
||||||
|
@ -7481,5 +7487,5 @@ int main(int argc, char *argv[]) {
|
||||||
CheckForMemoryLeaks();
|
CheckForMemoryLeaks();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue