]> git.armaanb.net Git - mmenu.git/blob - mmenu.c
e5860bd47b1bb0668b61c8fe041c7b5c7a8d78f8
[mmenu.git] / mmenu.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdbool.h>
4
5 #include <Xm/Xm.h>
6 #include <Xm/Text.h>
7 #include <Xm/RowColumn.h>
8 #include <Xm/List.h>
9
10 #include <curl/curl.h>
11
12 struct memstruct {
13         char *memory;
14         size_t size;
15 };
16
17 char *days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
18
19 int
20 memfail(void)
21 {
22         printf("ERROR: Out of memory\n");
23         exit(4);
24 }
25
26
27 static size_t
28 memback(void *contents, size_t size, size_t nmemb, void *userp)
29 {
30         size_t realsize = size * nmemb;
31         struct memstruct *mem = (struct memstruct *)userp;
32
33         char *ptr = realloc(mem->memory, mem->size + realsize + 1);
34         if (!ptr) memfail();
35
36         mem->memory = ptr;
37         memcpy(&(mem->memory[mem->size]), contents, realsize);
38         mem->size += realsize;
39         mem->memory[mem->size] = 0;
40
41         return realsize;
42 }
43
44 char *
45 getmenu(char *date)
46 {
47         CURL *curl = curl_easy_init();
48         CURLcode res;
49
50         if (!curl) {
51                 fprintf(stderr, "ERROR: Failed to initialize cURL\n");
52                 exit(1);
53         }
54
55         struct memstruct chunk;
56         chunk.memory = malloc(1); // Grown as needed by the realloc in memback()
57         chunk.size = 0; // No data yet
58
59         const char *baseurl =
60                 "https://nobilis.nobles.edu/skyworld/castlemenu.php?Date=";
61         int len = strlen(baseurl) + strlen(date) + 1;
62         char *url = (char *)malloc(len);
63         snprintf(url, len, "%s%s", baseurl, date);
64         printf("INFO: Using URL %s\n", url);
65         curl_easy_setopt(curl, CURLOPT_URL, url);
66         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memback);
67         curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
68         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
69
70         printf("INFO: Fetching wepage\n");
71         res = curl_easy_perform(curl);
72         free(url);
73
74         if (res != CURLE_OK) {
75                 fprintf(stderr,"ERROR: Could not fetch webpage\n%s\n",
76                                                 curl_easy_strerror(res));
77                 exit(2);
78         }
79
80         curl_easy_cleanup(curl);
81
82         // Parse HTML
83         printf("INFO: Parsing HTML\n");
84         bool intag = false;
85         char *outp = (char *) calloc(1, sizeof(char));
86         if (!outp) memfail();
87
88         // Extract text from between HTML tags
89         int j = 1;
90         char next[2];
91         next[1] = '\0';
92         for (int i = 345; chunk.memory[i]; i++) {
93                 char c = chunk.memory[i];
94                 if (c == '<') intag = true;
95                 if (!intag) {
96                         j++;
97                         outp = (char *) realloc(outp, j);
98                         if (!outp) memfail();
99                         next[0] = c;
100                         strncat(outp, next, 2);
101                 }
102                 if (c == '>') intag = false;
103         }
104
105         // Strip empty newlines
106         char *nl = (char *) calloc(1, sizeof(char));
107         if (!nl) memfail();
108
109         j = 1;
110         for (int i = 0; outp[i]; i++) {
111                 j++;
112                 if (outp[i] == '\n' && outp[i+1] == '\n') i+=3;
113                 nl = (char *) realloc(nl, j);
114                 if (!nl) memfail();
115                 next[0] = outp[i+1];
116                 strncat(nl, next, 2);
117         }
118         free(outp);
119         return nl;
120 }
121
122 void
123 dayback(Widget w, XtPointer client_data, XtPointer call_data)
124 {
125         Widget text_w = (Widget) client_data;
126         XmListCallbackStruct *list_cbs = (XmListCallbackStruct *) call_data;
127         char *nl = getmenu(days[list_cbs->item_position - 1]);
128         XmTextReplace((Widget) text_w, 0, strlen(XmTextGetString(text_w)), nl);
129 }
130
131 /* Convert an array of string to an array of compound strings */
132 XmStringTable ArgvToXmStringTable (int argc, char **argv)
133 {
134         XmStringTable new =
135                 (XmStringTable) XtMalloc ((argc+1) * sizeof (XmString));
136         if (!new)
137                 return (XmStringTable) 0;
138         new[argc] = (XmString) 0;
139         while (--argc >= 0)
140                 new[argc] = XmStringGenerate ((XtPointer) argv[argc], "tag2",
141                                                                                                                                         XmCHARSET_TEXT, NULL);
142         return new;
143 }
144
145 int
146 main(int argc, char *argv[])
147 {
148         // Initialize motif
149         Widget         toplevel, rowcol, w, text_w;
150         XmStringTable  strs;
151         XtAppContext   app;
152         Arg            args[5];
153
154         XtSetLanguageProc(NULL, NULL, NULL);
155         toplevel = XtVaOpenApplication(&app, "Castle Menu", NULL, 0, &argc, argv,
156                                                                                                                                  NULL, sessionShellWidgetClass, NULL);
157
158         int n = 0;
159         XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++;
160         rowcol = XmCreateRowColumn(toplevel, "rowcol", args, n);
161
162         // Create text widget to display menu
163         n = 0;
164         char *nl = getmenu("");
165         XtSetArg(args[n], XmNvalue, nl); n++;
166         XtSetArg(args[n], XmNeditable, False); n++;
167         XtSetArg(args[n], XmNcolumns, 80); n++;
168         XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
169         text_w = XmCreateScrolledText(rowcol, "text", args, n);
170         XtManageChild(text_w);
171
172         // Create day selector
173         strs = ArgvToXmStringTable (XtNumber (days), days);
174         w = XmCreateScrolledList(rowcol, "list", NULL, 0);
175         XtVaSetValues(w,
176                                                                 XmNitems, strs,
177                                                                 XmNitemCount, XtNumber (days),
178                                                                 NULL);
179         XtAddCallback(w, XmNbrowseSelectionCallback, dayback, (XtPointer) text_w);
180         XtManageChild(w);
181         XtManageChild(rowcol);
182
183         // Display everything
184         XtRealizeWidget(toplevel);
185         XtAppMainLoop(app);
186 }