2 * Copyright 2012 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.tizenopensource.org/license
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <sys/types.h>
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
34 #define DbgPrint(format, arg...) LOGD("[
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg)
35 #define ErrPrint(format, arg...) LOGE("[
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg)
44 * +-------+------+------+------+
45 * | appid | icon | name | desc |
46 * +-------+------+------+------|
48 * +-------+------+------+------+
49 * CREATE TABLE homescreen ( appid TEXT PRIMARY KEY NOT NULL, icon TEXT, name TEXT, desc TEXT )
52 * +-------+----+------------+
53 * | appid | ID | image path |
54 * +-------+----+------------+
56 * +-------+----+------------+
57 * CREATE TABLE image ( appid TEXT NOT NULL, id INTEGER, path TEXT NOT NULL, FOREIGN KEY(appid) REFERENCES homescreen(appid) )
60 * +-------+------+------+------+
61 * | appid | lang | desc | name |
62 * +-------+------+------+------+
64 * +-------+------+------+------+
65 * CREATE TABLE desc ( appid TEXT NOT NULL, lang TEXT NOT NULL, desc NOT NULL, name NOT NULL, FOREIGN KEY(appid) REFERENCES homescreen(appid) )
69 #if !defined(LIBXML_TREE_ENABLED)
70 #error "LIBXML is not supporting the tree"
77 #define LOG_TAG "pkgmgr_homescreen"
85 .dbfile = "/opt/dbspace/.shortcut_service.db",
89 static inline int begin_transaction(void)
94 ret = sqlite3_prepare_v2(s_info.handle, "BEGIN TRANSACTION", -1, &stmt, NULL);
96 if (ret != SQLITE_OK) {
97 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
101 if (sqlite3_step(stmt) != SQLITE_DONE) {
102 ErrPrint("Failed to do update (%s)\n",
103 sqlite3_errmsg(s_info.handle));
104 sqlite3_finalize(stmt);
108 sqlite3_finalize(stmt);
112 static inline int rollback_transaction(void)
117 ret = sqlite3_prepare_v2(s_info.handle, "ROLLBACK TRANSACTION", -1, &stmt, NULL);
118 if (ret != SQLITE_OK) {
119 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
123 if (sqlite3_step(stmt) != SQLITE_DONE) {
124 ErrPrint("Failed to do update (%s)\n",
125 sqlite3_errmsg(s_info.handle));
126 sqlite3_finalize(stmt);
130 sqlite3_finalize(stmt);
134 static inline int commit_transaction(void)
139 ret = sqlite3_prepare_v2(s_info.handle, "COMMIT TRANSACTION", -1, &stmt, NULL);
140 if (ret != SQLITE_OK) {
141 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
145 if (sqlite3_step(stmt) != SQLITE_DONE) {
146 ErrPrint("Failed to do update (%s)\n",
147 sqlite3_errmsg(s_info.handle));
148 sqlite3_finalize(stmt);
152 sqlite3_finalize(stmt);
157 static inline int db_create_homescreen(void)
161 static const char *ddl = "CREATE TABLE homescreen ( appid TEXT PRIMARY KEY NOT NULL, icon TEXT, name TEXT, desc TEXT )";
163 ret = sqlite3_exec(s_info.handle, ddl, NULL, NULL, &err);
164 if (ret != SQLITE_OK) {
165 ErrPrint("Failed to execute the DDL(%s)\n", err);
169 if (sqlite3_changes(s_info.handle) == 0)
170 ErrPrint("No changes to DB\n");
175 static inline int db_insert_homescreen(const char *appid, const char *icon, const char *name, const char *desc)
178 static const char *dml = "INSERT INTO homescreen (appid, icon, name, desc) VALUES (?, ?, ?, ?)";
181 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
182 if (ret != SQLITE_OK) {
183 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
187 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
188 if (ret != SQLITE_OK) {
189 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
194 ret = sqlite3_bind_text(stmt, 2, icon, -1, NULL);
195 if (ret != SQLITE_OK) {
196 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
201 ret = sqlite3_bind_text(stmt, 3, name, -1, NULL);
202 if (ret != SQLITE_OK) {
203 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
208 ret = sqlite3_bind_text(stmt, 4, desc, -1, NULL);
209 if (ret != SQLITE_OK) {
210 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
215 ret = sqlite3_step(stmt);
216 if (ret != SQLITE_DONE) {
217 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
223 sqlite3_clear_bindings(stmt);
224 sqlite3_finalize(stmt);
228 static inline int db_remove_homescreen(const char *appid)
231 static const char *dml = "DELETE FROM homescreen WHERE appid = ?";
234 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
235 if (ret != SQLITE_OK) {
236 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
241 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
242 if (ret != SQLITE_OK) {
243 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
248 ret = sqlite3_step(stmt);
249 if (ret != SQLITE_OK) {
250 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
255 if (sqlite3_changes(s_info.handle) == 0)
256 DbgPrint("DB has no changes\n");
260 sqlite3_clear_bindings(stmt);
261 sqlite3_finalize(stmt);
265 static inline int db_update_homescreen(const char *appid, const char *icon, const char *name, const char *desc)
268 static const char *dml = "UPDATE homescreen SET icon = ?, name = ?, desc = ? WHERE appid = ?";
271 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
272 if (ret != SQLITE_OK) {
273 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
277 ret = sqlite3_bind_text(stmt, 1, icon, -1, NULL);
278 if (ret != SQLITE_OK) {
279 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
284 ret = sqlite3_bind_text(stmt, 2, name, -1, NULL);
285 if (ret != SQLITE_OK) {
286 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
291 ret = sqlite3_bind_text(stmt, 3, desc, -1, NULL);
292 if (ret != SQLITE_OK) {
293 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
298 ret = sqlite3_bind_text(stmt, 4, appid, -1, NULL);
299 if (ret != SQLITE_OK) {
300 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
305 ret = sqlite3_step(stmt);
306 if (sqlite3_changes(s_info.handle) == 0)
307 DbgPrint("DB has no changes\n");
311 sqlite3_clear_bindings(stmt);
312 sqlite3_finalize(stmt);
316 static inline int db_create_image(void)
320 static const char *ddl = "CREATE TABLE image ( appid TEXT NOT NULL, id INTEGER, path TEXT NOT NULL, FOREIGN KEY(appid) REFERENCES homescreen(appid) )";
322 ret = sqlite3_exec(s_info.handle, ddl, NULL, NULL, &err);
323 if (ret != SQLITE_OK) {
324 ErrPrint("Failed to execute the DDL(%s)\n", err);
328 if (sqlite3_changes(s_info.handle) == 0)
329 ErrPrint("No changes to DB\n");
334 static inline int db_insert_image(const char *appid, int id, const char *path)
337 static const char *dml = "INSERT INTO image (appid, id, path) VALUES (?, ?, ?)";
340 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
341 if (ret != SQLITE_OK) {
342 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
346 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
347 if (ret != SQLITE_OK) {
348 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
353 ret = sqlite3_bind_int(stmt, 2, id);
354 if (ret != SQLITE_OK) {
355 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
360 ret = sqlite3_bind_text(stmt, 3, path, -1, NULL);
361 if (ret != SQLITE_OK) {
362 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
367 ret = sqlite3_step(stmt);
368 if (ret != SQLITE_DONE) {
369 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
375 sqlite3_clear_bindings(stmt);
376 sqlite3_finalize(stmt);
380 static inline int db_remove_image(const char *appid)
383 static const char *dml = "DELETE FROM image WHERE appid = ?";
386 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
387 if (ret != SQLITE_OK) {
388 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
393 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
394 if (ret != SQLITE_OK) {
395 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
400 ret = sqlite3_step(stmt);
401 if (ret != SQLITE_DONE) {
402 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
406 if (sqlite3_changes(s_info.handle) == 0)
407 DbgPrint("DB has no changes\n");
411 sqlite3_clear_bindings(stmt);
412 sqlite3_finalize(stmt);
416 static inline int db_update_image(const char *appid, int id, const char *path)
419 static const char *dml = "UPDATE image SET path = ? WHERE appid = ? AND id = ?";
422 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
423 if (ret != SQLITE_OK) {
424 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
429 ret = sqlite3_bind_text(stmt, 1, path, -1, NULL);
430 if (ret != SQLITE_OK) {
431 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
436 ret = sqlite3_bind_text(stmt, 2, appid, -1, NULL);
437 if (ret != SQLITE_OK) {
438 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
443 ret = sqlite3_bind_int(stmt, 3, id);
444 if (ret != SQLITE_OK) {
445 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
450 ret = sqlite3_step(stmt);
451 if (ret != SQLITE_DONE) {
452 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
456 if (sqlite3_changes(s_info.handle) == 0)
457 DbgPrint("DB has no changes\n");
461 sqlite3_clear_bindings(stmt);
462 sqlite3_finalize(stmt);
466 static inline int db_create_desc(void)
470 static const char *ddl = "CREATE TABLE desc ( appid TEXT NOT NULL, lang TEXT NOT NULL, desc NOT NULL, name NOT NULL, FOREIGN KEY(appid) REFERENCES homescreen(appid) )";
472 ret = sqlite3_exec(s_info.handle, ddl, NULL, NULL, &err);
473 if (ret != SQLITE_OK) {
474 ErrPrint("Failed to execute the DDL(%s)\n", err);
478 if (sqlite3_changes(s_info.handle) == 0)
479 ErrPrint("No changes to DB\n");
484 static inline int db_insert_desc(const char *appid, const char *lang, const char *desc, const char *name)
487 static const char *dml = "INSERT INTO desc ( appid, lang, desc, name ) VALUES ( ?, ?, ?, ? )";
502 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
503 if (ret != SQLITE_OK) {
504 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
508 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
509 if (ret != SQLITE_DONE) {
510 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
515 ret = sqlite3_bind_text(stmt, 2, lang, -1, NULL);
516 if (ret != SQLITE_DONE) {
517 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
522 ret = sqlite3_bind_text(stmt, 3, desc, -1, NULL);
523 if (ret != SQLITE_DONE) {
524 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
529 ret = sqlite3_bind_text(stmt, 4, name, -1, NULL);
530 if (ret != SQLITE_DONE) {
531 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
536 ret = sqlite3_step(stmt);
537 if (ret != SQLITE_DONE) {
538 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
544 sqlite3_clear_bindings(stmt);
545 sqlite3_finalize(stmt);
549 static inline int db_remove_desc(const char *appid)
552 static const char *dml;
558 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
559 if (ret != SQLITE_OK) {
560 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
564 ret = sqlite3_bind_text(stmt, 1, appid, -1, NULL);
565 if (ret != SQLITE_OK) {
566 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
572 if (sqlite3_step(stmt) != SQLITE_DONE) {
573 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
577 if (sqlite3_changes(s_info.handle) == 0)
578 ErrPrint("DB has no changes\n");
582 sqlite3_clear_bindings(stmt);
583 sqlite3_finalize(stmt);
587 static inline int db_update_desc(const char *appid, const char *lang, const char *desc, const char *name)
590 static const char *dml = "UPDATE desc SET desc = ?, name = ? WHERE appid = ? AND lang = ?";
599 ret = sqlite3_prepare_v2(s_info.handle, dml, -1, &stmt, NULL);
600 if (ret != SQLITE_OK) {
601 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
605 ret = sqlite3_bind_text(stmt, 1, desc, -1, NULL);
606 if (ret != SQLITE_OK) {
607 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
612 ret = sqlite3_bind_text(stmt, 2, name, -1, NULL);
613 if (ret != SQLITE_OK) {
614 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
619 ret = sqlite3_bind_text(stmt, 3, appid, -1, NULL);
620 if (ret != SQLITE_OK) {
621 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
626 ret = sqlite3_bind_text(stmt, 4, lang, -1, NULL);
627 if (ret != SQLITE_OK) {
628 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
633 ret = sqlite3_step(stmt);
634 if (ret != SQLITE_DONE) {
635 ErrPrint("Error: %s\n", sqlite3_errmsg(s_info.handle));
639 if (sqlite3_changes(s_info.handle) == 0)
640 ErrPrint("DB has no changes\n");
644 sqlite3_clear_bindings(stmt);
645 sqlite3_finalize(stmt);
649 static inline void db_create_table(void)
654 ret = db_create_homescreen();
656 rollback_transaction();
660 ret = db_create_image();
662 rollback_transaction();
666 ret = db_create_desc();
668 rollback_transaction();
672 commit_transaction();
675 static inline int db_init(void)
680 ret = db_util_open(s_info.dbfile, &s_info.handle, DB_UTIL_REGISTER_HOOK_METHOD);
681 if (ret != SQLITE_OK) {
682 ErrPrint("Failed to open a DB\n");
686 if (lstat(s_info.dbfile, &stat) < 0) {
687 ErrPrint("%s\n", strerror(errno));
688 db_util_close(s_info.handle);
689 s_info.handle = NULL;
693 if (!S_ISREG(stat.st_mode)) {
694 ErrPrint("Invalid file\n");
695 db_util_close(s_info.handle);
696 s_info.handle = NULL;
706 static inline int db_fini(void)
711 db_util_close(s_info.handle);
712 s_info.handle = NULL;
733 struct dlist *image_list;
734 struct dlist *desc_list;
737 static inline int homescreen_destroy(struct homescreen *homescreen)
744 free(homescreen->appid);
745 xmlFree(homescreen->icon);
746 xmlFree(homescreen->name);
747 xmlFree(homescreen->desc);
749 dlist_foreach_safe(homescreen->image_list, l, n, image) {
750 homescreen->image_list = dlist_remove(homescreen->image_list, l);
751 xmlFree(image->path);
755 dlist_foreach_safe(homescreen->desc_list, l, n, desc) {
756 homescreen->desc_list = dlist_remove(homescreen->desc_list, l);
766 static inline int db_insert_item(struct homescreen *homescreen)
771 static inline int update_name(struct homescreen *homescreen, xmlNodePtr node)
778 name = xmlNodeGetContent(node);
782 lang = xmlNodeGetLang(node);
784 if (homescreen->name) {
785 DbgPrint("Overwrite the name: %s\n", homescreen->name);
786 xmlFree(homescreen->name);
789 homescreen->name = name;
793 dlist_foreach(homescreen->desc_list, l, desc) {
794 if (!xmlStrcmp(desc->lang, lang)) {
796 DbgPrint("Overwrite the name: %s\n", desc->name);
805 desc = calloc(1, sizeof(*desc));
807 ErrPrint("Heap: %s\n", strerror(errno));
817 homescreen->desc_list = dlist_append(homescreen->desc_list, desc);
821 static inline int update_icon(struct homescreen *homescreen, xmlNodePtr node)
825 path = xmlNodeGetContent(node);
829 if (homescreen->icon) {
830 DbgPrint("Overwrite icon: %s\n", homescreen->icon);
831 xmlFree(homescreen->icon);
834 homescreen->icon = path;
838 static inline int update_image(struct homescreen *homescreen, xmlNodePtr node)
846 if (!xmlHasProp(node, (const xmlChar *)"src")) {
847 DbgPrint("Has no source\n");
851 if (!xmlHasProp(node, (const xmlChar *)"id")) {
852 DbgPrint("Has no id\n");
856 path = xmlGetProp(node, (const xmlChar *)"src");
858 ErrPrint("Invalid path\n");
862 id_str = xmlGetProp(node, (const xmlChar *)"id");
864 ErrPrint("Invalid id\n");
869 id = atoi((char *)id_str);
872 dlist_foreach(homescreen->image_list, l, image) {
873 if (image->id == id) {
874 xmlFree(image->path);
880 image = calloc(1, sizeof(*image));
882 ErrPrint("Heap: %s\n", strerror(errno));
889 homescreen->image_list = dlist_append(homescreen->image_list, image);
893 static inline int update_desc(struct homescreen *homescreen, xmlNodePtr node)
900 desc_str = xmlNodeGetContent(node);
902 desc_str = xmlStrdup((const xmlChar *)"No description");
904 ErrPrint("Heap: %s\n", strerror(errno));
909 lang = xmlNodeGetLang(node);
911 if (homescreen->desc) {
912 DbgPrint("Overwrite desc: %s\n", homescreen->desc);
913 xmlFree(homescreen->desc);
916 homescreen->desc = desc_str;
920 dlist_foreach(homescreen->desc_list, l, desc) {
921 if (!xmlStrcmp(desc->lang, lang)) {
923 DbgPrint("Overwrite desc: %s\n", desc->desc);
927 desc->desc = desc_str;
932 desc = calloc(1, sizeof(*desc));
934 ErrPrint("Heap: %s\n", strerror(errno));
939 desc->desc = desc_str;
942 homescreen->desc_list = dlist_append(homescreen->desc_list, desc);
946 int PKGMGR_PARSER_PLUGIN_UPGRADE(xmlDocPtr docPtr, const char *appid)
950 if (!s_info.handle) {
952 ErrPrint("Failed to init DB\n");
957 node = xmlDocGetRootElement(docPtr);
959 ErrPrint("Invalid document\n");
963 for (node = node->children; node; node = node->next) {
964 if (!xmlStrcasecmp(node->name, (const xmlChar *)"homescreen"))
969 ErrPrint("Root has no homescreen\n");
976 int PKGMGR_PARSER_PLUGIN_INSTALL(xmlDocPtr docPtr, const char *pkgname)
979 struct homescreen *homescreen;
981 if (!s_info.handle) {
983 ErrPrint("Failed to init DB\n");
988 node = xmlDocGetRootElement(docPtr);
990 ErrPrint("Invalid document\n");
994 for (node = node->children; node; node = node->next) {
995 if (!xmlStrcasecmp(node->name, (const xmlChar *)"homescreen"))
1000 ErrPrint("Root has no children\n");
1004 homescreen = calloc(1, sizeof(*homescreen));
1006 ErrPrint("Heap: %s\n", strerror(errno));
1010 homescreen->appid = strdup(pkgname);
1011 if (!homescreen->appid) {
1012 ErrPrint("Heap: %s\n", strerror(errno));
1016 for (node = node->children; node; node = node->next) {
1017 if (!xmlStrcmp(node->name, (const xmlChar *)"text"))
1020 DbgPrint("Nodename: %s\n", node->name);
1021 if (!xmlStrcasecmp(node->name, (const xmlChar *)"label"))
1022 update_name(homescreen, node);
1023 else if (!xmlStrcasecmp(node->name, (const xmlChar *)"icon"))
1024 update_icon(homescreen, node);
1025 else if (!xmlStrcasecmp(node->name, (const xmlChar *)"img"))
1026 update_image(homescreen, node);
1027 else if (!xmlStrcasecmp(node->name, (const xmlChar *)"desc"))
1028 update_desc(homescreen, node);
1030 ErrPrint("Unknown node: %s\n", node->name);
1033 return db_insert_item(homescreen);
1036 int PKGMGR_PARSER_PLUGIN_UNINSTALL(xmlDocPtr docPtr, const char *pkgname)
1041 if (!s_info.handle) {
1042 if (db_init() < 0) {
1043 ErrPrint("Failed to init DB\n");
1048 node = xmlDocGetRootElement(docPtr);
1050 ErrPrint("Invalid document\n");
1054 for (node = node->children; node; node = node->next) {
1055 if (!xmlStrcasecmp(node->name, (const xmlChar *)"homescreen"))
1060 ErrPrint("Root has no children\n");
1064 begin_transaction();
1065 ret = db_remove_image(pkgname);
1067 rollback_transaction();
1071 ret = db_remove_desc(pkgname);
1073 rollback_transaction();
1077 ret = db_remove_homescreen(pkgname);
1079 rollback_transaction();
1083 commit_transaction();
1088 int main(int argc, char *argv[])
1094 ErrPRint("Invalid argument: %s XML_FILENAME\n", argv[0]);
1098 doc = xmlReadFile(argv[1], NULL, 0);
1100 ErrPrint("Failed to parse %s\n", argv[1]);
1104 root = xmlDocGetRootElement(doc);
1107 install_shortcut("", root);