external_xkb_compilers.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. * Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #pragma once
  6. #include "config.h"
  7. #include <stdlib.h>
  8. #include <limits.h>
  9. #include <stdio.h>
  10. #include <spawn.h>
  11. #include <unistd.h>
  12. #include <assert.h>
  13. #include <signal.h>
  14. #include <sys/types.h>
  15. #include <sys/wait.h>
  16. #include <getopt.h>
  17. #include "utils.h"
  18. #include "darray.h"
  19. #define PIPE_WRITE 1
  20. #define PIPE_READ 0
  21. #define SETUP_FAILURE 99
  22. int
  23. compile_with(const char* compiler_name, const char* const *compiler_argv,
  24. const char *keymap_in, size_t keymap_size_in,
  25. char **keymap_out, size_t *keymap_size_out);
  26. int
  27. compile_with(const char* compiler_name, const char* const *compiler_argv,
  28. const char *keymap_in, size_t keymap_size_in,
  29. char **keymap_out, size_t *keymap_size_out)
  30. {
  31. int ret = EXIT_FAILURE;
  32. assert(keymap_in);
  33. /* Prepare parameters */
  34. char *envp[] = { NULL };
  35. /* Prepare input */
  36. int stdin_pipe[2] = {0};
  37. int stdout_pipe[2] = {0};
  38. posix_spawn_file_actions_t action;
  39. if (pipe(stdin_pipe) == -1) {
  40. perror("stdin pipe");
  41. ret = SETUP_FAILURE;
  42. goto pipe_error;
  43. }
  44. if (posix_spawn_file_actions_init(&action)) {
  45. perror("spawn_file_actions_init error");
  46. goto posix_spawn_file_actions_init_error;
  47. }
  48. /* Make spawned process close unused write-end of pipe, else it will not
  49. * receive EOF when write-end of the pipe is closed below and it will result
  50. * in a deadlock. */
  51. if (posix_spawn_file_actions_addclose(&action, stdin_pipe[PIPE_WRITE]))
  52. goto posix_spawn_file_actions_error;
  53. /* Make spawned process replace stdin with read end of the pipe */
  54. if (posix_spawn_file_actions_adddup2(&action, stdin_pipe[PIPE_READ], STDIN_FILENO))
  55. goto posix_spawn_file_actions_error;
  56. /* Prepare stdout */
  57. if (pipe(stdout_pipe) == -1) {
  58. perror("stdout pipe");
  59. ret = SETUP_FAILURE;
  60. goto pipe_error;
  61. }
  62. if (posix_spawn_file_actions_addclose(&action, stdout_pipe[PIPE_READ]))
  63. goto posix_spawn_file_actions_error;
  64. if (posix_spawn_file_actions_adddup2(&action, stdout_pipe[PIPE_WRITE], STDOUT_FILENO))
  65. goto posix_spawn_file_actions_error;
  66. /* Launch compiler */
  67. pid_t compiler_pid;
  68. ret = posix_spawnp(&compiler_pid, compiler_name, &action, NULL,
  69. (char* const*)compiler_argv, envp);
  70. if (ret != 0) {
  71. errno = ret;
  72. perror("posix_spawnp XKB compiler failed");
  73. goto posix_spawn_file_actions_init_error;
  74. }
  75. /* Close unused read-end of pipe */
  76. close(stdin_pipe[PIPE_READ]);
  77. close(stdout_pipe[PIPE_WRITE]);
  78. ssize_t ret2 = write(stdin_pipe[PIPE_WRITE], keymap_in, keymap_size_in);
  79. /* Close write-end of the pipe, to emit EOF */
  80. close(stdin_pipe[PIPE_WRITE]);
  81. if (ret2 == -1) {
  82. perror("Cannot write keymap to stdin");
  83. kill(compiler_pid, SIGTERM);
  84. }
  85. /* Capture stdout */
  86. const size_t buffer_size = 1024;
  87. darray_char stdout = darray_new();
  88. darray_resize0(stdout, buffer_size);
  89. size_t size = 0;
  90. ssize_t count;
  91. while ((count = read(stdout_pipe[PIPE_READ], darray_items(stdout) + size,
  92. buffer_size)) > 0) {
  93. size += (size_t) count;
  94. darray_resize0(stdout, size + buffer_size);
  95. }
  96. close(stdout_pipe[PIPE_READ]);
  97. /* Ensure we get a NULL-terminated string */
  98. if (size > 0 && darray_item(stdout, size - 1) != '\0')
  99. darray_resize0(stdout, size + 1);
  100. darray_steal(stdout, keymap_out, NULL);
  101. *keymap_size_out = size;
  102. /* Wait for compiler to complete */
  103. int status;
  104. ret = waitpid(compiler_pid, &status, 0);
  105. ret = (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
  106. ? SETUP_FAILURE
  107. : EXIT_SUCCESS;
  108. goto cleanup;
  109. posix_spawn_file_actions_error:
  110. perror("posix_spawn_file_actions_* error");
  111. posix_spawn_file_actions_init_error:
  112. close(stdin_pipe[PIPE_WRITE]);
  113. close(stdin_pipe[PIPE_READ]);
  114. close(stdout_pipe[PIPE_READ]);
  115. close(stdout_pipe[PIPE_WRITE]);
  116. ret = SETUP_FAILURE;
  117. cleanup:
  118. posix_spawn_file_actions_destroy(&action);
  119. pipe_error:
  120. return ret;
  121. }
  122. int
  123. compile_with_xkbcomp(const char* display, const char *include_path,
  124. const char *keymap_in, size_t keymap_size_in,
  125. char **keymap_out, size_t *keymap_size_out);
  126. int
  127. compile_with_xkbcomp(const char* display, const char *include_path,
  128. const char *keymap_in, size_t keymap_size_in,
  129. char **keymap_out, size_t *keymap_size_out)
  130. {
  131. const bool has_display = !isempty(display);
  132. const char* const out = (has_display) ? display : "-";
  133. /* Prepare xkbcomp parameters */
  134. char include_path_arg[PATH_MAX+2] = "-I";
  135. const char *xkbcomp_argv[] = {
  136. "xkbcomp", "-I" /* reset include path*/, include_path_arg,
  137. "-opt", "g", "-w", "10", "-xkb", "-" /* stdin */, out, NULL
  138. };
  139. if (include_path) {
  140. int ret = snprintf(include_path_arg, ARRAY_SIZE(include_path_arg),
  141. "-I%s", include_path);
  142. if (ret < 0 || ret >= (int)ARRAY_SIZE(include_path_arg)) {
  143. return SETUP_FAILURE;
  144. }
  145. }
  146. return compile_with("xkbcomp", xkbcomp_argv,
  147. keymap_in, keymap_size_in,
  148. keymap_out, keymap_size_out);
  149. }
  150. int
  151. compile_with_kbvm(const char *include_path,
  152. const char *keymap_in, size_t keymap_size_in,
  153. char **keymap_out, size_t *keymap_size_out);
  154. int
  155. compile_with_kbvm(const char *include_path,
  156. const char *keymap_in, size_t keymap_size_in,
  157. char **keymap_out, size_t *keymap_size_out)
  158. {
  159. /* Prepare xkbcomp parameters */
  160. const char *kbvm_argv[] = {
  161. "kbvm", "compile-xkb", "-", NULL, NULL, NULL, NULL
  162. };
  163. if (include_path) {
  164. kbvm_argv[3] = "--no-default-includes";
  165. kbvm_argv[4] = "--append-include";
  166. kbvm_argv[5] = include_path;
  167. }
  168. return compile_with("kbvm", kbvm_argv,
  169. keymap_in, keymap_size_in,
  170. keymap_out, keymap_size_out);
  171. }