vis: remove z| pairwise union
[vis.git] / text.h
blob5d653ff4a24312962e31bfab68e833e916409a88
1 #ifndef TEXT_H
2 #define TEXT_H
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>
12 /** A mark. */
13 typedef uintptr_t Mark;
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)
20 /** A range. */
21 typedef struct {
22 size_t start; /**< Absolute byte position. */
23 size_t end; /**< Absolute byte position. */
24 } Filerange;
26 /**
27 * Text object storing the buffer content being edited.
29 typedef struct Text Text;
30 typedef struct Piece Piece;
31 typedef struct TextSave TextSave;
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;
39 /**
40 * Iterator used to navigate the buffer content.
42 * Captures the position within a Piece.
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
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;
57 /**
58 * @defgroup load
59 * @{
61 /**
62 * Method used to load existing file content.
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.
72 * @rst
73 * .. note:: Load time is linear in the file size.
74 * @endrst
76 TEXT_LOAD_READ,
77 /**
78 * Memory map the 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, truncatenation will raise ``SIGBUS``
85 * and result in data loss.
86 * @endrst
88 TEXT_LOAD_MMAP,
90 /**
91 * Create a text instance populated with the given file content.
93 * @rst
94 * .. note:: Equivalent to ``text_load_method(filename, TEXT_LOAD_AUTO)``.
95 * @endrst
97 Text *text_load(const char *filename);
98 /**
99 * Create a text instance populated with the given file content.
101 * @param filename The name of the file to load, if ``NULL`` an empty text is created.
102 * @param method How the file content should be loaded.
103 * @return The new Text object or ``NULL`` in case of an error.
104 * @rst
105 * .. note:: When attempting to load a non-regular file, ``errno`` will be set to:
107 * - ``EISDIR`` for a directory.
108 * - ``ENOTSUP`` otherwise.
109 * @endrst
111 Text *text_load_method(const char *filename, enum TextLoadMethod);
112 /** Release all ressources associated with this text instance. */
113 void text_free(Text*);
115 * @}
116 * @defgroup state
117 * @{
119 /** Return the size in bytes of the whole text. */
120 size_t text_size(Text*);
122 * Get file information at time of load or last save, whichever happened more
123 * recently.
124 * @rst
125 * .. note:: If an empty text instance was created using ``text_load(NULL)``
126 * and it has not yet been saved, an all zero ``struct stat`` will
127 * be returned.
128 * @endrst
129 * @return See ``stat(2)`` for details.
131 struct stat text_stat(Text*);
132 /** Query whether the text contains any unsaved modifications. */
133 bool text_modified(Text*);
135 * @}
136 * @defgroup modify
137 * @{
140 * Insert data at the given byte position.
142 * @param pos The absolute byte position.
143 * @param data The data to insert.
144 * @param len The length of the data in bytes.
145 * @return Whether the insertion succeeded.
147 bool text_insert(Text*, size_t pos, const char *data, size_t len);
149 * Delete data at given byte position.
151 * @param pos The absolute byte position.
152 * @param len The number of bytes to delete, starting from ``pos``.
153 * @return Whether the deletion succeeded.
155 bool text_delete(Text*, size_t pos, size_t len);
156 bool text_delete_range(Text*, Filerange*);
157 bool text_printf(Text*, size_t pos, const char *format, ...) __attribute__((format(printf, 3, 4)));
158 bool text_appendf(Text*, const char *format, ...) __attribute__((format(printf, 2, 3)));
160 * @}
161 * @defgroup history
162 * @{
165 * Create a text snapshot, that is a vertice in the history graph.
167 void text_snapshot(Text*);
169 * Revert to previous snapshot along the main branch.
170 * @rst
171 * .. note:: Takes an implicit snapshot.
172 * @endrst
173 * @return The position of the first change or ``EPOS``, if already at the
174 * oldest state i.e. there was nothing to undo.
176 size_t text_undo(Text*);
178 * Reapply an older change along the main brach.
179 * @rst
180 * .. note:: Takes an implicit snapshot.
181 * @endrst
182 * @return The position of the first change or ``EPOS``, if already at the
183 * newest state i.e. there was nothing to redo.
185 size_t text_redo(Text*);
186 size_t text_earlier(Text*);
187 size_t text_later(Text*);
189 * Restore the text to the state closest to the time given
191 size_t text_restore(Text*, time_t);
193 * Get creation time of current state.
194 * @rst
195 * .. note:: TODO: This is currently not the same as the time of the last snapshot.
196 * @endrst
198 time_t text_state(Text*);
200 * @}
201 * @defgroup lines
202 * @{
204 size_t text_pos_by_lineno(Text*, size_t lineno);
205 size_t text_lineno_by_pos(Text*, size_t pos);
208 * @}
209 * @defgroup access
210 * @{
213 * Get byte stored at ``pos``.
214 * @param pos The absolute position.
215 * @param byte Destination address to store the byte.
216 * @return Whether ``pos`` was valid and ``byte`` updated accordingly.
217 * @rst
218 * .. note:: Unlike :c:func:`text_iterator_byte_get()` this function does not
219 * return an artificial NUL byte at EOF.
220 * @endrst
222 bool text_byte_get(Text*, size_t pos, char *byte);
224 * Store at most `len` bytes starting from ``pos`` into ``buf``.
225 * @param pos The absolute starting position.
226 * @param len The length in bytes.
227 * @param buf The destination buffer.
228 * @return The number of bytes (``<= len``) stored at ``buf``.
229 * @rst
230 * .. warning:: ``buf`` will not be NUL terminated.
231 * @endrst
233 size_t text_bytes_get(Text*, size_t pos, size_t len, char *buf);
235 * Fetch text range into newly allocate memory region.
236 * @param pos The absolute starting position.
237 * @param len The length in bytes.
238 * @return A contigious NUL terminated buffer holding the requested range, or
239 * ``NULL`` in error case.
240 * @rst
241 * .. warning:: The returned pointer must be `free(3)`-ed by the caller.
242 * @endrst
244 char *text_bytes_alloc0(Text*, size_t pos, size_t len);
246 * @}
247 * @defgroup iterator
248 * @{
250 Iterator text_iterator_get(Text*, size_t pos);
251 bool text_iterator_valid(const Iterator*);
252 bool text_iterator_next(Iterator*);
253 bool text_iterator_prev(Iterator*);
255 * @}
256 * @defgroup iterator_byte
257 * @{
259 bool text_iterator_byte_get(Iterator*, char *b);
260 bool text_iterator_byte_prev(Iterator*, char *b);
261 bool text_iterator_byte_next(Iterator*, char *b);
262 bool text_iterator_byte_find_prev(Iterator*, char b);
263 bool text_iterator_byte_find_next(Iterator*, char b);
265 * @}
266 * @defgroup iterator_code
267 * @{
269 bool text_iterator_codepoint_next(Iterator *it, char *c);
270 bool text_iterator_codepoint_prev(Iterator *it, char *c);
272 * @}
273 * @defgroup iterator_char
274 * @{
276 bool text_iterator_char_next(Iterator*, char *c);
277 bool text_iterator_char_prev(Iterator*, char *c);
279 * @}
280 * @defgroup mark
281 * @{
284 * Set a mark.
285 * @rst
286 * .. note:: Setting a mark to `text_size` will always return the current text
287 * size upon lookup.
288 * @endrst
289 * @param pos The position at which to store the mark.
290 * @return The mark or `EMARK` if an invalid position was given.
292 Mark text_mark_set(Text*, size_t pos);
294 * Lookup a mark.
295 * @param mark The mark to look up.
296 * @return The byte position or `EPOS` for an invalid mark.
298 size_t text_mark_get(Text*, Mark);
300 * @}
301 * @defgroup save
302 * @{
305 * Method used to save the text.
307 enum TextSaveMethod {
308 /** Automatically chose best option. */
309 TEXT_SAVE_AUTO,
311 * Save file atomically using `rename(2)`.
313 * Creates a temporary file, restores all important meta data,
314 * before moving it atomically to its final (possibly already
315 * existing) destination using `rename(2)`. For new files,
316 * permissions are set to `0666 & ~umask`.
318 * @rst
319 * .. warning:: This approach does not work if:
321 * - The file is a symbolic link.
322 * - The file is a hard link.
323 * - File ownership can not be preserved.
324 * - File group can not be preserved.
325 * - Directory permissions do not allow creation of a new file.
326 * - POSXI ACL can not be preserved (if enabled).
327 * - SELinux security context can not be preserved (if enabled).
328 * @endrst
330 TEXT_SAVE_ATOMIC,
332 * Overwrite file in place.
333 * @rst
334 * .. warning:: I/O failure might cause data loss.
335 * @endrst
337 TEXT_SAVE_INPLACE,
341 * Save the whole text to the given file name.
343 bool text_save(Text*, const char *filename);
345 * Save the whole text to the given file name, using the specified method.
347 * @rst
348 * .. note:: Equivalent to ``text_save_method(filename, TEXT_SAVE_AUTO)``.
349 * @endrst
351 bool text_save_method(Text*, const char *filename, enum TextSaveMethod);
354 * Setup a sequence of write operations.
356 * The returned `TextSave` pointer can be used to write multiple, possibly
357 * non-contigious, file ranges.
358 * @rst
359 * .. warning:: For every call to `text_save_begin` there must be exactly
360 * one matching call to either `text_save_commit` or
361 * `text_save_cancel` to release the underlying resources.
362 * @endrst
364 TextSave *text_save_begin(Text*, const char *filename, enum TextSaveMethod);
366 * Write file range.
367 * @return The number of bytes written or ``-1`` in case of an error.
369 ssize_t text_save_write_range(TextSave*, Filerange*);
371 * Commit changes to disk.
372 * @return Whether changes have been saved.
373 * @rst
374 * .. note:: Releases the underlying resources and `free(3)`'s the given `TextSave`
375 * pointer which must no longer be used.
376 * @endrst
378 bool text_save_commit(TextSave*);
380 * Abort a save operation.
381 * @rst
382 * .. note:: Does not guarantee to undo the previous writes (they might have been
383 * performed in-place). However, it releases the underlying resources and
384 * `free(3)`'s the given `TextSave` pointer which must no longer be used.
385 * @endrst
387 void text_save_cancel(TextSave*);
389 * Write whole text content to file descriptor.
390 * @return The number of bytes written or ``-1`` in case of an error.
392 ssize_t text_write(Text*, int fd);
394 * Write file range to file descriptor.
395 * @return The number of bytes written or ``-1`` in case of an error.
397 ssize_t text_write_range(Text*, Filerange*, int fd);
399 * @}
400 * @defgroup misc
401 * @{
404 * Check whether ``ptr`` is part of a memory mapped region associated with
405 * this text instance.
407 bool text_mmaped(Text*, const char *ptr);
408 /** @} */
410 #endif