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