aboutsummaryrefslogtreecommitdiffstats
path: root/libpatch.h
diff options
context:
space:
mode:
Diffstat (limited to 'libpatch.h')
-rw-r--r--libpatch.h1233
1 files changed, 1233 insertions, 0 deletions
diff --git a/libpatch.h b/libpatch.h
new file mode 100644
index 0000000..ee7c1c9
--- /dev/null
+++ b/libpatch.h
@@ -0,0 +1,1233 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBPATCH_H
+#define LIBPATCH_H
+
+#include <sys/stat.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+
+/**
+ * Patch file format
+ */
+enum libpatch_style {
+ /**
+ * Style could not be determined
+ *
+ * Always invalid as function input
+ * (EINVAL error)
+ */
+ LIBPATCH_STYLE_GARBAGE,
+
+ /**
+ * “Normal” format
+ *
+ * See “Diff Default Output Format”
+ * in diff(1p) for details
+ *
+ * If nonstandard syntax is enabled, context
+ * will be printed with a “ ”-prefix
+ *
+ * As a common extension to the format,
+ * if a file does not end with a <newline>,
+ * “\” followed by some text will be printed
+ * after that line is included in the patch
+ */
+ LIBPATCH_STYLE_NORMAL,
+
+ /**
+ * “Copied” format
+ *
+ * See “Diff -c or -C Output Format”
+ * in diff(1p) for details
+ *
+ * As a common extension to the format,
+ * if a file does not end with a <newline>,
+ * “\” followed by some text will be printed
+ * after that line is included in the patch
+ */
+ LIBPATCH_STYLE_COPIED,
+
+ /**
+ * “Unified” format
+ *
+ * See “Diff -u or -U Output Format”
+ * in diff(1p) for details
+ *
+ * As a common extension to the format,
+ * if a file does not end with a <newline>,
+ * “\” followed by some text will be printed
+ * after that line is included in the patch
+ */
+ LIBPATCH_STYLE_UNIFIED,
+
+ /**
+ * ed(1) format
+ *
+ * See “Diff -e Output Format”
+ * in diff(1p) for details
+ */
+ LIBPATCH_STYLE_ED,
+
+ /**
+ * Alternative ed(1) format
+ *
+ * See “Diff -f Output Format”
+ * in diff(1p) for details
+ *
+ * Diff scripts in this format are usually
+ * ambiguous and can therefore not be parsed
+ */
+ LIBPATCH_STYLE_ED_ALTERNATIVE,
+
+ /**
+ * RCS format
+ *
+ * This is a non-POSIX format
+ */
+ LIBPATCH_STYLE_RCS
+};
+
+
+/**
+ * How a file label shall be formatted
+ */
+enum libpatch_label_format {
+ /**
+ * An error occured
+ */
+ LIBPATCH_LABEL_ERROR = -1,
+
+ /**
+ * The patch format does not support file labels
+ */
+ LIBPATCH_LABEL_NONE,
+
+ /**
+ * The patch format file label shall
+ * just be the filepath
+ */
+ LIBPATCH_LABEL_FILE_ONLY,
+
+ /**
+ * The patch format file label shall
+ * be the filepath followed by a regular
+ * space, followed by the file's mtime
+ */
+ LIBPATCH_LABEL_FILE_SPACE_MTIME,
+
+ /**
+ * The patch format file label shall
+ * be the filepath followed by a regular
+ * space, followed by the file's mtime
+ */
+ LIBPATCH_LABEL_FILE_TAB_MTIME
+};
+
+
+/**
+ * Partial text comparision
+ */
+struct libpatch_diff2 {
+ /**
+ * Which files are changed, or if
+ * the text included as context?
+ */
+ enum {
+ /**
+ * Text appears in both file
+ */
+ LIBPATCH_DIFF2_SKIP = 0x00,
+
+ /**
+ * Text only appears in file 1 (removed)
+ */
+ LIBPATCH_DIFF2_FILE1_ONLY = 0x01,
+
+ /**
+ * Text only appears in file 2 (added)
+ */
+ LIBPATCH_DIFF2_FILE2_ONLY = 0x02,
+
+ /**
+ * Text is different between file 1 and file 2
+ */
+ LIBPATCH_DIFF2_TWO_FILE_CHANGE = 0x03,
+
+ /**
+ * Same as `LIBPATCH_DIFF2_SKIP` except
+ * the text shall be included as context
+ * for a change
+ */
+ LIBPATCH_DIFF2_CONTEXT = 1 << 2
+ } change : CHAR_BIT;
+
+ /**
+ * The number of consecutive units of
+ * text that `.change` apply to
+ */
+ unsigned int repetition : sizeof(int) * CHAR_BIT - CHAR_BIT;
+};
+
+
+/**
+ * Specifications for how a diff string shall be organised
+ */
+struct libpatch_diff2_spec {
+ /**
+ * The number of units of text that shall
+ * be included as context before for a change
+ */
+ size_t context_before;
+
+ /**
+ * If there are up to (inclusive) this number
+ * of units of texts between two changes, the
+ * two changes shall be included as one hunk
+ *
+ * If `.context_after + .context_before > .context_between`,
+ * `.context_after + .context_before` is effective
+ * used for `.context_between`
+ */
+ size_t context_between;
+
+ /**
+ * The number of units of text that shall
+ * be included as context after for a change
+ */
+ size_t context_after;
+
+ /**
+ * Whether to include all unchanged code as
+ * context. This is the same as setting
+ * `.context_before` and `.context_after`
+ * to `SIZE_MAX`, except that if there are
+ * no changes, the entire file is still used
+ * as context
+ */
+ unsigned full_context : 1;
+
+ /**
+ * If 0, keep each `LIBPATCH_DIFF2_FILE1_ONLY`
+ * and `LIBPATCH_DIFF2_FILE2_ONLY`. If 1,
+ * replace each pair of `LIBPATCH_DIFF2_FILE1_ONLY`
+ * and `LIBPATCH_DIFF2_FILE2_ONLY`, that appear
+ * in a consecutive group of changes, with a
+ * `LIBPATCH_DIFF2_TWO_FILE_CHANGE`
+ *
+ * Has no effect if `.two_file_change_order` is
+ * `LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES` or
+ * `LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES_INTERLEAVE`
+ */
+ unsigned keep_split_changes : 1;
+
+ enum {
+ /**
+ * Text that only appear in file 1
+ * shall be included in the patch before
+ * any text that appears in file 2
+ * (that is not indentical in file 1)
+ */
+ LIBPATCH_DIFF2_FILE1_ONLY_FIRST,
+
+ /**
+ * Text that only appear in file 2
+ * shall be included in the patch before
+ * any text that appears in file 1
+ * (that is not indentical in file 2)
+ */
+ LIBPATCH_DIFF2_FILE2_ONLY_FIRST
+ } single_file_change_order : 1;
+
+ enum {
+ /**
+ * Each `LIBPATCH_DIFF2_TWO_FILE_CHANGE` shall
+ * be rewritten as `LIBPATCH_DIFF2_FILE1_ONLY`
+ * and `LIBPATCH_DIFF2_FILE2_ONLY` (doubling the
+ * number of reported changes). Each group of
+ * `LIBPATCH_DIFF2_FILE1_ONLY` and
+ * `LIBPATCH_DIFF2_FILE2_ONLY` shall be sorted
+ * according to `.single_file_change_order`
+ */
+ LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES,
+
+ /**
+ * Same as `LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES_INTERLEAVE`
+ * except that, to the extent possible, there shall be
+ * and alternation between `LIBPATCH_DIFF2_FILE1_ONLY`
+ * and `LIBPATCH_DIFF2_FILE2_ONLY`
+ */
+ LIBPATCH_DIFF2_NO_TWO_FILE_CHANGES_INTERLEAVE,
+
+ /**
+ * In consecutive changes, all
+ * `LIBPATCH_DIFF2_TWO_FILE_CHANGE`s shall
+ * be put in the front
+ */
+ LIBPATCH_DIFF2_TWO_FILE_CHANGES_BEFORE_SINGLE_FILE,
+
+ /**
+ * In consecutive changes, all
+ * `LIBPATCH_DIFF2_TWO_FILE_CHANGE`s shall
+ * be put after all of whichever shall appear
+ * first of `LIBPATCH_DIFF2_FILE1_ONLY` and
+ * `LIBPATCH_DIFF2_FILE2_ONLY`, but before
+ * the other ones
+ */
+ LIBPATCH_DIFF2_TWO_FILE_CHANGES_BETWEEN_SINGLE_FILE,
+
+ /**
+ * In consecutive changes, all
+ * `LIBPATCH_DIFF2_TWO_FILE_CHANGE`s shall
+ * be put in the back
+ */
+ LIBPATCH_DIFF2_TWO_FILE_CHANGES_AFTER_SINGLE_FILE
+ } two_file_change_order : 3;
+};
+
+
+/**
+ * A line from a patch file
+ */
+struct libpatch_patch {
+ /**
+ * What the line represents
+ */
+ enum {
+ /**
+ * The line does not mean anything and
+ * should be ignored if at an appropriate
+ * location
+ */
+ LIBPATCH_PATCH_GARBAGE,
+
+ /**
+ * The line is simply require by the
+ * patch format and can be ignored
+ */
+ LIBPATCH_PATCH_SYNTACTICAL,
+
+ /**
+ * Line describing what files are compared
+ * and how (used in multi-file patchsets)
+ *
+ * The line's non-syntactical text is the
+ * command line arguments after "diff"
+ */
+ LIBPATCH_PATCH_DIFF_LINE,
+
+ /**
+ * The line's non-syntactical text is the first
+ * file's label (see `libpatch_get_label_format`)
+ */
+ LIBPATCH_PATCH_FILE1_LABEL,
+
+ /**
+ * The line's non-syntactical text is the second
+ * file's label (see `libpatch_get_label_format`)
+ */
+ LIBPATCH_PATCH_FILE2_LABEL,
+
+ /**
+ * Beginning of a hunk, `.first_line` and
+ * `.line_count` are set to indicate
+ * required deletion
+ *
+ * The line's non-syntactical text is context
+ * for the hunk intended for human reading
+ */
+ LIBPATCH_PATCH_HUNK_WITH_DELETION,
+
+ /**
+ * Beginning of a hunk
+ *
+ * The line's non-syntactical text is context
+ * for the hunk intended for human reading
+ */
+ LIBPATCH_PATCH_HUNK,
+
+ /**
+ * Beginning of text deletions in a hunk
+ *
+ * The line's non-syntactical text is context
+ * for the hunk intended for human reading
+ */
+ LIBPATCH_PATCH_HUNK_FILE1,
+
+ /**
+ * Beginning of text insertions in a hunk
+ *
+ * The line's non-syntactical text is context
+ * for the hunk intended for human reading
+ */
+ LIBPATCH_PATCH_HUNK_FILE2,
+
+ /**
+ * The line's non-syntactical text is
+ * context for an hunk
+ */
+ LIBPATCH_PATCH_CONTEXT,
+
+ /**
+ * The line's non-syntactical text is
+ * context for text deletion
+ */
+ LIBPATCH_PATCH_FILE1_CONTEXT,
+
+ /**
+ * The line's non-syntactical text is
+ * context for text insertion
+ */
+ LIBPATCH_PATCH_FILE2_CONTEXT,
+
+ /**
+ * The line's non-syntactical text is
+ * a line of text to delete
+ */
+ LIBPATCH_PATCH_FILE1_ONLY,
+
+ /**
+ * The line's non-syntactical text is
+ * a line of text to insert
+ */
+ LIBPATCH_PATCH_FILE2_ONLY,
+
+ /**
+ * The line's non-syntactical text is
+ * a line of text to delete for which
+ * there also is an insertion
+ */
+ LIBPATCH_PATCH_PRECHANGE,
+
+ /**
+ * The line's non-syntactical text is
+ * a line of text to insertion for which
+ * there also is an deletion
+ */
+ LIBPATCH_PATCH_POSTCHANGE
+ } type : 7;
+
+ /**
+ * Usually 1, but if the text line shall
+ * not be <newline>-terminated, it is 0
+ */
+ unsigned lf_terminated : 1;
+
+ /**
+ * The byte in the patch file where
+ * the line's non-syntactical text begins
+ */
+ size_t text_offset;
+
+ /**
+ * The length of the line's non-syntactical text
+ */
+ size_t text_length;
+
+ /**
+ * The first line (counted from 1) to
+ * delete from the first line, or 0 if
+ * not applicable
+ */
+ size_t first_line;
+
+ /**
+ * The number of lines to delete from
+ * the first file
+ */
+ size_t line_count;
+
+ /**
+ * The line number (counted from 1) in the
+ * first file, or 0 if not applicable
+ */
+ size_t file1_lineno;
+
+ /**
+ * The line number (counted from 1) in the
+ * second file, or 0 if not applicable
+ */
+ size_t file2_lineno;
+};
+
+
+/**
+ * A line of text
+ */
+struct libpatch_line {
+ /**
+ * The number of bytes in the text
+ */
+ size_t len;
+
+ /**
+ * The text
+ */
+ char text[];
+};
+
+
+/**
+ * Description of a compared file
+ */
+struct libpatch_file {
+ /**
+ * The file's label (see `libpatch_get_label_format`)
+ */
+ const char *label;
+
+ /**
+ * Whether the file ends with a <newline>
+ */
+ unsigned lf_terminated;
+
+ /**
+ * The number of lines in the file
+ */
+ size_t nlines;
+
+ /**
+ * Each line in the file, these are not <newline> terminated
+ */
+ struct libpatch_line **lines;
+};
+
+
+/**
+ * How a diff should be printed
+ */
+struct libpatch_diff2_printer {
+ /* TODO doc */
+ void (*put_end_of_change)(struct libpatch_diff2_printer *this, const char *text);
+ void (*put_syntactical_escape)(struct libpatch_diff2_printer *this, const char *text);
+ void (*put_syntactical_garbage)(struct libpatch_diff2_printer *this, const char *text);
+ void (*put_hunk_head_prefix)(struct libpatch_diff2_printer *this, const char *text, int file /* 0 if both */);
+ void (*put_hunk_head_suffix)(struct libpatch_diff2_printer *this, const char *text, int file /* 0 if both */);
+ void (*put_hunk_start)(struct libpatch_diff2_printer *this, const char *prefix, size_t value, int file /* 1 or 2 */);
+ void (*put_hunk_end)(struct libpatch_diff2_printer *this, const char *prefix, size_t value, int file /* 1 or 2 */);
+ void (*put_hunk_length)(struct libpatch_diff2_printer *this, const char *prefix, size_t value, int file /* 1 or 2 */);
+ void (*put_hunk_operation)(struct libpatch_diff2_printer *this, const char *text, int file1, int file2);
+ void (*put_label_prefix)(struct libpatch_diff2_printer *this, const char *prefix, int file /* 1 or 2 */);
+ void (*put_label)(struct libpatch_diff2_printer *this, const char *label, int file /* 1 or 2 */);
+ void (*put_context_prefix)(struct libpatch_diff2_printer *this, const char *prefix,
+ int file1, int file2, size_t lineno);
+ void (*put_change_prefix)(struct libpatch_diff2_printer *this, const char *prefix,
+ int file /* 1 or 2 */, int two_file_change, size_t lineno);
+
+ /**
+ * Output text from a compared file
+ *
+ * Fill be preceeded by one `.put_context_prefix` or
+ * one `.put_change_prefix`, even if the prefix text
+ * is empty, followed by any number of `.put_whitespace`
+ *
+ * @param this The container of this member function
+ * @param line The text to output
+ */
+ void (*put_line)(struct libpatch_diff2_printer *this, struct libpatch_line *line);
+
+ /**
+ * Called any time syntactical whitespace shall be
+ * output
+ *
+ * @param this The container of this member function
+ * @param text The text to output
+ */
+ void (*put_whitespace)(struct libpatch_diff2_printer *this, const char *text);
+
+ /**
+ * Called any time <newline> should be output
+ *
+ * @param this The container of this member function
+ */
+ void (*put_newline)(struct libpatch_diff2_printer *this);
+
+ /**
+ * Output a text that is used to mark that a line
+ * is not properly terminated with a <newline>
+ *
+ * @param this The container of this member function
+ * @param text The text to output
+ */
+ void (*put_no_newline)(struct libpatch_diff2_printer *this, const char *text);
+
+ /**
+ * If 1, patches with “unified” format shall will
+ * use text from the second file for context
+ *
+ * If 0, patches with “unified” format shall will
+ * use text from the first file for context
+ */
+ unsigned use_file2_when_common : 1;
+
+ /**
+ * If 0, the output shall be as portable as possible
+ * (changes that cannot be describe in standard format
+ * or would safely enough be interpreted as garbage
+ * and is very rare will still be included.)
+ *
+ * If 1, patches in “normal” format can include
+ * context, if 0, such context would be printed
+ * as changes (that don't actually change anything)
+ */
+ unsigned use_nonstandard_syntax : 1;
+
+ /**
+ * If an error is encountered, the errno(3) value is stored here
+ */
+ int error;
+
+ /**
+ * User-defined data (implementation-definied if set by the library)
+ */
+ void *user_data;
+};
+
+
+/**
+ * Create a `struct libpatch_diff2_printer` to
+ * print a patch file that is parsable by patch(1)
+ * (that is, without any colours or other additions
+ * to aid human reading)
+ *
+ * @param output The file stream to print to
+ * @return The printer, `NULL` on failure; deallocate with free(3)
+ * @throws ENOMEM Failed to allocate enough memory
+ */
+struct libpatch_diff2_printer *libpatch_plain_stream_diff2_printer(FILE *output);
+
+/**
+ * Create a `struct libpatch_diff2_printer` to
+ * print a patch file that is parsable by patch(1)
+ * (that is, without any colours or other additions
+ * to aid human reading)
+ *
+ * @param output The file descriptor to print to
+ * @return The printer, `NULL` on failure; deallocate with free(3)
+ * @throws ENOMEM Failed to allocate enough memory
+ */
+struct libpatch_diff2_printer *libpatch_plain_fd_diff2_printer(int output);
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param tm The time to use in the timestamp
+ * @param subseconds The subseconds in the timestamp
+ * @param subseconds_decimals The number of decimals in the subseconds
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ */
+size_t libpatch_create_timestamp(char *buf, size_t bufsize, const struct tm *tm,
+ uintmax_t subseconds, unsigned subseconds_decimals,
+ unsigned frac, int zone, enum libpatch_style style);
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param tm The time to use in the timestamp
+ * @param frac `NULL` or the string of subsecond decimals (any non-digit
+ * character is interpreted as a string termination)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ */
+size_t libpatch_create_timestamp_frac_string(char *buf, size_t bufsize, const struct tm *tm,
+ const char *frac, int zone, enum libpatch_style style);
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param tm The time to use in the timestamp
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ */
+inline size_t
+libpatch_create_timestamp_tm(char *buf, size_t bufsize, const struct tm *tm,
+ int zone, enum libpatch_style style)
+{
+ return libpatch_create_timestamp(buf, bufsize, tm, 0, 0, 0, zone, style);
+}
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param ts The time to use in the timestamp
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3)
+ */
+inline size_t
+libpatch_create_timestamp_ts(char *buf, size_t bufsize, const struct timespec *ts,
+ unsigned frac, int zone, enum libpatch_style style)
+{
+ struct tm *tm, tmbuf;
+ tm = localtime_r(&ts->tv_sec, &tmbuf);
+ return tm ? libpatch_create_timestamp(buf, bufsize, tm, (uintmax_t)ts->tv_nsec, 9, frac, zone, style) : 0;
+}
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param tv The time to use in the timestamp
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3)
+ */
+inline size_t
+libpatch_create_timestamp_tv(char *buf, size_t bufsize, const struct timeval *tv,
+ unsigned frac, int zone, enum libpatch_style style)
+{
+ struct tm *tm, tmbuf;
+ tm = localtime_r(&tv->tv_sec, &tmbuf);
+ return tm ? libpatch_create_timestamp(buf, bufsize, tm, (uintmax_t)tv->tv_usec, 6, frac, zone, style) : 0;
+}
+
+/**
+ * Create file timestamp for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param secs The time to use in the timestamp
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3)
+ */
+inline size_t
+libpatch_create_timestamp_secs(char *buf, size_t bufsize, time_t secs,
+ int zone, enum libpatch_style style)
+{
+ struct tm *tm, tmbuf;
+ tm = localtime_r(&secs, &tmbuf);
+ return tm ? libpatch_create_timestamp(buf, bufsize, tm, 0, 0, 0, zone, style) : 0;
+}
+
+/**
+ * Create file timestamp, from file status, for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param st The file's status (mtime will be used)
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3)
+ */
+inline size_t
+libpatch_create_timestamp_stat(char *buf, size_t bufsize, const struct stat *st,
+ unsigned frac, int zone, enum libpatch_style style)
+{
+#ifdef st_mtime /* = st_mtim.tv_sec, infers existance of st->st_mtim */
+ return libpatch_create_timestamp_ts(buf, bufsize, &st->st_mtim, frac, zone, style);
+#else
+ return libpatch_create_timestamp_secs(buf, bufsize, &st->st_mtime, zone, style);
+#endif
+}
+
+/**
+ * Create file timestamp, from a file descriptor, for diff header
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param fd The file descriptor (mtime will be used)
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less)
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3) or fstat(3)
+ */
+inline size_t
+libpatch_create_timestamp_fd(char *buf, size_t bufsize, int fd,
+ unsigned frac, int zone, enum libpatch_style style)
+{
+ struct stat st;
+ return fstat(fd, &st) ? 0 : libpatch_create_timestamp_stat(buf, bufsize, &st, frac, zone, style);
+}
+
+/**
+ * Create file timestamp, from the current time, for diff header
+ *
+ * According to POSIX, diff(1) shall (not explicitly a strict requirement)
+ * use the current time in the timestamp if the file is standard input.
+ * GNU diff uses the current time where the "-" operand is specified.
+ * On Linux, pipes have timestamps so you would automatically get the
+ * current time if standard input is a pipe.
+ *
+ * @param buf Output buffer for the time stamp; will be NUL-terminated
+ * provided that the function returns a value that is no
+ * greater than `bufsize` (may or may not be NUL-terminated
+ * otherwise)
+ * @param bufsize The size of `buf`
+ * @param frac The number of subsecond decimals to include in the
+ * timestamp (of allowed), at most (the function may choose
+ * to include less); -1 for autoselection
+ * @param zone Whether to include the timezone (ignored unless optional)
+ * @param style The diff style the timestamp shall be generated for
+ * @return The length of the time stamp, including terminal NUL byte, 0 on
+ * failure. This value will exceed `bufsize` if `buf` is to small.
+ * It is possible that if `bufsize` is too small, an overly large
+ * value is returned.
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from localtime(3)
+ */
+size_t libpatch_create_timestamp_now(char *buf, size_t bufsize, signed frac, int zone,
+ enum libpatch_style style);
+
+/**
+ * Parse a timestamp from a diff header
+ *
+ * @param text The timestamp to parse (the mtime for a file)
+ * @param time_out Output parameter for the timestamp (second-resolution);
+ * `time_out->tm_isdst` and `time_out->tm_zone` will be undefined
+ * @param frac_out Output parameter for the pointer to where in `text`
+ * the fractions of the second start. If there are not
+ * fractions of the second in the timestamp, `NULL` will
+ * be stored in `*frac_out`.
+ * @param has_zone_out Output parameter for whether the timestamp contained a
+ * time zone
+ * @param style The diff style the timestamp is assumed to use
+ * @return Pointer to pass the last parsed character in `text`, or
+ * `NULL` on failure
+ * @throws EINVAL `style` is not a style that uses a diff header with timestamps
+ * @throws Any error from strptime(3)
+ */
+const char *libpatch_parse_timestamp(const char *text, struct tm *time_out, const char **frac_out,
+ int *has_zone_out, enum libpatch_style style);
+
+/**
+ * Get how a file label shall be formatted
+ *
+ * @param style The diff style
+ * @return How file labels shall be formatted,
+ * `LIBPATCH_LABEL_ERROR` on failure
+ * @throws EINVAL `style` is not a recognised style
+ */
+enum libpatch_label_format libpatch_get_label_format(enum libpatch_style style);
+
+/**
+ * Rewrite a diff string
+ *
+ * @param diff The output string, will be updated
+ * @param difflen The string length, may be updated
+ * @param spec Specification for desired diff string
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM Failed to allocate enough memory
+ *
+ * Update to `*diff` may be incomplete on failure (causing corruption)
+ */
+int libpatch_rewrite_diff2(struct libpatch_diff2 **diff, size_t *difflen, const struct libpatch_diff2_spec *spec);
+
+/**
+ * Check what properties a file's content must have
+ *
+ * Support for non-text files may be an extension of the standard
+ *
+ * @param style The diff style
+ * @param multifile Whether the created patchset may contain multiple diff scripts
+ * @return Bitmask of text properties the diff style requires:
+ * * 0x01: File must be LF-terminated
+ * (subject to additional bits in future version)
+ * -1 on failure; if 0, there no restrictions on the file
+ * @throws EINVAL `style` is not a recognised style
+ */
+int libpatch_style_requires_text(enum libpatch_style style, int multifile);
+#define LIBPATCH_STYLE_REQUIRES_LF_TERMINATION 0x01
+
+/**
+ * Check that a diff style supports a file's content
+ *
+ * @param file The file
+ * @param style The diff style
+ * @param multifile Whether the created patchset may contain multiple diff scripts
+ * @return 0 if the file is supported, positive if otherwise,
+ * and -1 on failure; if the value is positive, the
+ * bits as returned by `libpatch_style_requires_text`
+ * that denote a property that is not satisfied as set
+ * (bits for satisfied properties are cleared)
+ * @throws EINVAL `style` is not a recognised style
+ */
+int libpatch_check_file(struct libpatch_file *file, enum libpatch_style style, int multifile);
+
+/**
+ * Check if a file descriptor refers to /dev/null
+ *
+ * Since diff(1) can be used to find whether a file
+ * is identical or not to another file, diff(1) is
+ * often piped to /dev/null to discard the diff output
+ * (piping to /dev/zero has the same effect, but is
+ * not common practice, and could be useful for
+ * benchmarking). Therefore it can be a good idea to
+ * check if standard out is /dev/null, and if so,
+ * check whether the files are identical or not,
+ * rather than doing the expensive work of creating
+ * a patch file.
+ *
+ * @param fd The file descriptor
+ * @return 0 if the file descriptor is not /dev/null,
+ * 1 if the file descriptor is /dev/null,
+ * -1 on failure
+ * @throws Any error for fstat(3) or stat(3)
+ */
+int libpatch_is_devnull(int fd);
+
+/**
+ * Get the next hunk in a patch
+ *
+ * @param diff The diff string for the patch
+ * @param difflen The length of `diff`
+ * @param position The current position in `diff`, will be updated, start at 0
+ * @param ai The current position in the first file, will be updated, start at 0
+ * @param bi The current position in the second file, will be updated, start at 0
+ * @param an_out Output parameter for the number of lines in the first file within
+ * the hunk; before calling again, add this value to `*ai`
+ * @param bn_out Output parameter for the number of lines in the second file within
+ * the hunk; before calling again, add this value to `*bi`
+ * @param skip_context If 0, include `LIBPATCH_DIFF2_CONTEXT` lines, exclude them otherwise
+ * @return The number of elements in `diff` that the hunk uses,
+ * 0 if there are no more hunks in the patch;
+ * before calling again, add this value to `*position`
+ */
+size_t libpatch_next_hunk(struct libpatch_diff2 *diff, size_t difflen, size_t *position,
+ size_t *ai, size_t *bi, size_t *an_out, size_t *bn_out, int skip_context);
+
+/**
+ * Get the previous hunk in a patch
+ *
+ * @param diff The diff string for the patch
+ * @param position The position in `diff` where the current hunk ends, will be
+ * updated; the initial value shall be the number of elements in `diff`
+ * @param ai The current position in the first file, will be updated;
+ * the initial value shall be the number of lines in the first file
+ * @param bi The current position in the second file, will be updated;
+ * the initial value shall be the number of lines in the second file
+ * @param an_out Output parameter for the number of lines in the first file within
+ * the hunk (do _not_ use this value to modify `*ai`)
+ * @param bn_out Output parameter for the number of lines in the second file within
+ * the hunk (do _not_ use this value to modify `*bi`)
+ * @param skip_context If 0, include `LIBPATCH_DIFF2_CONTEXT` lines, exclude them otherwise
+ * @return The number of elements in `diff` that the hunk uses,
+ * 0 if there are no more hunks in the patch;
+ * (do _not_ use this value to modify `*position`)
+ */
+size_t libpatch_previous_hunk(struct libpatch_diff2 *diff, size_t *position,
+ size_t *ai, size_t *bi, size_t *an_out, size_t *bn_out, int skip_context);
+
+/**
+ * Print a patch in “unified” format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure (always successful in the current implementation)
+ */
+int libpatch_format_unified_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch in “copied” format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure (always successful in the current implementation)
+ */
+int libpatch_format_copied_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch in “normal” format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure (always successful in the current implementation)
+ */
+int libpatch_format_normal_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch in “alternative ed” format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_format_ed_alternative_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch in ed(1) format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_format_ed_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch in RCS format
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_format_rcs_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2);
+
+/**
+ * Print a patch
+ *
+ * @param printer Patch printer
+ * @param diff The diff string for the patch
+ * @param difflen The number of elements in `diff`
+ * @param file1 The first file
+ * @param file2 The second file
+ * @param style The format the patch file should use
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ * @throws EINVAL `style` is not a recognised style
+ */
+int libpatch_format_patch(struct libpatch_diff2_printer *printer, struct libpatch_diff2 *diff, size_t difflen,
+ const struct libpatch_file *file1, const struct libpatch_file *file2, enum libpatch_style style);
+
+/**
+ * Parse a patch file in “unified” format
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_parse_unified_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out);
+
+/**
+ * Parse a patch file in “copied” format
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_parse_copied_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out);
+
+/**
+ * Parse a patch file in “normal” format
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_parse_normal_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out);
+
+/**
+ * Parse a patch file in ed(1) format
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_parse_ed_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out);
+
+/**
+ * Parse a patch file in RCS format
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ */
+int libpatch_parse_rcs_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out);
+
+/**
+ * Parse a patch file
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @parma textend_out Unless `NULL`, parsing will at the end of the first patch
+ * in the file and the number of bytes included in the patch
+ * is stored to `*textend_out`
+ * @param patch_out Output parameter for the description of patch
+ * @param patchlen_out Output parameter for the number of eleemnts in `*patch_out`
+ * @param style The format the patch file is in
+ * @return 0 on success, -1 on failure
+ * @throws ENOMEM If enough memory cannot be allocated
+ * @throws EINVAL `style` is not a recognised style
+ * @throws EINVAL `style` is LIBPATCH_STYLE_ED_ALTERNATIVE
+ */
+int libpatch_parse_patch(const char *text, size_t textlen, size_t *textend_out,
+ struct libpatch_patch **patch_out, size_t *patchlen_out,
+ enum libpatch_style style);
+
+/**
+ * Determine how a patch file is formatted
+ *
+ * If the file does not contain any diff content,
+ * `LIBPATCH_STYLE_GARBAGE` will be returned, but
+ * it can still contain useful information that
+ * can be parsed by arbitrary parsing function.
+ * If `LIBPATCH_STYLE_GARBAGE` is returned, there
+ * is no point in calling the function again.
+ *
+ * @param text The content of the patch file
+ * @param textlen The size of the patch file
+ * @param offset Offset for where the function shall start
+ * reading the patch file to determine the
+ * format. Shall be 0 on the first. The function
+ * will update the value so that it is set
+ * appropriately for calling the function again
+ * to make a new guess. (This value shall not
+ * be used as an offset for parsing.)
+ * @return The style the patch file appears to use,
+ * or `LIBPATCH_STYLE_GARBAGE` if it couldn't
+ * be determined
+ */
+enum libpatch_style libpatch_guess_format(const char *text, size_t textlen, size_t *offset);
+
+/**
+ * Reverse the direction of a diff script
+ *
+ * @param diff The diff string, will be modified in-place
+ * @param difflen The number of elements in `diff`
+ */
+void libpatch_reverse_diff2(struct libpatch_diff2 *diff, size_t difflen);
+
+
+#endif