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