解析結果をきちんとエスケープしてXMLで出力する
前回のコードには、正当な入力にも関わらず、例えば不等号記号などが入っていると、
誤った形式のXMLを出力してしまうバグがあることに気が付いた。
入力に含まれている文字のうち事前定義された実体がある5種類の文字については、
それが出力すべき文字列に含まれるなら置換するよう修正する。
translator.y
%{ ...snip static char *estrdupcat(const char *s1, const char *s2); static char *escape(const char *s); %} ...snip string_headed : string_except_parenthesis { char *s = escape($1); fputs(s, stdout); free(s); free($1); } | string_except_parenthesis { char *s = escape($1); fputs(s, stdout); free(s); free($1); } marked_string_headed ; ...snip marked_string_body_with_annotation : STRING_EXCEPT_SPECIAL { char *s = escape($1); printf("<mark>%s</mark>", s); free(s); free($1); } | STRING_EXCEPT_SPECIAL START_ANNOTATION string_except_parenthesis { char *s1 = escape($1), *s3 = escape($3); printf("<mark>%s<annotation>%s</annotation></mark>", s1, s3); free(s1); free(s3); free($1); free($2); free($3); } ; ...snip %% ...snip static char *escape(const char *s) { const char *p = s; char *q; size_t len = 1; while (*p != '\0') { switch (*p++) { case '<': case '>': len += 4; break; case '&': len += 5; break; case '\'': case '"': len += 6; break; default: len++; break; } } q = malloc(len); while (*s != '\0') { switch (*s) { case '<': *q++ = '&', *q++ = 'l', *q++ = 't', *q++ = ';'; break; case '>': *q++ = '&', *q++ = 'g', *q++ = 't', *q++ = ';'; break; case '&': *q++ = '&', *q++ = 'a', *q++ = 'm', *q++ = 'p', *q++ = ';'; break; case '\'': *q++ = '&', *q++ = 'a', *q++ = 'p', *q++ = 'o', *q++ = 's', *q++ = ';'; break; case '"': *q++ = '&', *q++ = 'q', *q++ = 'u', *q++ = 'o', *q++ = 't', *q++ = ';'; break; default: *q++ = *s; break; } s++; } *q = '\0'; return q - len + 1; }
文字列中の'<'
、'>'
、'&'
、'\''
、'"'
をエスケープする関数escapeを定義し、
二つ目の%%の後の追加のCコード部の末尾に追加した。
構文規則部のアクションで入力から得た文字列を出力する場合は、この関数を通した文字列を出力するようにする。
修正前のコードでは、
$ ./translator << ----- > XML provides five predefined entities: (<,>,&,',"). > ----- <text>XML provides five predefined entities: <mark><,>,&,',"</mark>. </text>
のように正しくマーク付けされた入力だがXMLとして誤った形式で出力される。
修正後は、
$ ./translator << ----- > XML provides five predefined entities: (<,>,&,',"). > ----- <text>XML provides five predefined entities: <mark><,>,&,',"</mark>. </text>