IRC client
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.

tab.c 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <err.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <sysexits.h>
  20. #include "chat.h"
  21. static struct Entry {
  22. struct Tag tag;
  23. char *word;
  24. struct Entry *prev;
  25. struct Entry *next;
  26. } *head;
  27. static void prepend(struct Entry *entry) {
  28. entry->prev = NULL;
  29. entry->next = head;
  30. if (head) head->prev = entry;
  31. head = entry;
  32. }
  33. static void unlink(struct Entry *entry) {
  34. if (entry->prev) entry->prev->next = entry->next;
  35. if (entry->next) entry->next->prev = entry->prev;
  36. if (head == entry) head = entry->next;
  37. }
  38. static void touch(struct Entry *entry) {
  39. if (head == entry) return;
  40. unlink(entry);
  41. prepend(entry);
  42. }
  43. static struct Entry *find(struct Tag tag, const char *word) {
  44. for (struct Entry *entry = head; entry; entry = entry->next) {
  45. if (entry->tag.id != tag.id) continue;
  46. if (strcmp(entry->word, word)) continue;
  47. return entry;
  48. }
  49. return NULL;
  50. }
  51. static void add(struct Tag tag, const char *word) {
  52. struct Entry *entry = malloc(sizeof(*entry));
  53. if (!entry) err(EX_OSERR, "malloc");
  54. entry->tag = tag;
  55. entry->word = strdup(word);
  56. if (!entry->word) err(EX_OSERR, "strdup");
  57. prepend(entry);
  58. }
  59. void tabTouch(struct Tag tag, const char *word) {
  60. struct Entry *entry = find(tag, word);
  61. if (entry) {
  62. touch(entry);
  63. } else {
  64. add(tag, word);
  65. }
  66. }
  67. void tabAdd(struct Tag tag, const char *word) {
  68. if (!find(tag, word)) add(tag, word);
  69. }
  70. void tabReplace(struct Tag tag, const char *prev, const char *next) {
  71. struct Entry *entry = find(tag, prev);
  72. if (!entry) return;
  73. touch(entry);
  74. free(entry->word);
  75. entry->word = strdup(next);
  76. if (!entry->word) err(EX_OSERR, "strdup");
  77. }
  78. static struct Entry *iter;
  79. void tabRemove(struct Tag tag, const char *word) {
  80. for (struct Entry *entry = head; entry; entry = entry->next) {
  81. if (entry->tag.id != tag.id) continue;
  82. if (strcmp(entry->word, word)) continue;
  83. if (iter == entry) iter = entry->prev;
  84. unlink(entry);
  85. free(entry->word);
  86. free(entry);
  87. return;
  88. }
  89. }
  90. void tabClear(struct Tag tag) {
  91. for (struct Entry *entry = head; entry; entry = entry->next) {
  92. if (entry->tag.id != tag.id) continue;
  93. if (iter == entry) iter = entry->prev;
  94. unlink(entry);
  95. free(entry->word);
  96. free(entry);
  97. }
  98. }
  99. struct Tag tabTag(const char *word) {
  100. struct Entry *start = (iter ? iter->next : head);
  101. for (struct Entry *entry = start; entry; entry = entry->next) {
  102. if (strcmp(entry->word, word)) continue;
  103. iter = entry;
  104. return entry->tag;
  105. }
  106. iter = NULL;
  107. return TagNone;
  108. }
  109. const char *tabNext(struct Tag tag, const char *prefix) {
  110. size_t len = strlen(prefix);
  111. struct Entry *start = (iter ? iter->next : head);
  112. for (struct Entry *entry = start; entry; entry = entry->next) {
  113. if (entry->tag.id != TagNone.id && entry->tag.id != tag.id) continue;
  114. if (strncasecmp(entry->word, prefix, len)) continue;
  115. iter = entry;
  116. return entry->word;
  117. }
  118. if (!iter) return NULL;
  119. iter = NULL;
  120. return tabNext(tag, prefix);
  121. }
  122. void tabAccept(void) {
  123. if (iter) touch(iter);
  124. iter = NULL;
  125. }
  126. void tabReject(void) {
  127. iter = NULL;
  128. }