Fix bug #12: Balanced square brackets test should recognize multi-line [Illustration] tags
authorali <ali@juiblex.co.uk>
Mon Sep 23 21:18:27 2013 +0100 (2013-09-23)
changeset 1069fb13a5dde3b
parent 99 7a62c77a0dbe
child 107 25501ca64443
child 111 f805130deb6f
child 115 df21841a2b64
child 123 ddb5ddba6ef3
child 127 badcc3b340ab
child 131 2ff298db529e
child 136 2f3762ff90d8
child 142 466f43a12118
Fix bug #12: Balanced square brackets test should recognize multi-line [Illustration] tags
bookloupe/Makefile.am
bookloupe/bookloupe.c
bookloupe/bookloupe.h
bookloupe/counters.c
bookloupe/counters.h
bookloupe/pending.c
bookloupe/pending.h
test/bookloupe/Makefile.am
test/bookloupe/multi-line-illustration.tst
test/compatibility/example.tst
test/harness/testcase.c
     1.1 --- a/bookloupe/Makefile.am	Sat Sep 21 23:40:18 2013 +0100
     1.2 +++ b/bookloupe/Makefile.am	Mon Sep 23 21:18:27 2013 +0100
     1.3 @@ -1,6 +1,7 @@
     1.4  INCLUDES=-I$(top_srcdir)
     1.5  bin_PROGRAMS=bookloupe
     1.6 -bookloupe_SOURCES=bookloupe.c bookloupe.h counters.c counters.h
     1.7 +bookloupe_SOURCES=bookloupe.c bookloupe.h counters.c counters.h \
     1.8 +	pending.c pending.h
     1.9  pkgdata_DATA=bookloupe.typ
    1.10  AM_CFLAGS=$(GLIB_CFLAGS)
    1.11  LIBS=$(GLIB_LIBS)
     2.1 --- a/bookloupe/bookloupe.c	Sat Sep 21 23:40:18 2013 +0100
     2.2 +++ b/bookloupe/bookloupe.c	Mon Sep 23 21:18:27 2013 +0100
     2.3 @@ -29,6 +29,7 @@
     2.4  #include <bl/bl.h>
     2.5  #include "bookloupe.h"
     2.6  #include "counters.h"
     2.7 +#include "pending.h"
     2.8  #include "HTMLentities.h"
     2.9  
    2.10  gchar *prevline;
    2.11 @@ -867,10 +868,26 @@
    2.12  	    isemptyline=FALSE;  /* ignore lines like  *  *  *  as spacers */
    2.13  	if (c==CHAR_UNDERSCORE)
    2.14  	    counters->c_unders++;
    2.15 -	if (c==CHAR_OPEN_CBRACK || c==CHAR_OPEN_RBRACK || c==CHAR_OPEN_SBRACK)
    2.16 +	if (c==CHAR_OPEN_SBRACK)
    2.17 +	{
    2.18 +	    if (!matching_difference(counters,COUNTER_ILLUSTRATION) &&
    2.19 +	      !matching_difference(counters,c) && s==aline &&
    2.20 +	      g_str_has_prefix(s,"[Illustration:"))
    2.21 +		increment_matching(counters,COUNTER_ILLUSTRATION,TRUE);
    2.22 +	    else
    2.23 +		increment_matching(counters,c,TRUE);
    2.24 +	}
    2.25 +	else if (c==CHAR_OPEN_CBRACK || c==CHAR_OPEN_RBRACK)
    2.26  	    increment_matching(counters,c,TRUE);
    2.27 -	if (c==CHAR_CLOSE_CBRACK || c==CHAR_CLOSE_RBRACK ||
    2.28 -	  c==CHAR_CLOSE_SBRACK)
    2.29 +	if (c==CHAR_CLOSE_SBRACK)
    2.30 +	{
    2.31 +	    if (!matching_count(counters,COUNTER_ILLUSTRATION,FALSE) &&
    2.32 +	      !matching_difference(counters,c) && !*snext)
    2.33 +		increment_matching(counters,COUNTER_ILLUSTRATION,FALSE);
    2.34 +	    else
    2.35 +		increment_matching(counters,c,FALSE);
    2.36 +	}
    2.37 +	else if (c==CHAR_CLOSE_CBRACK || c==CHAR_CLOSE_RBRACK)
    2.38  	    increment_matching(counters,c,FALSE);
    2.39  	sprev=s;
    2.40  	s=snext;
    2.41 @@ -2363,168 +2380,6 @@
    2.42  }
    2.43  
    2.44  /*
    2.45 - * print_pending:
    2.46 - *
    2.47 - * If we are in a state of unbalanced quotes, and this line
    2.48 - * doesn't begin with a quote, output the stored error message.
    2.49 - * If the -P switch was used, print the warning even if the
    2.50 - * new para starts with quotes.
    2.51 - */
    2.52 -void print_pending(const char *aline,const char *parastart,
    2.53 -  struct pending *pending)
    2.54 -{
    2.55 -    const char *s;
    2.56 -    gunichar c;
    2.57 -    s=aline;
    2.58 -    while (*s==' ')
    2.59 -	s++;
    2.60 -    c=g_utf8_get_char(s);
    2.61 -    if (pending->dquote)
    2.62 -    {
    2.63 -	if (c!=CHAR_DQUOTE || pswit[QPARA_SWITCH])
    2.64 -	{
    2.65 -	    if (!pswit[OVERVIEW_SWITCH])
    2.66 -	    {
    2.67 -		if (pswit[ECHO_SWITCH])
    2.68 -		    g_print("\n%s\n",parastart);
    2.69 -		g_print("%s\n",pending->dquote);
    2.70 -	    }
    2.71 -	    else
    2.72 -		cnt_dquot++;
    2.73 -	}
    2.74 -	g_free(pending->dquote);
    2.75 -	pending->dquote=NULL;
    2.76 -    }
    2.77 -    if (pending->squote)
    2.78 -    {
    2.79 -	if (!CHAR_IS_SQUOTE(c) || pswit[QPARA_SWITCH] || pending->squot)
    2.80 -	{
    2.81 -	    if (!pswit[OVERVIEW_SWITCH])
    2.82 -	    {
    2.83 -		if (pswit[ECHO_SWITCH])
    2.84 -		    g_print("\n%s\n",parastart);
    2.85 -		g_print("%s\n",pending->squote);
    2.86 -	    }
    2.87 -	    else
    2.88 -		cnt_squot++;
    2.89 -	}
    2.90 -	g_free(pending->squote);
    2.91 -	pending->squote=NULL;
    2.92 -    }
    2.93 -    if (pending->rbrack)
    2.94 -    {
    2.95 -	if (!pswit[OVERVIEW_SWITCH])
    2.96 -	{
    2.97 -	    if (pswit[ECHO_SWITCH])
    2.98 -		g_print("\n%s\n",parastart);
    2.99 -	    g_print("%s\n",pending->rbrack);
   2.100 -	}
   2.101 -	else
   2.102 -	    cnt_brack++;
   2.103 -	g_free(pending->rbrack);
   2.104 -	pending->rbrack=NULL;
   2.105 -    }
   2.106 -    if (pending->sbrack)
   2.107 -    {
   2.108 -	if (!pswit[OVERVIEW_SWITCH])
   2.109 -	{
   2.110 -	    if (pswit[ECHO_SWITCH])
   2.111 -		g_print("\n%s\n",parastart);
   2.112 -	    g_print("%s\n",pending->sbrack);
   2.113 -	}
   2.114 -	else
   2.115 -	    cnt_brack++;
   2.116 -	g_free(pending->sbrack);
   2.117 -	pending->sbrack=NULL;
   2.118 -    }
   2.119 -    if (pending->cbrack)
   2.120 -    {
   2.121 -	if (!pswit[OVERVIEW_SWITCH])
   2.122 -	{
   2.123 -	    if (pswit[ECHO_SWITCH])
   2.124 -		g_print("\n%s\n",parastart);
   2.125 -	    g_print("%s\n",pending->cbrack);
   2.126 -	}
   2.127 -	else
   2.128 -	    cnt_brack++;
   2.129 -	g_free(pending->cbrack);
   2.130 -	pending->cbrack=NULL;
   2.131 -    }
   2.132 -    if (pending->unders)
   2.133 -    {
   2.134 -	if (!pswit[OVERVIEW_SWITCH])
   2.135 -	{
   2.136 -	    if (pswit[ECHO_SWITCH])
   2.137 -		g_print("\n%s\n",parastart);
   2.138 -	    g_print("%s\n",pending->unders);
   2.139 -	}
   2.140 -	else
   2.141 -	    cnt_brack++;
   2.142 -	g_free(pending->unders);
   2.143 -	pending->unders=NULL;
   2.144 -    }
   2.145 -}
   2.146 -
   2.147 -/*
   2.148 - * check_for_mismatched_quotes:
   2.149 - *
   2.150 - * At end of paragraph, check for mismatched quotes.
   2.151 - *
   2.152 - * We don't want to report an error immediately, since it is a
   2.153 - * common convention to omit the quotes at end of paragraph if
   2.154 - * the next paragraph is a continuation of the same speaker.
   2.155 - * Where this is the case, the next para should begin with a
   2.156 - * quote, so we store the warning message and only display it
   2.157 - * at the top of the next iteration if the new para doesn't
   2.158 - * start with a quote.
   2.159 - * The -p switch overrides this default, and warns of unclosed
   2.160 - * quotes on _every_ paragraph, whether the next begins with a
   2.161 - * quote or not.
   2.162 - */
   2.163 -void check_for_mismatched_quotes(const struct counters *counters,
   2.164 -  struct pending *pending)
   2.165 -{
   2.166 -    int squote_straight,squote_curved;
   2.167 -    if (counters->quot%2)
   2.168 -	pending->dquote=
   2.169 -	  g_strdup_printf("    Line %ld - Mismatched quotes",linecnt);
   2.170 -    if (pswit[SQUOTE_SWITCH])
   2.171 -    {
   2.172 -	if (matching_count(counters,CHAR_SQUOTE,TRUE))
   2.173 -	    squote_straight=matching_difference(counters,CHAR_SQUOTE);
   2.174 -	else
   2.175 -	    squote_straight=0;
   2.176 -	if (matching_count(counters,CHAR_LS_QUOTE,TRUE))
   2.177 -	    squote_curved=matching_difference(counters,CHAR_LS_QUOTE);
   2.178 -	else
   2.179 -	    squote_curved=0;
   2.180 -	if (squote_straight || squote_curved)
   2.181 -	    pending->squote=
   2.182 -	      g_strdup_printf("    Line %ld - Mismatched singlequotes?",
   2.183 -	      linecnt);
   2.184 -	if (squote_straight && squote_straight!=1 ||
   2.185 -	  squote_curved && squote_curved!=1)
   2.186 -	    /*
   2.187 -	     * Flag it to be noted regardless of the
   2.188 -	     * first char of the next para.
   2.189 -	     */
   2.190 -	    pending->squot=1;
   2.191 -    }
   2.192 -    if (matching_difference(counters,CHAR_OPEN_RBRACK))
   2.193 -	pending->rbrack=
   2.194 -	  g_strdup_printf("    Line %ld - Mismatched round brackets?",linecnt);
   2.195 -    if (matching_difference(counters,CHAR_OPEN_SBRACK))
   2.196 -	pending->sbrack=
   2.197 -	  g_strdup_printf("    Line %ld - Mismatched square brackets?",linecnt);
   2.198 -    if (matching_difference(counters,CHAR_OPEN_CBRACK))
   2.199 -	pending->cbrack=
   2.200 -	  g_strdup_printf("    Line %ld - Mismatched curly brackets?",linecnt);
   2.201 -    if (counters->c_unders%2)
   2.202 -	pending->unders=
   2.203 -	  g_strdup_printf("    Line %ld - Mismatched underscores?",linecnt);
   2.204 -}
   2.205 -
   2.206 -/*
   2.207   * check_for_omitted_punctuation:
   2.208   *
   2.209   * Check for omitted punctuation at end of paragraph by working back
   2.210 @@ -2693,7 +2548,6 @@
   2.211  	}
   2.212  	checked_linecnt++;
   2.213  	print_pending(aline,parastart,&pending);
   2.214 -	memset(&pending,0,sizeof(pending));
   2.215  	isemptyline=analyse_quotes(aline,&counters);
   2.216  	if (isnewpara && !isemptyline)
   2.217  	{
   2.218 @@ -2774,7 +2628,7 @@
   2.219  	if (isemptyline)
   2.220  	{
   2.221  	    check_for_mismatched_quotes(&counters,&pending);
   2.222 -	    memset(&counters,0,sizeof(counters));
   2.223 +	    counters_reset(&counters);
   2.224  	    /* let the next iteration know that it's starting a new para */
   2.225  	    isnewpara=TRUE;
   2.226  	    if (prevline)
   2.227 @@ -2783,6 +2637,10 @@
   2.228  	g_free(prevline);
   2.229  	prevline=g_strdup(aline);
   2.230      }
   2.231 +    linecnt++;
   2.232 +    check_for_mismatched_quotes(&counters,&pending);
   2.233 +    print_pending(NULL,parastart,&pending);
   2.234 +    reset_pending(&pending);
   2.235      if (prevline)
   2.236      {
   2.237  	g_free(prevline);
     3.1 --- a/bookloupe/bookloupe.h	Sat Sep 21 23:40:18 2013 +0100
     3.2 +++ b/bookloupe/bookloupe.h	Mon Sep 23 21:18:27 2013 +0100
     3.3 @@ -79,9 +79,10 @@
     3.4      int dquote,squote;
     3.5  };
     3.6  
     3.7 -struct pending {
     3.8 -    char *dquote,*squote,*rbrack,*sbrack,*cbrack,*unders;
     3.9 -    long squot;
    3.10 -};
    3.11 +extern gboolean pswit[SWITNO];
    3.12 +
    3.13 +extern long cnt_dquot,cnt_squot,cnt_brack,cnt_bin,cnt_odd,cnt_long,cnt_short;
    3.14 +extern long cnt_punct,cnt_dash,cnt_word,cnt_html,cnt_lineend,cnt_spacend;
    3.15 +extern long linecnt,checked_linecnt;
    3.16  
    3.17  #endif /* BOOKOUPE_H */
     4.1 --- a/bookloupe/counters.c	Sat Sep 21 23:40:18 2013 +0100
     4.2 +++ b/bookloupe/counters.c	Mon Sep 23 21:18:27 2013 +0100
     4.3 @@ -1,4 +1,5 @@
     4.4  #include <stdlib.h>
     4.5 +#include <string.h>
     4.6  #include <glib.h>
     4.7  #include "bookloupe.h"
     4.8  #include "counters.h"
     4.9 @@ -44,9 +45,9 @@
    4.10  	return GINT_TO_POINTER((gint)CHAR_SQUOTE);
    4.11      else if (ch==CHAR_LS_QUOTE || ch==CHAR_RS_QUOTE)
    4.12  	return GINT_TO_POINTER((gint)CHAR_LS_QUOTE);
    4.13 -    else
    4.14 +    else if (ch<0x4000 || ch-0x4000>=NO_SPECIAL_COUNTERS)
    4.15      {
    4.16 -	g_warning("Matching pair not found for U+%04"G_GINT32_FORMAT"X",ch);
    4.17 +	g_warning("Matching pair not found for U+%04" G_GINT32_MODIFIER "X",ch);
    4.18  	return GINT_TO_POINTER((gint)ch);
    4.19      }
    4.20  }
    4.21 @@ -96,6 +97,13 @@
    4.22      return value->open-value->close;
    4.23  }
    4.24  
    4.25 +void counters_reset(struct counters *counters)
    4.26 +{
    4.27 +    if (counters->matching)
    4.28 +	g_tree_destroy(counters->matching);
    4.29 +    memset(counters,0,sizeof(*counters));
    4.30 +}
    4.31 +
    4.32  void counters_destroy(struct counters *counters)
    4.33  {
    4.34      if (counters->matching)
     5.1 --- a/bookloupe/counters.h	Sat Sep 21 23:40:18 2013 +0100
     5.2 +++ b/bookloupe/counters.h	Mon Sep 23 21:18:27 2013 +0100
     5.3 @@ -3,6 +3,12 @@
     5.4  
     5.5  #include <glib.h>
     5.6  
     5.7 +/* Special counters live in the private use area */
     5.8 +enum {
     5.9 +    COUNTER_ILLUSTRATION=0xE000,
    5.10 +    NO_SPECIAL_COUNTERS
    5.11 +};
    5.12 +
    5.13  struct counters {
    5.14      GTree *matching;
    5.15      long quot;
    5.16 @@ -12,6 +18,7 @@
    5.17  void increment_matching(struct counters *counters,gunichar ch,gboolean open);
    5.18  int matching_count(const struct counters *counters,gunichar ch,gboolean open);
    5.19  int matching_difference(const struct counters *counters,gunichar ch);
    5.20 +void counters_reset(struct counters *counters);
    5.21  void counters_destroy(struct counters *counters);
    5.22  
    5.23  #endif /* COUNTERS_H */
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/bookloupe/pending.c	Mon Sep 23 21:18:27 2013 +0100
     6.3 @@ -0,0 +1,239 @@
     6.4 +#include <stdlib.h>
     6.5 +#include <string.h>
     6.6 +#include <glib.h>
     6.7 +#include "bookloupe.h"
     6.8 +#include "pending.h"
     6.9 +
    6.10 +/*
    6.11 + * print_pending:
    6.12 + *
    6.13 + * If we are in a state of unbalanced quotes, and this line
    6.14 + * doesn't begin with a quote, output the stored error message.
    6.15 + * If the -p switch was used, print the warning even if the
    6.16 + * new para starts with quotes.
    6.17 + */
    6.18 +void print_pending(const char *aline,const char *parastart,
    6.19 +  struct pending *pending)
    6.20 +{
    6.21 +    const char *s;
    6.22 +    gunichar c;
    6.23 +    if (aline)
    6.24 +    {
    6.25 +	s=aline;
    6.26 +	while (*s==' ')
    6.27 +	    s++;
    6.28 +	c=g_utf8_get_char(s);
    6.29 +    }
    6.30 +    else
    6.31 +    {
    6.32 +	s=NULL;
    6.33 +	c='\0';
    6.34 +    }
    6.35 +    if (pending->illustration.warning_text)
    6.36 +    {
    6.37 +	if (aline)
    6.38 +	{
    6.39 +	    if (pswit[ECHO_SWITCH] && !pending->illustration.queried_line)
    6.40 +		pending->illustration.queried_line=g_strdup(parastart);
    6.41 +	}
    6.42 +	else
    6.43 +	{
    6.44 +	    if (!pswit[OVERVIEW_SWITCH])
    6.45 +	    {
    6.46 +		if (pending->illustration.queried_line)
    6.47 +		    g_print("\n%s\n",pending->illustration.queried_line);
    6.48 +		g_print("%s\n",pending->illustration.warning_text);
    6.49 +	    }
    6.50 +	    else
    6.51 +		cnt_brack++;
    6.52 +	    g_free(pending->illustration.warning_text);
    6.53 +	    pending->illustration.warning_text=NULL;
    6.54 +	    g_free(pending->illustration.queried_line);
    6.55 +	    pending->illustration.queried_line=NULL;
    6.56 +	}
    6.57 +    }
    6.58 +    if (pending->dquote)
    6.59 +    {
    6.60 +	if (c!=CHAR_DQUOTE || pswit[QPARA_SWITCH])
    6.61 +	{
    6.62 +	    if (!pswit[OVERVIEW_SWITCH])
    6.63 +	    {
    6.64 +		if (pswit[ECHO_SWITCH])
    6.65 +		    g_print("\n%s\n",parastart);
    6.66 +		g_print("%s\n",pending->dquote);
    6.67 +	    }
    6.68 +	    else
    6.69 +		cnt_dquot++;
    6.70 +	}
    6.71 +	g_free(pending->dquote);
    6.72 +	pending->dquote=NULL;
    6.73 +    }
    6.74 +    if (pending->squote)
    6.75 +    {
    6.76 +	if (!CHAR_IS_SQUOTE(c) || pswit[QPARA_SWITCH] || pending->squot)
    6.77 +	{
    6.78 +	    if (!pswit[OVERVIEW_SWITCH])
    6.79 +	    {
    6.80 +		if (pswit[ECHO_SWITCH])
    6.81 +		    g_print("\n%s\n",parastart);
    6.82 +		g_print("%s\n",pending->squote);
    6.83 +	    }
    6.84 +	    else
    6.85 +		cnt_squot++;
    6.86 +	}
    6.87 +	g_free(pending->squote);
    6.88 +	pending->squote=NULL;
    6.89 +    }
    6.90 +    if (pending->rbrack)
    6.91 +    {
    6.92 +	if (!pswit[OVERVIEW_SWITCH])
    6.93 +	{
    6.94 +	    if (pswit[ECHO_SWITCH])
    6.95 +		g_print("\n%s\n",parastart);
    6.96 +	    g_print("%s\n",pending->rbrack);
    6.97 +	}
    6.98 +	else
    6.99 +	    cnt_brack++;
   6.100 +	g_free(pending->rbrack);
   6.101 +	pending->rbrack=NULL;
   6.102 +    }
   6.103 +    if (pending->sbrack)
   6.104 +    {
   6.105 +	if (!pswit[OVERVIEW_SWITCH])
   6.106 +	{
   6.107 +	    if (pswit[ECHO_SWITCH])
   6.108 +		g_print("\n%s\n",parastart);
   6.109 +	    g_print("%s\n",pending->sbrack);
   6.110 +	}
   6.111 +	else
   6.112 +	    cnt_brack++;
   6.113 +	g_free(pending->sbrack);
   6.114 +	pending->sbrack=NULL;
   6.115 +    }
   6.116 +    if (pending->cbrack)
   6.117 +    {
   6.118 +	if (!pswit[OVERVIEW_SWITCH])
   6.119 +	{
   6.120 +	    if (pswit[ECHO_SWITCH])
   6.121 +		g_print("\n%s\n",parastart);
   6.122 +	    g_print("%s\n",pending->cbrack);
   6.123 +	}
   6.124 +	else
   6.125 +	    cnt_brack++;
   6.126 +	g_free(pending->cbrack);
   6.127 +	pending->cbrack=NULL;
   6.128 +    }
   6.129 +    if (pending->unders)
   6.130 +    {
   6.131 +	if (!pswit[OVERVIEW_SWITCH])
   6.132 +	{
   6.133 +	    if (pswit[ECHO_SWITCH])
   6.134 +		g_print("\n%s\n",parastart);
   6.135 +	    g_print("%s\n",pending->unders);
   6.136 +	}
   6.137 +	else
   6.138 +	    cnt_brack++;
   6.139 +	g_free(pending->unders);
   6.140 +	pending->unders=NULL;
   6.141 +    }
   6.142 +}
   6.143 +
   6.144 +void reset_pending(struct pending *pending)
   6.145 +{
   6.146 +    memset(pending,0,sizeof(*pending));
   6.147 +}
   6.148 +
   6.149 +/*
   6.150 + * check_for_mismatched_quotes:
   6.151 + *
   6.152 + * At end of paragraph, check for mismatched quotes.
   6.153 + *
   6.154 + * We don't want to report an error immediately, since it is a
   6.155 + * common convention to omit the quotes at end of paragraph if
   6.156 + * the next paragraph is a continuation of the same speaker.
   6.157 + * Where this is the case, the next para should begin with a
   6.158 + * quote, so we store the warning message and only display it
   6.159 + * at the top of the next iteration if the new para doesn't
   6.160 + * start with a quote.
   6.161 + * The -p switch overrides this default, and warns of unclosed
   6.162 + * quotes on _every_ paragraph, whether the next begins with a
   6.163 + * quote or not.
   6.164 + */
   6.165 +void check_for_mismatched_quotes(const struct counters *counters,
   6.166 +  struct pending *pending)
   6.167 +{
   6.168 +    int squote_straight,squote_curved,difference;
   6.169 +    if (counters->quot%2)
   6.170 +	pending->dquote=
   6.171 +	  g_strdup_printf("    Line %ld - Mismatched quotes",linecnt);
   6.172 +    if (pswit[SQUOTE_SWITCH])
   6.173 +    {
   6.174 +	if (matching_count(counters,CHAR_SQUOTE,TRUE))
   6.175 +	    squote_straight=matching_difference(counters,CHAR_SQUOTE);
   6.176 +	else
   6.177 +	    squote_straight=0;
   6.178 +	if (matching_count(counters,CHAR_LS_QUOTE,TRUE))
   6.179 +	    squote_curved=matching_difference(counters,CHAR_LS_QUOTE);
   6.180 +	else
   6.181 +	    squote_curved=0;
   6.182 +	if (squote_straight || squote_curved)
   6.183 +	    pending->squote=
   6.184 +	      g_strdup_printf("    Line %ld - Mismatched singlequotes?",
   6.185 +	      linecnt);
   6.186 +	if (squote_straight && squote_straight!=1 ||
   6.187 +	  squote_curved && squote_curved!=1)
   6.188 +	    /*
   6.189 +	     * Flag it to be noted regardless of the
   6.190 +	     * first char of the next para.
   6.191 +	     */
   6.192 +	    pending->squot=1;
   6.193 +    }
   6.194 +    difference=matching_difference(counters,COUNTER_ILLUSTRATION);
   6.195 +    if (difference)
   6.196 +    {
   6.197 +	if (difference<0 && pending->illustration.warning_text)
   6.198 +	{
   6.199 +	    difference++;
   6.200 +	    g_free(pending->illustration.queried_line);
   6.201 +	    pending->illustration.queried_line=NULL;
   6.202 +	    g_free(pending->illustration.warning_text);
   6.203 +	    pending->illustration.warning_text=NULL;
   6.204 +	}
   6.205 +	if (difference<0)
   6.206 +	{
   6.207 +	    increment_matching(counters,CHAR_OPEN_SBRACK,FALSE);
   6.208 +	    difference++;
   6.209 +	}
   6.210 +	if (difference)
   6.211 +	{
   6.212 +	    if (pending->illustration.warning_text)
   6.213 +	    {
   6.214 +		if (!pswit[OVERVIEW_SWITCH])
   6.215 +		{
   6.216 +		    if (pending->illustration.queried_line)
   6.217 +			g_print("\n%s\n",pending->illustration.queried_line);
   6.218 +		    g_print("%s\n",pending->illustration.warning_text);
   6.219 +		}
   6.220 +		else
   6.221 +		    cnt_brack++;
   6.222 +		g_free(pending->illustration.warning_text);
   6.223 +	    }
   6.224 +	    pending->illustration.warning_text=g_strdup_printf(
   6.225 +	      "    Line %ld - Mismatched illustration tag?",linecnt);
   6.226 +	    g_free(pending->illustration.queried_line);
   6.227 +	    pending->illustration.queried_line=NULL;
   6.228 +	}
   6.229 +    }
   6.230 +    if (matching_difference(counters,CHAR_OPEN_RBRACK))
   6.231 +	pending->rbrack=
   6.232 +	  g_strdup_printf("    Line %ld - Mismatched round brackets?",linecnt);
   6.233 +    if (matching_difference(counters,CHAR_OPEN_SBRACK))
   6.234 +	pending->sbrack=
   6.235 +	  g_strdup_printf("    Line %ld - Mismatched square brackets?",linecnt);
   6.236 +    if (matching_difference(counters,CHAR_OPEN_CBRACK))
   6.237 +	pending->cbrack=
   6.238 +	  g_strdup_printf("    Line %ld - Mismatched curly brackets?",linecnt);
   6.239 +    if (counters->c_unders%2)
   6.240 +	pending->unders=
   6.241 +	  g_strdup_printf("    Line %ld - Mismatched underscores?",linecnt);
   6.242 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/bookloupe/pending.h	Mon Sep 23 21:18:27 2013 +0100
     7.3 @@ -0,0 +1,23 @@
     7.4 +#ifndef PENDING_H
     7.5 +#define PENDING_H
     7.6 +
     7.7 +#include "counters.h"
     7.8 +
     7.9 +struct pending_warning {
    7.10 +    char *queried_line;
    7.11 +    char *warning_text;
    7.12 +};
    7.13 +
    7.14 +struct pending {
    7.15 +    char *dquote,*squote,*rbrack,*sbrack,*cbrack,*unders;
    7.16 +    long squot;
    7.17 +    struct pending_warning illustration;
    7.18 +};
    7.19 +
    7.20 +void print_pending(const char *aline,const char *parastart,
    7.21 +  struct pending *pending);
    7.22 +void reset_pending(struct pending *pending);
    7.23 +void check_for_mismatched_quotes(const struct counters *counters,
    7.24 +  struct pending *pending);
    7.25 +
    7.26 +#endif /* PENDING_H */
     8.1 --- a/test/bookloupe/Makefile.am	Sat Sep 21 23:40:18 2013 +0100
     8.2 +++ b/test/bookloupe/Makefile.am	Mon Sep 23 21:18:27 2013 +0100
     8.3 @@ -1,5 +1,5 @@
     8.4  TESTS_ENVIRONMENT=BOOKLOUPE=../../bookloupe/bookloupe ../harness/loupe-test
     8.5  TESTS=non-ascii.tst long-line.tst curved-single-quotes.tst \
     8.6 -	curved-genitives.tst
     8.7 +	curved-genitives.tst multi-line-illustration.tst
     8.8  
     8.9  dist_pkgdata_DATA=$(TESTS)
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/bookloupe/multi-line-illustration.tst	Mon Sep 23 21:18:27 2013 +0100
     9.3 @@ -0,0 +1,29 @@
     9.4 +**************** INPUT ****************
     9.5 +[Illustration: One or more Caption lines
     9.6 +
     9.7 +One or more Sub-caption lines]
     9.8 +
     9.9 +[Illustration: The first line of a caption.
    9.10 +
    9.11 +A caption that contains balanced [square] brackets in a paragraph.]
    9.12 +
    9.13 +[Square brackets can be used to group several paragraphs.
    9.14 +
    9.15 +This will generate warnings as normal.]
    9.16 +
    9.17 +[Illustration: An ill-formed illustration might confuse.
    9.18 +
    9.19 +It generates warnings.] The opening and closing tags no longer match.
    9.20 +**************** WARNINGS ****************
    9.21 +<expected>
    9.22 +  <error>
    9.23 +    <at line="10"/>
    9.24 +    <at line="12"/>
    9.25 +    <at line="16"/>
    9.26 +    <text>Mismatched square brackets?</text>
    9.27 +  </error>
    9.28 +  <error>
    9.29 +    <at line="14"/>
    9.30 +    <text>Mismatched illustration tag?</text>
    9.31 +  </error>
    9.32 +</expected>
    10.1 --- a/test/compatibility/example.tst	Sat Sep 21 23:40:18 2013 +0100
    10.2 +++ b/test/compatibility/example.tst	Mon Sep 23 21:18:27 2013 +0100
    10.3 @@ -33,6 +33,8 @@
    10.4  travels were profitable to himself. The fact is, that tbere are
    10.5  cousins who come to greatness and rnust be pacified, or they will
    10.6  prove annoying. Heaven forefend a collision between cousins!
    10.7 +
    10.8 +
    10.9  **************** EXPECTED ****************
   10.10  
   10.11  Japan, China, Australia , nay, the continent of Europe, holding an
   10.12 @@ -85,3 +87,6 @@
   10.13  
   10.14  cousins who come to greatness and rnust be pacified, or they will
   10.15      Line 33 column 34 - Query word rnust - not reporting duplicates
   10.16 +
   10.17 +correspond with some of them. On the whole, forgetting two or
   10.18 +    Line 35 - Mismatched quotes
    11.1 --- a/test/harness/testcase.c	Sat Sep 21 23:40:18 2013 +0100
    11.2 +++ b/test/harness/testcase.c	Mon Sep 23 21:18:27 2013 +0100
    11.3 @@ -469,13 +469,7 @@
    11.4  	g_error_free(error);
    11.5  	return FALSE;
    11.6      }
    11.7 -    if (testcase->expected || testcase->warnings)
    11.8 -	r=testcase_spawn_bookloupe(testcase,&output,&error);
    11.9 -    else
   11.10 -    {
   11.11 -	r=testcase_spawn_bookloupe(testcase,NULL,&error);
   11.12 -        output=NULL;
   11.13 -    }
   11.14 +    r=testcase_spawn_bookloupe(testcase,&output,&error);
   11.15      if (!r)
   11.16      {
   11.17  	g_print("%s: FAIL\n",testcase->basename);
   11.18 @@ -492,37 +486,33 @@
   11.19  	g_error_free(error);
   11.20  	return FALSE;
   11.21      }
   11.22 -    if (testcase->expected || testcase->warnings)
   11.23 +    header=g_string_new("\n\nFile: ");
   11.24 +    g_string_append(header,filename);
   11.25 +    g_string_append(header,"\n");
   11.26 +    if (!g_str_has_prefix(output,header->str))
   11.27      {
   11.28 -	header=g_string_new("\n\nFile: ");
   11.29 -	g_string_append(header,filename);
   11.30 -	g_string_append(header,"\n");
   11.31 -	if (!g_str_has_prefix(output,header->str))
   11.32 +	g_print("%s: FAIL\n",testcase->basename);
   11.33 +	g_print("Unexpected header from bookloupe:\n");
   11.34 +	offset=common_prefix_length(output,header->str);
   11.35 +	print_unexpected(output,offset);
   11.36 +	r=FALSE;
   11.37 +    }
   11.38 +    pos=header->len;
   11.39 +    if (r)
   11.40 +    {
   11.41 +	/* Skip the summary */
   11.42 +	s=strstr(output+pos,"\n\n");
   11.43 +	if (s)
   11.44 +	    pos=s-output+2;
   11.45 +	else
   11.46  	{
   11.47  	    g_print("%s: FAIL\n",testcase->basename);
   11.48 -	    g_print("Unexpected header from bookloupe:\n");
   11.49 -	    offset=common_prefix_length(output,header->str);
   11.50 -	    print_unexpected(output,offset);
   11.51 +	    g_print("Unterminated summary from bookloupe:\n%s\n",output+pos);
   11.52  	    r=FALSE;
   11.53  	}
   11.54 -	pos=header->len;
   11.55 -	if (r)
   11.56 -	{
   11.57 -	    /* Skip the summary */
   11.58 -	    s=strstr(output+pos,"\n\n");
   11.59 -	    if (s)
   11.60 -		pos=s-output+2;
   11.61 -	    else
   11.62 -	    {
   11.63 -		g_print("%s: FAIL\n",testcase->basename);
   11.64 -		g_print("Unterminated summary from bookloupe:\n%s\n",
   11.65 -		  output+pos);
   11.66 -		r=FALSE;
   11.67 -	    }
   11.68 -	}
   11.69 -	g_string_free(header,TRUE);
   11.70 -	r=testcase_check_warnings(testcase,output+pos,&xfail);
   11.71      }
   11.72 +    g_string_free(header,TRUE);
   11.73 +    r=testcase_check_warnings(testcase,output+pos,&xfail);
   11.74      g_free(filename);
   11.75      g_free(output);
   11.76      if (r)