~jbicha/hud/build-depend-on-valac-not-gir

« back to all changes in this revision

Viewing changes to service/load-app-info.c

Merging the HUD into indicator-appmenu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Loads application info for initial app usage and verification
 
3
 
 
4
Copyright 2011 Canonical Ltd.
 
5
 
 
6
Authors:
 
7
    Ted Gould <ted@canonical.com>
 
8
 
 
9
This program is free software: you can redistribute it and/or modify it 
 
10
under the terms of the GNU General Public License version 3, as published 
 
11
by the Free Software Foundation.
 
12
 
 
13
This program is distributed in the hope that it will be useful, but 
 
14
WITHOUT ANY WARRANTY; without even the implied warranties of 
 
15
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
 
16
PURPOSE.  See the GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License along 
 
19
with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include <gio/gio.h>
 
23
#include <glib/gi18n.h>
 
24
 
 
25
#include "load-app-info.h"
 
26
#include "shared-values.h"
 
27
 
 
28
static void new_element (GMarkupParseContext *context, const gchar * name, const gchar ** attribute_names, const gchar ** attribute_values, gpointer user_data, GError **error);
 
29
static void end_element (GMarkupParseContext  *context, const gchar * name, gpointer user_data, GError ** error);
 
30
 
 
31
static GMarkupParser app_info_parser = {
 
32
        start_element:  new_element,
 
33
        end_element:    end_element,
 
34
        text:           NULL,
 
35
        passthrough:    NULL,
 
36
        error:          NULL
 
37
};
 
38
 
 
39
typedef struct _menu_data_t menu_data_t;
 
40
struct _menu_data_t {
 
41
        sqlite3 * db;
 
42
        gchar * desktopfile;
 
43
        gchar * domain;
 
44
        gboolean seen_header;
 
45
        gboolean seen_menus;
 
46
        GQueue queue;
 
47
        GString * statement;
 
48
};
 
49
 
 
50
typedef enum _menu_errors_t menu_errors_t;
 
51
enum _menu_errors_t {
 
52
        DUPLICATE_HEADERS,
 
53
        DUPLICATE_DESKTOPFILE,
 
54
        DUPLICATE_MENUS,
 
55
        MISSING_HEADER,
 
56
        MISSING_DESKTOP,
 
57
        MISSING_MENUS,
 
58
        ERROR_LAST
 
59
};
 
60
 
 
61
gboolean
 
62
load_app_info (const gchar * filename, sqlite3 * db)
 
63
{
 
64
        /* verify */
 
65
        g_return_val_if_fail(filename != NULL, FALSE);
 
66
        g_return_val_if_fail(db != NULL, FALSE);
 
67
 
 
68
        if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
 
69
                return FALSE;
 
70
        }
 
71
 
 
72
        /* get data */
 
73
        GFile * file = g_file_new_for_path(filename);
 
74
        gchar * data = NULL;
 
75
        gsize len = 0;
 
76
        GError * error = NULL;
 
77
 
 
78
        gboolean load = g_file_load_contents(file,
 
79
                                             NULL, /* cancelable */
 
80
                                             &data,
 
81
                                             &len,
 
82
                                             NULL, /* end tag */
 
83
                                             &error);
 
84
 
 
85
        if (error != NULL) {
 
86
                g_warning("Unable to load file '%s': %s", filename, error->message);
 
87
                g_error_free(error);
 
88
                g_object_unref(file);
 
89
                return FALSE;
 
90
        }
 
91
 
 
92
        if (!load) {
 
93
                g_warning("Unable to load file '%s'", filename);
 
94
                g_object_unref(file);
 
95
                return FALSE;
 
96
        }
 
97
 
 
98
        /* parse it */
 
99
        menu_data_t menu_data = {
 
100
                db: db,
 
101
                seen_header: FALSE,
 
102
                seen_menus: FALSE,
 
103
                desktopfile: NULL,
 
104
                domain: NULL,
 
105
                queue: G_QUEUE_INIT,
 
106
                statement: NULL
 
107
        };
 
108
 
 
109
        menu_data.statement = g_string_new("");
 
110
 
 
111
        GMarkupParseContext * context = g_markup_parse_context_new(&app_info_parser,
 
112
                                                                   0, /* flags */
 
113
                                                                   &menu_data,
 
114
                                                                   NULL /* destroy func */);
 
115
 
 
116
        gboolean parsed = g_markup_parse_context_parse(context, data, len, &error);
 
117
 
 
118
        if (error != NULL) {
 
119
                g_warning("Unable to parse file '%s': %s", filename, error->message);
 
120
                g_error_free(error);
 
121
                error = NULL;
 
122
        }
 
123
 
 
124
        if (!parsed) {
 
125
                g_warning("Unable to parse file '%s'", filename);
 
126
        }
 
127
 
 
128
        if (!menu_data.seen_header) {
 
129
                g_warning("Never found a header in '%s'", filename);
 
130
                parsed = FALSE;
 
131
        }
 
132
 
 
133
        if (!menu_data.seen_menus) {
 
134
                g_warning("Never found menus in '%s'", filename);
 
135
                parsed = FALSE;
 
136
        }
 
137
 
 
138
        g_markup_parse_context_free(context);
 
139
 
 
140
        /* Execute SQL Statement */
 
141
        /* If we have one */
 
142
        if (parsed && menu_data.statement->str[0] != '\0') {
 
143
                int exec_status = SQLITE_OK;
 
144
                gchar * failstring = NULL;
 
145
                exec_status = sqlite3_exec(db,
 
146
                                           menu_data.statement->str,
 
147
                                           NULL, NULL, &failstring);
 
148
                if (exec_status != SQLITE_OK) {
 
149
                        g_warning("Unable to execute SQL statement to load DB: %s", failstring);
 
150
                }
 
151
        }
 
152
 
 
153
        g_free(menu_data.desktopfile);
 
154
        g_free(menu_data.domain);
 
155
        g_string_free(menu_data.statement, TRUE);
 
156
 
 
157
        /* Free data */
 
158
        g_free(data);
 
159
        g_object_unref(file);
 
160
 
 
161
        return parsed;
 
162
}
 
163
 
 
164
static GQuark
 
165
error_domain (void)
 
166
{
 
167
        static GQuark domain = 0;
 
168
        if (domain == 0) {
 
169
                domain = g_quark_from_static_string("hud-app-info-parser");
 
170
        }
 
171
        return domain;
 
172
}
 
173
 
 
174
#define COLLECT(first, ...) \
 
175
  g_markup_collect_attributes (name,                                         \
 
176
                               attribute_names, attribute_values, error,     \
 
177
                               first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
 
178
#define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
 
179
#define STRDUP     G_MARKUP_COLLECT_STRDUP
 
180
#define STRING     G_MARKUP_COLLECT_STRING
 
181
#define NO_ATTRS() COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
 
182
 
 
183
static void
 
184
new_element (GMarkupParseContext *context, const gchar * name, const gchar ** attribute_names, const gchar ** attribute_values, gpointer user_data, GError **error)
 
185
{
 
186
        menu_data_t * menu_data = (menu_data_t *)user_data;
 
187
 
 
188
        if (g_strcmp0(name, "hudappinfo") == 0) {
 
189
                if (menu_data->seen_header) {
 
190
                        g_set_error(error, error_domain(), DUPLICATE_HEADERS, "Recieved second header");
 
191
                }
 
192
 
 
193
                menu_data->seen_header = TRUE;
 
194
                return;
 
195
        }
 
196
 
 
197
        if (!menu_data->seen_header) {
 
198
                g_set_error(error, error_domain(), MISSING_HEADER, "Missing the header when we got to element '%s'", name);
 
199
                return;
 
200
        }
 
201
 
 
202
        if (g_strcmp0(name, "desktopfile") == 0) {
 
203
                const gchar * desktopfile;
 
204
 
 
205
                if (!COLLECT(STRING, "path", &desktopfile)) {
 
206
                        return;
 
207
                }
 
208
 
 
209
                if (menu_data->desktopfile != NULL) {
 
210
                        g_set_error(error, error_domain(), DUPLICATE_DESKTOPFILE, "Two desktop file definitions.  First as '%s' then as '%s'.", menu_data->desktopfile, desktopfile);
 
211
                        return;
 
212
                }
 
213
 
 
214
                menu_data->desktopfile = g_strdup(desktopfile);
 
215
                return;
 
216
        }
 
217
 
 
218
        if (g_strcmp0(name, "menus") == 0) {
 
219
                if (menu_data->desktopfile == NULL) {
 
220
                        g_set_error(error, error_domain(), MISSING_DESKTOP, "No desktop file is defined");
 
221
                }
 
222
 
 
223
                if (menu_data->seen_menus) {
 
224
                        g_set_error(error, error_domain(), DUPLICATE_MENUS, "Second set of menus found");
 
225
                }
 
226
 
 
227
                menu_data->seen_menus = TRUE;
 
228
 
 
229
                return;
 
230
        }
 
231
 
 
232
        if (g_strcmp0(name, "menu") == 0) {
 
233
                if (!menu_data->seen_menus) {
 
234
                        g_set_error(error, error_domain(), MISSING_MENUS, "Menu tag found without enclosing menus");
 
235
                        return;
 
236
                }
 
237
 
 
238
                const gchar * mname;
 
239
 
 
240
                if (!COLLECT(STRING, "name", &mname)) {
 
241
                        return;
 
242
                }
 
243
 
 
244
                const gchar * translated = NULL;
 
245
                if (menu_data->domain != NULL) {
 
246
                        translated = g_dgettext(menu_data->domain, mname);
 
247
                } else {
 
248
                        translated = _(mname);
 
249
                }
 
250
 
 
251
                if (g_queue_is_empty(&menu_data->queue)) {
 
252
                        g_queue_push_head(&menu_data->queue, g_strdup(translated));
 
253
                } else {
 
254
                        g_queue_push_head(&menu_data->queue, g_strconcat((gchar *)g_queue_peek_head(&menu_data->queue), DB_SEPARATOR, translated, NULL));
 
255
                }
 
256
 
 
257
                return;
 
258
        }
 
259
 
 
260
        if (g_strcmp0(name, "item") == 0) {
 
261
                if (!menu_data->seen_menus) {
 
262
                        g_set_error(error, error_domain(), MISSING_MENUS, "Item tag found without enclosing menus");
 
263
                        return;
 
264
                }
 
265
 
 
266
                const gchar * iname;
 
267
                const gchar * scount;
 
268
 
 
269
                if (!COLLECT(STRING, "name", &iname,
 
270
                             STRING, "count", &scount)) {
 
271
                        return;
 
272
                }
 
273
 
 
274
                const gchar * translated = NULL;
 
275
                if (menu_data->domain != NULL) {
 
276
                        translated = g_dgettext(menu_data->domain, iname);
 
277
                } else {
 
278
                        translated = _(iname);
 
279
                }
 
280
 
 
281
                gchar * finalitem = g_strconcat((gchar *)g_queue_peek_head(&menu_data->queue), DB_SEPARATOR, translated, NULL);
 
282
                gint64 count = g_ascii_strtoll(scount, NULL, 10);
 
283
 
 
284
                int i;
 
285
                for (i = 0; i < count; i++) {
 
286
                        if (i == 0) {
 
287
                                g_string_append_printf(menu_data->statement, "insert into usage (application, entry, timestamp) values ('%s', '%s', date('now', 'utc'));", menu_data->desktopfile, finalitem);
 
288
                        } else {
 
289
                                g_string_append_printf(menu_data->statement, "insert into usage (application, entry, timestamp) values ('%s', '%s', date('now', 'utc', '-%d days'));", menu_data->desktopfile, finalitem, i);
 
290
                        }
 
291
                }
 
292
 
 
293
                g_free(finalitem);
 
294
                return;
 
295
        }
 
296
 
 
297
        return;
 
298
}
 
299
 
 
300
static void
 
301
end_element (GMarkupParseContext  *context, const gchar * name, gpointer user_data, GError ** error)
 
302
{
 
303
        menu_data_t * menu_data = (menu_data_t *)user_data;
 
304
 
 
305
        if (g_strcmp0(name, "menu") == 0) {
 
306
                if (g_queue_is_empty(&menu_data->queue)) {
 
307
                        g_warning("Menu stack is empty!");
 
308
                } else {
 
309
                        g_free(g_queue_pop_head(&menu_data->queue));
 
310
                }
 
311
 
 
312
                return;
 
313
        }
 
314
 
 
315
        return;
 
316
}
 
317