1#include <stdbool.h>
2#include <alloc.h>
3#include <byte.h>
4#include <buffer.h>
5#include <str.h>
6#include <strerr.h>
7#include <glob.h>
8#include <sys/stat.h>
9
10#include "parse.h"
11#include "mdoc.h"
12#include "protocol.h"
13#include "util.h"
14
15const char *argv0;
16
17static void usage(buffer *);
18static const char *progname(const char *, const char *);
19static bool fileexists(const char *);
20static bool haspathsep(const char *);
21static char *joinpath(const char *, const char *);
22static char *searchsystem(const char *);
23static char *resolvepath(const char *);
24static void print_protocol_summary(const struct protocol *);
25static void print_interface_summary(const struct protocol *, const struct interface *);
26static void print_message(const struct message *);
27static void print_args(const struct message *);
28static void print_arg(const struct argument *);
29static void print_description(const struct description *, const char *);
30static void put_line(buffer *, const char *);
31
32static void
33put_line(buffer *b, const char *s)
34{
35 buffer_puts(b, s);
36 buffer_puts(b, "\n");
37}
38
39static void
40usage(buffer *b)
41{
42 buffer_puts(b, "usage: ");
43 buffer_puts(b, argv0);
44 buffer_puts(b, " [-h] [-m] FILE [INTERFACE]\n");
45 buffer_flush(b);
46}
47
48static const char *
49progname(const char *arg, const char *def)
50{
51 const char *slash;
52
53 if (!arg)
54 return def;
55 slash = arg + str_rchr((char *)arg, '/');
56 return *slash ? slash + 1 : arg;
57}
58
59static bool
60fileexists(const char *path)
61{
62 struct stat st;
63
64 return stat(path, &st) == 0 && S_ISREG(st.st_mode);
65}
66
67static bool
68haspathsep(const char *path)
69{
70 return path[str_chr(path, '/')];
71}
72
73static char *
74joinpath(const char *dir, const char *name)
75{
76 size_t ndir, nname;
77 char *path;
78
79 ndir = str_len(dir);
80 nname = str_len(name);
81 path = alloc((unsigned int)(ndir + 1 + nname + 1));
82 if (!path)
83 strerr_die2sys(111, argv0, ": alloc");
84 byte_copy(path, ndir, (char *)dir);
85 path[ndir] = '/';
86 byte_copy(path + ndir + 1, nname + 1, (char *)name);
87 return path;
88}
89
90static char *
91searchsystem(const char *name)
92{
93 static const char *prefixes[] = {
94 "/usr/share/wayland/",
95 "/usr/share/wayland/",
96 "/usr/share/wayland-protocols/*/*/",
97 "/usr/share/wayland-protocols/*/*/",
98 "/usr/share/wayland-protocols/*/*/",
99 "/usr/share/wayland-protocols/*/*/",
100 };
101 static const char *suffixes[] = {
102 "",
103 ".xml",
104 "",
105 ".xml",
106 "",
107 ".xml",
108 };
109 glob_t g;
110 char *pattern;
111 char *found;
112 size_t i, nprefix, nname, nsuffix;
113
114 for (i = 0; i < countof(prefixes); ++i) {
115 nprefix = str_len(prefixes[i]);
116 nname = str_len(name);
117 nsuffix = str_len(suffixes[i]);
118 pattern = alloc((unsigned int)(nprefix + nname + nsuffix + 1));
119 if (!pattern)
120 die_nomem();
121 byte_copy(pattern, nprefix, (char *)prefixes[i]);
122 byte_copy(pattern + nprefix, nname, (char *)name);
123 byte_copy(pattern + nprefix + nname, nsuffix + 1, (char *)suffixes[i]);
124 byte_zero(&g, sizeof(g));
125 if (glob(pattern, 0, NULL, &g) == 0 && g.gl_pathc > 0 && fileexists(g.gl_pathv[0])) {
126 found = copystr(g.gl_pathv[0]);
127 globfree(&g);
128 alloc_free(pattern);
129 return found;
130 }
131 globfree(&g);
132 alloc_free(pattern);
133 }
134 return NULL;
135}
136
137static char *
138resolvepath(const char *arg)
139{
140 char *path, *found;
141
142 if (fileexists(arg))
143 return copystr(arg);
144 if (!haspathsep(arg)) {
145 path = joinpath(".", arg);
146 if (fileexists(path))
147 return path;
148 alloc_free(path);
149 found = searchsystem(arg);
150 if (found)
151 return found;
152 }
153 return copystr(arg);
154}
155
156static void
157print_description(const struct description *desc, const char *prefix)
158{
159 const char *line, *next;
160
161 if (desc->summary && desc->summary[0])
162 buffer_puts(buffer_1, prefix), put_line(buffer_1, desc->summary);
163 if (!desc->text || !desc->text[0])
164 return;
165 line = desc->text;
166 while (*line) {
167 next = line + str_chr(line, '\n');
168 if (*next == '\0')
169 next = line + str_len(line);
170 buffer_puts(buffer_1, prefix);
171 buffer_put(buffer_1, line, (unsigned int)(next - line));
172 buffer_puts(buffer_1, "\n");
173 line = *next ? next + 1 : next;
174 }
175}
176
177static void
178print_protocol_summary(const struct protocol *proto)
179{
180 size_t i;
181
182 put_line(buffer_1, proto->name);
183 buffer_puts(buffer_1, "\n");
184 print_description(&proto->desc, "");
185 if ((proto->desc.summary && proto->desc.summary[0]) || (proto->desc.text && proto->desc.text[0]))
186 buffer_puts(buffer_1, "\n");
187 buffer_puts(buffer_1, "interfaces: "); put_ulong(buffer_1, proto->nifaces); buffer_puts(buffer_1, "\n");
188 buffer_puts(buffer_1, "requests: "); put_ulong(buffer_1, proto->total_requests); buffer_puts(buffer_1, "\n");
189 buffer_puts(buffer_1, "events: "); put_ulong(buffer_1, proto->total_events); buffer_puts(buffer_1, "\n");
190 buffer_puts(buffer_1, "enums: "); put_ulong(buffer_1, proto->total_enums); buffer_puts(buffer_1, "\n\n");
191
192 for (i = 0; i < proto->nifaces; ++i) {
193 const struct interface *iface = &proto->ifaces[i];
194
195 buffer_puts(buffer_1, iface->name);
196 buffer_puts(buffer_1, " v");
197 put_ulong(buffer_1, (unsigned long)iface->version);
198 buffer_puts(buffer_1, " requests=");
199 put_ulong(buffer_1, iface->nrequests);
200 buffer_puts(buffer_1, " events=");
201 put_ulong(buffer_1, iface->nevents);
202 buffer_puts(buffer_1, " creates=");
203 put_ulong(buffer_1, iface->ncreates);
204 if (!iface->created_by_local)
205 buffer_puts(buffer_1, " root");
206 buffer_puts(buffer_1, "\n");
207 }
208}
209
210static void
211print_arg(const struct argument *arg)
212{
213 if (arg->nullable)
214 buffer_puts(buffer_1, "?");
215 if (arg->iface && arg->iface[0] && (str_equal(arg->type, "object") || str_equal(arg->type, "new_id")))
216 buffer_puts(buffer_1, arg->iface);
217 else
218 buffer_puts(buffer_1, arg->type);
219}
220
221static void
222print_args(const struct message *msg)
223{
224 size_t i;
225 bool first;
226
227 first = true;
228 for (i = 0; i < msg->nargs; ++i) {
229 const struct argument *arg = &msg->args[i];
230
231 if (arg->new_id && arg->iface && arg->iface[0])
232 continue;
233 if (!first)
234 buffer_puts(buffer_1, ", ");
235 print_arg(arg);
236 first = false;
237 }
238}
239
240static void
241print_message(const struct message *msg)
242{
243 const struct argument *creator;
244
245 buffer_puts(buffer_1, " ");
246 put_ulong(buffer_1, (unsigned long)msg->opcode);
247 buffer_puts(buffer_1, " ");
248 buffer_puts(buffer_1, msg->name);
249 buffer_puts(buffer_1, "(");
250 print_args(msg);
251 buffer_puts(buffer_1, ")");
252 creator = message_creator_arg(msg);
253 if (creator)
254 buffer_puts(buffer_1, " => "), buffer_puts(buffer_1, creator->iface);
255 if (msg->destructor)
256 buffer_puts(buffer_1, " [destructor]");
257 buffer_puts(buffer_1, "\n");
258 print_description(&msg->desc, " ");
259}
260
261static void
262print_interface_summary(const struct protocol *proto, const struct interface *iface)
263{
264 size_t i;
265
266 (void)proto;
267 buffer_puts(buffer_1, "interface ");
268 buffer_puts(buffer_1, iface->name);
269 buffer_puts(buffer_1, "@");
270 put_ulong(buffer_1, (unsigned long)iface->version);
271 buffer_puts(buffer_1, "\n\n");
272 print_description(&iface->desc, "");
273 if ((iface->desc.summary && iface->desc.summary[0]) || (iface->desc.text && iface->desc.text[0]))
274 buffer_puts(buffer_1, "\n");
275
276 put_line(buffer_1, "requests");
277 for (i = 0; i < iface->nrequests; ++i)
278 print_message(&iface->requests[i]);
279
280 buffer_puts(buffer_1, "\n");
281 put_line(buffer_1, "events");
282 for (i = 0; i < iface->nevents; ++i)
283 print_message(&iface->events[i]);
284
285 buffer_puts(buffer_1, "\n");
286 put_line(buffer_1, "enums");
287 for (i = 0; i < iface->nenums; ++i)
288 {
289 buffer_puts(buffer_1, " ");
290 put_line(buffer_1, iface->enums[i].name);
291 print_description(&iface->enums[i].desc, " ");
292 }
293}
294
295int
296main(int argc, char *argv[])
297{
298 struct protocol proto;
299 struct interface *iface;
300 const char *ifname;
301 char *path;
302 bool mflag;
303 int i;
304
305 byte_zero(&proto, sizeof(proto));
306 argv0 = progname(argv[0], "proto");
307 path = NULL;
308 ifname = NULL;
309 mflag = false;
310
311 for (i = 1; i < argc; ++i) {
312 if (str_equal(argv[i], "-h")) {
313 usage(buffer_1);
314 return 0;
315 }
316 if (str_equal(argv[i], "-m")) {
317 mflag = true;
318 continue;
319 }
320 if (argv[i][0] == '-' && argv[i][1] != '\0') {
321 usage(buffer_2);
322 return 2;
323 }
324 if (!path) {
325 path = argv[i];
326 continue;
327 }
328 if (!ifname) {
329 ifname = argv[i];
330 continue;
331 }
332 usage(buffer_2);
333 return 2;
334 }
335
336 if (!path) {
337 usage(buffer_2);
338 return 2;
339 }
340
341 path = resolvepath(path);
342 parsefile(path, &proto);
343
344 if (mflag && ifname)
345 strerr_die2x(111, argv0, ": -m cannot be used with an interface name");
346
347 iface = NULL;
348 if (ifname) {
349 iface = proto_find_interface(&proto, ifname);
350 if (!iface)
351 strerr_die4x(111, argv0, ": unknown interface: ", ifname, "");
352 }
353
354 if (mflag)
355 print_protocol_mdoc(&proto);
356 else if (iface)
357 print_interface_summary(&proto, iface);
358 else
359 print_protocol_summary(&proto);
360
361 buffer_flush(buffer_1);
362 alloc_free(path);
363 proto_free(&proto);
364 return 0;
365}