#include #include #include #include "bookloupe.h" #include "counters.h" struct matching_counter { int open,close; }; GQuark counters_error_quark(void) { static GQuark quark; if (!quark) quark=g_quark_from_static_string("counters_error"); return quark; } static struct matching_counter *matching_counter_new(void) { return g_slice_new0(struct matching_counter); } static void matching_counter_free(struct matching_counter *counter) { g_slice_free(struct matching_counter,counter); } static gint compar_unichars(gconstpointer a,gconstpointer b,gpointer unused) { /* * Unicode code points only go up to 0x10FFFF and thus this cannot overflow. */ return GPOINTER_TO_INT(a)-GPOINTER_TO_INT(b); } /* * For matching characters, we maintain a count of the opens and closes. * In the simplest case, we are dealing with a matching pair such as [ and ] * where there is a 1:1 mapping between an instance of [ with an open and * between an instance of ] with a close. matching_ket() is * responsible for selecting an arbitary base character of a matching pair. */ static gpointer matching_key(gunichar ch) { gunichar mirrored; if (g_unichar_get_mirror_char(ch,&mirrored)) if (ch=NO_SPECIAL_COUNTERS) g_warning("Matching pair not found for U+%04" G_GINT32_MODIFIER "X",ch); return GINT_TO_POINTER((gint)ch); } gboolean innermost_quote_matches(struct counters *counters,gunichar ch) { gpointer head; if (counters->open_quotes) head=counters->open_quotes->data; else head=NULL; return head==matching_key(ch); } gboolean count_quote(struct counters *counters,gunichar ch,QuoteClass klass, GError **err) { gboolean retval=TRUE; gpointer head; if (counters->open_quotes) head=counters->open_quotes->data; else head=NULL; switch(klass) { case NEUTRAL_QUOTE: if (head!=matching_key(ch)) goto opening; /* else fall through */ case CLOSING_QUOTE: if (head!=matching_key(ch)) { g_set_error(err,COUNTERS_ERROR,COUNTERS_ERROR_FAILED, "Closing quotation mark with no matching open?"); retval=FALSE; } else counters->open_quotes=g_slist_delete_link(counters->open_quotes, counters->open_quotes); break; case OPENING_QUOTE: if (head==matching_key(ch)) { g_set_error(err,COUNTERS_ERROR,COUNTERS_ERROR_FAILED, "Directly nested quotation marks of same type?"); retval=FALSE; } opening: head=matching_key(ch); counters->open_quotes=g_slist_prepend(counters->open_quotes,head); break; } return retval; } void increment_matching(struct counters *counters,gunichar ch,gboolean open) { gpointer key,orig_key; struct matching_counter *value; if (!counters->matching) counters->matching=g_tree_new_full(compar_unichars,NULL,NULL, (GDestroyNotify)matching_counter_free); key=matching_key(ch); if (!g_tree_lookup_extended(counters->matching,key,&orig_key, (gpointer *)&value)) { value=matching_counter_new(); g_tree_insert(counters->matching,key,value); } if (open) value->open++; else value->close++; } int matching_count(const struct counters *counters,gunichar ch,gboolean open) { struct matching_counter *value; if (!counters->matching) return 0; value=g_tree_lookup(counters->matching,matching_key(ch)); if (!value) return 0; return open?value->open:value->close; } /* * Return open count - closed count */ int matching_difference(const struct counters *counters,gunichar ch) { struct matching_counter *value; if (!counters->matching) return 0; value=g_tree_lookup(counters->matching,matching_key(ch)); if (!value) return 0; return value->open-value->close; } void counters_reset(struct counters *counters) { if (counters->matching) g_tree_destroy(counters->matching); memset(counters,0,sizeof(*counters)); } void counters_destroy(struct counters *counters) { if (counters->matching) { g_tree_destroy(counters->matching); counters->matching=NULL; } }