paste.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <sys/time.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include "tmux.h"
  22. /*
  23. * Set of paste buffers. Note that paste buffer data is not necessarily a C
  24. * string!
  25. */
  26. struct paste_buffer {
  27. char *data;
  28. size_t size;
  29. char *name;
  30. int automatic;
  31. u_int order;
  32. RB_ENTRY(paste_buffer) name_entry;
  33. RB_ENTRY(paste_buffer) time_entry;
  34. };
  35. u_int paste_next_index;
  36. u_int paste_next_order;
  37. u_int paste_num_automatic;
  38. RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
  39. RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
  40. int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
  41. RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
  42. RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
  43. int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
  44. RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
  45. RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
  46. int
  47. paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
  48. {
  49. return (strcmp(a->name, b->name));
  50. }
  51. int
  52. paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
  53. {
  54. if (a->order > b->order)
  55. return (-1);
  56. if (a->order < b->order)
  57. return (1);
  58. return (0);
  59. }
  60. /* Get paste buffer name. */
  61. const char *
  62. paste_buffer_name(struct paste_buffer *pb)
  63. {
  64. return (pb->name);
  65. }
  66. /* Get paste buffer data. */
  67. const char *
  68. paste_buffer_data(struct paste_buffer *pb, size_t *size)
  69. {
  70. if (size != NULL)
  71. *size = pb->size;
  72. return (pb->data);
  73. }
  74. /* Walk paste buffers by name. */
  75. struct paste_buffer *
  76. paste_walk(struct paste_buffer *pb)
  77. {
  78. if (pb == NULL)
  79. return (RB_MIN(paste_time_tree, &paste_by_time));
  80. return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
  81. }
  82. /* Get the most recent automatic buffer. */
  83. struct paste_buffer *
  84. paste_get_top(const char **name)
  85. {
  86. struct paste_buffer *pb;
  87. pb = RB_MIN(paste_time_tree, &paste_by_time);
  88. if (pb == NULL)
  89. return (NULL);
  90. if (name != NULL)
  91. *name = pb->name;
  92. return (pb);
  93. }
  94. /* Get a paste buffer by name. */
  95. struct paste_buffer *
  96. paste_get_name(const char *name)
  97. {
  98. struct paste_buffer pbfind;
  99. if (name == NULL || *name == '\0')
  100. return (NULL);
  101. pbfind.name = (char *)name;
  102. return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
  103. }
  104. /* Free a paste buffer. */
  105. void
  106. paste_free(struct paste_buffer *pb)
  107. {
  108. RB_REMOVE(paste_name_tree, &paste_by_name, pb);
  109. RB_REMOVE(paste_time_tree, &paste_by_time, pb);
  110. if (pb->automatic)
  111. paste_num_automatic--;
  112. free(pb->data);
  113. free(pb->name);
  114. free(pb);
  115. }
  116. /*
  117. * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
  118. * that the caller is responsible for allocating data.
  119. */
  120. void
  121. paste_add(char *data, size_t size)
  122. {
  123. struct paste_buffer *pb, *pb1;
  124. u_int limit;
  125. if (size == 0)
  126. return;
  127. limit = options_get_number(global_options, "buffer-limit");
  128. RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
  129. if (paste_num_automatic < limit)
  130. break;
  131. if (pb->automatic)
  132. paste_free(pb);
  133. }
  134. pb = xmalloc(sizeof *pb);
  135. pb->name = NULL;
  136. do {
  137. free(pb->name);
  138. xasprintf(&pb->name, "buffer%04u", paste_next_index);
  139. paste_next_index++;
  140. } while (paste_get_name(pb->name) != NULL);
  141. pb->data = data;
  142. pb->size = size;
  143. pb->automatic = 1;
  144. paste_num_automatic++;
  145. pb->order = paste_next_order++;
  146. RB_INSERT(paste_name_tree, &paste_by_name, pb);
  147. RB_INSERT(paste_time_tree, &paste_by_time, pb);
  148. }
  149. /* Rename a paste buffer. */
  150. int
  151. paste_rename(const char *oldname, const char *newname, char **cause)
  152. {
  153. struct paste_buffer *pb, *pb_new;
  154. if (cause != NULL)
  155. *cause = NULL;
  156. if (oldname == NULL || *oldname == '\0') {
  157. if (cause != NULL)
  158. *cause = xstrdup("no buffer");
  159. return (-1);
  160. }
  161. if (newname == NULL || *newname == '\0') {
  162. if (cause != NULL)
  163. *cause = xstrdup("new name is empty");
  164. return (-1);
  165. }
  166. pb = paste_get_name(oldname);
  167. if (pb == NULL) {
  168. if (cause != NULL)
  169. xasprintf(cause, "no buffer %s", oldname);
  170. return (-1);
  171. }
  172. pb_new = paste_get_name(newname);
  173. if (pb_new != NULL) {
  174. if (cause != NULL)
  175. xasprintf(cause, "buffer %s already exists", newname);
  176. return (-1);
  177. }
  178. RB_REMOVE(paste_name_tree, &paste_by_name, pb);
  179. free(pb->name);
  180. pb->name = xstrdup(newname);
  181. if (pb->automatic)
  182. paste_num_automatic--;
  183. pb->automatic = 0;
  184. RB_INSERT(paste_name_tree, &paste_by_name, pb);
  185. return (0);
  186. }
  187. /*
  188. * Add or replace an item in the store. Note that the caller is responsible for
  189. * allocating data.
  190. */
  191. int
  192. paste_set(char *data, size_t size, const char *name, char **cause)
  193. {
  194. struct paste_buffer *pb, *old;
  195. if (cause != NULL)
  196. *cause = NULL;
  197. if (size == 0) {
  198. free(data);
  199. return (0);
  200. }
  201. if (name == NULL) {
  202. paste_add(data, size);
  203. return (0);
  204. }
  205. if (*name == '\0') {
  206. if (cause != NULL)
  207. *cause = xstrdup("empty buffer name");
  208. return (-1);
  209. }
  210. pb = xmalloc(sizeof *pb);
  211. pb->name = xstrdup(name);
  212. pb->data = data;
  213. pb->size = size;
  214. pb->automatic = 0;
  215. pb->order = paste_next_order++;
  216. if ((old = paste_get_name(name)) != NULL)
  217. paste_free(old);
  218. RB_INSERT(paste_name_tree, &paste_by_name, pb);
  219. RB_INSERT(paste_time_tree, &paste_by_time, pb);
  220. return (0);
  221. }
  222. /* Convert start of buffer into a nice string. */
  223. char *
  224. paste_make_sample(struct paste_buffer *pb)
  225. {
  226. char *buf;
  227. size_t len, used;
  228. const int flags = VIS_OCTAL|VIS_TAB|VIS_NL;
  229. const size_t width = 200;
  230. len = pb->size;
  231. if (len > width)
  232. len = width;
  233. buf = xreallocarray(NULL, len, 4 + 4);
  234. used = utf8_strvis(buf, pb->data, len, flags);
  235. if (pb->size > width || used > width)
  236. strlcpy(buf + width, "...", 4);
  237. return (buf);
  238. }