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