xref: /Universal-ctags/parsers/asciidoc.c (revision 6b1a862e526d5017f9f212a321f59d67c859d521)
11eb07f9cSHadriel Kaplan /*
21eb07f9cSHadriel Kaplan  *
3734f4111SHadriel Kaplan  *  Copyright (c) 2007-2011, Nick Treleaven
41eb07f9cSHadriel Kaplan  * 	Copyright (c) 2012, Lex Trotman
51eb07f9cSHadriel Kaplan  *
61eb07f9cSHadriel Kaplan  *   This source code is released for free distribution under the terms of the
7734f4111SHadriel Kaplan  *   GNU General Public License version 2 or (at your option) any later version.
81eb07f9cSHadriel Kaplan  *
91eb07f9cSHadriel Kaplan  * This module contains functions for generating tags for asciidoc files.
101eb07f9cSHadriel Kaplan  *
11734f4111SHadriel Kaplan  * Based on Rest code by Nick Treleaven, see rest.c
12734f4111SHadriel Kaplan  *
131eb07f9cSHadriel Kaplan  * This code was ported from geany git commit 40396a3 at:
141eb07f9cSHadriel Kaplan  *   https://github.com/geany/geany/blob/master/ctags/parsers/asciidoc.c
151eb07f9cSHadriel Kaplan  * with the changes in geany's PR #1263, with some changes to work in uctags.
161eb07f9cSHadriel Kaplan  */
171eb07f9cSHadriel Kaplan 
181eb07f9cSHadriel Kaplan /*
191eb07f9cSHadriel Kaplan  *   INCLUDE FILES
201eb07f9cSHadriel Kaplan  */
211eb07f9cSHadriel Kaplan #include "general.h"	/* must always come first */
221eb07f9cSHadriel Kaplan 
231eb07f9cSHadriel Kaplan #include <ctype.h>
241eb07f9cSHadriel Kaplan #include <string.h>
251eb07f9cSHadriel Kaplan 
2638c27701SHadriel Kaplan #include "debug.h"
271eb07f9cSHadriel Kaplan #include "entry.h"
281eb07f9cSHadriel Kaplan #include "parse.h"
291eb07f9cSHadriel Kaplan #include "read.h"
301eb07f9cSHadriel Kaplan #include "vstring.h"
311eb07f9cSHadriel Kaplan #include "nestlevel.h"
321eb07f9cSHadriel Kaplan #include "routines.h"
331eb07f9cSHadriel Kaplan 
341eb07f9cSHadriel Kaplan /*
351eb07f9cSHadriel Kaplan  *   DATA DEFINITIONS
361eb07f9cSHadriel Kaplan  */
371eb07f9cSHadriel Kaplan typedef enum {
381eb07f9cSHadriel Kaplan 	K_CHAPTER = 0,
391eb07f9cSHadriel Kaplan 	K_SECTION,
401eb07f9cSHadriel Kaplan 	K_SUBSECTION,
411eb07f9cSHadriel Kaplan 	K_SUBSUBSECTION,
421eb07f9cSHadriel Kaplan 	K_LEVEL4SECTION,
43ab92488cSHadriel Kaplan 	/* level-5 section not in here because it only works for one-line */
44d776d040SHadriel Kaplan 	SECTION_COUNT, /* this is the same as level-5 kind number */
45d776d040SHadriel Kaplan 	K_ANCHOR
461eb07f9cSHadriel Kaplan } asciidocKind;
471eb07f9cSHadriel Kaplan 
481eb07f9cSHadriel Kaplan /*
491eb07f9cSHadriel Kaplan  * The following kind letters are based on the markdown parser kinds,
501eb07f9cSHadriel Kaplan  * and thus different than geany's.
511eb07f9cSHadriel Kaplan  */
521eb07f9cSHadriel Kaplan static kindDefinition AsciidocKinds[] = {
531eb07f9cSHadriel Kaplan 	{ true, 'c', "chapter",       "chapters"},
541eb07f9cSHadriel Kaplan 	{ true, 's', "section",       "sections" },
551eb07f9cSHadriel Kaplan 	{ true, 'S', "subsection",    "level 2 sections" },
561eb07f9cSHadriel Kaplan 	{ true, 't', "subsubsection", "level 3 sections" },
57ab92488cSHadriel Kaplan 	{ true, 'T', "l4subsection",  "level 4 sections" },
58d776d040SHadriel Kaplan 	{ true, 'u', "l5subsection",  "level 5 sections" },
59d776d040SHadriel Kaplan 	{ true, 'a', "anchor",        "anchors" }
601eb07f9cSHadriel Kaplan };
611eb07f9cSHadriel Kaplan 
621eb07f9cSHadriel Kaplan static char kindchars[SECTION_COUNT]={ '=', '-', '~', '^', '+' };
631eb07f9cSHadriel Kaplan 
641eb07f9cSHadriel Kaplan static NestingLevels *nestingLevels = NULL;
651eb07f9cSHadriel Kaplan 
661eb07f9cSHadriel Kaplan /*
671eb07f9cSHadriel Kaplan *   FUNCTION DEFINITIONS
681eb07f9cSHadriel Kaplan */
691eb07f9cSHadriel Kaplan 
getNestingLevel(const int kind)701eb07f9cSHadriel Kaplan static NestingLevel *getNestingLevel(const int kind)
711eb07f9cSHadriel Kaplan {
721eb07f9cSHadriel Kaplan 	NestingLevel *nl;
731eb07f9cSHadriel Kaplan 	tagEntryInfo *e;
741eb07f9cSHadriel Kaplan 
751eb07f9cSHadriel Kaplan 	while (1)
761eb07f9cSHadriel Kaplan 	{
771eb07f9cSHadriel Kaplan 		nl = nestingLevelsGetCurrent(nestingLevels);
781eb07f9cSHadriel Kaplan 		e = getEntryOfNestingLevel (nl);
791eb07f9cSHadriel Kaplan 		if ((nl && (e == NULL)) || (e && (e->kindIndex >= kind)))
801eb07f9cSHadriel Kaplan 			nestingLevelsPop(nestingLevels);
811eb07f9cSHadriel Kaplan 		else
821eb07f9cSHadriel Kaplan 			break;
831eb07f9cSHadriel Kaplan 	}
841eb07f9cSHadriel Kaplan 	return nl;
851eb07f9cSHadriel Kaplan }
861eb07f9cSHadriel Kaplan 
makeAsciidocTag(const vString * const name,const int kind,const bool two_line)8738c27701SHadriel Kaplan static int makeAsciidocTag (const vString* const name, const int kind, const bool two_line)
881eb07f9cSHadriel Kaplan {
891eb07f9cSHadriel Kaplan 	const NestingLevel *const nl = getNestingLevel(kind);
901eb07f9cSHadriel Kaplan 	int r = CORK_NIL;
911eb07f9cSHadriel Kaplan 
921eb07f9cSHadriel Kaplan 	if (vStringLength (name) > 0)
931eb07f9cSHadriel Kaplan 	{
941eb07f9cSHadriel Kaplan 		tagEntryInfo *parent = getEntryOfNestingLevel (nl);
951eb07f9cSHadriel Kaplan 		tagEntryInfo e;
961eb07f9cSHadriel Kaplan 
971eb07f9cSHadriel Kaplan 		initTagEntry (&e, vStringValue (name), kind);
981eb07f9cSHadriel Kaplan 
9938c27701SHadriel Kaplan 		if (two_line)
10038c27701SHadriel Kaplan 		{
10138c27701SHadriel Kaplan 			/* we want the line before the '---' underline chars */
10238c27701SHadriel Kaplan 			const unsigned long line = getInputLineNumber();
10338c27701SHadriel Kaplan 			Assert (line > 0);
10438c27701SHadriel Kaplan 			if (line > 0)
10538c27701SHadriel Kaplan 			{
10638c27701SHadriel Kaplan 				e.lineNumber--;
10738c27701SHadriel Kaplan 				e.filePosition = getInputFilePositionForLine(line - 1);
10838c27701SHadriel Kaplan 			}
10938c27701SHadriel Kaplan 		}
1101eb07f9cSHadriel Kaplan 
1111eb07f9cSHadriel Kaplan 		if (parent && (parent->kindIndex < kind))
1121eb07f9cSHadriel Kaplan 		{
1131eb07f9cSHadriel Kaplan 			/*
1141eb07f9cSHadriel Kaplan 			 * This doesn't use Cork, but in this case I think this is better,
1151eb07f9cSHadriel Kaplan 			 * because Cork would record the scopes of all parents in the chain
1161eb07f9cSHadriel Kaplan 			 * which is weird for text section identifiers, and also this is
1171eb07f9cSHadriel Kaplan 			 * what the rst.c reStructuredText parser does.
1181eb07f9cSHadriel Kaplan 			 */
1191eb07f9cSHadriel Kaplan 			e.extensionFields.scopeKindIndex = parent->kindIndex;
1201eb07f9cSHadriel Kaplan 			e.extensionFields.scopeName = parent->name;
1211eb07f9cSHadriel Kaplan 		}
1221eb07f9cSHadriel Kaplan 
1231eb07f9cSHadriel Kaplan 		r = makeTagEntry (&e);
1241eb07f9cSHadriel Kaplan 	}
125d776d040SHadriel Kaplan 	return r;
126d776d040SHadriel Kaplan }
127d776d040SHadriel Kaplan 
makeSectionAsciidocTag(const vString * const name,const int kind,const bool two_line)128d776d040SHadriel Kaplan static int makeSectionAsciidocTag (const vString* const name, const int kind, const bool two_line)
129d776d040SHadriel Kaplan {
130d776d040SHadriel Kaplan 	int r = makeAsciidocTag(name, kind, two_line);
1311eb07f9cSHadriel Kaplan 	nestingLevelsPush(nestingLevels, r);
1321eb07f9cSHadriel Kaplan 	return r;
1331eb07f9cSHadriel Kaplan }
1341eb07f9cSHadriel Kaplan 
1351eb07f9cSHadriel Kaplan 
get_kind(char c)1361eb07f9cSHadriel Kaplan static int get_kind(char c)
1371eb07f9cSHadriel Kaplan {
1381eb07f9cSHadriel Kaplan 	int i;
1391eb07f9cSHadriel Kaplan 
1401eb07f9cSHadriel Kaplan 	for (i = 0; i < SECTION_COUNT; i++)
1411eb07f9cSHadriel Kaplan 	{
1421eb07f9cSHadriel Kaplan 		if (kindchars[i] == c)
1431eb07f9cSHadriel Kaplan 			return i;
1441eb07f9cSHadriel Kaplan 	}
1451eb07f9cSHadriel Kaplan 	return -1;
1461eb07f9cSHadriel Kaplan }
1471eb07f9cSHadriel Kaplan 
1481eb07f9cSHadriel Kaplan 
is_anchor(const unsigned char * line)149d776d040SHadriel Kaplan static bool is_anchor(const unsigned char *line)
150d776d040SHadriel Kaplan {
151d776d040SHadriel Kaplan 	/* must be at least "[#a]" */
152d776d040SHadriel Kaplan 	return line[0] == '[' && (line[1] == '#' || line[1] == '[');
153d776d040SHadriel Kaplan }
154d776d040SHadriel Kaplan 
capture_anchor(const unsigned char * const orig,int * captured_len)155d54a1312SHadriel Kaplan static int capture_anchor(const unsigned char *const orig, int* captured_len)
156d776d040SHadriel Kaplan {
157d776d040SHadriel Kaplan 	vString *name = vStringNew ();
158d776d040SHadriel Kaplan 	int r = CORK_NIL;
159d54a1312SHadriel Kaplan 	const bool shorthand = orig[1] == '#' ? true : false;
1604ab8d171SHadriel Kaplan 	bool is_valid = false;
16152550b1eSHadriel Kaplan 	bool seen_comma = false;
162d54a1312SHadriel Kaplan 	const unsigned char *line = orig;
163d776d040SHadriel Kaplan 
164d776d040SHadriel Kaplan 	Assert (line[0] == '[');
165d776d040SHadriel Kaplan 	Assert (line[1] == '#' || line[1] == '[');
166d776d040SHadriel Kaplan 
167d54a1312SHadriel Kaplan 	if (captured_len) *captured_len = 0;
168d54a1312SHadriel Kaplan 
169d776d040SHadriel Kaplan 	line += 2;
170d776d040SHadriel Kaplan 
171d776d040SHadriel Kaplan 	while (*line != '\0')
172d776d040SHadriel Kaplan 	{
173d776d040SHadriel Kaplan 		if (*line == ']')
174d776d040SHadriel Kaplan 		{
1754ab8d171SHadriel Kaplan 			if (shorthand || line[1] == ']')
1764ab8d171SHadriel Kaplan 			{
1774ab8d171SHadriel Kaplan 				is_valid = true;
178d54a1312SHadriel Kaplan 				if (shorthand) line++;
179d54a1312SHadriel Kaplan 				else line += 2;
180d776d040SHadriel Kaplan 				break;
1814ab8d171SHadriel Kaplan 			}
182d776d040SHadriel Kaplan 			/* otherwise it's not the end, keep going */
183d776d040SHadriel Kaplan 		}
18452550b1eSHadriel Kaplan 
18552550b1eSHadriel Kaplan 		if (*line == ',')
18652550b1eSHadriel Kaplan 			seen_comma = true;
18752550b1eSHadriel Kaplan 
18852550b1eSHadriel Kaplan 		if (!seen_comma)
189d776d040SHadriel Kaplan 			vStringPut (name, *line);
19052550b1eSHadriel Kaplan 
191d776d040SHadriel Kaplan 		line++;
192d776d040SHadriel Kaplan 	}
193d776d040SHadriel Kaplan 
1944ab8d171SHadriel Kaplan 	if (is_valid && vStringLength (name) != 0)
195d776d040SHadriel Kaplan 	{
196d776d040SHadriel Kaplan 		r = makeAsciidocTag (name, K_ANCHOR, false);
197d54a1312SHadriel Kaplan 
198d54a1312SHadriel Kaplan 		if (captured_len)
199d54a1312SHadriel Kaplan 		{
200d54a1312SHadriel Kaplan 			*captured_len = line - orig;
201d54a1312SHadriel Kaplan 		}
202d776d040SHadriel Kaplan 	}
203d776d040SHadriel Kaplan 
204d776d040SHadriel Kaplan 	vStringDelete (name);
205d776d040SHadriel Kaplan 	return r;
206d776d040SHadriel Kaplan }
207d776d040SHadriel Kaplan 
208d776d040SHadriel Kaplan 
209d54a1312SHadriel Kaplan /* skips any leading anchor(s) in a one-line title, generating tags for them */
process_leading_anchors(const unsigned char * const begin)210d54a1312SHadriel Kaplan static int process_leading_anchors(const unsigned char *const begin)
211d54a1312SHadriel Kaplan {
212d54a1312SHadriel Kaplan 	int captured_len = 0;
213d54a1312SHadriel Kaplan 	const unsigned char *current = begin;
214d54a1312SHadriel Kaplan 
215d54a1312SHadriel Kaplan 	while (is_anchor(current) && capture_anchor(current, &captured_len) != CORK_NIL)
216d54a1312SHadriel Kaplan 	{
217d54a1312SHadriel Kaplan 		/* minimum is "[#a]" */
218d54a1312SHadriel Kaplan 		Assert (captured_len >= 4);
219d54a1312SHadriel Kaplan 		current += captured_len;
220d54a1312SHadriel Kaplan 		while (isspace(*current)) ++current;
221d54a1312SHadriel Kaplan 	}
222d54a1312SHadriel Kaplan 
223d54a1312SHadriel Kaplan 	return current - begin;
224d54a1312SHadriel Kaplan }
225d54a1312SHadriel Kaplan 
process_trailing_anchor(const unsigned char * const begin,const unsigned char * const end)2268f0a66adSHadriel Kaplan static int process_trailing_anchor(const unsigned char *const begin,
2278f0a66adSHadriel Kaplan 								   const unsigned char *const end)
2288f0a66adSHadriel Kaplan {
2298f0a66adSHadriel Kaplan 	int captured_len = 0;
2308f0a66adSHadriel Kaplan 	const unsigned char *found = NULL;
2318f0a66adSHadriel Kaplan 
2328f0a66adSHadriel Kaplan 	/* minimum is "[#a]" */
2338f0a66adSHadriel Kaplan 	if (*end == ']' && (end - begin) >= 4)
2348f0a66adSHadriel Kaplan 	{
2358f0a66adSHadriel Kaplan 		found = (const unsigned char*) strrchr((const char*) begin , '[');
2368f0a66adSHadriel Kaplan 		if (found && ((end - found) >= 4))
2378f0a66adSHadriel Kaplan 		{
2388f0a66adSHadriel Kaplan 			/* see if it's not shorthand [#a] but instead [[a]] */
2398f0a66adSHadriel Kaplan 			if (end[-1] == ']' && found > begin && found[-1] == '[')
2408f0a66adSHadriel Kaplan 				--found;
2418f0a66adSHadriel Kaplan 
2423030d1e8SHadriel Kaplan 			if (is_anchor (found))
2438f0a66adSHadriel Kaplan 				capture_anchor(found, &captured_len);
2448f0a66adSHadriel Kaplan 		}
2458f0a66adSHadriel Kaplan 	}
2468f0a66adSHadriel Kaplan 
2478f0a66adSHadriel Kaplan 	return captured_len;
2488f0a66adSHadriel Kaplan }
2498f0a66adSHadriel Kaplan 
process_name(vString * const name,const int kind,const unsigned char * line,const int line_len)2507344a35cSHadriel Kaplan static void process_name(vString *const name, const int kind,
2517344a35cSHadriel Kaplan 						 const unsigned char *line, const int line_len)
2527344a35cSHadriel Kaplan {
2537344a35cSHadriel Kaplan 	int start = kind + 1;
2547344a35cSHadriel Kaplan 	int end = line_len - 1;
2557344a35cSHadriel Kaplan 
2567344a35cSHadriel Kaplan 	Assert (kind >= 0 && kind < K_ANCHOR);
2577344a35cSHadriel Kaplan 	Assert (line_len > start);
2587344a35cSHadriel Kaplan 
259d54a1312SHadriel Kaplan 	vStringClear(name);
260d54a1312SHadriel Kaplan 
2617344a35cSHadriel Kaplan 	while (line[end] == line[0]) --end;
2627344a35cSHadriel Kaplan 	while (isspace(line[start])) ++start;
2637344a35cSHadriel Kaplan 	while (isspace(line[end])) --end;
2647344a35cSHadriel Kaplan 
265d54a1312SHadriel Kaplan 	if (start < end)
266d54a1312SHadriel Kaplan 	{
267cadfac37SHadriel Kaplan 		/* pop nesting levels, so that anchors get the parent's scope */
268cadfac37SHadriel Kaplan 		getNestingLevel(kind);
2698f0a66adSHadriel Kaplan 		end -= process_trailing_anchor(line + start, line + end);
270d54a1312SHadriel Kaplan 		start += process_leading_anchors(line + start);
271d54a1312SHadriel Kaplan 	}
272d54a1312SHadriel Kaplan 
2738f0a66adSHadriel Kaplan 	while (isspace(line[end])) --end;
2748f0a66adSHadriel Kaplan 
275b4474ed0SMasatake YAMATO 	if (start <= end)
2767344a35cSHadriel Kaplan 		vStringNCatS(name, (const char*)(&(line[start])), end - start + 1);
2777344a35cSHadriel Kaplan }
2787344a35cSHadriel Kaplan 
2797344a35cSHadriel Kaplan 
2801eb07f9cSHadriel Kaplan /* computes the length of an UTF-8 string
2811eb07f9cSHadriel Kaplan  * if the string doesn't look like UTF-8, return -1
2821eb07f9cSHadriel Kaplan  * FIXME consider East_Asian_Width Unicode property */
utf8_strlen(const char * buf,int buf_len)2831eb07f9cSHadriel Kaplan static int utf8_strlen(const char *buf, int buf_len)
2841eb07f9cSHadriel Kaplan {
2851eb07f9cSHadriel Kaplan 	int len = 0;
2861eb07f9cSHadriel Kaplan 	const char *end = buf + buf_len;
2871eb07f9cSHadriel Kaplan 
2881eb07f9cSHadriel Kaplan 	for (len = 0; buf < end; len ++)
2891eb07f9cSHadriel Kaplan 	{
2901eb07f9cSHadriel Kaplan 		/* perform quick and naive validation (no sub-byte checking) */
2911eb07f9cSHadriel Kaplan 		if (! (*buf & 0x80))
2921eb07f9cSHadriel Kaplan 			buf ++;
2931eb07f9cSHadriel Kaplan 		else if ((*buf & 0xe0) == 0xc0)
2941eb07f9cSHadriel Kaplan 			buf += 2;
2951eb07f9cSHadriel Kaplan 		else if ((*buf & 0xf0) == 0xe0)
2961eb07f9cSHadriel Kaplan 			buf += 3;
2971eb07f9cSHadriel Kaplan 		else if ((*buf & 0xf8) == 0xf0)
2981eb07f9cSHadriel Kaplan 			buf += 4;
2991eb07f9cSHadriel Kaplan 		else /* not a valid leading UTF-8 byte, abort */
3001eb07f9cSHadriel Kaplan 			return -1;
3011eb07f9cSHadriel Kaplan 
3021eb07f9cSHadriel Kaplan 		if (buf > end) /* incomplete last byte */
3031eb07f9cSHadriel Kaplan 			return -1;
3041eb07f9cSHadriel Kaplan 	}
3051eb07f9cSHadriel Kaplan 
3061eb07f9cSHadriel Kaplan 	return len;
3071eb07f9cSHadriel Kaplan }
3081eb07f9cSHadriel Kaplan 
3091eb07f9cSHadriel Kaplan 
findAsciidocTags(void)3101eb07f9cSHadriel Kaplan static void findAsciidocTags(void)
3111eb07f9cSHadriel Kaplan {
3121eb07f9cSHadriel Kaplan 	vString *name = vStringNew();
3131eb07f9cSHadriel Kaplan 	const unsigned char *line;
3141eb07f9cSHadriel Kaplan 	unsigned char in_block = '\0';  /* holds the block marking char or \0 if not in block */
3151eb07f9cSHadriel Kaplan 
3161eb07f9cSHadriel Kaplan 	nestingLevels = nestingLevelsNew(0);
3171eb07f9cSHadriel Kaplan 
3181eb07f9cSHadriel Kaplan 	while ((line = readLineFromInputFile()) != NULL)
3191eb07f9cSHadriel Kaplan 	{
320d776d040SHadriel Kaplan 		if (is_anchor (line))
321d776d040SHadriel Kaplan 		{
322d54a1312SHadriel Kaplan 			if (capture_anchor (line, NULL) != CORK_NIL)
323d776d040SHadriel Kaplan 			{
324d776d040SHadriel Kaplan 				vStringClear (name);
325d776d040SHadriel Kaplan 				continue;
326d776d040SHadriel Kaplan 			}
327d776d040SHadriel Kaplan 		}
328d776d040SHadriel Kaplan 
3291eb07f9cSHadriel Kaplan 		int line_len = strlen((const char*) line);
3301eb07f9cSHadriel Kaplan 		int name_len_bytes = vStringLength(name);
3311eb07f9cSHadriel Kaplan 		int name_len = utf8_strlen(vStringValue(name), name_len_bytes);
3321eb07f9cSHadriel Kaplan 
3331eb07f9cSHadriel Kaplan 		/* if the name doesn't look like UTF-8, assume one-byte charset */
3341eb07f9cSHadriel Kaplan 		if (name_len < 0) name_len = name_len_bytes;
3351eb07f9cSHadriel Kaplan 
3361eb07f9cSHadriel Kaplan 		/* if its a title underline, or a delimited block marking character */
3371eb07f9cSHadriel Kaplan 		if (line[0] == '=' || line[0] == '-' || line[0] == '~' ||
3381eb07f9cSHadriel Kaplan 			line[0] == '^' || line[0] == '+' || line[0] == '.' ||
3391eb07f9cSHadriel Kaplan 			line[0] == '*' || line[0] == '_' || line[0] == '/')
3401eb07f9cSHadriel Kaplan 		{
3411eb07f9cSHadriel Kaplan 			int n_same;
3421eb07f9cSHadriel Kaplan 			for (n_same = 1; line[n_same] == line[0]; ++n_same);
3431eb07f9cSHadriel Kaplan 
3441eb07f9cSHadriel Kaplan 			/* is it a two line title or a delimited block */
3451eb07f9cSHadriel Kaplan 			if (n_same == line_len)
3461eb07f9cSHadriel Kaplan 			{
3471eb07f9cSHadriel Kaplan 				/* if in a block, can't be block start or title, look for block end */
3481eb07f9cSHadriel Kaplan 				if (in_block)
3491eb07f9cSHadriel Kaplan 				{
3501eb07f9cSHadriel Kaplan 					if (line[0] == in_block) in_block = '\0';
3511eb07f9cSHadriel Kaplan 				}
3521eb07f9cSHadriel Kaplan 
3531eb07f9cSHadriel Kaplan 				/* if its a =_~^+ and the same length +-2 as the line before then its a title */
3541eb07f9cSHadriel Kaplan 				/* (except in the special case its a -- open block start line) */
3551eb07f9cSHadriel Kaplan 				else if ((line[0] == '=' || line[0] == '-' || line[0] == '~' ||
3561eb07f9cSHadriel Kaplan 							line[0] == '^' || line[0] == '+') &&
3571eb07f9cSHadriel Kaplan 						line_len <= name_len + 2 && line_len >= name_len - 2 &&
3581eb07f9cSHadriel Kaplan 						!(line_len == 2 && line[0] == '-'))
3591eb07f9cSHadriel Kaplan 				{
3601eb07f9cSHadriel Kaplan 					int kind = get_kind((char)(line[0]));
3611eb07f9cSHadriel Kaplan 					if (kind >= 0)
3621eb07f9cSHadriel Kaplan 					{
363d776d040SHadriel Kaplan 						makeSectionAsciidocTag(name, kind, true);
3641eb07f9cSHadriel Kaplan 						continue;
3651eb07f9cSHadriel Kaplan 					}
3661eb07f9cSHadriel Kaplan 				}
3671eb07f9cSHadriel Kaplan 
3681eb07f9cSHadriel Kaplan 				/* else if its 4 or more /+-.*_= (plus the -- special case) its a block start */
3691eb07f9cSHadriel Kaplan 				else if (((line[0] == '/' || line[0] == '+' || line[0] == '-' ||
3701eb07f9cSHadriel Kaplan 						   line[0] == '.' || line[0] == '*' || line[0] == '_' ||
3711eb07f9cSHadriel Kaplan 						   line[0] == '=') && line_len >= 4 )
3721eb07f9cSHadriel Kaplan 						 || (line[0] == '-' && line_len == 2))
3731eb07f9cSHadriel Kaplan 				{
3741eb07f9cSHadriel Kaplan 					in_block = line[0];
3751eb07f9cSHadriel Kaplan 				}
3761eb07f9cSHadriel Kaplan 			}
3771eb07f9cSHadriel Kaplan 
3781eb07f9cSHadriel Kaplan 			/* otherwise is it a one line title */
379ab92488cSHadriel Kaplan 			else if (line[0] == '=' && n_same <= 6 && isspace(line[n_same]) &&
3801eb07f9cSHadriel Kaplan 					!in_block)
3811eb07f9cSHadriel Kaplan 			{
3821eb07f9cSHadriel Kaplan 				int kind = n_same - 1;
3837344a35cSHadriel Kaplan 				process_name(name, kind, line, line_len);
384d776d040SHadriel Kaplan 				makeSectionAsciidocTag(name, kind, false);
3851eb07f9cSHadriel Kaplan 				continue;
3861eb07f9cSHadriel Kaplan 			}
3871eb07f9cSHadriel Kaplan 		}
3881eb07f9cSHadriel Kaplan 		vStringClear(name);
3891eb07f9cSHadriel Kaplan 		if (! isspace(*line))
3901eb07f9cSHadriel Kaplan 			vStringCatS(name, (const char*) line);
3911eb07f9cSHadriel Kaplan 	}
3921eb07f9cSHadriel Kaplan 	vStringDelete(name);
3931eb07f9cSHadriel Kaplan 	nestingLevelsFree(nestingLevels);
3941eb07f9cSHadriel Kaplan }
3951eb07f9cSHadriel Kaplan 
AsciidocParser(void)3961eb07f9cSHadriel Kaplan extern parserDefinition* AsciidocParser (void)
3971eb07f9cSHadriel Kaplan {
3981eb07f9cSHadriel Kaplan 	static const char *const patterns [] = { "*.asc", "*.adoc", "*.asciidoc", NULL };
3991eb07f9cSHadriel Kaplan 	static const char *const extensions [] = { "asc", "adoc", "asciidoc", NULL };
4001eb07f9cSHadriel Kaplan 
4011eb07f9cSHadriel Kaplan 	parserDefinition* const def = parserNew ("Asciidoc");
4021eb07f9cSHadriel Kaplan 
4031eb07f9cSHadriel Kaplan 	def->kindTable = AsciidocKinds;
4041eb07f9cSHadriel Kaplan 	def->kindCount = ARRAY_SIZE (AsciidocKinds);
4051eb07f9cSHadriel Kaplan 	def->patterns = patterns;
4061eb07f9cSHadriel Kaplan 	def->extensions = extensions;
4071eb07f9cSHadriel Kaplan 	def->parser = findAsciidocTags;
4081eb07f9cSHadriel Kaplan 	/* do we even need to use Cork? */
409*6b1a862eSMasatake YAMATO 	def->useCork = CORK_QUEUE;
4101eb07f9cSHadriel Kaplan 
4111eb07f9cSHadriel Kaplan 	return def;
4121eb07f9cSHadriel Kaplan }
413