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