/* See LICENSE file for copyright and license details. */ #ifndef LIBPATCH_H #define LIBPATCH_H #include #include #include #include #include #include /** * 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 , * “\” 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 , * “\” 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 , * “\” 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 -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 */ unsigned lf_terminated; /** * The number of lines in the file */ size_t nlines; /** * Each line in the file, these are not 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 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 * * @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