Mercurial
comparison mrjunejune/latex_renderer.c @ 248:b8b6e726964a
[style] Use Dowa type aliases in C
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 04 Aug 2026 02:44:06 -0700 |
| parents | 70f2a3dafc1c |
| children |
comparison
equal
deleted
inserted
replaced
| 247:70f2a3dafc1c | 248:b8b6e726964a |
|---|---|
| 10 #include <linux/landlock.h> | 10 #include <linux/landlock.h> |
| 11 #include <linux/seccomp.h> | 11 #include <linux/seccomp.h> |
| 12 #include <limits.h> | 12 #include <limits.h> |
| 13 #include <poll.h> | 13 #include <poll.h> |
| 14 #include <signal.h> | 14 #include <signal.h> |
| 15 #include <stdbool.h> | |
| 16 #include <stddef.h> | 15 #include <stddef.h> |
| 17 #include <stdio.h> | 16 #include <stdio.h> |
| 18 #include <stdlib.h> | 17 #include <stdlib.h> |
| 19 #include <string.h> | 18 #include <string.h> |
| 20 #include <sys/prctl.h> | 19 #include <sys/prctl.h> |
| 66 .status = status, | 65 .status = status, |
| 67 .diagnostics = duplicate_message(message), | 66 .diagnostics = duplicate_message(message), |
| 68 }; | 67 }; |
| 69 } | 68 } |
| 70 | 69 |
| 71 static bool write_all(int fd, const uint8_t *data, size_t size) | 70 static boolean write_all(int fd, const uint8 *data, size_t size) |
| 72 { | 71 { |
| 73 while (size > 0) | 72 while (size > 0) |
| 74 { | 73 { |
| 75 ssize_t written = write(fd, data, size); | 74 ssize_t written = write(fd, data, size); |
| 76 if (written < 0) | 75 if (written < 0) |
| 77 { | 76 { |
| 78 if (errno == EINTR) | 77 if (errno == EINTR) |
| 79 continue; | 78 continue; |
| 80 return false; | 79 return FALSE; |
| 81 } | 80 } |
| 82 data += written; | 81 data += written; |
| 83 size -= (size_t)written; | 82 size -= (size_t)written; |
| 84 } | 83 } |
| 85 return true; | 84 return TRUE; |
| 86 } | 85 } |
| 87 | 86 |
| 88 static int remove_tree(const char *path, unsigned depth) | 87 static int remove_tree(const char *path, unsigned depth) |
| 89 { | 88 { |
| 90 if (depth > 16) | 89 if (depth > 16) |
| 125 } | 124 } |
| 126 closedir(directory); | 125 closedir(directory); |
| 127 return result == 0 ? rmdir(path) : result; | 126 return result == 0 ? rmdir(path) : result; |
| 128 } | 127 } |
| 129 | 128 |
| 130 static bool set_limit(int resource, rlim_t value) | 129 static boolean set_limit(int resource, rlim_t value) |
| 131 { | 130 { |
| 132 struct rlimit limit = { | 131 struct rlimit limit = { |
| 133 .rlim_cur = value, | 132 .rlim_cur = value, |
| 134 .rlim_max = value, | 133 .rlim_max = value, |
| 135 }; | 134 }; |
| 136 return setrlimit(resource, &limit) == 0; | 135 return setrlimit(resource, &limit) == 0; |
| 137 } | 136 } |
| 138 | 137 |
| 139 static bool add_landlock_path_rule( | 138 static boolean add_landlock_path_rule( |
| 140 int ruleset_fd, | 139 int ruleset_fd, |
| 141 const char *path, | 140 const char *path, |
| 142 uint64_t access, | 141 uint64 access, |
| 143 bool required) | 142 boolean required) |
| 144 { | 143 { |
| 145 int path_fd = open(path, O_PATH | O_CLOEXEC); | 144 int path_fd = open(path, O_PATH | O_CLOEXEC); |
| 146 if (path_fd < 0) | 145 if (path_fd < 0) |
| 147 return !required && errno == ENOENT; | 146 return !required && errno == ENOENT; |
| 148 | 147 |
| 149 struct stat status; | 148 struct stat status; |
| 150 bool ok = fstat(path_fd, &status) == 0; | 149 boolean ok = fstat(path_fd, &status) == 0; |
| 151 if (ok && !S_ISDIR(status.st_mode)) | 150 if (ok && !S_ISDIR(status.st_mode)) |
| 152 { | 151 { |
| 153 access &= LANDLOCK_ACCESS_FS_EXECUTE | | 152 access &= LANDLOCK_ACCESS_FS_EXECUTE | |
| 154 LANDLOCK_ACCESS_FS_READ_FILE | | 153 LANDLOCK_ACCESS_FS_READ_FILE | |
| 155 LANDLOCK_ACCESS_FS_WRITE_FILE; | 154 LANDLOCK_ACCESS_FS_WRITE_FILE; |
| 168 0) == 0; | 167 0) == 0; |
| 169 close(path_fd); | 168 close(path_fd); |
| 170 return ok; | 169 return ok; |
| 171 } | 170 } |
| 172 | 171 |
| 173 static bool add_landlock_tree_rules( | 172 static boolean add_landlock_tree_rules( |
| 174 int ruleset_fd, | 173 int ruleset_fd, |
| 175 const char *path, | 174 const char *path, |
| 176 uint64_t access, | 175 uint64 access, |
| 177 unsigned depth) | 176 unsigned depth) |
| 178 { | 177 { |
| 179 if (depth > 32 || | 178 if (depth > 32 || |
| 180 !add_landlock_path_rule(ruleset_fd, path, access, true)) | 179 !add_landlock_path_rule(ruleset_fd, path, access, TRUE)) |
| 181 return false; | 180 return FALSE; |
| 182 | 181 |
| 183 struct stat status; | 182 struct stat status; |
| 184 if (lstat(path, &status) != 0) | 183 if (lstat(path, &status) != 0) |
| 185 return false; | 184 return FALSE; |
| 186 if (!S_ISDIR(status.st_mode)) | 185 if (!S_ISDIR(status.st_mode)) |
| 187 return true; | 186 return TRUE; |
| 188 | 187 |
| 189 DIR *directory = opendir(path); | 188 DIR *directory = opendir(path); |
| 190 if (!directory) | 189 if (!directory) |
| 191 return false; | 190 return FALSE; |
| 192 bool ok = true; | 191 boolean ok = TRUE; |
| 193 struct dirent *entry; | 192 struct dirent *entry; |
| 194 while (ok && (entry = readdir(directory)) != NULL) | 193 while (ok && (entry = readdir(directory)) != NULL) |
| 195 { | 194 { |
| 196 if (strcmp(entry->d_name, ".") == 0 || | 195 if (strcmp(entry->d_name, ".") == 0 || |
| 197 strcmp(entry->d_name, "..") == 0) | 196 strcmp(entry->d_name, "..") == 0) |
| 213 } | 212 } |
| 214 closedir(directory); | 213 closedir(directory); |
| 215 return ok; | 214 return ok; |
| 216 } | 215 } |
| 217 | 216 |
| 218 static bool apply_filesystem_sandbox( | 217 static boolean apply_filesystem_sandbox( |
| 219 const char *job_directory, | 218 const char *job_directory, |
| 220 const char *compiler, | 219 const char *compiler, |
| 221 const char *cache_directory, | 220 const char *cache_directory, |
| 222 const char *fontconfig_sysroot) | 221 const char *fontconfig_sysroot) |
| 223 { | 222 { |
| 225 SYS_landlock_create_ruleset, | 224 SYS_landlock_create_ruleset, |
| 226 NULL, | 225 NULL, |
| 227 0, | 226 0, |
| 228 LANDLOCK_CREATE_RULESET_VERSION); | 227 LANDLOCK_CREATE_RULESET_VERSION); |
| 229 if (abi < 1) | 228 if (abi < 1) |
| 230 return false; | 229 return FALSE; |
| 231 | 230 |
| 232 uint64_t read_access = | 231 uint64 read_access = |
| 233 LANDLOCK_ACCESS_FS_EXECUTE | | 232 LANDLOCK_ACCESS_FS_EXECUTE | |
| 234 LANDLOCK_ACCESS_FS_READ_FILE | | 233 LANDLOCK_ACCESS_FS_READ_FILE | |
| 235 LANDLOCK_ACCESS_FS_READ_DIR; | 234 LANDLOCK_ACCESS_FS_READ_DIR; |
| 236 uint64_t write_access = | 235 uint64 write_access = |
| 237 LANDLOCK_ACCESS_FS_WRITE_FILE | | 236 LANDLOCK_ACCESS_FS_WRITE_FILE | |
| 238 LANDLOCK_ACCESS_FS_REMOVE_DIR | | 237 LANDLOCK_ACCESS_FS_REMOVE_DIR | |
| 239 LANDLOCK_ACCESS_FS_REMOVE_FILE | | 238 LANDLOCK_ACCESS_FS_REMOVE_FILE | |
| 240 LANDLOCK_ACCESS_FS_MAKE_CHAR | | 239 LANDLOCK_ACCESS_FS_MAKE_CHAR | |
| 241 LANDLOCK_ACCESS_FS_MAKE_DIR | | 240 LANDLOCK_ACCESS_FS_MAKE_DIR | |
| 256 SYS_landlock_create_ruleset, | 255 SYS_landlock_create_ruleset, |
| 257 &ruleset, | 256 &ruleset, |
| 258 sizeof(ruleset), | 257 sizeof(ruleset), |
| 259 0); | 258 0); |
| 260 if (ruleset_fd < 0) | 259 if (ruleset_fd < 0) |
| 261 return false; | 260 return FALSE; |
| 262 | 261 |
| 263 bool ok = true; | 262 boolean ok = TRUE; |
| 264 if (ok) | 263 if (ok) |
| 265 ok = add_landlock_path_rule( | 264 ok = add_landlock_path_rule( |
| 266 ruleset_fd, | 265 ruleset_fd, |
| 267 compiler, | 266 compiler, |
| 268 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE, | 267 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE, |
| 269 true); | 268 TRUE); |
| 270 if (ok) | 269 if (ok) |
| 271 ok = add_landlock_tree_rules( | 270 ok = add_landlock_tree_rules( |
| 272 ruleset_fd, | 271 ruleset_fd, |
| 273 cache_directory, | 272 cache_directory, |
| 274 read_access | LANDLOCK_ACCESS_FS_MAKE_DIR, | 273 read_access | LANDLOCK_ACCESS_FS_MAKE_DIR, |
| 282 if (ok) | 281 if (ok) |
| 283 ok = add_landlock_path_rule( | 282 ok = add_landlock_path_rule( |
| 284 ruleset_fd, | 283 ruleset_fd, |
| 285 "/dev/null", | 284 "/dev/null", |
| 286 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, | 285 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, |
| 287 true); | 286 TRUE); |
| 288 if (ok) | 287 if (ok) |
| 289 ok = add_landlock_path_rule( | 288 ok = add_landlock_path_rule( |
| 290 ruleset_fd, | 289 ruleset_fd, |
| 291 "/dev/urandom", | 290 "/dev/urandom", |
| 292 LANDLOCK_ACCESS_FS_READ_FILE, | 291 LANDLOCK_ACCESS_FS_READ_FILE, |
| 293 false); | 292 FALSE); |
| 294 if (ok) | 293 if (ok) |
| 295 ok = add_landlock_path_rule( | 294 ok = add_landlock_path_rule( |
| 296 ruleset_fd, | 295 ruleset_fd, |
| 297 job_directory, | 296 job_directory, |
| 298 read_access | write_access, | 297 read_access | write_access, |
| 299 true); | 298 TRUE); |
| 300 if (ok) | 299 if (ok) |
| 301 ok = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0; | 300 ok = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0; |
| 302 if (ok) | 301 if (ok) |
| 303 ok = syscall( | 302 ok = syscall( |
| 304 SYS_landlock_restrict_self, | 303 SYS_landlock_restrict_self, |
| 306 0) == 0; | 305 0) == 0; |
| 307 close(ruleset_fd); | 306 close(ruleset_fd); |
| 308 return ok; | 307 return ok; |
| 309 } | 308 } |
| 310 | 309 |
| 311 static bool apply_network_sandbox(void) | 310 static boolean apply_network_sandbox(void) |
| 312 { | 311 { |
| 313 if (LATEX_AUDIT_ARCH == 0) | 312 if (LATEX_AUDIT_ARCH == 0) |
| 314 return false; | 313 return FALSE; |
| 315 | 314 |
| 316 struct sock_filter filters[64]; | 315 struct sock_filter filters[64]; |
| 317 size_t count = 0; | 316 size_t count = 0; |
| 318 #define ADD_FILTER(value) filters[count++] = (struct sock_filter)value | 317 #define ADD_FILTER(value) filters[count++] = (struct sock_filter)value |
| 319 #define DENY_SYSCALL(number) \ | 318 #define DENY_SYSCALL(number) \ |
| 467 static int wait_for_compiler( | 466 static int wait_for_compiler( |
| 468 pid_t child, | 467 pid_t child, |
| 469 int output_fd, | 468 int output_fd, |
| 470 char *diagnostics, | 469 char *diagnostics, |
| 471 size_t diagnostics_size, | 470 size_t diagnostics_size, |
| 472 bool *timed_out) | 471 boolean *timed_out) |
| 473 { | 472 { |
| 474 int flags = fcntl(output_fd, F_GETFL, 0); | 473 int flags = fcntl(output_fd, F_GETFL, 0); |
| 475 if (flags >= 0) | 474 if (flags >= 0) |
| 476 fcntl(output_fd, F_SETFL, flags | O_NONBLOCK); | 475 fcntl(output_fd, F_SETFL, flags | O_NONBLOCK); |
| 477 | 476 |
| 478 size_t used = 0; | 477 size_t used = 0; |
| 479 int child_status = 0; | 478 int child_status = 0; |
| 480 bool child_done = false; | 479 boolean child_done = FALSE; |
| 481 struct timespec start; | 480 struct timespec start; |
| 482 clock_gettime(CLOCK_MONOTONIC, &start); | 481 clock_gettime(CLOCK_MONOTONIC, &start); |
| 483 | 482 |
| 484 while (!child_done) | 483 while (!child_done) |
| 485 { | 484 { |
| 497 } | 496 } |
| 498 | 497 |
| 499 pid_t waited = waitpid(child, &child_status, WNOHANG); | 498 pid_t waited = waitpid(child, &child_status, WNOHANG); |
| 500 if (waited == child) | 499 if (waited == child) |
| 501 { | 500 { |
| 502 child_done = true; | 501 child_done = TRUE; |
| 503 break; | 502 break; |
| 504 } | 503 } |
| 505 if (waited < 0 && errno != EINTR) | 504 if (waited < 0 && errno != EINTR) |
| 506 break; | 505 break; |
| 507 | 506 |
| 508 struct timespec now; | 507 struct timespec now; |
| 509 clock_gettime(CLOCK_MONOTONIC, &now); | 508 clock_gettime(CLOCK_MONOTONIC, &now); |
| 510 if (elapsed_seconds(&start, &now) >= LATEX_WALL_TIMEOUT_SECONDS) | 509 if (elapsed_seconds(&start, &now) >= LATEX_WALL_TIMEOUT_SECONDS) |
| 511 { | 510 { |
| 512 *timed_out = true; | 511 *timed_out = TRUE; |
| 513 kill(-child, SIGKILL); | 512 kill(-child, SIGKILL); |
| 514 while (waitpid(child, &child_status, 0) < 0 && errno == EINTR) | 513 while (waitpid(child, &child_status, 0) < 0 && errno == EINTR) |
| 515 { | 514 { |
| 516 } | 515 } |
| 517 child_done = true; | 516 child_done = TRUE; |
| 518 break; | 517 break; |
| 519 } | 518 } |
| 520 | 519 |
| 521 struct pollfd poll_fd = { | 520 struct pollfd poll_fd = { |
| 522 .fd = output_fd, | 521 .fd = output_fd, |
| 540 diagnostics[used] = '\0'; | 539 diagnostics[used] = '\0'; |
| 541 kill(-child, SIGKILL); | 540 kill(-child, SIGKILL); |
| 542 return child_status; | 541 return child_status; |
| 543 } | 542 } |
| 544 | 543 |
| 545 static bool read_pdf( | 544 static boolean read_pdf( |
| 546 const char *path, | 545 const char *path, |
| 547 uint8_t **data, | 546 uint8 **data, |
| 548 size_t *size) | 547 size_t *size) |
| 549 { | 548 { |
| 550 int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); | 549 int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); |
| 551 if (fd < 0) | 550 if (fd < 0) |
| 552 return false; | 551 return FALSE; |
| 553 | 552 |
| 554 struct stat status; | 553 struct stat status; |
| 555 bool ok = fstat(fd, &status) == 0 && | 554 boolean ok = fstat(fd, &status) == 0 && |
| 556 S_ISREG(status.st_mode) && | 555 S_ISREG(status.st_mode) && |
| 557 status.st_size > 4 && | 556 status.st_size > 4 && |
| 558 status.st_size <= LATEX_PDF_MAX_BYTES; | 557 status.st_size <= LATEX_PDF_MAX_BYTES; |
| 559 uint8_t *content = NULL; | 558 uint8 *content = NULL; |
| 560 if (ok) | 559 if (ok) |
| 561 { | 560 { |
| 562 content = malloc((size_t)status.st_size); | 561 content = malloc((size_t)status.st_size); |
| 563 ok = content != NULL; | 562 ok = content != NULL; |
| 564 } | 563 } |
| 571 (size_t)status.st_size - offset); | 570 (size_t)status.st_size - offset); |
| 572 if (count < 0 && errno == EINTR) | 571 if (count < 0 && errno == EINTR) |
| 573 continue; | 572 continue; |
| 574 if (count <= 0) | 573 if (count <= 0) |
| 575 { | 574 { |
| 576 ok = false; | 575 ok = FALSE; |
| 577 break; | 576 break; |
| 578 } | 577 } |
| 579 offset += (size_t)count; | 578 offset += (size_t)count; |
| 580 } | 579 } |
| 581 close(fd); | 580 close(fd); |
| 582 | 581 |
| 583 if (!ok || | 582 if (!ok || |
| 584 memcmp(content, "%PDF-", 5) != 0) | 583 memcmp(content, "%PDF-", 5) != 0) |
| 585 { | 584 { |
| 586 free(content); | 585 free(content); |
| 587 return false; | 586 return FALSE; |
| 588 } | 587 } |
| 589 *data = content; | 588 *data = content; |
| 590 *size = offset; | 589 *size = offset; |
| 591 return true; | 590 return TRUE; |
| 592 } | 591 } |
| 593 | 592 |
| 594 static bool resolve_runtime_path( | 593 static boolean resolve_runtime_path( |
| 595 const char *relative_path, | 594 const char *relative_path, |
| 596 char resolved[PATH_MAX]) | 595 char resolved[PATH_MAX]) |
| 597 { | 596 { |
| 598 if (realpath(relative_path, resolved)) | 597 if (realpath(relative_path, resolved)) |
| 599 return true; | 598 return TRUE; |
| 600 | 599 |
| 601 const char *runfiles = getenv("RUNFILES_DIR"); | 600 const char *runfiles = getenv("RUNFILES_DIR"); |
| 602 const char *workspace = getenv("TEST_WORKSPACE"); | 601 const char *workspace = getenv("TEST_WORKSPACE"); |
| 603 char candidate[PATH_MAX]; | 602 char candidate[PATH_MAX]; |
| 604 if (runfiles && workspace) | 603 if (runfiles && workspace) |
| 611 workspace, | 610 workspace, |
| 612 relative_path); | 611 relative_path); |
| 613 if (length >= 0 && | 612 if (length >= 0 && |
| 614 (size_t)length < sizeof(candidate) && | 613 (size_t)length < sizeof(candidate) && |
| 615 realpath(candidate, resolved)) | 614 realpath(candidate, resolved)) |
| 616 return true; | 615 return TRUE; |
| 617 } | 616 } |
| 618 | 617 |
| 619 char executable[PATH_MAX]; | 618 char executable[PATH_MAX]; |
| 620 ssize_t executable_length = readlink( | 619 ssize_t executable_length = readlink( |
| 621 "/proc/self/exe", | 620 "/proc/self/exe", |
| 622 executable, | 621 executable, |
| 623 sizeof(executable) - 1); | 622 sizeof(executable) - 1); |
| 624 if (executable_length <= 0) | 623 if (executable_length <= 0) |
| 625 return false; | 624 return FALSE; |
| 626 executable[executable_length] = '\0'; | 625 executable[executable_length] = '\0'; |
| 627 | 626 |
| 628 char *last_slash = strrchr(executable, '/'); | 627 char *last_slash = strrchr(executable, '/'); |
| 629 if (last_slash) | 628 if (last_slash) |
| 630 { | 629 { |
| 636 executable, | 635 executable, |
| 637 relative_path); | 636 relative_path); |
| 638 if (length >= 0 && | 637 if (length >= 0 && |
| 639 (size_t)length < sizeof(candidate) && | 638 (size_t)length < sizeof(candidate) && |
| 640 realpath(candidate, resolved)) | 639 realpath(candidate, resolved)) |
| 641 return true; | 640 return TRUE; |
| 642 } | 641 } |
| 643 | 642 |
| 644 char *package = strstr(executable, "/mrjunejune/"); | 643 char *package = strstr(executable, "/mrjunejune/"); |
| 645 if (!package) | 644 if (!package) |
| 646 return false; | 645 return FALSE; |
| 647 *package = '\0'; | 646 *package = '\0'; |
| 648 int length = snprintf( | 647 int length = snprintf( |
| 649 candidate, | 648 candidate, |
| 650 sizeof(candidate), | 649 sizeof(candidate), |
| 651 "%s/%s", | 650 "%s/%s", |
| 654 return length >= 0 && | 653 return length >= 0 && |
| 655 (size_t)length < sizeof(candidate) && | 654 (size_t)length < sizeof(candidate) && |
| 656 realpath(candidate, resolved) != NULL; | 655 realpath(candidate, resolved) != NULL; |
| 657 } | 656 } |
| 658 | 657 |
| 659 Latex_Render_Result Latex_Render(const uint8_t *source, size_t source_size) | 658 Latex_Render_Result Latex_Render(const uint8 *source, size_t source_size) |
| 660 { | 659 { |
| 661 if (!source || source_size == 0) | 660 if (!source || source_size == 0) |
| 662 return result_with_message( | 661 return result_with_message( |
| 663 LATEX_RENDER_INVALID_INPUT, | 662 LATEX_RENDER_INVALID_INPUT, |
| 664 "LaTeX source is required."); | 663 "LaTeX source is required."); |
| 753 return result_with_message( | 752 return result_with_message( |
| 754 LATEX_RENDER_INTERNAL_ERROR, | 753 LATEX_RENDER_INTERNAL_ERROR, |
| 755 "Unable to allocate compiler diagnostics."); | 754 "Unable to allocate compiler diagnostics."); |
| 756 } | 755 } |
| 757 | 756 |
| 758 bool timed_out = false; | 757 boolean timed_out = FALSE; |
| 759 int child_status = wait_for_compiler( | 758 int child_status = wait_for_compiler( |
| 760 child, | 759 child, |
| 761 output_pipe[0], | 760 output_pipe[0], |
| 762 diagnostics, | 761 diagnostics, |
| 763 LATEX_DIAGNOSTICS_MAX_BYTES + 1, | 762 LATEX_DIAGNOSTICS_MAX_BYTES + 1, |
| 839 #else | 838 #else |
| 840 | 839 |
| 841 #include <stdlib.h> | 840 #include <stdlib.h> |
| 842 #include <string.h> | 841 #include <string.h> |
| 843 | 842 |
| 844 Latex_Render_Result Latex_Render(const uint8_t *source, size_t source_size) | 843 Latex_Render_Result Latex_Render(const uint8 *source, size_t source_size) |
| 845 { | 844 { |
| 846 (void)source; | 845 (void)source; |
| 847 (void)source_size; | 846 (void)source_size; |
| 848 const char *message = | 847 const char *message = |
| 849 "The LaTeX security sandbox is only available on Linux."; | 848 "The LaTeX security sandbox is only available on Linux."; |