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