1 /*
2 * Copyright (c) 2009, Eric Forgeot
3 * Copyright (c) 2014, Colomban Wendling <colomban@geany.org>
4 *
5 * Based on work by Jon Strait
6 *
7 * This source code is released for free distribution under the terms of the
8 * GNU General Public License version 2 or (at your opinion) any later version.
9 *
10 * This module contains functions for generating tags for Txt2tags files
11 * (https://en.wikipedia.org/wiki/Txt2tags).
12 */
13
14 /*
15 * INCLUDE FILES
16 */
17 #include "general.h" /* must always come first */
18
19 #include <ctype.h>
20 #include <string.h>
21
22 #include "parse.h"
23 #include "read.h"
24 #include "nestlevel.h"
25 #include "vstring.h"
26 #include "routines.h"
27 #include "entry.h"
28
29
30 #define SCOPE_SEPARATOR "\"\""
31
32 /*
33 * DATA DEFINITIONS
34 */
35
36 typedef enum {
37 K_SECTION = 0
38 } Txt2tagsKind;
39
40 static scopeSeparator Txt2TagsSeparators [] = {
41 { KIND_WILDCARD_INDEX, SCOPE_SEPARATOR }
42 };
43
44 static kindDefinition Txt2tagsKinds[] = {
45 { true, 's', "section", "sections",
46 ATTACH_SEPARATORS(Txt2TagsSeparators) },
47 };
48
49 struct nestingLevelUserData {
50 int indentation;
51 };
52 #define NL_INDENTATION(nl) ((struct nestingLevelUserData *)nestingLevelGetUserData(nl))->indentation
53
54 /*
55 * FUNCTION DEFINITIONS
56 */
57
makeTxt2tagsTag(const vString * const name,const NestingLevels * const nls,Txt2tagsKind type)58 static int makeTxt2tagsTag (const vString* const name,
59 const NestingLevels *const nls,
60 Txt2tagsKind type)
61 {
62 tagEntryInfo e;
63 NestingLevel *nl;
64 initTagEntry (&e, vStringValue(name), type);
65
66 nl = nestingLevelsGetCurrent (nls);
67 if (nl)
68 e.extensionFields.scopeIndex = nl->corkIndex;
69
70 return makeTagEntry(&e);
71 }
72
73 /* matches: ^ *[=_-]{20,} *$ */
isTxt2tagsLine(const unsigned char * line)74 static bool isTxt2tagsLine (const unsigned char *line)
75 {
76 unsigned int len;
77
78 while (isspace(*line)) line++;
79 for (len = 0; *line == '=' || *line == '-' || *line == '_'; len++)
80 line++;
81 while (isspace(*line)) line++;
82
83 return len >= 20 && *line == 0;
84 }
85
parseTxt2tagsTitle(const unsigned char * line,vString * const title,int * const depth_)86 static bool parseTxt2tagsTitle (const unsigned char *line,
87 vString *const title,
88 int *const depth_)
89 {
90 const int MAX_TITLE_DEPTH = 5; /* maximum length of a title delimiter */
91 unsigned char delim;
92 int delim_delta = 0;
93 const unsigned char *end;
94
95 /* skip leading spaces, but no tabs (probably because they create quotes) */
96 while (*line == ' ') line++;
97
98 /* normal/numbered titles */
99 if (*line != '=' && *line != '+')
100 return false;
101
102 delim = *line;
103
104 /* find the start delimiter length */
105 while (*line == delim && delim_delta < MAX_TITLE_DEPTH+1)
106 {
107 line++;
108 delim_delta++;
109 }
110 while (isspace(*line))
111 line++;
112
113 if (delim_delta > MAX_TITLE_DEPTH) /* invalid */
114 return false;
115
116 *depth_ = delim_delta;
117
118 /* find the end delimiter */
119 end = line + strlen((const char *) line) - 1;
120 while (end > line && isspace(*end)) end--;
121 /* skip a possible label: \[[A-Za-z0-9_-]+\] */
122 if (*end == ']')
123 {
124 end--;
125 while (end > line && (isalnum(*end) || *end == '_' || *end == '-'))
126 end--;
127 if (*end != '[') /* invalid */
128 return false;
129 end--;
130 }
131 while (end > line && *end == delim && delim_delta >= 0)
132 {
133 delim_delta--;
134 end--;
135 }
136 while (end > line && isspace(*end)) end--;
137 end++;
138
139 /* if start and end delimiters are not identical, or the the name is empty */
140 if (delim_delta != 0 || (end - line) <= 0)
141 return false;
142
143 vStringNCopyS(title, (const char *) line, end - line);
144 return true;
145 }
146
findTxt2tagsTags(void)147 static void findTxt2tagsTags (void)
148 {
149 NestingLevels *nls = nestingLevelsNew(sizeof(struct nestingLevelUserData));
150 vString *name = vStringNew();
151 const unsigned char *line;
152
153 while ((line = readLineFromInputFile()) != NULL)
154 {
155 int depth;
156
157 if (isTxt2tagsLine(line))
158 ; /* skip not to improperly match titles */
159 else if (parseTxt2tagsTitle(line, name, &depth))
160 {
161 NestingLevel *nl = nestingLevelsGetCurrent(nls);
162 int r;
163
164 while (nl && NL_INDENTATION(nl) >= depth)
165 {
166 nestingLevelsPop(nls);
167 nl = nestingLevelsGetCurrent(nls);
168 }
169
170 r = makeTxt2tagsTag(name, nls, K_SECTION);
171 nestingLevelsPush(nls, r);
172 nl = nestingLevelsGetCurrent(nls);
173 NL_INDENTATION(nl) = depth;
174 }
175 }
176 vStringDelete (name);
177 nestingLevelsFree(nls);
178 }
179
Txt2tagsParser(void)180 extern parserDefinition* Txt2tagsParser (void)
181 {
182 static const char *const patterns [] = { "*.t2t", NULL };
183 static const char *const extensions [] = { "t2t", NULL };
184 parserDefinition* const def = parserNew ("Txt2tags");
185
186 def->kindTable = Txt2tagsKinds;
187 def->kindCount = ARRAY_SIZE (Txt2tagsKinds);
188 def->patterns = patterns;
189 def->extensions = extensions;
190 def->parser = findTxt2tagsTags;
191 def->useCork = CORK_QUEUE;
192 return def;
193 }
194
195