1#include <stdbool.h>
2#include <glob.h>
3#include <sys/stat.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "parse.h"
9#include "protocol.h"
10#include "util.h"
11
12const char *argv0;
13
14struct nameset {
15 const char **items;
16 size_t len;
17 size_t cap;
18};
19
20static void usage(FILE *);
21static const char *progname(const char *, const char *);
22static bool fileexists(const char *);
23static bool haspathsep(const char *);
24static char *joinpath(const char *, const char *);
25static char *searchsystem(const char *);
26static char *resolvepath(const char *);
27static void print_protocol_summary(const struct protocol *);
28static void print_interface_summary(const struct protocol *, const struct interface *);
29static void print_message(const struct message *);
30static void print_args(const struct message *);
31static void print_arg(const struct argument *);
32static void print_description(const struct description *, const char *);
33
34static void
35usage(FILE *fp)
36{
37 fprintf(fp, "usage: %s [-h] FILE [INTERFACE]\n", argv0);
38}
39
40static const char *
41progname(const char *arg, const char *def)
42{
43 const char *slash;
44
45 if (!arg)
46 return def;
47 slash = strrchr(arg, '/');
48 return slash ? slash + 1 : arg;
49}
50
51static bool
52fileexists(const char *path)
53{
54 struct stat st;
55
56 return stat(path, &st) == 0 && S_ISREG(st.st_mode);
57}
58
59static bool
60haspathsep(const char *path)
61{
62 return strchr(path, '/') != NULL;
63}
64
65static char *
66joinpath(const char *dir, const char *name)
67{
68 size_t ndir, nname;
69 char *path;
70
71 ndir = strlen(dir);
72 nname = strlen(name);
73 path = xmalloc(ndir + 1 + nname + 1);
74 memcpy(path, dir, ndir);
75 path[ndir] = '/';
76 memcpy(path + ndir + 1, name, nname + 1);
77 return path;
78}
79
80static char *
81searchsystem(const char *name)
82{
83 static const char *patterns[] = {
84 "/usr/share/wayland/%s",
85 "/usr/share/wayland/%s.xml",
86 "/usr/share/wayland-protocols/*/*/%s",
87 "/usr/share/wayland-protocols/*/*/%s.xml",
88 "/use/share/wayland-protocols/*/*/%s",
89 "/use/share/wayland-protocols/*/*/%s.xml",
90 };
91 glob_t g;
92 char *pattern, *found;
93 size_t i;
94
95 for (i = 0; i < countof(patterns); ++i) {
96 xasprintf(&pattern, patterns[i], name);
97 memset(&g, 0, sizeof(g));
98 if (glob(pattern, 0, NULL, &g) == 0 && g.gl_pathc > 0 && fileexists(g.gl_pathv[0])) {
99 found = xstrdup(g.gl_pathv[0]);
100 globfree(&g);
101 free(pattern);
102 return found;
103 }
104 globfree(&g);
105 free(pattern);
106 }
107 return NULL;
108}
109
110static char *
111resolvepath(const char *arg)
112{
113 char *path, *found;
114
115 if (fileexists(arg))
116 return xstrdup(arg);
117 if (!haspathsep(arg)) {
118 path = joinpath(".", arg);
119 if (fileexists(path))
120 return path;
121 free(path);
122 found = searchsystem(arg);
123 if (found)
124 return found;
125 }
126 return xstrdup(arg);
127}
128
129static void
130print_description(const struct description *desc, const char *prefix)
131{
132 const char *line, *next;
133
134 if (desc->summary && desc->summary[0])
135 printf("%s%s\n", prefix, desc->summary);
136 if (!desc->text || !desc->text[0])
137 return;
138 line = desc->text;
139 while (*line) {
140 next = strchr(line, '\n');
141 if (!next)
142 next = line + strlen(line);
143 printf("%s%.*s\n", prefix, (int)(next - line), line);
144 line = *next ? next + 1 : next;
145 }
146}
147
148static void
149print_protocol_summary(const struct protocol *proto)
150{
151 size_t i;
152
153 printf("%s\n\n", proto->name);
154 print_description(&proto->desc, "");
155 if ((proto->desc.summary && proto->desc.summary[0]) || (proto->desc.text && proto->desc.text[0]))
156 printf("\n");
157 printf("interfaces: %zu\n", proto->nifaces);
158 printf("requests: %zu\n", proto->total_requests);
159 printf("events: %zu\n", proto->total_events);
160 printf("enums: %zu\n\n", proto->total_enums);
161
162 for (i = 0; i < proto->nifaces; ++i) {
163 const struct interface *iface = &proto->ifaces[i];
164
165 printf("%-18s v%-2d requests=%-3zu events=%-2zu creates=%-2zu",
166 iface->name, iface->version, iface->nrequests, iface->nevents, iface->ncreates);
167 if (!iface->created_by_local)
168 printf(" root");
169 putchar('\n');
170 }
171}
172
173static void
174print_arg(const struct argument *arg)
175{
176 if (arg->nullable)
177 putchar('?');
178 if (arg->iface && arg->iface[0] && (strcmp(arg->type, "object") == 0 || strcmp(arg->type, "new_id") == 0))
179 printf("%s", arg->iface);
180 else
181 printf("%s", arg->type);
182}
183
184static void
185print_args(const struct message *msg)
186{
187 size_t i;
188 bool first;
189
190 first = true;
191 for (i = 0; i < msg->nargs; ++i) {
192 const struct argument *arg = &msg->args[i];
193
194 if (arg->new_id && arg->iface && arg->iface[0])
195 continue;
196 if (!first)
197 printf(", ");
198 print_arg(arg);
199 first = false;
200 }
201}
202
203static void
204print_message(const struct message *msg)
205{
206 const struct argument *creator;
207
208 printf(" %d %s(", msg->opcode, msg->name);
209 print_args(msg);
210 printf(")");
211 creator = message_creator_arg(msg);
212 if (creator)
213 printf(" => %s", creator->iface);
214 if (msg->destructor)
215 printf(" [destructor]");
216 putchar('\n');
217 print_description(&msg->desc, " ");
218}
219
220static void
221print_interface_summary(const struct protocol *proto, const struct interface *iface)
222{
223 size_t i;
224
225 (void)proto;
226 printf("interface %s@%d\n\n", iface->name, iface->version);
227 print_description(&iface->desc, "");
228 if ((iface->desc.summary && iface->desc.summary[0]) || (iface->desc.text && iface->desc.text[0]))
229 printf("\n");
230
231 printf("requests\n");
232 for (i = 0; i < iface->nrequests; ++i)
233 print_message(&iface->requests[i]);
234
235 printf("\n");
236 printf("events\n");
237 for (i = 0; i < iface->nevents; ++i)
238 print_message(&iface->events[i]);
239
240 printf("\n");
241 printf("enums\n");
242 for (i = 0; i < iface->nenums; ++i)
243 {
244 printf(" %s\n", iface->enums[i].name);
245 print_description(&iface->enums[i].desc, " ");
246 }
247}
248
249int
250main(int argc, char *argv[])
251{
252 struct protocol proto;
253 struct interface *iface;
254 const char *ifname;
255 char *path;
256 int i;
257
258 memset(&proto, 0, sizeof(proto));
259 argv0 = progname(argv[0], "proto");
260 path = NULL;
261 ifname = NULL;
262
263 for (i = 1; i < argc; ++i) {
264 if (strcmp(argv[i], "-h") == 0) {
265 usage(stdout);
266 return 0;
267 }
268 if (argv[i][0] == '-' && argv[i][1] != '\0') {
269 usage(stderr);
270 return 2;
271 }
272 if (!path) {
273 path = argv[i];
274 continue;
275 }
276 if (!ifname) {
277 ifname = argv[i];
278 continue;
279 }
280 usage(stderr);
281 return 2;
282 }
283
284 if (!path) {
285 usage(stderr);
286 return 2;
287 }
288
289 path = resolvepath(path);
290 parsefile(path, &proto);
291
292 iface = NULL;
293 if (ifname) {
294 iface = proto_find_interface(&proto, ifname);
295 if (!iface)
296 die("unknown interface: %s", ifname);
297 }
298
299 if (iface)
300 print_interface_summary(&proto, iface);
301 else
302 print_protocol_summary(&proto);
303
304 free(path);
305 proto_free(&proto);
306 return 0;
307}