bookloupe/counters.c
author ali <ali@juiblex.co.uk>
Sun Oct 27 18:08:57 2013 +0000 (2013-10-27)
changeset 205 29f73e876121
parent 103 adc06e9e8470
permissions -rw-r--r--
Update documentation for 2.0.66
     1 #include <stdlib.h>
     2 #include <string.h>
     3 #include <glib.h>
     4 #include "bookloupe.h"
     5 #include "counters.h"
     6 
     7 struct matching_counter {
     8     int open,close;
     9 };
    10 
    11 GQuark counters_error_quark(void)
    12 {
    13     static GQuark quark;
    14     if (!quark)
    15 	quark=g_quark_from_static_string("counters_error");
    16     return quark;
    17 }
    18 
    19 static struct matching_counter *matching_counter_new(void)
    20 {
    21     return g_slice_new0(struct matching_counter);
    22 }
    23 
    24 static void matching_counter_free(struct matching_counter *counter)
    25 {
    26     g_slice_free(struct matching_counter,counter);
    27 }
    28 
    29 static gint compar_unichars(gconstpointer a,gconstpointer b,gpointer unused)
    30 {
    31     /*
    32      * Unicode code points only go up to 0x10FFFF and thus this cannot overflow.
    33      */
    34     return GPOINTER_TO_INT(a)-GPOINTER_TO_INT(b);
    35 }
    36 
    37 /*
    38  * For matching characters, we maintain a count of the opens and closes.
    39  * In the simplest case, we are dealing with a matching pair such as [ and ]
    40  * where there is a 1:1 mapping between an instance of [ with an open and
    41  * between an instance of ] with a close. matching_ket() is
    42  * responsible for selecting an arbitary base character of a matching pair.
    43  */
    44 static gpointer matching_key(gunichar ch)
    45 {
    46     gunichar mirrored;
    47     if (g_unichar_get_mirror_char(ch,&mirrored))
    48 	if (ch<mirrored)
    49 	    return GINT_TO_POINTER((gint)ch);
    50 	else
    51 	    return GINT_TO_POINTER((gint)mirrored);
    52     else if (ch==CHAR_SQUOTE || ch==CHAR_OPEN_SQUOTE)
    53 	return GINT_TO_POINTER((gint)CHAR_SQUOTE);
    54     else if (ch==CHAR_LS_QUOTE || ch==CHAR_RS_QUOTE)
    55 	return GINT_TO_POINTER((gint)CHAR_LS_QUOTE);
    56     else if (ch==CHAR_LD_QUOTE || ch==CHAR_RD_QUOTE)
    57 	return GINT_TO_POINTER((gint)CHAR_LD_QUOTE);
    58     else if (ch==CHAR_DQUOTE)
    59 	return GINT_TO_POINTER((gint)ch);
    60     else if (ch<0x4000 || ch-0x4000>=NO_SPECIAL_COUNTERS)
    61 	g_warning("Matching pair not found for U+%04" G_GINT32_MODIFIER "X",ch);
    62     return GINT_TO_POINTER((gint)ch);
    63 }
    64 
    65 gboolean innermost_quote_matches(struct counters *counters,gunichar ch)
    66 {
    67     gpointer head;
    68     if (counters->open_quotes)
    69 	head=counters->open_quotes->data;
    70     else
    71 	head=NULL;
    72     return head==matching_key(ch);
    73 }
    74 
    75 gboolean count_quote(struct counters *counters,gunichar ch,QuoteClass klass,
    76   GError **err)
    77 {
    78     gboolean retval=TRUE;
    79     gpointer head;
    80     if (counters->open_quotes)
    81 	head=counters->open_quotes->data;
    82     else
    83 	head=NULL;
    84     switch(klass)
    85     {
    86 	case NEUTRAL_QUOTE:
    87 	    if (head!=matching_key(ch))
    88 		goto opening;
    89 	    /* else fall through */
    90 	case CLOSING_QUOTE:
    91 	    if (head!=matching_key(ch))
    92 	    {
    93 		g_set_error(err,COUNTERS_ERROR,COUNTERS_ERROR_FAILED,
    94 		  "Closing quotation mark with no matching open?");
    95 		retval=FALSE;
    96 	    }
    97 	    else
    98 		counters->open_quotes=g_slist_delete_link(counters->open_quotes,
    99 		  counters->open_quotes);
   100 	    break;
   101 	case OPENING_QUOTE:
   102 	    if (head==matching_key(ch))
   103 	    {
   104 		g_set_error(err,COUNTERS_ERROR,COUNTERS_ERROR_FAILED,
   105 		  "Directly nested quotation marks of same type?");
   106 		retval=FALSE;
   107 	    }
   108 opening:
   109 	    head=matching_key(ch);
   110 	    counters->open_quotes=g_slist_prepend(counters->open_quotes,head);
   111 	    break;
   112     }
   113     return retval;
   114 }
   115 
   116 void increment_matching(struct counters *counters,gunichar ch,gboolean open)
   117 {
   118     gpointer key,orig_key;
   119     struct matching_counter *value;
   120     if (!counters->matching)
   121 	counters->matching=g_tree_new_full(compar_unichars,NULL,NULL,
   122 	  (GDestroyNotify)matching_counter_free);
   123     key=matching_key(ch);
   124     if (!g_tree_lookup_extended(counters->matching,key,&orig_key,
   125       (gpointer *)&value))
   126     {
   127 	value=matching_counter_new();
   128 	g_tree_insert(counters->matching,key,value);
   129     }
   130     if (open)
   131 	value->open++;
   132     else
   133 	value->close++;
   134 }
   135 
   136 int matching_count(const struct counters *counters,gunichar ch,gboolean open)
   137 {
   138     struct matching_counter *value;
   139     if (!counters->matching)
   140 	return 0;
   141     value=g_tree_lookup(counters->matching,matching_key(ch));
   142     if (!value)
   143 	return 0;
   144     return open?value->open:value->close;
   145 }
   146 
   147 /*
   148  * Return open count - closed count
   149  */
   150 int matching_difference(const struct counters *counters,gunichar ch)
   151 {
   152     struct matching_counter *value;
   153     if (!counters->matching)
   154 	return 0;
   155     value=g_tree_lookup(counters->matching,matching_key(ch));
   156     if (!value)
   157 	return 0;
   158     return value->open-value->close;
   159 }
   160 
   161 void counters_reset(struct counters *counters)
   162 {
   163     if (counters->matching)
   164 	g_tree_destroy(counters->matching);
   165     memset(counters,0,sizeof(*counters));
   166 }
   167 
   168 void counters_destroy(struct counters *counters)
   169 {
   170     if (counters->matching)
   171     {
   172 	g_tree_destroy(counters->matching);
   173 	counters->matching=NULL;
   174     }
   175 }