vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

text-iterator.c

(3699B)


      1 #ifndef _GNU_SOURCE
      2 #define _GNU_SOURCE /* memrchr(3) is non-standard */
      3 #endif
      4 #include <limits.h>
      5 #include <stddef.h>
      6 #include <errno.h>
      7 #include <wchar.h>
      8 #include <string.h>
      9 #include "text.h"
     10 #include "util.h"
     11 
     12 bool text_iterator_byte_get(const Iterator *it, char *b) {
     13 	if (text_iterator_valid(it)) {
     14 		const Text *txt = text_iterator_text(it);
     15 		if (it->start <= it->text && it->text < it->end) {
     16 			*b = *it->text;
     17 			return true;
     18 		} else if (it->pos == text_size(txt)) {
     19 			*b = '\0';
     20 			return true;
     21 		}
     22 	}
     23 	return false;
     24 }
     25 
     26 bool text_iterator_byte_next(Iterator *it, char *b) {
     27 	if (!text_iterator_has_next(it))
     28 		return false;
     29 	bool eof = true;
     30 	if (it->text != it->end) {
     31 		it->text++;
     32 		it->pos++;
     33 		eof = false;
     34 	} else if (!text_iterator_has_prev(it)) {
     35 		eof = false;
     36 	}
     37 
     38 	while (it->text == it->end) {
     39 		if (!text_iterator_next(it)) {
     40 			if (eof)
     41 				return false;
     42 			if (b)
     43 				*b = '\0';
     44 			return text_iterator_prev(it);
     45 		}
     46 	}
     47 
     48 	if (b)
     49 		*b = *it->text;
     50 	return true;
     51 }
     52 
     53 bool text_iterator_byte_prev(Iterator *it, char *b) {
     54 	if (!text_iterator_has_prev(it))
     55 		return false;
     56 	bool eof = !text_iterator_has_next(it);
     57 	while (it->text == it->start) {
     58 		if (!text_iterator_prev(it)) {
     59 			if (!eof)
     60 				return false;
     61 			if (b)
     62 				*b = '\0';
     63 			return text_iterator_next(it);
     64 		}
     65 	}
     66 
     67 	--it->text;
     68 	--it->pos;
     69 
     70 	if (b)
     71 		*b = *it->text;
     72 	return true;
     73 }
     74 
     75 bool text_iterator_byte_find_prev(Iterator *it, char b) {
     76 	while (it->text) {
     77 		const char *match = memrchr(it->start, b, it->text - it->start);
     78 		if (match) {
     79 			it->pos -= it->text - match;
     80 			it->text = match;
     81 			return true;
     82 		}
     83 		text_iterator_prev(it);
     84 	}
     85 	text_iterator_next(it);
     86 	return false;
     87 }
     88 
     89 bool text_iterator_byte_find_next(Iterator *it, char b) {
     90 	while (it->text) {
     91 		const char *match = memchr(it->text, b, it->end - it->text);
     92 		if (match) {
     93 			it->pos += match - it->text;
     94 			it->text = match;
     95 			return true;
     96 		}
     97 		text_iterator_next(it);
     98 	}
     99 	text_iterator_prev(it);
    100 	return false;
    101 }
    102 
    103 bool text_iterator_codepoint_next(Iterator *it, char *c) {
    104 	while (text_iterator_byte_next(it, NULL)) {
    105 		if (ISUTF8(*it->text)) {
    106 			if (c)
    107 				*c = *it->text;
    108 			return true;
    109 		}
    110 	}
    111 	return false;
    112 }
    113 
    114 bool text_iterator_codepoint_prev(Iterator *it, char *c) {
    115 	while (text_iterator_byte_prev(it, NULL)) {
    116 		if (ISUTF8(*it->text)) {
    117 			if (c)
    118 				*c = *it->text;
    119 			return true;
    120 		}
    121 	}
    122 	return false;
    123 }
    124 
    125 bool text_iterator_char_next(Iterator *it, char *c) {
    126 	if (!text_iterator_codepoint_next(it, c))
    127 		return false;
    128 	mbstate_t ps = { 0 };
    129 	const Text *txt = text_iterator_text(it);
    130 	for (;;) {
    131 		char buf[MB_LEN_MAX];
    132 		size_t len = text_bytes_get(txt, it->pos, sizeof buf, buf);
    133 		wchar_t wc;
    134 		size_t wclen = mbrtowc(&wc, buf, len, &ps);
    135 		if (wclen == (size_t)-1 && errno == EILSEQ) {
    136 			return true;
    137 		} else if (wclen == (size_t)-2) {
    138 			return false;
    139 		} else if (wclen == 0) {
    140 			return true;
    141 		} else {
    142 			int width = wcwidth(wc);
    143 			if (width != 0)
    144 				return true;
    145 			if (!text_iterator_codepoint_next(it, c))
    146 				return false;
    147 		}
    148 	}
    149 	return true;
    150 }
    151 
    152 bool text_iterator_char_prev(Iterator *it, char *c) {
    153 	if (!text_iterator_codepoint_prev(it, c))
    154 		return false;
    155 	const Text *txt = text_iterator_text(it);
    156 	for (;;) {
    157 		char buf[MB_LEN_MAX];
    158 		size_t len = text_bytes_get(txt, it->pos, sizeof buf, buf);
    159 		wchar_t wc;
    160 		mbstate_t ps = { 0 };
    161 		size_t wclen = mbrtowc(&wc, buf, len, &ps);
    162 		if (wclen == (size_t)-1 && errno == EILSEQ) {
    163 			return true;
    164 		} else if (wclen == (size_t)-2) {
    165 			return false;
    166 		} else if (wclen == 0) {
    167 			return true;
    168 		} else {
    169 			int width = wcwidth(wc);
    170 			if (width != 0)
    171 				return true;
    172 			if (!text_iterator_codepoint_prev(it, c))
    173 				return false;
    174 		}
    175 	}
    176 	return true;
    177 }