RichString.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. htop - RichString.c
  3. (C) 2004,2011 Hisham H. Muhammad
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "RichString.h"
  9. #include <assert.h>
  10. #include <ctype.h>
  11. #include <limits.h> // IWYU pragma: keep
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "Macros.h"
  15. #include "XUtils.h"
  16. #define charBytes(n) (sizeof(CharType) * (n))
  17. static void RichString_extendLen(RichString* this, int len) {
  18. if (this->chptr == this->chstr) {
  19. // String is in internal buffer
  20. if (len > RICHSTRING_MAXLEN) {
  21. // Copy from internal buffer to allocated string
  22. this->chptr = xMalloc(charBytes(len + 1));
  23. memcpy(this->chptr, this->chstr, charBytes(this->chlen));
  24. } else {
  25. // Still fits in internal buffer, do nothing
  26. assert(this->chlen <= RICHSTRING_MAXLEN);
  27. }
  28. } else {
  29. // String is managed externally
  30. if (len > RICHSTRING_MAXLEN) {
  31. // Just reallocate the buffer accordingly
  32. this->chptr = xRealloc(this->chptr, charBytes(len + 1));
  33. } else {
  34. // Move string into internal buffer and free resources
  35. memcpy(this->chstr, this->chptr, charBytes(len));
  36. free(this->chptr);
  37. this->chptr = this->chstr;
  38. }
  39. }
  40. RichString_setChar(this, len, 0);
  41. this->chlen = len;
  42. }
  43. static void RichString_setLen(RichString* this, int len) {
  44. if (len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) {
  45. RichString_setChar(this, len, 0);
  46. this->chlen = len;
  47. } else {
  48. RichString_extendLen(this, len);
  49. }
  50. }
  51. void RichString_rewind(RichString* this, int count) {
  52. RichString_setLen(this, this->chlen - count);
  53. }
  54. #ifdef HAVE_LIBNCURSESW
  55. static size_t mbstowcs_nonfatal(wchar_t* restrict dest, const char* restrict src, size_t n) {
  56. size_t written = 0;
  57. mbstate_t ps = { 0 };
  58. bool broken = false;
  59. while (n > 0) {
  60. size_t ret = mbrtowc(dest, src, n, &ps);
  61. if (ret == (size_t)-1 || ret == (size_t)-2) {
  62. if (!broken) {
  63. broken = true;
  64. *dest++ = L'\xFFFD';
  65. written++;
  66. }
  67. src++;
  68. n--;
  69. continue;
  70. }
  71. broken = false;
  72. if (ret == 0) {
  73. break;
  74. }
  75. dest++;
  76. written++;
  77. src += ret;
  78. n -= ret;
  79. }
  80. return written;
  81. }
  82. static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
  83. wchar_t data[len];
  84. len = mbstowcs_nonfatal(data, data_c, len);
  85. if (len <= 0)
  86. return 0;
  87. int newLen = from + len;
  88. RichString_setLen(this, newLen);
  89. for (int i = from, j = 0; i < newLen; i++, j++) {
  90. this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : L'\xFFFD') } };
  91. }
  92. return len;
  93. }
  94. int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
  95. wchar_t data[len];
  96. len = mbstowcs_nonfatal(data, data_c, len);
  97. if (len <= 0)
  98. return 0;
  99. int from = this->chlen;
  100. int newLen = from + len;
  101. RichString_setLen(this, newLen);
  102. int columnsWritten = 0;
  103. int pos = from;
  104. for (int j = 0; j < len; j++) {
  105. wchar_t c = iswprint(data[j]) ? data[j] : L'\xFFFD';
  106. int cwidth = wcwidth(c);
  107. if (cwidth > *columns)
  108. break;
  109. *columns -= cwidth;
  110. columnsWritten += cwidth;
  111. this->chptr[pos] = (CharType) { .attr = attrs & 0xffffff, .chars = { c, '\0' } };
  112. pos++;
  113. }
  114. RichString_setLen(this, pos);
  115. *columns = columnsWritten;
  116. return pos - from;
  117. }
  118. static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
  119. int newLen = from + len;
  120. RichString_setLen(this, newLen);
  121. for (int i = from, j = 0; i < newLen; i++, j++) {
  122. assert((unsigned char)data[j] <= SCHAR_MAX);
  123. this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint((unsigned char)data[j]) ? data[j] : L'\xFFFD') } };
  124. }
  125. return len;
  126. }
  127. inline void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
  128. int end = CLAMP(start + charcount, 0, this->chlen);
  129. for (int i = start; i < end; i++) {
  130. this->chptr[i].attr = attrs;
  131. }
  132. }
  133. void RichString_appendChr(RichString* this, int attrs, char c, int count) {
  134. int from = this->chlen;
  135. int newLen = from + count;
  136. RichString_setLen(this, newLen);
  137. for (int i = from; i < newLen; i++) {
  138. this->chptr[i] = (CharType) { .attr = attrs, .chars = { c, 0 } };
  139. }
  140. }
  141. int RichString_findChar(const RichString* this, char c, int start) {
  142. const wchar_t wc = btowc(c);
  143. const cchar_t* ch = this->chptr + start;
  144. for (int i = start; i < this->chlen; i++) {
  145. if (ch->chars[0] == wc)
  146. return i;
  147. ch++;
  148. }
  149. return -1;
  150. }
  151. #else /* HAVE_LIBNCURSESW */
  152. static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
  153. int newLen = from + len;
  154. RichString_setLen(this, newLen);
  155. for (int i = from, j = 0; i < newLen; i++, j++) {
  156. this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
  157. }
  158. this->chptr[newLen] = 0;
  159. return len;
  160. }
  161. int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
  162. int written = RichString_writeFromWide(this, attrs, data_c, this->chlen, MINIMUM(len, *columns));
  163. *columns = written;
  164. return written;
  165. }
  166. static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
  167. return RichString_writeFromWide(this, attrs, data_c, from, len);
  168. }
  169. void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
  170. int end = CLAMP(start + charcount, 0, this->chlen);
  171. for (int i = start; i < end; i++) {
  172. this->chptr[i] = (this->chptr[i] & 0xff) | attrs;
  173. }
  174. }
  175. void RichString_appendChr(RichString* this, int attrs, char c, int count) {
  176. int from = this->chlen;
  177. int newLen = from + count;
  178. RichString_setLen(this, newLen);
  179. for (int i = from; i < newLen; i++) {
  180. this->chptr[i] = c | attrs;
  181. }
  182. }
  183. int RichString_findChar(const RichString* this, char c, int start) {
  184. const chtype* ch = this->chptr + start;
  185. for (int i = start; i < this->chlen; i++) {
  186. if ((*ch & 0xff) == (chtype) c)
  187. return i;
  188. ch++;
  189. }
  190. return -1;
  191. }
  192. #endif /* HAVE_LIBNCURSESW */
  193. void RichString_delete(RichString* this) {
  194. if (this->chlen > RICHSTRING_MAXLEN) {
  195. free(this->chptr);
  196. this->chptr = this->chstr;
  197. }
  198. }
  199. void RichString_setAttr(RichString* this, int attrs) {
  200. RichString_setAttrn(this, attrs, 0, this->chlen);
  201. }
  202. int RichString_appendWide(RichString* this, int attrs, const char* data) {
  203. return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
  204. }
  205. int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
  206. return RichString_writeFromWide(this, attrs, data, this->chlen, len);
  207. }
  208. int RichString_writeWide(RichString* this, int attrs, const char* data) {
  209. return RichString_writeFromWide(this, attrs, data, 0, strlen(data));
  210. }
  211. int RichString_appendAscii(RichString* this, int attrs, const char* data) {
  212. return RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
  213. }
  214. int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
  215. return RichString_writeFromAscii(this, attrs, data, this->chlen, len);
  216. }
  217. int RichString_writeAscii(RichString* this, int attrs, const char* data) {
  218. return RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
  219. }