/* 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