xref: /Universal-ctags/parsers/txt2tags.c (revision bc29a326c2717ce7dcaef19f811d6e5738ef98a2)
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