xref: /Universal-ctags/parsers/windres.c (revision 16a2541c0698bd8ee03c1be8172ef3191f6e695a)
1 /*
2  *	windres.c
3  *
4  *	Copyright (c) 2013, Frank Fesevur <ffes(at)users.sourceforge.net>
5  *
6  *	This source code is released for free distribution under the terms of the
7  *	GNU General Public License version 2 or (at your option) any later version.
8  *
9  *	This module contains functions for generating tags for Windows Resource files.
10  */
11 
12 #include "general.h"
13 
14 #include <string.h>
15 #include <ctype.h>
16 
17 #include "parse.h"
18 #include "read.h"
19 #include "routines.h"
20 
21 static int _blockDepth = 0;
22 
23 typedef enum _WindResKinds
24 {
25 	K_NONE = -1,
26 	K_DIALOG,
27 	K_MENU,
28 	K_ICON,
29 	K_BITMAP,
30 	K_CURSOR,
31 	K_FONT,
32 	K_VERSION,
33 	K_ACCELERATORS
34 } ResKind;
35 
36 static kindDefinition ResKinds [] = {
37 	{ true, 'd', "dialog",			"dialogs"		},
38 	{ true, 'm', "menu",			"menus"			},
39 	{ true, 'i', "icon",			"icons"			},
40 	{ true, 'b', "bitmap",			"bitmaps"		},
41 	{ true, 'c', "cursor",			"cursors"		},
42 	{ true, 'f', "font",			"fonts"			},
43 	{ true, 'v', "version",			"versions"		},
44 	{ true, 'a', "accelerators",	"accelerators"	}
45 };
46 
47 typedef enum _WindResParserState
48 {
49 	P_STATE_NONE,
50 	P_STATE_IN_COMMENT,
51 	P_STATE_IN_STATEMENTS,		/* Between first line of item and BEGIN/{ */
52 	P_STATE_IN_BLOCK,			/* In a BEGIN/END or {/} block */
53 	P_STATE_AT_END
54 } ResParserState;
55 
makeResTag(vString * name,ResKind kind)56 static void makeResTag(vString *name, ResKind kind)
57 {
58 	vStringStripTrailing(name);
59 	makeSimpleTag(name, kind);
60 	vStringClear(name);
61 }
62 
parseResDefinition(const unsigned char * line)63 static ResParserState parseResDefinition(const unsigned char *line)
64 {
65 	ResParserState state = P_STATE_NONE;
66 	vString *name, *type;
67 
68 	name = vStringNew();
69 	while (*line && !isspace((int) *line))
70 	{
71 		vStringPut(name, (int) *line);
72 		line++;
73 	}
74 
75 	while (*line && isspace((int) *line))
76 		line++;
77 
78 	type = vStringNew();
79 	while (*line && !isspace((int) *line))
80 	{
81 		vStringPut(type, (int) *line);
82 		line++;
83 	}
84 
85 	if (strcmp(vStringValue(type), "DIALOG") == 0 || strcmp(vStringValue(type), "DIALOGEX") == 0)
86 	{
87 		makeResTag(name, K_DIALOG);
88 		state = P_STATE_IN_STATEMENTS;
89 	}
90 	else if (strcmp(vStringValue(type), "MENU") == 0 || strcmp(vStringValue(type), "MENUEX") == 0)
91 	{
92 		makeResTag(name, K_MENU);
93 		state = P_STATE_IN_STATEMENTS;
94 	}
95 	else if (strcmp(vStringValue(type), "VERSIONINFO") == 0)
96 	{
97 		makeResTag(name, K_VERSION);
98 		state = P_STATE_IN_STATEMENTS;
99 	}
100 	else if (strcmp(vStringValue(type), "ACCELERATORS") == 0)
101 	{
102 		makeResTag(name, K_ACCELERATORS);
103 		state = P_STATE_IN_STATEMENTS;
104 	}
105 	else if (strcmp(vStringValue(type), "ICON") == 0)
106 	{
107 		makeResTag(name, K_ICON);
108 		state = P_STATE_NONE;
109 	}
110 	else if (strcmp(vStringValue(type), "CURSOR") == 0)
111 	{
112 		makeResTag(name, K_CURSOR);
113 		state = P_STATE_NONE;
114 	}
115 	else if (strcmp(vStringValue(type), "BITMAP") == 0)
116 	{
117 		makeResTag(name, K_BITMAP);
118 		state = P_STATE_NONE;
119 	}
120 	else if (strcmp(vStringValue(type), "FONT") == 0)
121 	{
122 		makeResTag(name, K_FONT);
123 		state = P_STATE_NONE;
124 	}
125 
126 	vStringDelete(name);
127 	vStringDelete(type);
128 
129 	return state;
130 }
131 
parseResLine(const unsigned char * line,ResParserState state)132 static ResParserState parseResLine(const unsigned char *line, ResParserState state)
133 {
134 	while (*line != '\0')	/* readLineFromInputFile returns NULL terminated strings */
135 	{
136 		while (isspace((int) *line))
137 			line++;
138 
139 		switch (state)
140 		{
141 			case P_STATE_NONE:
142 			{
143 				/* C-styled # line (#include, #define, etc), ignore rest of line */
144 				if (*line == '#')
145 				{
146 					return P_STATE_NONE;
147 				}
148 				/* single line comment, ignore rest of line */
149 				else if (*line == '/' && line[1] == '/')
150 				{
151 					return P_STATE_NONE;
152 				}
153 				/* multi-line comment */
154 				else if( *line == '/' && line[1] == '*' )
155 				{
156 					state = P_STATE_IN_COMMENT;
157 				}
158 				else if (isalnum((int) *line))
159 				{
160 					return parseResDefinition(line);
161 				}
162 				break;
163 			}
164 			case P_STATE_IN_COMMENT:
165 			{
166 				if (*line == '*' && line[1] == '/')
167 					state = P_STATE_NONE;
168 				break;
169 			}
170 			case P_STATE_IN_STATEMENTS:
171 			{
172 				/* First BEGIN block */
173 				if (*line == '{' || strcmp((const char *) line, "BEGIN") == 0)
174 				{
175 					_blockDepth = 1;
176 					return P_STATE_IN_BLOCK;
177 				}
178 				break;
179 			}
180 			case P_STATE_IN_BLOCK:
181 			{
182 				/* Nested BEGIN blocks? */
183 				if (*line == '{' || strcmp((const char *) line, "BEGIN") == 0)
184 				{
185 					_blockDepth++;
186 				}
187 				else if (*line == '}' || strcmp((const char *) line, "END") == 0)
188 				{
189 					if (_blockDepth == 1)
190 						return P_STATE_NONE;
191 					else
192 						_blockDepth--;
193 				}
194 				break;
195 			}
196 			case P_STATE_AT_END:
197 			{
198 				return state;
199 			}
200 		}
201 		if (line == NULL)
202 			return P_STATE_AT_END;
203 		line++;
204 	}
205 
206 	return state;
207 }
208 
findResTags(void)209 static void findResTags(void)
210 {
211 	const unsigned char *line;
212 	ResParserState state = P_STATE_NONE;
213 	_blockDepth = 0;
214 
215 	while ((line = readLineFromInputFile()) != NULL)
216 	{
217 		state = parseResLine(line, state);
218 		if (state == P_STATE_AT_END)
219 			return;
220 	}
221 }
222 
223 /* parser definition */
WindResParser(void)224 extern parserDefinition* WindResParser(void)
225 {
226 	static const char *const extensions [] = { "rc", NULL };
227 	parserDefinition* def = parserNew("WindRes");
228 	def->kindTable	= ResKinds;
229 	def->kindCount	= ARRAY_SIZE(ResKinds);
230 	def->extensions	= extensions;
231 	def->parser		= findResTags;
232 	return def;
233 }
234