Misc mnemonics fixes.
[gliv.git] / src / mnemonics.c
blob6b986eace5fdc633262be42b01e1e08fba39fe46
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /*****************************************
22 * Try to add unique mnemonics to labels *
23 *****************************************/
25 #include <string.h> /* strlen(), memcpy() */
27 #include "gliv.h"
28 #include "mnemonics.h"
29 #include "str_utils.h"
31 static GSList *stack = NULL;
32 static GHashTable *mnemonics = NULL;
34 static gint count_underscores(const gchar * str, gint len)
36 gint nb_underscores = 0;
37 gulong himagic, lomagic, magic_bits;
38 const gulong *long_ptr;
39 gulong mask;
41 while ((gulong) str & (sizeof(gulong) - 1))
42 switch (*str) {
43 case '\0':
44 return nb_underscores;
46 case '_':
47 nb_underscores++;
48 /* Fall through */
50 default:
51 str++;
52 len--;
55 long_ptr = (gulong *) str;
56 INIT_MAGIC();
58 /* '_' == 0x5F */
59 mask = 0x5F5F5F5F;
60 if (sizeof(gulong) > 4)
61 /* 64-bit */
62 mask = ((mask << 16) << 16) | mask;
64 while (len >= sizeof(gulong)) {
65 gulong masked;
67 masked = *long_ptr ^ mask;
68 if (HAS_ZERO(masked)) {
69 /* A '_' has been detected. */
70 gchar *char_ptr = (gchar *) & masked;
71 nb_underscores += (*char_ptr++ == 0);
72 nb_underscores += (*char_ptr++ == 0);
73 nb_underscores += (*char_ptr++ == 0);
74 nb_underscores += (*char_ptr++ == 0);
75 if (sizeof(gulong) > 4) {
76 /* 64-bit */
77 nb_underscores += (*char_ptr++ == 0);
78 nb_underscores += (*char_ptr++ == 0);
79 nb_underscores += (*char_ptr++ == 0);
80 nb_underscores += (*char_ptr++ == 0);
84 long_ptr++;
85 len -= sizeof(gulong);
88 str = (const gchar *) long_ptr;
89 while (len > 0) {
90 nb_underscores += (*str == '_');
91 str++;
92 len--;
95 return nb_underscores;
98 static const gchar *find_mnemonic_position(const gchar * str)
100 const gchar *ptr;
102 for (ptr = str; *ptr != '\0'; ptr = g_utf8_next_char(ptr)) {
103 gunichar ch = g_unichar_tolower(g_utf8_get_char(ptr));
104 gpointer ch_key = GINT_TO_POINTER(ch);
106 if (g_unichar_isalnum(ch)) {
107 if (mnemonics == NULL) {
108 mnemonics = g_hash_table_new(g_direct_hash, g_direct_equal);
109 g_hash_table_insert(mnemonics, ch_key, mnemonics);
110 return ptr;
113 if (g_hash_table_lookup(mnemonics, ch_key) == NULL) {
114 g_hash_table_insert(mnemonics, ch_key, mnemonics);
115 return ptr;
120 /* No position found, add in front. */
121 return str;
125 * Underscores in labels are replaced with mnemonics, so we duplicate them.
126 * The caller has to determine whether the returned string has to be freed.
128 static gchar *duplicate_underscores(const gchar * orig)
130 gint nb_underscores, len;
131 const gchar *ptr_orig;
132 gchar *new, *ptr_new;
134 /* How many underscores? */
135 len = strlen(orig);
136 nb_underscores = count_underscores(orig, len);
137 len += nb_underscores;
139 if (nb_underscores == 0)
140 return (gchar *) orig;
142 ptr_new = new = g_new(gchar, len + 1);
144 for (ptr_orig = orig; *ptr_orig != '\0'; ptr_orig++) {
145 *ptr_new = *ptr_orig;
146 if (*ptr_orig == '_') {
147 /* Duplicate this one. */
148 ptr_new++;
149 *ptr_new = '_';
151 ptr_new++;
154 *ptr_new = '\0';
156 return new;
160 * The returned string should not be freed. We keep track of already used
161 * mnemonics to avoid giving twice the same letter.
163 const gchar *add_mnemonic(const gchar * str)
165 static gchar *result = NULL;
166 static gint size = 0;
167 const gchar *mnemonic_pos, *end;
168 gint new_size, offset;
169 gchar *work;
171 work = duplicate_underscores(str);
172 mnemonic_pos = find_mnemonic_position(work);
173 end = mnemonic_pos + strlen(mnemonic_pos);
175 /* + 2: '_' and '\0'. */
176 new_size = end - work + 2;
177 if (new_size > size) {
178 size = new_size;
179 g_free(result);
181 result = g_new(gchar, size);
184 offset = mnemonic_pos - work;
185 memcpy(result, work, offset);
186 result[offset] = '_';
187 memcpy(result + offset + 1, mnemonic_pos, end - mnemonic_pos + 1);
189 if (work != str)
190 g_free(work);
192 return result;
195 void reset_mnemonics(void)
197 if (mnemonics != NULL) {
198 g_hash_table_destroy(mnemonics);
199 mnemonics = NULL;
203 void push_mnemonics(void)
205 stack = g_slist_prepend(stack, mnemonics);
206 mnemonics = NULL;
209 void pop_mnemonics(void)
211 reset_mnemonics();
212 mnemonics = g_slist_nth_data(stack, 0);
213 stack = g_slist_delete_link(stack, stack);