|
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 |
}
|