Update to latest ctags main
[geany-mirror.git] / ctags / main / writer-json.c
blob4cb4194d0ffdcf26bed033a4685783ca352a30f1
1 /*
2 * Copyright (c) 2016, Aman Gupta
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * External interface to entry.c
8 */
10 #include "general.h" /* must always come first */
12 #include "debug.h"
13 #include "entry_p.h"
14 #include "field_p.h"
15 #include "mio.h"
16 #include "options_p.h"
17 #include "read.h"
18 #include "routines.h"
19 #include "ptag_p.h"
20 #include "writer_p.h"
23 #include <string.h>
25 #ifdef HAVE_JANSSON
26 #include <jansson.h>
28 #ifndef json_boolean /* compat with jansson < 2.4 */
29 #define json_boolean(val) ((val) ? json_true() : json_false())
30 #endif
33 static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
34 MIO * mio, const tagEntryInfo *const tag,
35 void *clientData);
37 static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
38 MIO * mio, const ptagDesc *desc,
39 const char *const fileName,
40 const char *const pattern,
41 const char *const parserName,
42 void *clientData);
44 tagWriter jsonWriter = {
45 .writeEntry = writeJsonEntry,
46 .writePtagEntry = writeJsonPtagEntry,
47 .printPtagByDefault = true,
48 .preWriteEntry = NULL,
49 .postWriteEntry = NULL,
50 .rescanFailedEntry = NULL,
51 .treatFieldAsFixed = NULL,
52 .defaultFileName = NULL,
55 static const char* escapeFieldValueRaw (const tagEntryInfo * tag, fieldType ftype, int fieldIndex)
57 const char *v;
58 if (doesFieldHaveRenderer(ftype, true))
59 v = renderFieldNoEscaping (ftype, tag, fieldIndex);
60 else
61 v = renderField (ftype, tag, fieldIndex);
63 return v;
66 static json_t* escapeFieldValue (const tagEntryInfo * tag, fieldType ftype, bool returnEmptyStringAsNoValue)
68 const char *str = escapeFieldValueRaw (tag, ftype, NO_PARSER_FIELD);
70 if (str)
72 unsigned int dt = getFieldDataType(ftype);
73 if (dt & FIELDTYPE_STRING)
75 if (dt & FIELDTYPE_BOOL && str[0] == '\0')
76 return json_false();
77 else
78 return json_string (str);
80 else if (dt & FIELDTYPE_INTEGER)
82 long tmp;
84 if (strToLong (str, 10, &tmp))
85 return json_integer (tmp);
86 else
87 return NULL;
89 else if (dt & FIELDTYPE_BOOL)
91 /* TODO: This must be fixed when new boolean field is added.
92 Currently only `file:' field use this. */
93 return json_boolean (strcmp ("-", str)); /* "-" -> false */
95 AssertNotReached ();
96 return NULL;
98 else if (returnEmptyStringAsNoValue)
99 return json_false();
100 else
101 return NULL;
104 static void renderExtensionFieldMaybe (int xftype, const tagEntryInfo *const tag, json_t *response)
106 const char *fname = getFieldName (xftype);
108 if (fname && doesFieldHaveRenderer (xftype, false) && isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag))
110 switch (xftype)
112 case FIELD_LINE_NUMBER:
113 json_object_set_new (response, fname,
114 json_integer (tag->lineNumber));
115 break;
116 case FIELD_FILE_SCOPE:
117 json_object_set_new (response, fname,
118 json_boolean(1));
119 break;
120 default:
121 json_object_set_new (response, fname,
122 escapeFieldValue (tag, xftype, false));
127 static void addParserFields (json_t *response, const tagEntryInfo *const tag)
129 unsigned int i;
131 for (i = 0; i < tag->usedParserFields; i++)
133 const tagField *f = getParserFieldForIndex(tag, i);
134 fieldType ftype = f->ftype;
135 if (! isFieldEnabled (ftype))
136 continue;
138 unsigned int dt = getFieldDataType (ftype);
139 json_t *o;
140 if (dt & FIELDTYPE_STRING)
142 const char *str = escapeFieldValueRaw (tag, ftype, i);
143 if (dt & FIELDTYPE_BOOL && str[0] == '\0')
144 o = json_false ();
145 else
146 o = json_string (str);
148 else if (dt & FIELDTYPE_INTEGER)
150 /* NOT IMPLEMENTED YET */
151 AssertNotReached ();
152 o = json_null ();
154 else if (dt & FIELDTYPE_BOOL)
155 o = json_true ();
156 else
158 AssertNotReached ();
159 o = json_null ();
162 json_object_set_new (response, getFieldName (ftype), o);
166 static void addExtensionFields (json_t *response, const tagEntryInfo *const tag)
168 int k;
170 /* FIELD_KIND has no name; getFieldName (FIELD_KIND) returns NULL.
171 FIELD_KIND_LONG does, too.
172 That cannot be changed to keep the compatibility of tags file format.
173 Use FIELD_KIND_KEY instead */
174 if (isFieldEnabled (FIELD_KIND) || isFieldEnabled (FIELD_KIND_LONG))
175 enableField (FIELD_KIND_KEY, true, false);
177 /* FIELD_SCOPE has no name; getFieldName (FIELD_KIND_KEY) returns NULL.
178 That cannot be changed to keep the compatibility of tags file format.
179 Use FIELD_SCOPE_KEY and FIELD_SCOPE_KIND_LONG instead. */
180 if (isFieldEnabled (FIELD_SCOPE))
182 enableField (FIELD_SCOPE_KEY, true, false);
183 enableField (FIELD_SCOPE_KIND_LONG, true, false);
186 for (k = FIELD_EXTENSION_START; k <= FIELD_BUILTIN_LAST; k++)
187 renderExtensionFieldMaybe (k, tag, response);
190 static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
191 MIO * mio, const tagEntryInfo *const tag,
192 void *clientData CTAGS_ATTR_UNUSED)
194 int length = 0;
195 json_t *response = json_pack ("{ss}", "_type", "tag");
197 if (isFieldEnabled (FIELD_NAME))
199 json_t *name = json_string (tag->name);
200 if (name == NULL)
201 goto out;
202 json_object_set_new (response, "name", name);
204 if (isFieldEnabled (FIELD_INPUT_FILE))
205 json_object_set_new (response, "path", json_string (tag->sourceFileName));
206 if (isFieldEnabled (FIELD_PATTERN))
208 json_t *pat = escapeFieldValue(tag, FIELD_PATTERN, true);
209 json_object_set_new (response, "pattern", pat);
212 if (includeExtensionFlags ())
214 addExtensionFields (response, tag);
215 addParserFields (response, tag);
218 /* Print nothing if RESPONSE has only "_type" field. */
219 if (json_object_size (response) == 1)
220 goto out;
222 char *buf = json_dumps (response, JSON_PRESERVE_ORDER);
223 length = mio_printf (mio, "%s\n", buf);
225 free (buf);
226 out:
227 json_decref (response);
229 return length;
232 static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
233 MIO * mio, const ptagDesc *desc,
234 const char *const fileName,
235 const char *const pattern,
236 const char *const parserName,
237 void *clientData CTAGS_ATTR_UNUSED)
239 #define OPT(X) ((X)?(X):"")
240 json_t *response;
242 if (parserName)
244 response = json_pack ("{ss ss ss ss ss}",
245 "_type", "ptag",
246 "name", desc->name,
247 "parserName", parserName,
248 "path", OPT(fileName),
249 "pattern", OPT(pattern));
251 else
253 response = json_pack ("{ss ss ss ss}",
254 "_type", "ptag",
255 "name", desc->name,
256 "path", OPT(fileName),
257 "pattern", OPT(pattern));
260 char *buf = json_dumps (response, JSON_PRESERVE_ORDER);
261 int length = mio_printf (mio, "%s\n", buf);
262 free (buf);
263 json_decref (response);
265 return length;
266 #undef OPT
269 extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED,
270 const void *data CTAGS_ATTR_UNUSED)
272 return writePseudoTag (desc,
273 "0.0",
274 "in development",
275 NULL);
278 #else /* HAVE_JANSSON */
280 tagWriter jsonWriter = {
281 .writeEntry = NULL,
282 .writePtagEntry = NULL,
283 .preWriteEntry = NULL,
284 .postWriteEntry = NULL,
285 .defaultFileName = "-",
288 extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED,
289 const void *data CTAGS_ATTR_UNUSED)
291 return false;
294 #endif