+/*
+ * Scan a block scalar.
+ */
+
+static yaml_token_t *
+yaml_parser_scan_block_scalar(yaml_parser_t *parser, int literal)
+{
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+ yaml_string_t string = yaml_parser_new_string(parser);
+ yaml_string_t line_break = yaml_parser_new_string(parser);
+ yaml_string_t breaks = yaml_parser_new_string(parser);
+ yaml_token_t *token = NULL;
+ int chomping = 0;
+ int increment = 0;
+ int indent = 0;
+ int leading_blank = 0;
+ int trailing_blank = 0;
+
+ if (!string.buffer) goto error;
+ if (!line_break.buffer) goto error;
+ if (!breaks.buffer) goto error;
+
+ /* Eat the indicator '|' or '>'. */
+
+ start_mark = yaml_parser_get_mark(parser);
+
+ FORWARD(parser);
+
+ /* Scan the additional block scalar indicators. */
+
+ if (!UPDATE(parser, 1)) goto error;
+
+ /* Check for a chomping indicator. */
+
+ if (CHECK(parser, '+') || CHECK(parser, '-'))
+ {
+ /* Set the chomping method and eat the indicator. */
+
+ chomping = CHECK(parser, '+') ? +1 : -1;
+
+ FORWARD(parser);
+
+ /* Check for an indentation indicator. */
+
+ if (!UPDATE(parser, 1)) goto error;
+
+ if (IS_DIGIT(parser))
+ {
+ /* Check that the intendation is greater than 0. */
+
+ if (CHECK(parser, '0')) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an intendation indicator equal to 0");
+ goto error;
+ }
+
+ /* Get the intendation level and eat the indicator. */
+
+ increment = AS_DIGIT(parser);
+
+ FORWARD(parser);
+ }
+ }
+
+ /* Do the same as above, but in the opposite order. */
+
+ else if (IS_DIGIT(parser))
+ {
+ if (CHECK(parser, '0')) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an intendation indicator equal to 0");
+ goto error;
+ }
+
+ increment = AS_DIGIT(parser);
+
+ FORWARD(parser);
+
+ if (!UPDATE(parser, 1)) goto error;
+
+ if (CHECK(parser, '+') || CHECK(parser, '-')) {
+ chomping = CHECK(parser, '+') ? +1 : -1;
+ FORWARD(parser);
+ }
+ }
+
+ /* Eat whitespaces and comments to the end of the line. */
+
+ if (!UPDATE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser)) {
+ FORWARD(parser);
+ if (!UPDATE(parser, 1)) goto error;
+ }
+
+ if (CHECK(parser, '#')) {
+ while (!IS_BREAKZ(parser)) {
+ FORWARD(parser);
+ if (!UPDATE(parser, 1)) goto error;
+ }
+ }
+
+ /* Check if we are at the end of the line. */
+
+ if (!IS_BREAKZ(parser)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "did not found expected comment or line break");
+ goto error;
+ }
+
+ /* Eat a line break. */
+
+ if (IS_BREAK(parser)) {
+ if (!UPDATE(parser, 2)) goto error;
+ FORWARD_LINE(parser);
+ }
+
+ end_mark = yaml_parser_get_mark(parser);
+
+ /* Set the intendation level if it was specified. */
+
+ if (increment) {
+ indent = parser->indent >= 0 ? parser->indent+increment : increment;
+ }
+
+ /* Scan the leading line breaks and determine the indentation level if needed. */
+
+ if (!yaml_parser_scan_block_scalar_breaks(parser, &indent, &breaks,
+ start_mark, &end_mark)) goto error;
+
+ /* Scan the block scalar content. */
+
+ if (!UPDATE(parser, 1)) goto error;
+
+ while (parser->column == indent && !IS_Z(parser))
+ {
+ /*
+ * We are at the beginning of a non-empty line.
+ */
+
+ /* Is it a trailing whitespace? */
+
+ trailing_blank = IS_BLANK(parser);
+
+ /* Check if we need to fold the leading line break. */
+
+ if (!literal && (*line_break.buffer == '\n')
+ && !leading_blank && !trailing_blank)
+ {
+ /* Do we need to join the lines by space? */
+
+ if (*breaks.buffer == '\0') {
+ if (!RESIZE(parser, string)) goto error;
+ *(string.pointer ++) = ' ';
+ }
+
+ yaml_parser_clear_string(parser, &line_break);
+ }
+ else {
+ if (!JOIN(parser, string, line_break)) goto error;
+ }
+
+ /* Append the remaining line breaks. */
+
+ if (!JOIN(parser, string, breaks)) goto error;
+
+ /* Is it a leading whitespace? */
+
+ leading_blank = IS_BLANK(parser);
+
+ /* Consume the current line. */
+
+ while (!IS_BREAKZ(parser)) {
+ if (!RESIZE(parser, string)) goto error;
+ COPY(parser, string);
+ if (!UPDATE(parser, 1)) goto error;
+ }
+
+ /* Consume the line break. */
+
+ if (!UPDATE(parser, 2)) goto error;
+
+ COPY_LINE(parser, line_break);
+
+ /* Eat the following intendation spaces and line breaks. */
+
+ if (!yaml_parser_scan_block_scalar_breaks(parser,
+ &indent, &breaks, start_mark, &end_mark)) goto error;
+ }
+
+ /* Chomp the tail. */
+
+ if (chomping != -1) {
+ if (!JOIN(parser, string, line_break)) goto error;
+ }
+ if (chomping == 1) {
+ if (!JOIN(parser, string, breaks)) goto error;
+ }
+
+ /* Create a token. */
+
+ token = yaml_scalar_token_new(string.buffer, string.pointer-string.buffer,
+ literal ? YAML_LITERAL_SCALAR_STYLE : YAML_FOLDED_SCALAR_STYLE,
+ start_mark, end_mark);
+ if (!token) {
+ parser->error = YAML_MEMORY_ERROR;
+ return 0;
+ }
+
+ yaml_free(line_break.buffer);
+ yaml_free(breaks.buffer);
+
+ return token;
+
+error:
+ yaml_free(string.buffer);
+ yaml_free(line_break.buffer);
+ yaml_free(breaks.buffer);
+
+ return NULL;
+}
+
+/*
+ * Scan intendation spaces and line breaks for a block scalar. Determine the
+ * intendation level if needed.
+ */
+
+static int
+yaml_parser_scan_block_scalar_breaks(yaml_parser_t *parser,
+ int *indent, yaml_string_t *breaks,
+ yaml_mark_t start_mark, yaml_mark_t *end_mark)
+{
+ int max_indent = 0;
+
+ *end_mark = yaml_parser_get_mark(parser);
+
+ /* Eat the intendation spaces and line breaks. */
+
+ while (1)
+ {
+ /* Eat the intendation spaces. */
+
+ if (!UPDATE(parser, 1)) return 0;
+
+ while ((!*indent || parser->column < *indent) && IS_SPACE(parser)) {
+ FORWARD(parser);
+ if (!UPDATE(parser, 1)) return 0;
+ }
+
+ if (parser->column > max_indent)
+ max_indent = parser->column;
+
+ /* Check for a tab character messing the intendation. */
+
+ if ((!*indent || parser->column < *indent) && IS_TAB(parser)) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found a tab character where an intendation space is expected");
+ }
+
+ /* Have we found a non-empty line? */
+
+ if (!IS_BREAK(parser)) break;
+
+ /* Consume the line break. */
+
+ if (!UPDATE(parser, 2)) return 0;
+ if (!RESIZE(parser, *breaks)) return 0;
+ COPY_LINE(parser, *breaks);
+ *end_mark = yaml_parser_get_mark(parser);
+ }
+
+ /* Determine the indentation level if needed. */
+
+ if (!*indent) {
+ *indent = max_indent;
+ if (*indent < parser->indent + 1)
+ *indent = parser->indent + 1;
+ if (*indent < 1)
+ *indent = 1;
+ }
+
+ return 1;
+}
+