/* */
This source file includes following definitions.
- copy_langmap_converting_cpp
- start_ctags
- terminate_ctags
- get_line
- put_line
- parser
1 /*
2 * Copyright (c) 2010
3 * Tama Communications Corporation
4 *
5 * This file is part of GNU GLOBAL.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <sys/types.h>
25 #if !defined(_WIN32) || defined(__CYGWIN__)
26 #include <sys/wait.h>
27 #endif
28 #include <assert.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #ifdef STDC_HEADERS
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #else
38 #include <strings.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #include "parser.h"
45
46 /*
47 * Function layer plugin parser sample
48 */
49
50 #define TERMINATOR "###terminator###"
51 #define LANGMAP_OPTION "--langmap="
52 #define INITIAL_BUFSIZE 1024
53
54 static char *argv[] = {
55 EXUBERANT_CTAGS,
56 NULL,
57 #ifdef USE_TYPE_STRING
58 "--gtags",
59 #endif
60 "-xu",
61 "--filter",
62 "--filter-terminator=" TERMINATOR "\n",
63 "--format=1",
64 NULL
65 };
66 static pid_t pid;
67 static FILE *ip, *op;
68 static char *linebuf;
69 static size_t bufsize;
70 static char *ctagsnotfound = "Exuberant Ctags not found. Please see ./configure --help.";
71
72 static void
73 copy_langmap_converting_cpp(char *dst, const char *src)
74 {
75 const char *p;
76
77 if (strncmp(src, "cpp:", 4) == 0) {
78 memcpy(dst, "c++:", 4);
79 dst += 4;
80 src += 4;
81 }
82 while ((p = strstr(src, ",cpp:")) != NULL) {
83 memcpy(dst, src, p - src);
84 dst += p - src;
85 memcpy(dst, ",c++:", 5);
86 dst += 5;
87 src = p + 5;
88 }
89 strcpy(dst, src);
90 }
91
92 static void
93 start_ctags(const struct parser_param *param)
94 {
95 int opipe[2], ipipe[2];
96
97 if (strlen(EXUBERANT_CTAGS) == 0 || !strcmp(EXUBERANT_CTAGS, "no"))
98 param->die(ctagsnotfound);
99 argv[1] = malloc(sizeof(LANGMAP_OPTION) + strlen(param->langmap));
100 if (argv[1] == NULL)
101 param->die("short of memory.");
102 memcpy(argv[1], LANGMAP_OPTION, sizeof(LANGMAP_OPTION) - 1);
103 copy_langmap_converting_cpp(argv[1] + sizeof(LANGMAP_OPTION) - 1, param->langmap);
104
105 if (pipe(opipe) < 0 || pipe(ipipe) < 0)
106 param->die("cannot create pipe.");
107 pid = fork();
108 if (pid == 0) {
109 /* child process */
110 close(opipe[1]);
111 close(ipipe[0]);
112 if (dup2(opipe[0], STDIN_FILENO) < 0
113 || dup2(ipipe[1], STDOUT_FILENO) < 0)
114 param->die("dup2 failed.");
115 close(opipe[0]);
116 close(ipipe[1]);
117 execvp(EXUBERANT_CTAGS, argv);
118 param->die("execvp failed.");
119 }
120 /* parent process */
121 if (pid < 0)
122 param->die("fork failed.");
123 free(argv[1]);
124 close(opipe[0]);
125 close(ipipe[1]);
126 ip = fdopen(ipipe[0], "r");
127 op = fdopen(opipe[1], "w");
128 if (ip == NULL || op == NULL)
129 param->die("fdopen failed.");
130
131 bufsize = INITIAL_BUFSIZE;
132 linebuf = malloc(bufsize);
133 if (linebuf == NULL)
134 param->die("short of memory.");
135 }
136
137 #ifdef __GNUC__
138 static void terminate_ctags(void) __attribute__((destructor));
139 #endif
140
141 static void
142 terminate_ctags(void)
143 {
144 if (op == NULL)
145 return;
146 free(linebuf);
147 fclose(op);
148 fclose(ip);
149 while (waitpid(pid, NULL, 0) < 0 && errno == EINTR)
150 ;
151 }
152
153 static char *
154 get_line(const struct parser_param *param)
155 {
156 size_t linelen = 0;
157
158 for (;;) {
159 if (fgets(linebuf + linelen, bufsize - linelen, ip) == NULL) {
160 if (linelen == 0)
161 return NULL;
162 break;
163 }
164 linelen += strlen(linebuf + linelen);
165 if (linelen < bufsize - 1 || linebuf[linelen - 1] == '\n'
166 || feof(ip))
167 break;
168 bufsize *= 2;
169 linebuf = realloc(linebuf, bufsize);
170 if (linebuf == NULL)
171 param->die("short of memory.");
172 }
173 while (linelen-- > 0
174 && (linebuf[linelen] == '\n' || linebuf[linelen] == '\r'))
175 linebuf[linelen] = '\0';
176 return linebuf;
177 }
178
179 static void
180 put_line(char *ctags_x, const struct parser_param *param)
181 {
182 int lineno;
183 int type = PARSER_DEF;
184 char *p, *tagname, *filename;
185
186 #ifdef USE_TYPE_STRING
187 /*
188 * Output of ctags:
189 * ctags -x ...
190 * main 326 global/global.c main(int argc, char **argv)
191 *
192 * ctags -x --gtags ...
193 * D main 326 global/global.c main(int argc, char **argv)
194 */
195 switch (*ctags_x) {
196 case 'D':
197 type = PARSER_DEF;
198 break;
199 case 'R':
200 type = PARSER_REF_SYM;
201 break;
202 default:
203 param->die("unexpected type string.");
204 }
205 /* skip type string */
206 while (*ctags_x && !isspace((unsigned char)*ctags_x))
207 ctags_x++;
208 while (*ctags_x && isspace((unsigned char)*ctags_x))
209 ctags_x++;
210 #endif
211 filename = strstr(ctags_x, param->file);
212 if (filename == NULL || filename == ctags_x)
213 return;
214 p = filename - 1;
215 if (!isspace((unsigned char)*p))
216 return;
217 while (p >= ctags_x && isspace((unsigned char)*p))
218 *p-- = '\0';
219 if (p < ctags_x)
220 return;
221 if (!isdigit((unsigned char)*p))
222 return;
223 while (p >= ctags_x && isdigit((unsigned char)*p))
224 p--;
225 if (p < ctags_x)
226 return;
227 lineno = atoi(p + 1);
228 if (!isspace((unsigned char)*p))
229 return;
230 while (p >= ctags_x && isspace((unsigned char)*p))
231 *p-- = '\0';
232 if (p < ctags_x)
233 return;
234 while (p >= ctags_x && !isspace((unsigned char)*p))
235 p--;
236 tagname = p + 1;
237 p = filename + strlen(param->file);
238 if (*p != '\0') {
239 if (!isspace((unsigned char)*p))
240 return;
241 *p++ = '\0';
242 while (isspace((unsigned char)*p))
243 p++;
244 }
245 param->put(type, tagname, lineno, filename, p, param->arg);
246 }
247
248 void
249 parser(const struct parser_param *param)
250 {
251 char *ctags_x;
252
253 assert(param->size >= sizeof(*param));
254
255 if (op == NULL)
256 start_ctags(param);
257
258 /* Write path of input file to pipe. */
259 fputs(param->file, op);
260 putc('\n', op);
261 fflush(op);
262
263 /* Read output of ctags command. */
264 for (;;) {
265 ctags_x = get_line(param);
266 if (ctags_x == NULL)
267 param->die("unexpected EOF.");
268 if (strcmp(ctags_x, TERMINATOR) == 0)
269 break;
270 put_line(ctags_x, param);
271 }
272 }
/* */