1 /*
2 *
3 *   Copyright (c) 2000-2001, Darren Hiebert
4 *
5 *   This source code is released for free distribution under the terms of the
6 *   GNU General Public License version 2 or (at your option) any later version.
7 *
8 *   This module contains functions for generating tags for diff files (based on Sh parser).
9 */
10 
11 /*
12 *   INCLUDE FILES
13 */
14 #include "general.h"	/* must always come first */
15 
16 #include <ctype.h>
17 #include <string.h>
18 
19 #include "entry.h"
20 #include "parse.h"
21 #include "routines.h"
22 #include "read.h"
23 #include "vstring.h"
24 
25 /*
26 *   DATA DEFINITIONS
27 */
28 typedef enum {
29 	K_MODIFIED_FILE,
30 	K_NEW_FILE,
31 	K_DELETED_FILE,
32 	K_HUNK,
33 } diffKind;
34 
35 static kindDefinition DiffKinds [] = {
36 	{ true, 'm', "modifiedFile",  "modified files"},
37 	{ true, 'n', "newFile",       "newly created files"},
38 	{ true, 'd', "deletedFile",   "deleted files"},
39 	{ true, 'h', "hunk",          "hunks"},
40 };
41 
42 enum {
43 	DIFF_DELIM_MINUS = 0,
44 	DIFF_DELIM_PLUS
45 };
46 
47 static const char *DiffDelims[2] = {
48 	"--- ",
49 	"+++ "
50 };
51 
52 static const char *HunkDelim[2] = {
53 	"@@ ",
54 	" @@",
55 };
56 
57 /*
58 *   FUNCTION DEFINITIONS
59 */
60 
stripAbsolute(const unsigned char * filename)61 static const unsigned char *stripAbsolute (const unsigned char *filename)
62 {
63 	const unsigned char *tmp;
64 
65 	/* strip any absolute path */
66 	if (*filename == '/' || *filename == '\\')
67 	{
68 		bool skipSlash = true;
69 
70 		tmp = (const unsigned char*) strrchr ((const char*) filename,  '/');
71 		if (tmp == NULL)
72 		{	/* if no / is contained try \ in case of a Windows filename */
73 			tmp = (const unsigned char*) strrchr ((const char*) filename, '\\');
74 			if (tmp == NULL)
75 			{	/* last fallback, probably the filename doesn't contain a path, so take it */
76 				tmp = filename;
77 				skipSlash = false;
78 			}
79 		}
80 
81 		/* skip the leading slash or backslash */
82 		if (skipSlash)
83 			tmp++;
84 	}
85 	else
86 		tmp = filename;
87 
88 	return tmp;
89 }
90 
parseHunk(const unsigned char * cp,vString * hunk,int scope_index)91 static int parseHunk (const unsigned char* cp, vString *hunk, int scope_index)
92 {
93 	/*
94 	   example input: @@ -0,0 +1,134 @@
95 	   expected output: -0,0 +1,134
96 	*/
97 
98 	const char *next_delim;
99 	const char *start, *end;
100 	const char *c;
101 	int i = CORK_NIL;
102 
103 	cp += 3;
104 	start = (const char*)cp;
105 
106 	if (*start != '-')
107 		return i;
108 
109 	next_delim = strstr ((const char*)cp, HunkDelim[1]);
110 	if ((next_delim == NULL)
111 	    || (! (start < next_delim )))
112 		return i;
113 	end = next_delim;
114 	if (! ( '0' <= *( end - 1 ) && *( end - 1 ) <= '9'))
115 		return i;
116 	for (c = start; c < end; c++)
117 		if (*c == '\t')
118 			return i;
119 	vStringNCopyS (hunk, start, end - start);
120 	i = makeSimpleTag (hunk, K_HUNK);
121 	tagEntryInfo *e =  getEntryInCorkQueue (i);
122 	if (e && scope_index > CORK_NIL)
123 		e->extensionFields.scopeIndex = scope_index;
124 	return i;
125 }
126 
markTheLastTagAsDeletedFile(int scope_index)127 static void markTheLastTagAsDeletedFile (int scope_index)
128 {
129 	tagEntryInfo *e =  getEntryInCorkQueue (scope_index);
130 
131 	if (e)
132 		e->kindIndex = K_DELETED_FILE;
133 }
134 
findDiffTags(void)135 static void findDiffTags (void)
136 {
137 	vString *filename = vStringNew ();
138 	vString *hunk = vStringNew ();
139 	const unsigned char *line, *tmp;
140 	int delim = DIFF_DELIM_MINUS;
141 	diffKind kind;
142 	int scope_index = CORK_NIL;
143 
144 	while ((line = readLineFromInputFile ()) != NULL)
145 	{
146 		const unsigned char* cp = line;
147 
148 		if (strncmp ((const char*) cp, DiffDelims[delim], 4u) == 0)
149 		{
150 			scope_index = CORK_NIL;
151 			cp += 4;
152 			if (isspace ((int) *cp)) continue;
153 			/* when original filename is /dev/null use the new one instead */
154 			if (delim == DIFF_DELIM_MINUS &&
155 				strncmp ((const char*) cp, "/dev/null", 9u) == 0 &&
156 				(cp[9] == 0 || isspace (cp[9])))
157 			{
158 				delim = DIFF_DELIM_PLUS;
159 				continue;
160 			}
161 
162 			tmp = stripAbsolute (cp);
163 
164 			if (tmp != NULL)
165 			{
166 				while (! isspace(*tmp) && *tmp != '\0')
167 				{
168 					vStringPut(filename, *tmp);
169 					tmp++;
170 				}
171 
172 				if (delim == DIFF_DELIM_PLUS)
173 					kind = K_NEW_FILE;
174 				else
175 					kind = K_MODIFIED_FILE;
176 				scope_index = makeSimpleTag (filename, kind);
177 				vStringClear (filename);
178 			}
179 
180 			/* restore default delim */
181 			delim = DIFF_DELIM_MINUS;
182 		}
183 		else if ((scope_index > CORK_NIL)
184 			 && (strncmp ((const char*) cp, DiffDelims[1], 4u) == 0))
185 		{
186 			cp += 4;
187 			if (isspace ((int) *cp)) continue;
188 			/* when modified filename is /dev/null, the original name is deleted. */
189 			if (strncmp ((const char*) cp, "/dev/null", 9u) == 0 &&
190 			    (cp[9] == 0 || isspace (cp[9])))
191 				markTheLastTagAsDeletedFile (scope_index);
192 		}
193 		else if (strncmp ((const char*) cp, HunkDelim[0], 3u) == 0)
194 		{
195 			if (parseHunk (cp, hunk, scope_index) != CORK_NIL)
196 				vStringClear (hunk);
197 		}
198 	}
199 	vStringDelete (hunk);
200 	vStringDelete (filename);
201 }
202 
DiffParser(void)203 extern parserDefinition* DiffParser (void)
204 {
205 	static const char *const extensions [] = { "diff", "patch", NULL };
206 	parserDefinition* const def = parserNew ("Diff");
207 	def->kindTable      = DiffKinds;
208 	def->kindCount  = ARRAY_SIZE (DiffKinds);
209 	def->extensions = extensions;
210 	def->parser     = findDiffTags;
211 	def->useCork    = CORK_QUEUE;
212 	return def;
213 }
214