vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
text.h
(12751B)
1 #ifndef TEXT_H
2 #define TEXT_H
3
4 #include <stdbool.h>
5 #include <stdint.h>
6 #include <time.h>
7 #include <unistd.h>
8 #include <stdarg.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11
12 /** A mark. */
13 typedef uintptr_t Mark;
14
15 /** An invalid mark, lookup of which will yield ``EPOS``. */
16 #define EMARK ((Mark)0)
17 /** An invalid position. */
18 #define EPOS ((size_t)-1)
19
20 /** A range. */
21 typedef struct {
22 size_t start; /**< Absolute byte position. */
23 size_t end; /**< Absolute byte position. */
24 } Filerange;
25
26 /**
27 * Text object storing the buffer content being edited.
28 */
29 typedef struct Text Text;
30 typedef struct Piece Piece;
31 typedef struct TextSave TextSave;
32
33 /** A contiguous part of the text. */
34 typedef struct {
35 const char *data; /**< Content, might not be NUL-terminated. */
36 size_t len; /**< Length in bytes. */
37 } TextString;
38
39 /**
40 * Iterator used to navigate the buffer content.
41 *
42 * Captures the position within a Piece.
43 *
44 * @rst
45 * .. warning:: Any change to the Text will invalidate the iterator state.
46 * .. note:: Should be treated as an opaque type.
47 * @endrst
48 */
49 typedef struct {
50 const char *start; /**< Start of the piece data. */
51 const char *end; /**< End of piece data. Addressable range is ``[start, end)``. */
52 const char *text; /**< Current position within piece. Invariant ``start <= text < end`` holds. */
53 const Piece *piece; /**< Internal state of current piece. */
54 size_t pos; /**< Absolute position in bytes from start of buffer. */
55 } Iterator;
56
57 /**
58 * @defgroup load Text Loading
59 * @{
60 */
61 /**
62 * Method used to load existing file content.
63 */
64 enum TextLoadMethod {
65 /** Automatically chose best option. */
66 TEXT_LOAD_AUTO,
67 /**
68 * Read file content and copy it to an in-memory buffer.
69 * Subsequent changes to the underlying file will have no
70 * effect on this text instance.
71 *
72 * @rst
73 * .. note:: Load time is linear in the file size.
74 * @endrst
75 */
76 TEXT_LOAD_READ,
77 /**
78 * Memory map the file from disk. Use file system / virtual memory
79 * subsystem as a caching layer.
80 * @rst
81 * .. note:: Load time is (almost) independent of the file size.
82 * .. warning:: Inplace modifications of the underlying file
83 * will be reflected in the current text content.
84 * In particular, truncation will raise ``SIGBUS``
85 * and result in data loss.
86 * @endrst
87 */
88 TEXT_LOAD_MMAP,
89 };
90 /**
91 * Create a text instance populated with the given file content.
92 *
93 * @rst
94 * .. note:: Equivalent to ``text_load_method(filename, TEXT_LOAD_AUTO)``.
95 * @endrst
96 */
97 Text *text_load(const char *filename);
98 Text *text_loadat(int dirfd, const char *filename);
99 /**
100 * Create a text instance populated with the given file content.
101 *
102 * @param filename The name of the file to load, if ``NULL`` an empty text is created.
103 * @param method How the file content should be loaded.
104 * @return The new Text object or ``NULL`` in case of an error.
105 * @rst
106 * .. note:: When attempting to load a non-regular file, ``errno`` will be set to:
107 *
108 * - ``EISDIR`` for a directory.
109 * - ``ENOTSUP`` otherwise.
110 * @endrst
111 */
112 Text *text_load_method(const char *filename, enum TextLoadMethod method);
113 Text *text_loadat_method(int dirfd, const char *filename, enum TextLoadMethod);
114 /** Release all resources associated with this text instance. */
115 void text_free(Text*);
116 /**
117 * @}
118 * @defgroup state Text State
119 * @{
120 */
121 /** Return the size in bytes of the whole text. */
122 size_t text_size(const Text*);
123 /**
124 * Get file information at time of load or last save, whichever happened more
125 * recently.
126 * @rst
127 * .. note:: If an empty text instance was created using ``text_load(NULL)``
128 * and it has not yet been saved, an all zero ``struct stat`` will
129 * be returned.
130 * @endrst
131 * @return See ``stat(2)`` for details.
132 */
133 struct stat text_stat(const Text*);
134 /** Query whether the text contains any unsaved modifications. */
135 bool text_modified(const Text*);
136 /**
137 * @}
138 * @defgroup modify Text Modification
139 * @{
140 */
141 /**
142 * Insert data at the given byte position.
143 *
144 * @param txt The text instance to modify.
145 * @param pos The absolute byte position.
146 * @param data The data to insert.
147 * @param len The length of the data in bytes.
148 * @return Whether the insertion succeeded.
149 */
150 bool text_insert(Text *txt, size_t pos, const char *data, size_t len);
151 /**
152 * Delete data at given byte position.
153 *
154 * @param txt The text instance to modify.
155 * @param pos The absolute byte position.
156 * @param len The number of bytes to delete, starting from ``pos``.
157 * @return Whether the deletion succeeded.
158 */
159 bool text_delete(Text *txt, size_t pos, size_t len);
160 bool text_delete_range(Text *txt, const Filerange*);
161 bool text_printf(Text *txt, size_t pos, const char *format, ...) __attribute__((format(printf, 3, 4)));
162 bool text_appendf(Text *txt, const char *format, ...) __attribute__((format(printf, 2, 3)));
163 /**
164 * @}
165 * @defgroup history Undo/Redo History
166 * @{
167 */
168 /**
169 * Create a text snapshot, that is a vertex in the history graph.
170 */
171 bool text_snapshot(Text*);
172 /**
173 * Revert to previous snapshot along the main branch.
174 * @rst
175 * .. note:: Takes an implicit snapshot.
176 * @endrst
177 * @return The position of the first change or ``EPOS``, if already at the
178 * oldest state i.e. there was nothing to undo.
179 */
180 size_t text_undo(Text*);
181 /**
182 * Reapply an older change along the main branch.
183 * @rst
184 * .. note:: Takes an implicit snapshot.
185 * @endrst
186 * @return The position of the first change or ``EPOS``, if already at the
187 * newest state i.e. there was nothing to redo.
188 */
189 size_t text_redo(Text*);
190 size_t text_earlier(Text*);
191 size_t text_later(Text*);
192 /**
193 * Restore the text to the state closest to the time given
194 */
195 size_t text_restore(Text*, time_t);
196 /**
197 * Get creation time of current state.
198 * @rst
199 * .. note:: TODO: This is currently not the same as the time of the last snapshot.
200 * @endrst
201 */
202 time_t text_state(const Text*);
203 /**
204 * @}
205 * @defgroup lines Line Operations
206 * @{
207 */
208 size_t text_pos_by_lineno(Text*, size_t lineno);
209 size_t text_lineno_by_pos(Text*, size_t pos);
210
211 /**
212 * @}
213 * @defgroup access Text Access
214 * @{
215 */
216 /**
217 * Get byte stored at ``pos``.
218 * @param txt The text instance to modify.
219 * @param pos The absolute position.
220 * @param byte Destination address to store the byte.
221 * @return Whether ``pos`` was valid and ``byte`` updated accordingly.
222 * @rst
223 * .. note:: Unlike :c:func:`text_iterator_byte_get()` this function does not
224 * return an artificial NUL byte at EOF.
225 * @endrst
226 */
227 bool text_byte_get(const Text *txt, size_t pos, char *byte);
228 /**
229 * Store at most ``len`` bytes starting from ``pos`` into ``buf``.
230 * @param txt The text instance to modify.
231 * @param pos The absolute starting position.
232 * @param len The length in bytes.
233 * @param buf The destination buffer.
234 * @return The number of bytes (``<= len``) stored at ``buf``.
235 * @rst
236 * .. warning:: ``buf`` will not be NUL terminated.
237 * @endrst
238 */
239 size_t text_bytes_get(const Text *txt, size_t pos, size_t len, char *buf);
240 /**
241 * Fetch text range into newly allocate memory region.
242 * @param txt The text instance to modify.
243 * @param pos The absolute starting position.
244 * @param len The length in bytes.
245 * @return A contiguous NUL terminated buffer holding the requested range, or
246 * ``NULL`` in error case.
247 * @rst
248 * .. warning:: The returned pointer must be freed by the caller.
249 * @endrst
250 */
251 char *text_bytes_alloc0(const Text *txt, size_t pos, size_t len);
252 /**
253 * @}
254 * @defgroup iterator Text Iterators
255 * @{
256 */
257 Iterator text_iterator_get(const Text*, size_t pos);
258 bool text_iterator_init(const Text*, Iterator*, size_t pos);
259 const Text *text_iterator_text(const Iterator*);
260 bool text_iterator_valid(const Iterator*);
261 bool text_iterator_has_next(const Iterator*);
262 bool text_iterator_has_prev(const Iterator*);
263 bool text_iterator_next(Iterator*);
264 bool text_iterator_prev(Iterator*);
265 /**
266 * @}
267 * @defgroup iterator_byte Byte Iterators
268 * @{
269 */
270 bool text_iterator_byte_get(const Iterator*, char *b);
271 bool text_iterator_byte_prev(Iterator*, char *b);
272 bool text_iterator_byte_next(Iterator*, char *b);
273 bool text_iterator_byte_find_prev(Iterator*, char b);
274 bool text_iterator_byte_find_next(Iterator*, char b);
275 /**
276 * @}
277 * @defgroup iterator_code Codepoint Iterators
278 * @{
279 */
280 bool text_iterator_codepoint_next(Iterator *it, char *c);
281 bool text_iterator_codepoint_prev(Iterator *it, char *c);
282 /**
283 * @}
284 * @defgroup iterator_char Character Iterators
285 * @{
286 */
287 bool text_iterator_char_next(Iterator*, char *c);
288 bool text_iterator_char_prev(Iterator*, char *c);
289 /**
290 * @}
291 * @defgroup mark Marks
292 * @{
293 */
294 /**
295 * Set a mark.
296 * @rst
297 * .. note:: Setting a mark to ``text_size`` will always return the
298 * current text size upon lookup.
299 * @endrst
300 * @param txt The text instance to modify.
301 * @param pos The position at which to store the mark.
302 * @return The mark or ``EMARK`` if an invalid position was given.
303 */
304 Mark text_mark_set(Text *txt, size_t pos);
305 /**
306 * Lookup a mark.
307 * @param txt The text instance to modify.
308 * @param mark The mark to look up.
309 * @return The byte position or ``EPOS`` for an invalid mark.
310 */
311 size_t text_mark_get(const Text *txt, Mark mark);
312 /**
313 * @}
314 * @defgroup save Text Saving
315 * @{
316 */
317 /**
318 * Method used to save the text.
319 */
320 enum TextSaveMethod {
321 /** Automatically chose best option. */
322 TEXT_SAVE_AUTO,
323 /**
324 * Save file atomically using ``rename(2)``.
325 *
326 * Creates a temporary file, restores all important meta data,
327 * before moving it atomically to its final (possibly already
328 * existing) destination using ``rename(2)``. For new files,
329 * permissions are set to ``0666 & ~umask``.
330 *
331 * @rst
332 * .. warning:: This approach does not work if:
333 *
334 * - The file is a symbolic link.
335 * - The file is a hard link.
336 * - File ownership can not be preserved.
337 * - File group can not be preserved.
338 * - Directory permissions do not allow creation of a new file.
339 * - POSIX ACL can not be preserved (if enabled).
340 * - SELinux security context can not be preserved (if enabled).
341 * @endrst
342 */
343 TEXT_SAVE_ATOMIC,
344 /**
345 * Overwrite file in place.
346 * @rst
347 * .. warning:: I/O failure might cause data loss.
348 * @endrst
349 */
350 TEXT_SAVE_INPLACE,
351 };
352
353 /**
354 * Save the whole text to the given file name.
355 *
356 * @rst
357 * .. note:: Equivalent to ``text_save_method(filename, TEXT_SAVE_AUTO)``.
358 * @endrst
359 */
360 bool text_save(Text*, const char *filename);
361 bool text_saveat(Text*, int dirfd, const char *filename);
362 /**
363 * Save the whole text to the given file name, using the specified method.
364 */
365 bool text_save_method(Text*, const char *filename, enum TextSaveMethod);
366 bool text_saveat_method(Text*, int dirfd, const char *filename, enum TextSaveMethod);
367
368 /**
369 * Setup a sequence of write operations.
370 *
371 * The returned ``TextSave`` pointer can be used to write multiple, possibly
372 * non-contiguous, file ranges.
373 * @rst
374 * .. warning:: For every call to ``text_save_begin`` there must be exactly
375 * one matching call to either ``text_save_commit`` or
376 * ``text_save_cancel`` to release the underlying resources.
377 * @endrst
378 */
379 TextSave *text_save_begin(Text*, int dirfd, const char *filename, enum TextSaveMethod);
380 /**
381 * Write file range.
382 * @return The number of bytes written or ``-1`` in case of an error.
383 */
384 ssize_t text_save_write_range(TextSave*, const Filerange*);
385 /**
386 * Commit changes to disk.
387 * @return Whether changes have been saved.
388 * @rst
389 * .. note:: Releases the underlying resources and frees the given ``TextSave``
390 * pointer which must no longer be used.
391 * @endrst
392 */
393 bool text_save_commit(TextSave*);
394 /**
395 * Abort a save operation.
396 * @rst
397 * .. note:: Does not guarantee to undo the previous writes (they might have been
398 * performed in-place). However, it releases the underlying resources and
399 * frees the given ``TextSave`` pointer which must no longer be used.
400 * @endrst
401 */
402 void text_save_cancel(TextSave*);
403 /**
404 * Write whole text content to file descriptor.
405 * @return The number of bytes written or ``-1`` in case of an error.
406 */
407 ssize_t text_write(const Text*, int fd);
408 /**
409 * Write file range to file descriptor.
410 * @return The number of bytes written or ``-1`` in case of an error.
411 */
412 ssize_t text_write_range(const Text*, const Filerange*, int fd);
413 /**
414 * @}
415 * @defgroup misc Miscellaneous
416 * @{
417 */
418 /**
419 * Check whether ``ptr`` is part of a memory mapped region associated with
420 * this text instance.
421 */
422 bool text_mmaped(const Text*, const char *ptr);
423
424 /**
425 * Write complete buffer to file descriptor.
426 * @return The number of bytes written or ``-1`` in case of an error.
427 */
428 ssize_t write_all(int fd, const char *buf, size_t count);
429 /** @} */
430
431 #endif