3 * Copyright (c) 2012, Lex Trotman
4 * Based on Rest code by Nick Treleaven, see rest.c
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for asciidoc files.
15 #include "general.h" /* must always come first */
23 #include "nestlevel.h"
37 static kindOption AsciidocKinds
[] = {
38 { TRUE
, 'n', "namespace", "chapters"},
39 { TRUE
, 'm', "member", "sections" },
40 { TRUE
, 'd', "macro", "level2sections" },
41 { TRUE
, 'v', "variable", "level3sections" },
42 { TRUE
, 's', "struct", "level4sections" }
45 static char kindchars
[SECTION_COUNT
]={ '=', '-', '~', '^', '+' };
47 static NestingLevels
*nestingLevels
= NULL
;
50 * FUNCTION DEFINITIONS
53 static NestingLevel
*getNestingLevel(const int kind
)
59 nl
= nestingLevelsGetCurrent(nestingLevels
);
60 if (nl
&& nl
->type
>= kind
)
61 nestingLevelsPop(nestingLevels
);
68 static void makeAsciidocTag (const vString
* const name
, const int kind
)
70 const NestingLevel
*const nl
= getNestingLevel(kind
);
72 if (vStringLength (name
) > 0)
75 initTagEntry (&e
, vStringValue (name
));
77 e
.lineNumber
--; /* we want the line before the '---' underline chars */
78 e
.kindName
= AsciidocKinds
[kind
].name
;
79 e
.kind
= AsciidocKinds
[kind
].letter
;
81 if (nl
&& nl
->type
< kind
)
83 e
.extensionFields
.scope
[0] = AsciidocKinds
[nl
->type
].name
;
84 e
.extensionFields
.scope
[1] = vStringValue (nl
->name
);
88 nestingLevelsPush(nestingLevels
, name
, kind
);
92 static int get_kind(char c
)
96 for (i
= 0; i
< SECTION_COUNT
; i
++)
98 if (kindchars
[i
] == c
)
105 /* computes the length of an UTF-8 string
106 * if the string doesn't look like UTF-8, return -1
107 * FIXME consider East_Asian_Width Unicode property */
108 static int utf8_strlen(const char *buf
, int buf_len
)
111 const char *end
= buf
+ buf_len
;
113 for (len
= 0; buf
< end
; len
++)
115 /* perform quick and naive validation (no sub-byte checking) */
118 else if ((*buf
& 0xe0) == 0xc0)
120 else if ((*buf
& 0xf0) == 0xe0)
122 else if ((*buf
& 0xf8) == 0xf0)
124 else /* not a valid leading UTF-8 byte, abort */
127 if (buf
> end
) /* incomplete last byte */
135 static void findAsciidocTags(void)
137 vString
*name
= vStringNew();
138 const unsigned char *line
;
139 unsigned char in_block
= '\0'; /* holds the block marking char or \0 if not in block */
141 nestingLevels
= nestingLevelsNew();
143 while ((line
= fileReadLine()) != NULL
)
145 int line_len
= strlen((const char*) line
);
146 int name_len_bytes
= vStringLength(name
);
147 int name_len
= utf8_strlen(vStringValue(name
), name_len_bytes
);
149 /* if the name doesn't look like UTF-8, assume one-byte charset */
150 if (name_len
< 0) name_len
= name_len_bytes
;
152 /* if its a title underline, or a delimited block marking character */
153 if (line
[0] == '=' || line
[0] == '-' || line
[0] == '~' ||
154 line
[0] == '^' || line
[0] == '+' || line
[0] == '.' ||
155 line
[0] == '*' || line
[0] == '_' || line
[0] == '/')
158 for (n_same
= 1; line
[n_same
] == line
[0]; ++n_same
);
160 /* is it a two line title or a delimited block */
161 if (n_same
== line_len
)
163 /* if in a block, can't be block start or title, look for block end */
166 if (line
[0] == in_block
) in_block
= '\0';
169 /* if its a =_~^+ and the same length +-2 as the line before then its a title */
170 /* (except in the special case its a -- open block start line) */
171 else if ((line
[0] == '=' || line
[0] == '-' || line
[0] == '~' ||
172 line
[0] == '^' || line
[0] == '+') &&
173 line_len
<= name_len
+ 2 && line_len
>= name_len
- 2 &&
174 !(line_len
== 2 && line
[0] == '-'))
176 int kind
= get_kind((char)(line
[0]));
179 makeAsciidocTag(name
, kind
);
184 /* else if its 4 or more /+-.*_= (plus the -- special case) its a block start */
185 else if (((line
[0] == '/' || line
[0] == '+' || line
[0] == '-' ||
186 line
[0] == '.' || line
[0] == '*' || line
[0] == '_' ||
187 line
[0] == '=') && line_len
>= 4 )
188 || (line
[0] == '-' && line_len
== 2))
194 /* otherwise is it a one line title */
195 else if (line
[0] == '=' && n_same
<= 5 && isspace(line
[n_same
]) &&
198 int kind
= n_same
- 1;
200 int end
= line_len
- 1;
201 while (line
[end
] == line
[0])--end
;
202 while (isspace(line
[start
]))++start
;
203 while (isspace(line
[end
]))--end
;
205 vStringNCatS(name
, (const char*)(&(line
[start
])), end
- start
+ 1);
206 vStringTerminate(name
);
207 makeAsciidocTag(name
, kind
);
212 if (! isspace(*line
))
213 vStringCatS(name
, (const char*) line
);
214 vStringTerminate(name
);
217 nestingLevelsFree(nestingLevels
);
220 extern parserDefinition
* AsciidocParser (void)
222 static const char *const patterns
[] = { "*.asciidoc", NULL
};
223 static const char *const extensions
[] = { "asciidoc", NULL
};
224 parserDefinition
* const def
= parserNew ("Asciidoc");
226 def
->kinds
= AsciidocKinds
;
227 def
->kindCount
= KIND_COUNT (AsciidocKinds
);
228 def
->patterns
= patterns
;
229 def
->extensions
= extensions
;
230 def
->parser
= findAsciidocTags
;
234 /* vi:set tabstop=8 shiftwidth=4: */