main shrubtools / proto / proto.c
  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}