You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

194 lines
4.6 KiB

  1. #include "extract.h"
  2. #include <stdlib.h>
  3. #define LOG_MODULE "extract"
  4. #define LOG_ENABLE_DBG 1
  5. #include "log.h"
  6. struct extraction_context {
  7. wchar_t *buf;
  8. size_t size;
  9. size_t idx;
  10. size_t empty_count;
  11. size_t newline_count;
  12. bool failed;
  13. const struct row *last_row;
  14. const struct cell *last_cell;
  15. enum selection_kind selection_kind;
  16. };
  17. struct extraction_context *
  18. extract_begin(enum selection_kind kind)
  19. {
  20. struct extraction_context *ctx = malloc(sizeof(*ctx));
  21. if (unlikely(ctx == NULL)) {
  22. LOG_ERRNO("malloc() failed");
  23. return NULL;
  24. }
  25. *ctx = (struct extraction_context){
  26. .selection_kind = kind,
  27. };
  28. return ctx;
  29. }
  30. static bool
  31. ensure_size(struct extraction_context *ctx, size_t additional_chars)
  32. {
  33. while (ctx->size < ctx->idx + additional_chars) {
  34. size_t new_size = ctx->size == 0 ? 512 : ctx->size * 2;
  35. wchar_t *new_buf = realloc(ctx->buf, new_size * sizeof(wchar_t));
  36. if (new_buf == NULL)
  37. return false;
  38. ctx->buf = new_buf;
  39. ctx->size = new_size;
  40. }
  41. assert(ctx->size >= ctx->idx + additional_chars);
  42. return true;
  43. }
  44. bool
  45. extract_finish(struct extraction_context *ctx, char **text, size_t *len)
  46. {
  47. bool ret = false;
  48. if (text == NULL)
  49. return false;
  50. *text = NULL;
  51. if (len != NULL)
  52. *len = 0;
  53. if (ctx->failed)
  54. goto out;
  55. if (ctx->idx == 0) {
  56. /* Selection of empty cells only */
  57. if (!ensure_size(ctx, 1))
  58. goto out;
  59. ctx->buf[ctx->idx] = L'\0';
  60. } else {
  61. assert(ctx->idx > 0);
  62. assert(ctx->idx < ctx->size);
  63. if (ctx->buf[ctx->idx - 1] == L'\n')
  64. ctx->buf[ctx->idx - 1] = L'\0';
  65. else
  66. ctx->buf[ctx->idx] = L'\0';
  67. }
  68. size_t _len = wcstombs(NULL, ctx->buf, 0);
  69. if (_len == (size_t)-1) {
  70. LOG_ERRNO("failed to convert selection to UTF-8");
  71. goto out;
  72. }
  73. *text = malloc(_len + 1);
  74. if (unlikely(text == NULL)) {
  75. LOG_ERRNO("malloc() failed");
  76. goto out;
  77. }
  78. wcstombs(*text, ctx->buf, _len + 1);
  79. if (len != NULL)
  80. *len = _len;
  81. ret = true;
  82. out:
  83. free(ctx->buf);
  84. free(ctx);
  85. return ret;
  86. }
  87. bool
  88. extract_one(const struct terminal *term, const struct row *row,
  89. const struct cell *cell, int col, void *context)
  90. {
  91. struct extraction_context *ctx = context;
  92. if (cell->wc == CELL_MULT_COL_SPACER)
  93. return true;
  94. if (ctx->last_row != NULL && row != ctx->last_row) {
  95. /* New row - determine if we should insert a newline or not */
  96. if (ctx->selection_kind == SELECTION_NONE ||
  97. ctx->selection_kind == SELECTION_NORMAL)
  98. {
  99. if (ctx->last_row->linebreak ||
  100. ctx->empty_count > 0 ||
  101. cell->wc == 0)
  102. {
  103. /* Row has a hard linebreak, or either last cell or
  104. * current cell is empty */
  105. /* Don't emit newline just yet - only if there are
  106. * non-empty cells following it */
  107. ctx->newline_count++;
  108. ctx->empty_count = 0;
  109. }
  110. }
  111. else if (ctx->selection_kind == SELECTION_BLOCK) {
  112. /* Always insert a linebreak */
  113. if (!ensure_size(ctx, 1))
  114. goto err;
  115. ctx->buf[ctx->idx++] = L'\n';
  116. ctx->empty_count = 0;
  117. }
  118. }
  119. if (cell->wc == 0) {
  120. ctx->empty_count++;
  121. ctx->last_row = row;
  122. ctx->last_cell = cell;
  123. return true;
  124. }
  125. /* Insert pending newlines, and replace empty cells with spaces */
  126. if (!ensure_size(ctx, ctx->newline_count + ctx->empty_count))
  127. goto err;
  128. for (size_t i = 0; i < ctx->newline_count; i++)
  129. ctx->buf[ctx->idx++] = L'\n';
  130. for (size_t i = 0; i < ctx->empty_count; i++)
  131. ctx->buf[ctx->idx++] = L' ';
  132. ctx->newline_count = 0;
  133. ctx->empty_count = 0;
  134. if (cell->wc >= CELL_COMB_CHARS_LO &&
  135. cell->wc < (CELL_COMB_CHARS_LO + term->composed_count))
  136. {
  137. const struct composed *composed
  138. = &term->composed[cell->wc - CELL_COMB_CHARS_LO];
  139. if (!ensure_size(ctx, 1 + composed->count))
  140. goto err;
  141. ctx->buf[ctx->idx++] = composed->base;
  142. for (size_t i = 0; i < composed->count; i++)
  143. ctx->buf[ctx->idx++] = composed->combining[i];
  144. }
  145. else {
  146. if (!ensure_size(ctx, 1))
  147. goto err;
  148. ctx->buf[ctx->idx++] = cell->wc;
  149. }
  150. ctx->last_row = row;
  151. ctx->last_cell = cell;
  152. return true;
  153. err:
  154. ctx->failed = true;
  155. return false;
  156. }