read.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <reproc/reproc.h>
  4. // Prints the output of the given command using `reproc_read`. Usually, using
  5. // `reproc_run` or `reproc_drain` is a better solution when dealing with a
  6. // single child process.
  7. int main(int argc, const char **argv)
  8. {
  9. (void) argc;
  10. // `reproc_t` stores necessary information between calls to reproc's API.
  11. reproc_t *process = NULL;
  12. char *output = NULL;
  13. size_t size = 0;
  14. int r = REPROC_ENOMEM;
  15. process = reproc_new();
  16. if (process == NULL) {
  17. goto finish;
  18. }
  19. // `reproc_start` takes a child process instance (`reproc_t`), argv and
  20. // a set of options including the working directory and environment of the
  21. // child process. If the working directory is `NULL` the working directory of
  22. // the parent process is used. If the environment is `NULL`, the environment
  23. // of the parent process is used.
  24. r = reproc_start(process, argv + 1, (reproc_options){ 0 });
  25. // On failure, reproc's API functions return a negative `errno` (POSIX) or
  26. // `GetLastError` (Windows) style error code. To check against common error
  27. // codes, reproc provides cross platform constants such as `REPROC_EPIPE` and
  28. // `REPROC_ETIMEDOUT`.
  29. if (r < 0) {
  30. goto finish;
  31. }
  32. // Close the stdin stream since we're not going to write any input to the
  33. // child process.
  34. r = reproc_close(process, REPROC_STREAM_IN);
  35. if (r < 0) {
  36. goto finish;
  37. }
  38. // Read the entire output of the child process. I've found this pattern to be
  39. // the most readable when reading the entire output of a child process. The
  40. // while loop keeps running until an error occurs in `reproc_read` (the child
  41. // process closing its output stream is also reported as an error).
  42. for (;;) {
  43. uint8_t buffer[4096];
  44. r = reproc_read(process, REPROC_STREAM_OUT, buffer, sizeof(buffer));
  45. if (r < 0) {
  46. break;
  47. }
  48. // On success, `reproc_read` returns the amount of bytes read.
  49. size_t bytes_read = (size_t) r;
  50. // Increase the size of `output` to make sure it can hold the new output.
  51. // This is definitely not the most performant way to grow a buffer so keep
  52. // that in mind. Add 1 to size to leave space for the NUL terminator which
  53. // isn't included in `output_size`.
  54. char *result = realloc(output, size + bytes_read + 1);
  55. if (result == NULL) {
  56. r = REPROC_ENOMEM;
  57. goto finish;
  58. }
  59. output = result;
  60. // Copy new data into `output`.
  61. memcpy(output + size, buffer, bytes_read);
  62. output[size + bytes_read] = '\0';
  63. size += bytes_read;
  64. }
  65. // Check that the while loop stopped because the output stream of the child
  66. // process was closed and not because of any other error.
  67. if (r != REPROC_EPIPE) {
  68. goto finish;
  69. }
  70. printf("%s", output);
  71. // Wait for the process to exit. This should always be done since some systems
  72. // (POSIX) don't clean up system resources allocated to a child process until
  73. // the parent process explicitly waits for it after it has exited.
  74. r = reproc_wait(process, REPROC_INFINITE);
  75. if (r < 0) {
  76. goto finish;
  77. }
  78. finish:
  79. free(output);
  80. // Clean up all the resources allocated to the child process (including the
  81. // memory allocated by `reproc_new`). Unless custom stop actions are passed to
  82. // `reproc_start`, `reproc_destroy` will first wait indefinitely for the child
  83. // process to exit.
  84. reproc_destroy(process);
  85. if (r < 0) {
  86. fprintf(stderr, "%s\n", reproc_strerror(r));
  87. }
  88. return abs(r);
  89. }