* Change xml parsing to pass the parent node, rather than the first
[mikachu/openbox.git] / plugins / menu / fifo_menu.c
1 #include <glib.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <stdio.h>
9
10 #include "kernel/menu.h"
11 #include "kernel/event.h"
12
13 static char *PLUGIN_NAME = "fifo_menu";
14
15 typedef struct Fifo_Menu_Data {
16     char *fifo;
17     char *buf; /* buffer to hold partially read menu */
18     unsigned long buflen; /* how many bytes are in the buffer */
19     int fd; /* file descriptor to read menu from */
20     gboolean use_pid;
21     event_fd_handler *handler;
22 } Fifo_Menu_Data;
23
24 #define FIFO_MENU(m) ((ObMenu *)m)
25 #define FIFO_MENU_DATA(m) ((Fifo_Menu_Data *)((ObMenu *)m)->plugin_data)
26
27
28 void fifo_menu_clean_up(ObMenu *m) {
29     if (FIFO_MENU_DATA(m)->buf != NULL) {
30         g_free(FIFO_MENU_DATA(m)->buf);
31         FIFO_MENU_DATA(m)->buf = NULL;
32         FIFO_MENU_DATA(m)->buflen = 0;
33     }
34
35     if (FIFO_MENU_DATA(m)->fd != -1) {
36         close(FIFO_MENU_DATA(m)->fd);
37         FIFO_MENU_DATA(m)->fd = -1;
38     }
39 }
40
41 void plugin_setup_config() { }
42 void plugin_startup()
43 { }
44 void plugin_shutdown() { }
45
46 void fifo_menu_handler(int fd, void *d) {
47     ObMenu *menu = d;
48     char *tmpbuf = NULL;
49     unsigned long num_read;
50 #ifdef DEBUG
51     /* because gdb is dumb */
52 #if 0
53     Fifo_Menu_Data *d = FIFO_MENU_DATA(menu);
54 #endif
55 #endif
56     
57     /* if the menu is shown this will go into busy loop :(
58      fix me*/
59     if (!menu->shown) { 
60         unsigned long num_realloc;
61         /* if we have less than a quarter BUFSIZ left, allocate more */
62         num_realloc = (BUFSIZ - (FIFO_MENU_DATA(menu)->buflen % BUFSIZ) <
63                        BUFSIZ >> 2) ?
64             0 : BUFSIZ;
65     
66         tmpbuf = g_try_realloc(FIFO_MENU_DATA(menu)->buf,             
67                                FIFO_MENU_DATA(menu)->buflen + num_realloc);
68
69         if (tmpbuf == NULL) {
70             g_warning("Unable to allocate memory for read()");
71             return;
72         }
73     
74         FIFO_MENU_DATA(menu)->buf = tmpbuf;
75     
76         num_read = read(fd,
77                         FIFO_MENU_DATA(menu)->buf +
78                         FIFO_MENU_DATA(menu)->buflen,
79                         num_realloc);
80
81         if (num_read == 0) { /* eof */
82             xmlDocPtr doc;
83             xmlNodePtr node;
84
85             menu->invalid = TRUE;
86             menu_clear(menu);
87
88             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
89
90             doc = xmlParseMemory(FIFO_MENU_DATA(menu)->buf,
91                                  FIFO_MENU_DATA(menu)->buflen);
92
93             node = xmlDocGetRootElement(doc);
94             
95             if (node &&
96                 !xmlStrcasecmp(node->name, (const xmlChar*) "fifo_menu")) {
97                 if ((node = parse_find_node("item", node->xmlChildrenNode)))
98                     parse_menu_full(doc, node, menu, FALSE);
99             }
100             
101             fifo_menu_clean_up(menu);
102             
103             event_remove_fd(FIFO_MENU_DATA(menu)->handler->fd);
104         
105             if ((FIFO_MENU_DATA(menu)->fd =
106                  open(FIFO_MENU_DATA(menu)->fifo,
107                       O_NONBLOCK | O_RDONLY)) == -1) {
108                 g_warning("Can't reopen FIFO");
109                 fifo_menu_clean_up(menu);
110                 return;
111             }
112
113             FIFO_MENU_DATA(menu)->handler->fd = FIFO_MENU_DATA(menu)->fd;
114         
115             event_add_fd_handler(FIFO_MENU_DATA(menu)->handler);
116         } else if (num_read > 0) {
117             FIFO_MENU_DATA(menu)->buflen += num_read;
118             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
119         }
120     }
121 }
122
123 void plugin_destroy (ObMenu *m)
124 {
125     fifo_menu_clean_up(m);
126     if (FIFO_MENU_DATA(m)->handler != NULL) {
127         g_free(FIFO_MENU_DATA(m)->handler);
128         FIFO_MENU_DATA(m)->handler = NULL;
129     }
130
131     if (FIFO_MENU_DATA(m)->fifo != NULL) {
132         g_free(FIFO_MENU_DATA(m)->fifo);
133         FIFO_MENU_DATA(m)->fifo = NULL;
134     }
135
136     if (FIFO_MENU_DATA(m)->buf != NULL) {
137         g_free(FIFO_MENU_DATA(m)->buf);
138         FIFO_MENU_DATA(m)->buf = NULL;
139     }
140  
141     g_free(m->plugin_data);
142
143     menu_free(m->name);
144 }
145
146 void *plugin_create(PluginMenuCreateData *data)
147 {
148     char *fifo;
149     char *dir;
150     event_fd_handler *h;
151     Fifo_Menu_Data *d;
152     ObMenu *m;
153     char *label = NULL, *id = NULL;
154     char *attr_pid = NULL;
155         
156     d = g_new(Fifo_Menu_Data, 1);
157
158     parse_attr_string("id", data->node, &id);
159     parse_attr_string("label", data->node, &label);
160     
161     if (parse_attr_string("pid", data->node, &attr_pid) &&
162         g_strcasecmp(attr_pid, "true") == 0) {
163         d->use_pid = TRUE;
164     } else
165         d->use_pid = FALSE;
166     
167     m = menu_new( (label != NULL ? label : ""),
168                   (id != NULL ? id : PLUGIN_NAME),
169                   data->parent);
170
171     if (data->parent)
172         menu_add_entry(data->parent, menu_entry_new_submenu(
173                            (label != NULL ? label : ""),
174                            m));
175
176     g_free(label);
177     g_free(id);
178     d->fd = -1;
179     d->buf = NULL;
180     d->buflen = 0;
181     d->handler = NULL;
182     
183     m->plugin = PLUGIN_NAME;
184
185     d->fd = -1;
186     
187     m->plugin_data = (void *)d;
188
189         
190     dir = g_build_filename(g_get_home_dir(), ".openbox",
191                           PLUGIN_NAME, NULL);
192
193     if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST) {
194 /* technically, if ~/.openbox/fifo_menu exists and isn't a directory
195    this will fail, but we don't care because mkfifo will fail and warn
196    anyway */
197         g_warning("Can't create %s: %s", dir, strerror(errno));
198         g_free(dir);
199         plugin_destroy(m);
200         return NULL;
201     }
202
203     if (d->use_pid)
204     {
205         char *pid = g_strdup_printf("%s.%d", m->name, getpid());
206         fifo = g_build_filename(g_get_home_dir(), ".openbox",
207                                 PLUGIN_NAME,
208                                 pid, NULL);
209         g_free(pid);
210     } else {
211         fifo = g_build_filename(g_get_home_dir(), ".openbox",
212                                 PLUGIN_NAME,
213                                 m->name, NULL);
214     }
215     
216     if (mkfifo(fifo, S_IRUSR | S_IWUSR |
217                S_IRGRP | S_IWGRP | /* let umask do its thing */
218                S_IROTH | S_IWOTH) == -1 && errno != EEXIST) {
219         g_warning("Can't create FIFO %s: %s", fifo, strerror(errno));
220         g_free(fifo);
221         g_free(d);
222         menu_free(m->name);
223         return NULL;
224     }
225
226 /* open in non-blocking mode so we don't wait for a process to open FIFO
227    for writing */
228     if ((d->fd = open(fifo, O_NONBLOCK | O_RDONLY)) == -1) { 
229         g_warning("Can't open FIFO %s: %s", fifo, strerror(errno));
230         g_free(fifo);
231         g_free(d);
232         menu_free(m->name);
233         return NULL;
234     }
235
236     d->fifo = fifo;
237     
238     h = g_new(event_fd_handler, 1);
239
240     if (h == NULL) {
241         g_warning("Out of memory");
242         close(d->fd);
243         g_free(fifo);
244         g_free(d);
245         menu_free(m->name);
246         return NULL;
247     }
248     
249     h->fd = d->fd;
250     h->data = m;
251     h->handler = fifo_menu_handler;
252     d->handler = h;
253     
254     event_add_fd_handler(h);
255     
256     g_free(dir);
257     return (void *)m;
258 }