io.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #define _GNU_SOURCE 1 // Needed for getline to avoid implicit function declaration, see `man 3 getline`
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <readline/readline.h>
  6. #include <readline/history.h>
  7. #include <wordexp.h> // Needed to expand out history path eg involving '~'
  8. #include <errno.h>
  9. #include "io.h"
  10. #include "parse.h"
  11. #include "roll-engine.h"
  12. void getline_wrapper(struct parse_tree *t, struct arguments *args) {
  13. size_t bufsize = 0;
  14. char *line = NULL;
  15. errno = 0;
  16. int getline_retval = getline(&line, &bufsize, args->ist);
  17. if(line == NULL || line == 0 || feof(args->ist) || errno != 0 || getline_retval < 0) {
  18. if(errno != 0) {
  19. printf("Error %d (%s) getting line for reading.\n", errno, strerror(errno));
  20. }
  21. t->quit = true;
  22. goto end_of_getline;
  23. }
  24. parse(t, line, bufsize);
  25. t->current = t;
  26. while(t->current != NULL) {
  27. if(!t->current->suppress) {
  28. roll(t->current);
  29. }
  30. t->current = t->current->next;
  31. }
  32. end_of_getline:
  33. free(line);
  34. }
  35. void no_read(struct parse_tree *t, struct arguments *args) {
  36. printf("Unknown mode. Not reading any lines.\n");
  37. }
  38. void readline_wrapper(struct parse_tree *t, struct arguments *args) {
  39. char *line = readline(args->prompt);
  40. if(line == NULL || line == 0) {
  41. printf("\n");
  42. t->quit = true;
  43. goto end_of_readline;
  44. }
  45. size_t bufsize = strlen(line);
  46. int parse_success = parse(t, line, bufsize);
  47. t->current = t;
  48. while(t->current != NULL) {
  49. if(!t->current->suppress) {
  50. roll(t->current);
  51. }
  52. t->current = t->current->next;
  53. }
  54. if(0 == parse_success) {
  55. add_history(line);
  56. }
  57. end_of_readline:
  58. free(line);
  59. }
  60. void read_history_wrapper(const char *filename) {
  61. errno = 0;
  62. wordexp_t matched_paths;
  63. int we_ret = wordexp(filename, &matched_paths, 0);
  64. switch(we_ret) {
  65. case WRDE_NOSPACE:
  66. {
  67. fprintf(stderr, "Insufficient memory to expand path '%s'.\n", filename);
  68. }
  69. break;
  70. default:
  71. break;
  72. }
  73. char **path = matched_paths.we_wordv;
  74. int path_num = 0;
  75. for(path_num = 0; path_num < matched_paths.we_wordc; ++path_num) {
  76. errno = 0;
  77. read_history(path[path_num]);
  78. switch(errno) {
  79. case 0:
  80. break;
  81. case 2:
  82. // Ignore missing file error, it just means we're running interactively for the first time.
  83. break;
  84. default:
  85. fprintf(stderr, "Error %d (%s) opening %s for reading.\n", errno, strerror(errno), path[path_num]);
  86. }
  87. }
  88. wordfree(&matched_paths);
  89. }
  90. void write_history_wrapper(const char *filename) {
  91. errno = 0;
  92. wordexp_t matched_paths;
  93. int we_ret = wordexp(filename, &matched_paths, 0);
  94. switch(we_ret) {
  95. case WRDE_NOSPACE:
  96. {
  97. fprintf(stderr, "Insufficient memory to expand path '%s'.\n", filename);
  98. }
  99. break;
  100. default:
  101. break;
  102. }
  103. char **path = matched_paths.we_wordv;
  104. int path_num = 0;
  105. for(path_num = 0; path_num < matched_paths.we_wordc; ++path_num) {
  106. errno = 0;
  107. write_history(path[path_num]);
  108. switch(errno) {
  109. case 0:
  110. break;
  111. case 2:
  112. fprintf(stderr, "Created new history file '%s'.\n", path[path_num]);
  113. break;
  114. case 22:
  115. /*
  116. It seems that when the history file already exists,
  117. write_history emits an erroneous "Invalid argument"
  118. despite successfully replacing history file contents.
  119. */
  120. break;
  121. default:
  122. fprintf(stderr, "Error %d (%s) opening %s for writing.\n", errno, strerror(errno), path[path_num]);
  123. }
  124. }
  125. wordfree(&matched_paths);
  126. }