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