aboutsummaryrefslogtreecommitdiffstats
path: root/libpatch_guess_format.c
blob: 48ce419f87425f2079d075c3d6c2f8742e3776ed (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* See LICENSE file for copyright and license details. */
#include "common.h"


static int
is_unsigned(const char *text, size_t textlen, size_t *off)
{
	if (*off == textlen || !isdigit(text[*off]))
		return 0;
	while (isdigit(++*off));
	return 1;
}


enum libpatch_style
libpatch_guess_format(const char *text, size_t textlen, size_t *offset)
{
	enum libpatch_style guess = LIBPATCH_STYLE_GARBAGE;
	size_t off = offset ? *offset : 0;
	size_t two_nums;

	while (off < textlen) {
		if (text[off] == '+') {
			if (textlen - off < 3 || strncmp(&text[off], "+++", 3))
				goto skip_line;
			goto unified;

		} else if (text[off] == '*') {
			if (textlen - off < 3 || strncmp(&text[off], "***", 3))
				goto skip_line;
			goto copied;

		} else if (text[off] == '@') {
			if (textlen - off < 3 || strncmp(&text[off], "@@ ", 3))
				goto skip_line;
			goto unified;

		} else if (text[off] == 'a') {
			off++;
			if (!is_unsigned(text, textlen, &off))
				goto skip_line;
			if (off != textlen && text[off] == ' ')
				off++;
			if (is_unsigned(text, textlen, &off))
				goto rcs;
			else
				goto ed_alternative;

		} else if (text[off] == 'd') {
			off++;
			if (!is_unsigned(text, textlen, &off))
				goto skip_line;
			if (off != textlen && text[off] == ' ')
				off++;
			if (!is_unsigned(text, textlen, &off))
				goto ed_alternative;
			guess = LIBPATCH_STYLE_RCS;
			/* could also be LIBPATCH_STYLE_ED_ALTERNATIVE,
			 * whould cannot be parsed so LIBPATCH_STYLE_RCS
			 * is preferred if the patch happens to be valid
			 * in both */
			goto skip_line;

		} else if (text[off] == 'c') {
			off++;
			if (!is_unsigned(text, textlen, &off))
				goto skip_line;
			goto ed_alternative;

		} else if (isdigit(text[off])) {
			if (!is_unsigned(text, textlen, &off))
				goto skip_line;
			two_nums = (off != textlen && text[off] == ',');
			if (two_nums) {
				off += 1;
				if (!is_unsigned(text, textlen, &off))
					goto skip_line;
			}
			switch (text[off]) {
			case 'i':
				goto ed;
			case 'a':
				if (two_nums)
					goto normal;
				/* fall through */
			case 'c':
			case 'd':
				off++;
				if (off == textlen || text[off] == '\n')
					goto ed;
				else if (isdigit(text[off]))
					goto normal;
				/* fall through */
			default:
				goto skip_line;
			}

		} else {
		skip_line:
			while (off != textlen && text[off] != '\n')
				off++;
			off += (off != textlen);
		}
	}

	*offset = off;
	return guess;

determined:
	while (off != textlen && text[off] != '\n')
		off++;
	*offset = off += (off != textlen);
	return guess;

normal:         guess = LIBPATCH_STYLE_NORMAL;         goto determined;
copied:         guess = LIBPATCH_STYLE_COPIED;         goto determined;
unified:        guess = LIBPATCH_STYLE_UNIFIED;        goto determined;
ed:             guess = LIBPATCH_STYLE_ED;             goto determined;
ed_alternative: guess = LIBPATCH_STYLE_ED_ALTERNATIVE; goto determined;
rcs:            guess = LIBPATCH_STYLE_RCS;            goto determined;
}