History

Squashed 'c9/' changes from 3196726..e5e39ad

e5e39ad wip revamping aa98d28 C9stat: put qid first, as it has to match with qid[0]

Sigrid Solveig Haflínudóttir committed 2 years ago

Commit 9c70d9e1 parent d79335d29 files · +673 −4443Browse files

Unified · Split

29 files
  1. .gitignore +0 −2
  2. 9gc.c +0 −460
  3. 9pex.c +0 −1052
  4. README.md +17 −17
  5. build.sh +0 −5
  6. c9/LICENSE +0 −1
  7. c9/README.md +0 −8
  8. crc32.c +0 −52
  9. examples/dial.c +50 −0
  10. examples/fs9ptest.c +52 −0
  11. examples/sockets.c +127 −0
  12. fs9p.c +301 −0
  13. include/c9/fs.h +48 −0
  14. include/c9/fs9p.h +31 −0
  15. include/c9/proto.h +7 −11
  16. nixos/README.md +0 −61
  17. nixos/ninepex.nix +0 −16
  18. parg/.gitignore +0 −14
  19. parg/.travis.yml +0 −22
  20. parg/CMakeLists.txt +0 −56
  21. parg/COPYING +0 −113
  22. parg/Doxyfile +0 −10
  23. parg/README.md +0 −205
  24. parg/parg.c +0 −354
  25. parg/parg.h +0 −192
  26. parg/test/greatest.h +0 −1035
  27. parg/test/test_parg.c +0 −703
  28. proto.c +40 −52
  29. pull_subtree.sh +0 −2
.gitignore+0−2
@@ -1,2 +0,0 @@
1−9pex
2−9gc
9gc.c+0−460
@@ -1,460 +0,0 @@
1−#define _DEFAULT_SOURCE
2−#define _FILE_OFFSET_BITS 64
3−#include <errno.h>
4−#include <netdb.h>
5−#include <netinet/in.h>
6−#include <stdarg.h>
7−#include <stdio.h>
8−#include <stdlib.h>
9−#include <string.h>
10−#include <sys/select.h>
11−#include <sys/socket.h>
12−#include <sys/types.h>
13−#include <termios.h>
14−#include <unistd.h>
15−#include "c9.h"
16−#include "parg.h"
17−
18−enum {
19− Msize = 8192,
20−
21− Rootfid = 0,
22− Indexfid,
23− Chatfid,
24−
25− Error = 1<<0,
26− Joined = 1<<1,
27− Disconnected = 1<<2,
28−};
29−
30−typedef struct C9aux C9aux;
31−typedef struct REntry REntry;
32−
33−struct C9aux {
34− C9ctx c;
35− int f;
36− int flags;
37− uint8_t rdbuf[Msize];
38− uint8_t wrbuf[Msize];
39− uint32_t wroff;
40−};
41−
42−static const char *nick;
43−static int printjoin;
44−static uint64_t chatoff, skipuntil;
45−//static char *regsrv = "tcp!registry.9p.zone!6675";
46−static char *chatsrv = "tcp!chat.9p.zone!9990";
47−static const char *channel = "chat";
48−
49−static uint8_t *
50−ctxread(C9ctx *ctx, uint32_t size, int *err)
51−{
52− uint32_t n;
53− int r;
54− C9aux *a;
55−
56− a = ctx->aux;
57− *err = 0;
58− for (n = 0; n < size; n += r) {
59− if ((r = read(a->f, a->rdbuf+n, size-n)) <= 0) {
60− if (errno == EINTR)
61− continue;
62− a->flags |= Disconnected;
63− close(a->f);
64− return NULL;
65− }
66− }
67−
68− return a->rdbuf;
69−}
70−
71−static int
72−wrsend(C9aux *a)
73−{
74− uint32_t n;
75− int w;
76−
77− for (n = 0; n < a->wroff; n += w) {
78− if ((w = write(a->f, a->wrbuf+n, a->wroff-n)) <= 0) {
79− if (errno == EINTR)
80− continue;
81− if (errno != EPIPE) /* remote end closed */
82− perror("write");
83− return -1;
84− }
85− }
86− a->wroff = 0;
87−
88− return 0;
89−}
90−
91−static uint8_t *
92−ctxbegin(C9ctx *ctx, uint32_t size)
93−{
94− uint8_t *b;
95− C9aux *a;
96−
97− a = ctx->aux;
98− if (a->wroff + size > sizeof(a->wrbuf)) {
99− if (wrsend(a) != 0 || a->wroff + size > sizeof(a->wrbuf))
100− return NULL;
101− }
102− b = a->wrbuf + a->wroff;
103− a->wroff += size;
104−
105− return b;
106−}
107−
108−static int
109−ctxend(C9ctx *ctx)
110−{
111− (void)ctx;
112− return 0;
113−}
114−
115−static int
116−dial(char *s)
117−{
118− struct addrinfo *r, *a, hint = {.ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, 0};
119− char host[64], *port;
120− int e, f;
121−
122− if (strncmp(s, "udp!", 4) == 0) {
123− hint.ai_socktype = SOCK_DGRAM;
124− hint.ai_protocol = IPPROTO_UDP;
125− } else if (strncmp(s, "tcp!", 4) == 0) {
126− hint.ai_socktype = SOCK_STREAM;
127− hint.ai_protocol = IPPROTO_TCP;
128− } else {
129− fprintf(stderr, "invalid dial string: %s\n", s);
130− return -1;
131− }
132− if ((port = strchr(s+4, '!')) == NULL) {
133− fprintf(stderr, "invalid dial string: %s\n", s);
134− return -1;
135− }
136− if (snprintf(host, sizeof(host), "%.*s", (int)(port-s-4), s+4) >= (int)sizeof(host)) {
137− fprintf(stderr, "host name too large: %s\n", s);
138− return -1;
139− }
140− port++;
141− if ((e = getaddrinfo(host, port, &hint, &r)) != 0)
142− return -1;
143− f = -1;
144− for (a = r; a != NULL; a = a->ai_next) {
145− if ((f = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) < 0)
146− continue;
147− if (connect(f, a->ai_addr, a->ai_addrlen) != 0) {
148− close(f);
149− f = -1;
150− continue;
151− }
152− }
153− freeaddrinfo(r);
154−
155− return f;
156−}
157−
158−static void
159−output(uint8_t *d, int sz)
160−{
161− int i, j;
162−
163− for (i = j = 0; i < sz; i++) {
164− d[j] = d[i];
165− if (d[j] > 31 || d[j] == '\t' || d[j] == '\n')
166− j++;
167− }
168− write(1, d, j);
169−}
170−
171−static void
172−ctxchatR(C9ctx *ctx, C9r *r)
173−{
174− C9aux *a;
175− C9tag tag;
176− const char *path[2];
177− char buf[64];
178−
179− a = ctx->aux;
180− switch (r->type) {
181− case Rversion:
182− c9attach(ctx, &tag, Rootfid, C9nofid, "none", NULL);
183− path[0] = channel;
184− path[1] = NULL;
185− c9walk(ctx, &tag, Rootfid, Chatfid, path);
186− c9open(ctx, &tag, Chatfid, C9rdwr);
187− break;
188−
189− case Rread:
190− if (chatoff >= skipuntil)
191− output(r->read.data, r->read.size);
192− chatoff += r->read.size;
193− /* fallthrough */
194− case Ropen:
195− if ((a->flags & Joined) == 0 && printjoin) {
196− c9write(ctx, &tag, Chatfid, 0, buf, snprintf(buf, sizeof(buf), "JOIN %s to chat\n", nick));
197− a->flags |= Joined;
198− }
199− c9read(ctx, &tag, Chatfid, chatoff, chatoff < skipuntil ? skipuntil-chatoff : Msize);
200− break;
201−
202− case Rerror:
203− fprintf(stderr, "chat error: %s\n", r->error);
204− a->flags |= Error;
205− break;
206−
207− default:
208− break;
209− }
210−}
211−
212−static void
213−ctxregistryR(C9ctx *ctx, C9r *r)
214−{
215− char *s, *b;
216− C9aux *a;
217− C9tag tag;
218− const char *path[2];
219−
220− a = ctx->aux;
221− switch (r->type) {
222− case Rversion:
223− c9attach(ctx, &tag, Rootfid, C9nofid, "none", NULL);
224− path[0] = "index";
225− path[1] = NULL;
226− c9walk(ctx, &tag, Rootfid, Indexfid, path);
227− c9open(ctx, &tag, Indexfid, C9read);
228− break;
229−
230− case Ropen:
231− c9read(ctx, &tag, Indexfid, 0, Msize);
232− break;
233−
234− case Rread:
235− r->read.data[r->read.size] = 0;
236− for (s = (char*)r->read.data;;) {
237− if ((s = strstr(s, "chat")) == NULL)
238− break;
239− for (b = s; b != (char*)r->read.data && *b != '\n'; b--);
240− if (*b == '\n')
241− b++;
242− if ((s = strchr(s, '\n')) == NULL)
243− s = (char*)&r->read.data[r->read.size];
244− else
245− *s++ = 0;
246− if (strstr(b, "tlssrv") == NULL && (s = strchr(b, ' ')) != NULL) {
247− *s = 0;
248−fallback:
249− close(a->f);
250− if ((a->f = dial(b)) < 0)
251− exit(1);
252− a->flags = 0;
253− ctx->r = ctxchatR;
254− a->wroff = 0;
255− c9version(ctx, &tag, Msize);
256− if (wrsend(a) != 0)
257− exit(1);
258− return;
259− }
260− }
261− b = chatsrv;
262− goto fallback;
263−
264− case Rerror:
265− fprintf(stderr, "registry error: %s\n", r->error);
266− a->flags |= Error;
267− break;
268−
269− default:
270− break;
271− }
272−}
273−
274−__attribute__ ((format (printf, 1, 2)))
275−static void
276−ctxerror(const char *fmt, ...)
277−{
278− va_list ap;
279−
280− va_start(ap, fmt);
281− vfprintf(stderr, fmt, ap);
282− fprintf(stderr, "\n");
283− va_end(ap);
284−}
285−
286−static C9aux *
287−srv(char *s)
288−{
289− C9aux *c;
290− int f;
291−
292− if ((f = dial(s)) < 0)
293− return NULL;
294− c = calloc(1, sizeof(*c));
295− c->f = f;
296− c->c.read = ctxread;
297− c->c.begin = ctxbegin;
298− c->c.end = ctxend;
299− c->c.error = ctxerror;
300− c->c.aux = c;
301−
302− return c;
303−}
304−
305−static C9aux *
306−registry(void)
307−{
308− C9aux *a;
309− C9tag tag;
310− int i;
311−
312− for (i = 0; i < 10; i++) {
313−// if ((a = srv(regsrv)) == NULL) {
314− if ((a = srv(chatsrv)) != NULL) {
315− a->c.r = ctxchatR;
316− c9version(&a->c, &tag, Msize);
317− wrsend(a);
318− return a;
319− }
320−// } else {
321−// break;
322−// }
323− sleep(10);
324− }
325−
326− if (a == NULL)
327− return NULL;
328− a->c.r = ctxregistryR;
329− c9version(&a->c, &tag, Msize);
330− wrsend(a);
331− while (c9proc(&a->c) == 0 && a->c.r == ctxregistryR)
332− wrsend(a);
333−
334− return a;
335−}
336−
337−static int
338−chatrw(C9aux *a)
339−{
340− struct timeval t;
341− fd_set r, e;
342− int n, sz;
343− C9tag tag;
344− C9ctx *ctx;
345− char *s;
346−
347− FD_ZERO(&r);
348− FD_SET(a->f, &r);
349− FD_SET(0, &r);
350− FD_ZERO(&e);
351− FD_SET(a->f, &e);
352− FD_SET(0, &e);
353− memset(&t, 0, sizeof(t));
354− t.tv_sec = 10;
355− for (;;) {
356− errno = 0;
357− if ((n = select(a->f + 1, &r, NULL, &e, &t)) < 0 || FD_ISSET(a->f, &e) || FD_ISSET(0, &e)) {
358− if (errno == EINTR)
359− continue;
360− return -1;
361− }
362− break;
363− }
364−
365− ctx = &a->c;
366− if (FD_ISSET(a->f, &r)) {
367− c9proc(ctx);
368− } else if (FD_ISSET(0, &r)) {
369− s = (char*)a->rdbuf;
370− sz = sprintf(s, "%s → ", nick);
371− for (;;) {
372− if ((n = read(0, s+sz, sizeof(a->rdbuf)-sz)) > 0)
373− sz += n;
374− else
375− exit(0);
376− if (s[sz-1] != '\n'){
377− s[sz-1] = '\n';
378− }else{
379− c9write(ctx, &tag, Chatfid, 0, s, sz);
380− break;
381− }
382− }
383− } else {
384− const char *path[] = {NULL};
385− c9walk(ctx, &tag, Rootfid, Rootfid, path);
386− }
387−
388− return 0;
389−}
390−
391−int
392−main(int argc, char **argv)
393−{
394− struct parg_state ps;
395− struct termios t;
396− C9aux *a;
397− int c, noecho;
398−
399− parg_init(&ps);
400− noecho = 0;
401− while ((c = parg_getopt(&ps, argc, argv, "hjedc:")) >= 0) {
402− switch (c) {
403− case 1:
404− if (nick != NULL) {
405− fprintf(stderr, "only one nickname can be specified\n");
406− return 1;
407− }
408− nick = ps.optarg;
409− break;
410− case 'c':
411− channel = ps.optarg;
412− break;
413− case 'j':
414− printjoin = 1;
415− break;
416− case 'e':
417− noecho = 1;
418− break;
419− case 'h':
420− fprintf(stderr, "usage: 9gc [-j] [-e] [-c CHANNEL] NICKNAME\n");
421− return 0;
422− break;
423− case '?':
424− fprintf(stderr, "unknown option -%c\n", ps.optopt);
425− return 1;
426− break;
427− default:
428− fprintf(stderr, "unhandled option -%c\n", c);
429− return 1;
430− break;
431− }
432− }
433−
434− if (nick == NULL) {
435− fprintf(stderr, "no nickname specified\n");
436− return 1;
437− }
438−
439− if (noecho && tcgetattr(0, &t) == 0) {
440− t.c_lflag &= ~ECHO;
441− tcsetattr(STDIN_FILENO, TCSANOW, &t);
442− }
443−
444− for (;;) {
445− if ((a = registry()) == NULL)
446− return 1;
447− while (chatrw(a) == 0 && wrsend(a) == 0);
448− if (a->flags & (Disconnected|Error)) {
449− a->flags &= ~(Disconnected|Error);
450− skipuntil = chatoff;
451− chatoff = 0;
452− free(a);
453− } else {
454− printf("exiting\n");
455− exit(1);
456− }
457− }
458−
459− return 0;
460−}
9pex.c+0−1052
@@ -1,1052 +0,0 @@
1−#ifdef __linux__
2−#define _GNU_SOURCE
3−#else
4−#define _DEFAULT_SOURCE
5−#endif
6−#define _FILE_OFFSET_BITS 64
7−#include <dirent.h>
8−#include <errno.h>
9−#include <fcntl.h>
10−#include <grp.h>
11−#include <inttypes.h>
12−#include <pwd.h>
13−#include <signal.h>
14−#include <stdarg.h>
15−#include <stdint.h>
16−#include <stdio.h>
17−#include <stdlib.h>
18−#include <string.h>
19−#include <sys/select.h>
20−#include <sys/stat.h>
21−#include <sys/time.h>
22−#include <unistd.h>
23−#include "c9.h"
24−#include "parg.h"
25−
26−#define max(a,b) ((a)>(b)?(a):(b))
27−#define used(x) ((void)(x))
28−#define nelem(x) (int)(sizeof(x)/sizeof((x)[0]))
29−
30−uint32_t crc32(const void *data, int len);
31−
32−enum
33−{
34− Canrd = 1<<0,
35− Canwr = 1<<1,
36−};
37−
38−typedef struct
39−{
40− char *path; /* full path */
41− char *name; /* base name */
42−
43− DIR *dir; /* set when it's an opened directory */
44− uint64_t diroffset; /* to read dirs correctly */
45−
46− C9qid qid;
47− C9fid fid;
48− C9mode mode; /* mode in which the file was opened */
49− uint32_t iounit;
50−
51− int fd; /* set when it's an opened file */
52−}Fid;
53−
54−typedef struct
55−{
56− C9tag tag;
57−}Tag;
58−
59−typedef struct
60−{
61− char *name;
62− uint32_t id;
63−}Id;
64−
65−static char *t2s[] = {
66− [Tversion-Tversion] = "Tversion",
67− [Tauth-Tversion] = "Tauth",
68− [Tattach-Tversion] = "Tattach",
69− [Tflush-Tversion] = "Tflush",
70− [Twalk-Tversion] = "Twalk",
71− [Topen-Tversion] = "Topen",
72− [Tcreate-Tversion] = "Tcreate",
73− [Tread-Tversion] = "Tread",
74− [Twrite-Tversion] = "Twrite",
75− [Tclunk-Tversion] = "Tclunk",
76− [Tremove-Tversion] = "Tremove",
77− [Tstat-Tversion] = "Tstat",
78− [Twstat-Tversion] = "Twstat",
79−};
80−
81−static char *modes[] = {
82− "read", "write", "rdwr", "exec",
83−};
84−
85−static char *Enoauth = "authentication not required";
86−static char *Eunknownfid = "unknown fid";
87−static char *Enowstat = "wstat prohibited";
88−static char *Eperm = "permission denied";
89−static char *Enowrite = "write prohibited";
90−static char *Enomem = "out of memory";
91−static char *Edupfid = "duplicate fid";
92−static char *Ewalknodir = "walk in non-directory";
93−static char *Enotfound = "file not found";
94−static char *Eduptag = "duplicate tag";
95−static char *Ebotch = "9P protocol botch";
96−static char *Enocreate = "create prohibited";
97−static char *Eisdir = "is a directory";
98−static char *Ebadoffset = "bad offset";
99−
100−static int in, out, eof;
101−static C9ctx ctx;
102−static int debug, rootescape;
103−static Fid **fids;
104−static int numfids;
105−static Tag **tags;
106−static int numtags;
107−static char *rootpath;
108−static size_t rootlen;
109−static C9qid walkqids[C9maxpathel];
110−static uint8_t *rdbuf;
111−static uint8_t *wrbuf;
112−static uint32_t wroff, wrend, wrbufsz;
113−static Id *uids, *gids;
114−static int numuids, numgids;
115−
116−__attribute__ ((format (printf, 1, 2)))
117−static void
118−trace(const char *fmt, ...)
119−{
120− va_list ap;
121−
122− if (debug == 0)
123− return;
124−
125− va_start(ap, fmt);
126− vfprintf(stderr, fmt, ap);
127− va_end(ap);
128−}
129−
130−static int
131−canrw(int rdonly, int block)
132−{
133− struct timeval t;
134− fd_set r, w, e;
135− int n, fl;
136−
137− FD_ZERO(&r);
138− FD_SET(in, &r);
139− FD_ZERO(&w);
140− if (rdonly == 0)
141− FD_SET(out, &w);
142− FD_ZERO(&e);
143− FD_SET(in, &e);
144− FD_SET(out, &e);
145− memset(&t, 0, sizeof(t));
146− t.tv_usec = 1000;
147− for (;;) {
148− errno = 0;
149− if ((n = select(max(in, out) + 1, &r, &w, &e, block ? NULL : &t)) < 0 || FD_ISSET(in, &e) || FD_ISSET(out, &e)) {
150− if (errno == EINTR)
151− continue;
152− return -1;
153− }
154− break;
155− }
156−
157− fl = 0;
158− if (FD_ISSET(in, &r))
159− fl |= Canrd;
160− if (FD_ISSET(out, &w))
161− fl |= Canwr;
162−
163− return fl;
164−}
165−
166−static int
167−wrsend(void)
168−{
169− uint32_t n;
170− int w;
171−
172− if (wrend == 0)
173− return 0;
174− for (n = 0; n < wrend; n += w) {
175− errno = 0;
176− if ((w = write(out, wrbuf+n, wrend-n)) <= 0) {
177− if (errno == EINTR)
178− continue;
179− if (errno != EPIPE) /* remote end closed */
180− perror("write");
181− return -1;
182− }
183− }
184− if (debug >= 2)
185− trace("<- %d bytes, %d left\n", wrend, wroff-wrend);
186− memmove(wrbuf, wrbuf+wrend, wroff-wrend);
187− wroff = wroff - wrend;
188−
189− return 0;
190−}
191−
192−static int
193−hastag(C9tag tag)
194−{
195− int i;
196−
197− for (i = 0; i < numtags; i++) {
198− if (tags[i] != NULL && tags[i]->tag == tag)
199− return 1;
200− }
201−
202− return 0;
203−}
204−
205−static int
206−statpath(char *path, struct stat *st, char **err)
207−{
208− if (stat(path, st) == 0)
209− return 0;
210−
211− if (errno == EACCES)
212− *err = Eperm;
213− else if (errno == ENOMEM)
214− *err = Enomem;
215− else if (errno == ENOTDIR)
216− *err = Ewalknodir;
217− else if (errno == ENOENT)
218− *err = Enotfound;
219− else
220− *err = strerror(errno);
221−
222− return -1;
223−}
224−
225−static void
226−stat2qid(struct stat *st, C9qid *qid, uint32_t *iounit)
227−{
228− int fmt;
229−
230− qid->path = st->st_ino;
231− qid->version = crc32(&st->st_ctim, sizeof(st->st_ctim));
232− qid->type = C9qtfile;
233− fmt = st->st_mode & S_IFMT;
234− if (fmt == S_IFDIR)
235− qid->type |= C9qtdir;
236− if ((st->st_mode & 0222) != 0 && (fmt == S_IFCHR || fmt == S_IFCHR || fmt == S_IFSOCK || fmt == S_IFIFO))
237− qid->type |= C9qtappend;
238− if (iounit != NULL)
239− *iounit = st->st_blksize;
240−}
241−
242−static Fid *
243−newfid(C9fid fid, char *path, char **err)
244−{
245− Fid *f, **newfids;
246− struct stat st;
247− int i;
248−
249− for (i = 0; i < numfids; i++) {
250− if (fids[i] != NULL && fids[i]->fid == fid) {
251− *err = Edupfid;
252− return NULL;
253− }
254− }
255−
256− if (statpath(path, &st, err) != 0)
257− return NULL;
258−
259− if ((f = calloc(1, sizeof(*f))) == NULL) {
260− *err = Enomem;
261− return NULL;
262− }
263− f->fd = -1;
264− f->path = strdup(path);
265− f->name = strrchr(f->path, '/');
266− if (f->name == NULL)
267− f->name = f->path;
268− else
269− f->name++;
270− if (f->name[0] == 0)
271− fprintf(stderr, "%s -> empty file name\n", f->path);
272−
273− for (i = 0; i < numfids; i++) {
274− if (fids[i] == NULL) {
275− fids[i] = f;
276− break;
277− }
278− }
279− if (i >= numfids) {
280− if ((newfids = realloc(fids, (numfids+1)*sizeof(*fids))) == NULL) {
281− *err = Enomem;
282− return NULL;
283− }
284− fids = newfids;
285− fids[numfids++] = f;
286− }
287−
288− f->fid = fid;
289− stat2qid(&st, &f->qid, &f->iounit);
290−
291− return f;
292−}
293−
294−static Fid *
295−findfid(C9fid fid, char **err)
296−{
297− int i;
298−
299− for (i = 0; i < numfids; i++) {
300− if (fids[i] != NULL && fids[i]->fid == fid) {
301− return fids[i];
302− }
303− }
304−
305− *err = Eunknownfid;
306−
307− return NULL;
308−}
309−
310−static int
311−delfid(C9fid fid, char **err)
312−{
313− Fid *f;
314− int i;
315−
316− for (i = 0; i < numfids; i++) {
317− f = fids[i];
318− if (f != NULL && f->fid == fid) {
319− if (f->dir != NULL)
320− closedir(f->dir);
321− else if (f->fd >= 0)
322− close(f->fd);
323− free(f->path);
324− free(f);
325− fids[i] = NULL;
326− return 0;
327− }
328− }
329−
330− *err = Eunknownfid;
331−
332− return -1;
333−}
334−
335−static int
336−hasperm(struct stat *st, C9mode mode, char **err)
337−{
338− int m, stm, fmt;
339−
340− m = mode & 0xf;
341− stm = st->st_mode & 0777;
342− *err = Eperm;
343− if (((stm & 0111) == 0 || (stm & 0444) == 0) && m == C9exec) /* executing needs rx */
344− return 0;
345− if ((stm & 0222) == 0 && (m == C9write || m == C9rdwr)) /* writing needs w */
346− return 0;
347− if ((stm & 0444) == 0 && m != C9write) /* reading needs r */
348− return 0;
349− fmt = st->st_mode & S_IFMT;
350− if (fmt == S_IFDIR && ((stm & 0111) == 0 || (stm & 0444) == 0)) /* dirs need rx */
351− return 0;
352− *err = NULL;
353−
354− return 1;
355−}
356−
357−static Fid *
358−walk(C9fid fid, C9fid nfid, char *el[], C9qid *qids[], char **err)
359−{
360− Fid *f;
361− char *path, *real, *p;
362− struct stat st;
363− int i, plen, ellen;
364−
365− if ((f = findfid(fid, err)) == NULL)
366− return NULL;
367−
368− if (el[0] == NULL) { /* nwname = 0 */
369− qids[0] = NULL;
370− if (fid == nfid)
371− return f;
372− return newfid(nfid, f->path, err);
373− }
374−
375− if ((f->qid.type & C9qtdir) == 0) { /* has to be a dir */
376− *err = Ewalknodir;
377− return NULL;
378− }
379−
380− p = strdup(f->path);
381− f = NULL;
382− for (i = 0; el[i] != NULL; i++) {
383− plen = strlen(p);
384− ellen = strlen(el[i]);
385− path = malloc(plen + 1 + ellen + 1);
386− memmove(path, p, plen);
387− path[plen] = '/';
388− memmove(path+plen+1, el[i], ellen);
389− path[plen+1+ellen] = 0;
390−
391− if (!rootescape) {
392− if ((real = realpath(path, NULL)) == NULL)
393− break;
394− free(path);
395− if (strlen(real) < rootlen) { /* don't escape root */
396− free(real);
397− real = strdup(rootpath);
398− }
399− } else {
400− real = path;
401− }
402−
403− free(p);
404− p = real;
405−
406− if (statpath(p, &st, err) != 0)
407− break;
408− if (el[i+1] != NULL && !hasperm(&st, C9read, err))
409− break;
410−
411− qids[i] = &walkqids[i];
412− stat2qid(&st, qids[i], NULL);
413− }
414−
415− qids[i] = NULL;
416− if (el[i] == NULL) { /* could walk all the way */
417− f = newfid(nfid, p, err);
418− if (f != NULL && f->name[0] == '/' && f->name[1] == 0) /* root */
419− f->name = "/";
420− } else if (i != 0) { /* didn't fail on the first one */
421− *err = NULL;
422− }
423− free(p);
424−
425− return f;
426−}
427−
428−static int
429−mode2unix(C9mode mode, int qidtype)
430−{
431− int omode;
432−
433− omode = O_NOFOLLOW;
434− if ((mode & 3) == C9read)
435− omode |= O_RDONLY;
436− else if ((mode & 3) == C9write)
437− omode |= O_WRONLY;
438− else if ((mode & 3) == C9rdwr)
439− omode |= O_RDWR;
440− if (mode & C9trunc)
441− omode |= O_TRUNC;
442−#ifdef __linux__
443− if (mode & C9rclose)
444− omode |= O_TMPFILE;
445−#endif
446− if ((qidtype & C9qtappend) != 0)
447− omode |= O_APPEND;
448−
449− return omode;
450−}
451−
452−static int
453−openfid(Fid *f, C9mode mode, char **err)
454−{
455− struct stat st;
456− int omode;
457−
458− if (stat(f->path, &st) != 0 || !hasperm(&st, mode, err))
459− goto error;
460− stat2qid(&st, &f->qid, &f->iounit);
461−
462− if ((f->qid.type & C9qtdir) != 0) {
463− if ((f->dir = opendir(f->path)) == NULL) {
464− *err = strerror(errno);
465− return -1;
466− }
467− f->fd = dirfd(f->dir);
468− } else {
469− omode = mode2unix(mode, f->qid.type);
470− f->fd = open(f->path, omode);
471− }
472−
473− if (f->fd < 0)
474− goto error;
475− f->mode = mode;
476−
477− return 0;
478−error:
479− if (*err == NULL)
480− *err = strerror(errno);
481−
482− if (f->dir != NULL)
483− closedir(f->dir);
484− else if (f->fd >= 0)
485− close(f->fd);
486−
487− f->dir = NULL;
488− f->fd = -1;
489− return -1;
490−}
491−
492−static char *
493−uid2str(uid_t uid, char **err)
494−{
495− struct passwd *p;
496− Id *newuids;
497− int i;
498−
499− for (i = 0; i < numuids; i++) {
500− if (uids[i].id == uid)
501− return uids[i].name;
502− }
503− if ((p = getpwuid(uid)) == NULL) {
504− *err = strerror(errno);
505− return NULL;
506− }
507− if ((newuids = realloc(uids, sizeof(*uids)*(numuids+1))) == NULL) {
508− *err = Enomem;
509− return NULL;
510− }
511− uids = newuids;
512− uids[numuids].id = uid;
513− uids[numuids].name = strdup(p->pw_name);
514−
515− return uids[numuids++].name;
516−}
517−
518−static char *
519−gid2str(gid_t gid, char **err)
520−{
521− struct group *g;
522− Id *newgids;
523− int i;
524−
525− for (i = 0; i < numgids; i++) {
526− if (gids[i].id == gid)
527− return gids[i].name;
528− }
529− if ((g = getgrgid(gid)) == NULL) {
530− *err = strerror(errno);
531− return NULL;
532− }
533− if ((newgids = realloc(gids, sizeof(*gids)*(numgids+1))) == NULL) {
534− *err = Enomem;
535− return NULL;
536− }
537− gids = newgids;
538− gids[numgids].id = gid;
539− gids[numgids].name = strdup(g->gr_name);
540−
541− return gids[numgids++].name;
542−}
543−
544−static int
545−stat2c9stat(char *name, struct stat *st, C9stat *stout, char **err)
546−{
547− int fmt;
548−
549− memset(stout, 0, sizeof(*stout));
550− stout->size = st->st_size;
551− stat2qid(st, &stout->qid, NULL);
552− stout->name = name;
553− stout->atime = st->st_atim.tv_sec;
554− stout->mtime = st->st_ctim.tv_sec;
555−
556− fmt = st->st_mode & S_IFMT;
557− if (fmt == S_IFDIR)
558− stout->mode |= C9stdir;
559− if (fmt == S_IFCHR || fmt == S_IFCHR || fmt == S_IFSOCK || fmt == S_IFIFO)
560− stout->mode |= C9stappend;
561− stout->mode |= st->st_mode & 0x1ff;
562− if ((stout->uid = uid2str(st->st_uid, err)) == NULL)
563− return -1;
564− if ((stout->gid = gid2str(st->st_gid, err)) == NULL)
565− return -1;
566−
567− return 0;
568−}
569−
570−static int
571−statfid(Fid *f, C9stat *stout, char **err)
572−{
573− struct stat st;
574− int r;
575−
576− if (f->fd >= 0)
577− r = fstat(f->fd, &st);
578− else
579− r = stat(f->path, &st);
580− if (r != 0) {
581− *err = strerror(errno);
582− return -1;
583− }
584−
585− return stat2c9stat(f->name, &st, stout, err);
586−}
587−
588−static uint8_t *
589−ctxread(C9ctx *c, uint32_t size, int *err)
590−{
591− uint32_t n;
592− int r;
593−
594− used(c);
595− r = 0;
596− *err = 0;
597− for (n = 0; n < size; n += r) {
598− errno = 0;
599− if ((r = read(in, rdbuf+n, size-n)) <= 0) {
600− if (r == EINTR)
601− continue;
602− if (r == 0) {
603− eof = 1;
604− } else {
605− *err = 1;
606− perror("ctxread");
607− }
608− return NULL;
609− }
610− }
611−
612− return rdbuf;
613−}
614−
615−static uint8_t *
616−ctxbegin(C9ctx *c, uint32_t size)
617−{
618− uint8_t *b;
619−
620− used(c);
621− if (wroff + size > wrbufsz) {
622− if (wrsend() != 0 || wroff + size > wrbufsz)
623− return NULL;
624− }
625− b = wrbuf + wroff;
626− wroff += size;
627−
628− return b;
629−}
630−
631−static int
632−ctxend(C9ctx *c)
633−{
634− used(c);
635− wrend = wroff;
636− return 0;
637−}
638−
639−__attribute__ ((format (printf, 1, 2)))
640−static void
641−ctxerror(const char *fmt, ...)
642−{
643− va_list ap;
644−
645− va_start(ap, fmt);
646− vfprintf(stderr, fmt, ap);
647− fprintf(stderr, "\n");
648− va_end(ap);
649−}
650−
651−static int
652−s9do(C9error e, char **err)
653−{
654− if (e == 0) {
655− *err = NULL;
656− return 0;
657− }
658−
659− switch (e) {
660− case C9Einit: *err = "c9: initialization failed"; break;
661− case C9Ever: *err = "c9: protocol version doesn't match"; break;
662− case C9Epkt: *err = "c9: incoming packet error"; break;
663− case C9Etag: *err = "c9: no free tags or bad tag"; break;
664− case C9Ebuf: *err = Enomem; break;
665− case C9Epath: *err = "c9: path is too long or just invalid"; break;
666− case C9Eflush: *err = "c9: limit of outstanding flushes reached"; break;
667− case C9Esize: *err = "c9: can't fit data in one message"; break;
668− case C9Estr: *err = "c9: bad string"; break;
669− default: *err = "c9: unknown error"; break;
670− }
671−
672− return -1;
673−}
674−
675−static int
676−readf(C9ctx *c, C9tag tag, Fid *f, uint64_t offset, uint32_t size, char **err)
677−{
678− struct stat st;
679− void *p;
680− struct dirent *e;
681− ssize_t n;
682− C9stat c9st[16], *c9stp[16];
683− long dirpos[16];
684− int i, num, res;
685−
686− if (size > c->msize - 12) /* make sure it fits */
687− size = c->msize - 12;
688−
689− if (f->dir == NULL) { /* a file */
690− if ((p = malloc(size)) == NULL) {
691− *err = Enomem;
692− return -1;
693− }
694− if ((n = pread(f->fd, p, size, offset)) < 0) {
695− *err = strerror(errno);
696− free(p);
697− return -1;
698− }
699− res = s9do(s9read(c, tag, p, n), err);
700− free(p);
701− if (res != 0)
702− return -1;
703− trace("<- Rread tag=%d count=%zd data=...\n", tag, n);
704− return 0;
705− }
706−
707− /* dir */
708− if (offset != f->diroffset) {
709− if (offset == 0) {
710− rewinddir(f->dir);
711− f->diroffset = 0;
712− } else {
713− *err = Ebadoffset;
714− return -1;
715− }
716− }
717−
718− res = 0;
719− num = 0;
720− for (i = 0; i < nelem(c9st); i++) {
721− dirpos[i] = telldir(f->dir); /* so we can rewind in case another stat doesn't fit */
722−
723− errno = 0;
724− if ((e = readdir(f->dir)) == NULL && errno != 0) {
725− *err = strerror(errno);
726− res = -1;
727− goto done;
728− }
729− if (e == NULL) /* eof */
730− break;
731− if (e->d_name[0] == '.' && (e->d_name[1] == 0 || ((e->d_name[1] == '.' && e->d_name[2] == 0))))
732− continue;
733−
734− if (fstatat(f->fd, e->d_name, &st, 0) != 0) {
735− *err = strerror(errno);
736− if (fstatat(f->fd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { /* broken symlink, try to stat the link itself */
737− res = -1;
738− goto done;
739− }
740− }
741− if (stat2c9stat(e->d_name, &st, &c9st[i], err) != 0) {
742− res = -1;
743− goto done;
744− }
745− c9st[i].name = strdup(c9st[i].name);
746− c9stp[num++] = &c9st[i];
747− }
748−
749− i = num;
750− if (s9do(s9readdir(c, tag, c9stp, &num, &f->diroffset, size), err) != 0) {
751− res = -1;
752− goto done;
753− }
754− trace("<- Rread tag=%d count=%"PRIu64" data=...\n", tag, f->diroffset - offset);
755− if (i != num)
756− seekdir(f->dir, dirpos[num]);
757−
758−done:
759− for (i = 0; i < num; i++)
760− free(c9stp[i]->name);
761− return res;
762−}
763−
764−static void
765−ctxt(C9ctx *c, C9t *t)
766−{
767− Fid *f;
768− C9qid *qids[C9maxpathel+1];
769− char *err, *err2;
770− C9stat st;
771− int i;
772−
773− trace("-> %s tag=%d", t2s[t->type-Tversion], t->tag);
774−
775− err = NULL;
776− if (hastag(t->tag)) {
777− err = Eduptag;
778− } else {
779− switch (t->type){
780− case Tversion:
781− trace("\n");
782− if (s9do(s9version(c), &err) == 0)
783− trace("<- Rversion\n");
784− break;
785− case Tauth:
786− trace(" afid=%d uname=\"%s\" aname=\"%s\"\n", t->auth.afid, t->auth.uname, t->auth.aname);
787− err = Enoauth;
788− break;
789− case Tattach:
790− trace(" afid=%d fid=%d uname=\"%s\" aname=\"%s\"\n", t->attach.afid, t->fid, t->attach.uname, t->attach.aname);
791− if (t->attach.afid != C9nofid) {
792− err = Eunknownfid;
793− } else if ((f = newfid(t->fid, rootpath, &err)) != NULL) {
794− f->name = "/";
795− if (s9do(s9attach(c, t->tag, &f->qid), &err) == 0)
796− trace("<- Rattach\n");
797− }
798− break;
799− case Tflush:
800− trace(" oldtag=%d\n", t->flush.oldtag);
801− /* FIXME flush it for realz */
802− if (s9do(s9flush(c, t->tag), &err) == 0)
803− trace("<- Rflush tag=%d\n", t->tag);
804− break;
805− case Twalk:
806− trace(" fid=%d newfid=%d", t->fid, t->walk.newfid);
807− for (i = 0; t->walk.wname[i] != NULL; i++)
808− trace(" \"%s\"", t->walk.wname[i]);
809− trace("\n");
810− walk(t->fid, t->walk.newfid, t->walk.wname, qids, &err);
811− if (err == NULL && s9do(s9walk(c, t->tag, qids), &err) == 0) {
812− trace("<- Rwalk tag=%d ", t->tag);
813− for (i = 0; qids[i] != NULL; i++)
814− trace("qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] ", qids[i]->path, qids[i]->type, qids[i]->version);
815− trace("\n");
816− }
817− break;
818− case Topen:
819− trace(" fid=%d mode=0x%02x\n", t->fid, t->open.mode);
820− if ((f = findfid(t->fid, &err)) != NULL) {
821− if (f->fd >= 0)
822− err = Ebotch;
823− else if (t->open.mode != C9read && t->open.mode != C9exec)
824− err = Eperm;
825− else if (t->open.mode != C9read && (f->qid.type & C9qtdir) != 0)
826− err = Eisdir;
827− else if (openfid(f, t->open.mode, &err) == 0 && s9do(s9open(c, t->tag, &f->qid, f->iounit), &err) == 0)
828− trace("<- Ropen tag=%d qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] iounit=%d\n", t->tag, f->qid.path, f->qid.type, f->qid.version, f->iounit);
829− }
830− break;
831− case Tcreate:
832− trace("...\n");
833− err = Enocreate;
834− break;
835− case Tread:
836− trace(" fid=%d offset=%"PRIu64" count=%"PRIu32"\n", t->fid, t->read.offset, t->read.size);
837− if ((f = findfid(t->fid, &err)) != NULL) {
838− if ((f->dir == NULL && f->fd < 0) || (f->mode & 0xf) == C9write)
839− err = Ebotch;
840− else if (readf(c, t->tag, f, t->read.offset, t->read.size, &err) != 0)
841− trace("readf failed\n");
842− }
843− break;
844− case Twrite:
845− trace("...\n");
846− err = Enowrite;
847− break;
848− case Tclunk:
849− trace(" fid=%d\n", t->fid);
850− if (delfid(t->fid, &err) == 0 && s9do(s9clunk(c, t->tag), &err) == 0)
851− trace("<- Rclunk tag=%d\n", t->tag);
852− break;
853− case Tremove:
854− trace("\n");
855− err = Eperm;
856− break;
857− case Tstat:
858− trace(" fid=%d\n", t->fid);
859− if ((f = findfid(t->fid, &err)) != NULL && statfid(f, &st, &err) == 0 && s9do(s9stat(c, t->tag, &st), &err) == 0)
860− trace("<- Rstat tag=%d ...\n", t->tag);
861− break;
862− case Twstat:
863− trace("...\n");
864− err = Enowstat;
865− break;
866− }
867− }
868−
869− if (err != NULL) {
870− if (s9do(s9error(c, t->tag, err), &err2) == 0)
871− trace("<- Rerror tag=%d \"%s\"\n", t->tag, err);
872− else
873− fprintf(stderr, "s9error: %s\n", err2);
874− }
875−}
876−
877−static void
878−sigdebug(int s)
879−{
880− Fid *f;
881− int i, n;
882−
883− used(s);
884− n = 0;
885− for (i = 0; i < numfids; i++) {
886− f = fids[i];
887− if (f == NULL)
888− continue;
889−
890− fprintf(stderr, "fid %u ", f->fid);
891−
892− if (f->dir != NULL)
893− fprintf(stderr, "open mode=dir ");
894− else if (f->fd >= 0)
895− fprintf(stderr, "open mode=%s%s%s ", modes[(f->mode & 0xf)], (f->mode & C9trunc) ? ",trunc" : "", (f->mode & C9rclose) ? ",rclose" : "");
896−
897− fprintf(stderr, "qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] iounit=%d ", f->qid.path, f->qid.type, f->qid.version, f->iounit);
898− fprintf(stderr, " %s %s\n", f->path, f->name);
899− n++;
900− }
901−
902− fprintf(stderr, "fids\t%d\n", n);
903− fprintf(stderr, "tags\t%d\n", numtags);
904− fprintf(stderr, "uids\t%d\n", numuids);
905− fprintf(stderr, "gids\t%d\n", numgids);
906−}
907−
908−int
909−main(int argc, char **argv)
910−{
911− const char *dir;
912− char *err;
913− Fid *f;
914− struct parg_state ps;
915− struct sigaction sa;
916− int can, i, c, rdonly, block;
917−
918− parg_init(&ps);
919−
920− debug = 0;
921− dir = NULL;
922− while ((c = parg_getopt(&ps, argc, argv, "deh")) >= 0) {
923− switch (c) {
924− case 1:
925− if (dir != NULL) {
926− fprintf(stderr, "only one dir can be specified\n");
927− return 1;
928− }
929− dir = ps.optarg;
930− break;
931− case 'e':
932− rootescape++;
933− break;
934− case 'd':
935− debug++;
936− break;
937− case 'h':
938− fprintf(stderr, "usage: 9pex [-e] [-d] DIR\n");
939− return 0;
940− break;
941− case '?':
942− fprintf(stderr, "unknown option -%c\n", ps.optopt);
943− return 1;
944− break;
945− default:
946− fprintf(stderr, "unhandled option -%c\n", c);
947− return 1;
948− break;
949− }
950− }
951−
952− if (dir == NULL) {
953− fprintf(stderr, "no dir specified\n");
954− return 1;
955− }
956−
957− if ((rootpath = realpath(dir, NULL)) == NULL) {
958− trace("%s: %s\n", dir, strerror(errno));
959− return 1;
960− }
961− rootlen = strlen(rootpath);
962−
963− in = 0;
964− out = 1;
965− eof = 0;
966− fids = NULL;
967− numfids = 0;
968− tags = NULL;
969− numtags = 0;
970− uids = NULL;
971− numuids = 0;
972− gids = NULL;
973− numgids = 0;
974−
975− memset(&ctx, 0, sizeof(ctx));
976− ctx.msize = 64*1024;
977− ctx.read = ctxread;
978− ctx.begin = ctxbegin;
979− ctx.end = ctxend;
980− ctx.t = ctxt;
981− ctx.error = ctxerror;
982−
983− rdbuf = calloc(1, ctx.msize);
984− wrbufsz = ctx.msize;
985− wrbuf = calloc(1, wrbufsz);
986− wroff = wrend = 0;
987−
988− memset(&sa, 0, sizeof(sa));
989− sa.sa_handler = SIG_IGN;
990− sa.sa_flags = SA_RESTART;
991− sigaction(SIGPIPE, &sa, NULL);
992−
993− memset(&sa, 0, sizeof(sa));
994− sa.sa_handler = sigdebug;
995− sa.sa_flags = SA_RESTART;
996− sigfillset(&sa.sa_mask);
997− sigaction(SIGUSR1, &sa, NULL);
998−
999− err = NULL;
1000− rdonly = block = 1; /* at first we wait until the client sends in data */
1001− for (; !eof;) {
1002− if ((can = canrw(rdonly, block)) < 0)
1003− break;
1004− if ((can & Canrd) != 0) { /* if there is data, process it */
1005− if (s9do(s9proc(&ctx), &err) != 0)
1006− break;
1007− /* give it a chance to receive all the data first */
1008− rdonly = 1;
1009− block = 0;
1010− } else if (block == 0) { /* got all the data */
1011− if (rdonly != 0) { /* wait until we can send OR we get more data */
1012− rdonly = 0;
1013− block = 1;
1014− }
1015− } else if (rdonly == 0 && (can & Canwr) != 0) { /* can send */
1016− if (wrsend() != 0) /* send all the data */
1017− break;
1018− rdonly = 1; /* and go back to reading */
1019− block = 1;
1020− }
1021− }
1022−
1023− if (err != NULL)
1024− trace("s9proc: %s\n", err);
1025−
1026− for (i = 0; i < numfids; i++) {
1027− if ((f = fids[i]) != NULL) {
1028− if (f->dir != NULL)
1029− closedir(f->dir);
1030− else if (f->fd >= 0)
1031− close(f->fd);
1032− free(f->path);
1033− free(f);
1034− }
1035− }
1036−
1037− for (i = 0; i < numuids; i++)
1038− free(uids[i].name);
1039− free(uids);
1040− for (i = 0; i < numgids; i++)
1041− free(gids[i].name);
1042− free(gids);
1043−
1044− memset(wrbuf, 0, ctx.msize);
1045− free(wrbuf);
1046− memset(rdbuf, 0, ctx.msize);
1047− free(rdbuf);
1048− free(fids);
1049− free(rootpath);
1050−
1051− return 0;
1052−}
README.md+17−17
@@ -1,25 +1,25 @@
1−# 9pro
1+# c9
22
3−Plan9-related tools for Unix-like operating systems.
3+NOTE (2022-06-27): the library's (yep, it's going to be a library)
4+API is getting completely revamped, and examples are being added.
5+Use either an older version, or expect API breakage.
46
5− * 9pex - share a directory over stdin/stdout, can be used with socat/inetd
6− * 9gc - a very simple stdin/stdout 9gridchan client
7+A lightweight library for implmeneting 9p clients and servers in C.
78
8−This is all _WIP_ still. 9pex is working in read-only mode so far but
9−lacks proper auth, async IO, some more error control etc.
9+This is 9p client and server implementation which aims to be correct,
10+small and secure, targetting mainly low-resource MCUs.
1011
11−To build static binaries with musl:
12+`proto.[ch]`: lowest level of the library, contains nothing but
13+(de)serealization of the protocol messages and tag (de)allocation.
1214
13−```
14−CC=musl-gcc CFLAGS="-Os -s -g0 -static" ./build.sh
15−```
15+`fs.h`: higher level, blocking file API based on POSIX.
1616
17−# Why
17+`fs9p.[ch]`: blocking file API implementation for 9p - having a
18+context created with `proto.[ch]` one can use POSIX-like file API to
19+work with a remote filesystem.
1820
19−We can do better.
21+Goals:
2022
21−# Notes/todo
22−
23− * 9pex: to allow "escaping the root" by following symlinks you have to pass `-e` option
24− * 9pex: chrooting with musl makes `realpath` not work as it requires /proc to be mounted, get rid of `realpath`?
25− * 9gc: you can run it in termux on Android like so: `rlwrap -a'XXXXXXwth' -s 0 -t dumb 9gc -e NICKNAME`
23+* portability: zero external dependencies, no POSIX, just C
24+* no dynamic memory allocation
25+* security: no crashes due to incoming data
build.sh+0−5
@@ -1,5 +0,0 @@
1−#!/bin/sh
2−set -e
3−set -x
4−${CC:-gcc} -std=c99 -fms-extensions -DC9_NO_SERVER -O0 -g -Wall -Wextra -Wshadow $CFLAGS c9/*.c parg/*.c -Ic9 -Iparg 9gc.c -o 9gc || rm -f 9gc
5−${CC:-gcc} -std=c99 -fms-extensions -DC9_NO_CLIENT -O0 -g -Wall -Wextra -Wshadow $CFLAGS c9/*.c parg/*.c -Ic9 -Iparg 9pex.c crc32.c -o 9pex || rm -f 9pex
c9/LICENSE+0−1
@@ -1 +0,0 @@
1−Public domain.
c9/README.md+0−8
@@ -1,8 +0,0 @@
1−# c9
2−
3−Low level 9p client and server.
4−
5−## Examples
6−
7−Until I have time to write a minimal example you could take a look at
8−https://git.sr.ht/~ft/9pro/blob/master/9pex.c
crc32.c+0−52
@@ -1,52 +0,0 @@
1−#define _DEFAULT_SOURCE
2−#include <stdint.h>
3−
4−static uint32_t table[256] = {
5− 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
6− 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
7− 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
8− 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
9− 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
10− 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
11− 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
12− 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
13− 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
14− 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
15− 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
16− 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
17− 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
18− 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
19− 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
20− 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
21− 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
22− 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
23− 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
24− 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
25− 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
26− 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
27− 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
28− 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
29− 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
30− 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
31− 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
32− 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
33− 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
34− 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
35− 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
36− 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
37−};
38−
39−uint32_t
40−crc32(const void *data, int len)
41−{
42− const uint8_t *d;
43− uint32_t c;
44− int i;
45−
46− d = data;
47− c = 0xffffffff;
48− for (i = 0; i < len; i++)
49− c = table[(c & 0xff) ^ d[i]] ^ (c >> 8);
50−
51− return ~c;
52−}
examples/dial.c+50−0
@@ -0,0 +1,50 @@
1+#include <string.h>
2+#include <stdio.h>
3+#include <sys/types.h>
4+#include <sys/socket.h>
5+#include <netdb.h>
6+#include <unistd.h>
7+
8+int
9+dial(const char *s)
10+{
11+ struct addrinfo *r, *a, hint = {.ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, 0};
12+ char host[128], *port;
13+ int e, f;
14+
15+ if(strncmp(s, "udp!", 4) == 0){
16+ hint.ai_socktype = SOCK_DGRAM;
17+ hint.ai_protocol = IPPROTO_UDP;
18+ }else if(strncmp(s, "tcp!", 4) == 0){
19+ hint.ai_socktype = SOCK_STREAM;
20+ hint.ai_protocol = IPPROTO_TCP;
21+ }else{
22+ fprintf(stderr, "invalid dial string: %s\n", s);
23+ return -1;
24+ }
25+ if((port = strchr(s+4, '!')) == NULL){
26+ fprintf(stderr, "invalid dial string: %s\n", s);
27+ return -1;
28+ }
29+ if(snprintf(host, sizeof(host), "%.*s", (int)(port-s-4), s+4) >= (int)sizeof(host)){
30+ fprintf(stderr, "host name too large: %s\n", s);
31+ return -1;
32+ }
33+ port++;
34+ if((e = getaddrinfo(host, port, &hint, &r)) != 0){
35+ fprintf(stderr, "%s: %s\n", gai_strerror(e), s);
36+ return -1;
37+ }
38+ f = -1;
39+ for(a = r; a != NULL; a = a->ai_next){
40+ if((f = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) < 0)
41+ continue;
42+ if(connect(f, a->ai_addr, a->ai_addrlen) == 0)
43+ break;
44+ close(f);
45+ f = -1;
46+ }
47+ freeaddrinfo(r);
48+
49+ return f;
50+}
examples/fs9ptest.c+52−0
@@ -0,0 +1,52 @@
1+#include <stdio.h>
2+#include <stdint.h>
3+#include <stdarg.h>
4+#include "c9/proto.h"
5+#include "c9/fs.h"
6+#include "c9/fs9p.h"
7+
8+int dial(const char *s);
9+C9ctx *init9p(int f);
10+
11+static FS9pfd fds[8];
12+
13+static void
14+fserror(FS *fs, const char *fmt, ...)
15+{
16+ va_list ap;
17+
18+ (void)fs;
19+ va_start(ap, fmt);
20+ vfprintf(stderr, fmt, ap);
21+ fprintf(stderr, "\n");
22+ va_end(ap);
23+}
24+
25+int
26+main(int argc, char **argv)
27+{
28+ C9ctx *ctx;
29+ FSstat st;
30+ FS fs;
31+ FS9p fs9 = {
32+ .fds = fds,
33+ .numfds = sizeof(fds)/sizeof(fds[0]),
34+ };
35+ int f;
36+
37+ (void)argc; (void)argv;
38+
39+ fs.error = fserror;
40+ fs.aux = (void*)&fs9;
41+ if((f = dial("tcp!ftrv.se!564")) < 0)
42+ return 1;
43+ ctx = init9p(f);
44+ fs9.ctx = ctx;
45+ if(fs9pinit(&fs, 8192) != 0)
46+ return 1;
47+ if(fs.fstat(&fs, FS9proot, &st) != 0)
48+ return 1;
49+ printf("%s %d %s %o\n", st.name, (int)st.size, st.uid, st.mode&0777);
50+
51+ return 0;
52+}
examples/sockets.c+127−0
@@ -0,0 +1,127 @@
1+#include <stdio.h>
2+#include <stdint.h>
3+#include <stdarg.h>
4+#include <errno.h>
5+#include <unistd.h>
6+#include <stdlib.h>
7+#include "c9/proto.h"
8+
9+enum {
10+ Msize = 8192,
11+
12+ Disconnected = 1<<0,
13+};
14+
15+typedef struct C9aux C9aux;
16+
17+struct C9aux {
18+ C9ctx c;
19+ int f;
20+ int flags;
21+ uint8_t rdbuf[Msize];
22+ uint8_t wrbuf[Msize];
23+ uint32_t wroff;
24+};
25+
26+static uint8_t *
27+ctxread(C9ctx *ctx, uint32_t size, int *err)
28+{
29+ uint32_t n;
30+ int r;
31+ C9aux *a;
32+
33+ a = ctx->aux;
34+ *err = 0;
35+ for(n = 0; n < size; n += r){
36+ if((r = read(a->f, a->rdbuf+n, size-n)) <= 0){
37+ if(errno == EINTR)
38+ continue;
39+ a->flags |= Disconnected;
40+ close(a->f);
41+ return NULL;
42+ }
43+ }
44+
45+ return a->rdbuf;
46+}
47+
48+static int
49+wrsend(C9aux *a)
50+{
51+ uint32_t n;
52+ int w;
53+
54+ for(n = 0; n < a->wroff; n += w){
55+ if((w = write(a->f, a->wrbuf+n, a->wroff-n)) <= 0){
56+ if(errno == EINTR)
57+ continue;
58+ if(errno != EPIPE) /* remote end closed */
59+ perror("write");
60+ return -1;
61+ }
62+ }
63+ a->wroff = 0;
64+
65+ return 0;
66+}
67+
68+static uint8_t *
69+ctxbegin(C9ctx *ctx, uint32_t size)
70+{
71+ uint8_t *b;
72+ C9aux *a;
73+
74+ a = ctx->aux;
75+ if(a->wroff + size > sizeof(a->wrbuf)){
76+ if(wrsend(a) != 0 || a->wroff + size > sizeof(a->wrbuf))
77+ return NULL;
78+ }
79+ b = a->wrbuf + a->wroff;
80+ a->wroff += size;
81+
82+ return b;
83+}
84+
85+static int
86+ctxend(C9ctx *ctx)
87+{
88+ C9aux *a;
89+
90+ /*
91+ * To batch up requests and instead flush them all at once just return 0
92+ * here and call wrsend yourself when needed.
93+ */
94+ a = ctx->aux;
95+ return wrsend(a);
96+}
97+
98+static void
99+ctxerror(C9ctx *ctx, const char *fmt, ...)
100+{
101+ va_list ap;
102+
103+ (void)ctx;
104+ va_start(ap, fmt);
105+ vfprintf(stderr, fmt, ap);
106+ fprintf(stderr, "\n");
107+ va_end(ap);
108+}
109+
110+C9aux *
111+init9p(int f)
112+{
113+ C9aux *c;
114+
115+ if((c = calloc(1, sizeof(*c))) == NULL){
116+ close(f);
117+ return NULL;
118+ }
119+ c->f = f;
120+ c->c.read = ctxread;
121+ c->c.begin = ctxbegin;
122+ c->c.end = ctxend;
123+ c->c.error = ctxerror;
124+ c->c.aux = c;
125+
126+ return c;
127+}
fs9p.c+301−0
@@ -0,0 +1,301 @@
1+#include <stdint.h>
2+#include <string.h>
3+#include "c9/proto.h"
4+#include "c9/fs.h"
5+#include "c9/fs9p.h"
6+
7+enum
8+{
9+ Flinit = 1<<0,
10+ Flerror = 1<<1,
11+
12+ F9init = 0,
13+ F9chdir,
14+ F9open,
15+ F9create,
16+ F9read,
17+ F9readdir,
18+ F9write,
19+ F9seek,
20+ F9stat,
21+ F9fstat,
22+ F9close,
23+ F9remove,
24+ F9rename,
25+};
26+
27+static const FS9pfd freefd =
28+{
29+ .offset = ~0ULL,
30+};
31+
32+#define isfree(fd) ((fd).offset == ~0ULL)
33+
34+static const char *f2s[] =
35+{
36+ [F9init] = "init",
37+ [F9chdir] = "chdir",
38+ [F9open] = "open",
39+ [F9create] = "create",
40+ [F9read] = "read",
41+ [F9readdir] = "readdir",
42+ [F9write] = "write",
43+ [F9seek] = "seek",
44+ [F9stat] = "stat",
45+ [F9fstat] = "fstat",
46+ [F9close] = "close",
47+ [F9remove] = "remove",
48+ [F9rename] = "rename",
49+};
50+
51+static int
52+fs9pchdir(FS *fs, const char *path)
53+{
54+ FS9p *aux = (void*)fs->aux;
55+
56+ aux->f = F9chdir;
57+
58+ return -1;
59+}
60+
61+static int
62+fs9popen(FS *fs, const char *name, FSmode mode)
63+{
64+ FS9p *aux = (void*)fs->aux;
65+
66+ aux->f = F9open;
67+
68+ return -1;
69+}
70+
71+static int
72+fs9pcreate(FS *fs, const char *path, int perm)
73+{
74+ FS9p *aux = (void*)fs->aux;
75+
76+ aux->f = F9create;
77+
78+ return -1;
79+}
80+
81+static int64_t
82+fs9pread(FS *fs, int fd, void *buf, uint64_t count)
83+{
84+ FS9p *aux = (void*)fs->aux;
85+
86+ aux->f = F9read;
87+
88+ return -1;
89+}
90+
91+static int64_t
92+fs9preaddir(FS *fs, int fd, FSstat *st, int nst)
93+{
94+ FS9p *aux = (void*)fs->aux;
95+
96+ aux->f = F9readdir;
97+
98+ return -1;
99+}
100+
101+static int64_t
102+fs9pwrite(FS *fs, int fd, const void *buf, uint64_t count)
103+{
104+ FS9p *aux = (void*)fs->aux;
105+
106+ aux->f = F9write;
107+
108+ return -1;
109+}
110+
111+static int64_t
112+fs9pseek(FS *fs, int fd, int64_t offset, int whence)
113+{
114+ FS9p *aux = (void*)fs->aux;
115+
116+ aux->f = F9seek;
117+
118+ return -1;
119+}
120+
121+static int
122+fs9pstat(FS *fs, const char *path, FSstat *st)
123+{
124+ FS9p *aux = (void*)fs->aux;
125+
126+ aux->f = F9stat;
127+
128+ return -1;
129+}
130+
131+static int
132+fs9pfstat(FS *fs, int fd, FSstat *st)
133+{
134+ FS9p *aux = (void*)fs->aux;
135+ C9tag tag;
136+ int e;
137+
138+ aux->f = F9fstat;
139+ aux->p = st;
140+ if((e = c9stat(aux->ctx, &tag, fd)) < 0)
141+ return e;
142+ while(aux->f != -F9fstat)
143+ c9proc(aux->ctx);
144+
145+ return 0;
146+}
147+
148+static int
149+fs9pclose(FS *fs, int fd)
150+{
151+ FS9p *aux = (void*)fs->aux;
152+
153+ aux->f = F9close;
154+
155+ return -1;
156+}
157+
158+static int
159+fs9premove(FS *fs, const char *name)
160+{
161+ FS9p *aux = (void*)fs->aux;
162+
163+ aux->f = F9remove;
164+
165+ return -1;
166+}
167+
168+static int
169+fs9prename(FS *fs, const char *oldpath, const char *newpath)
170+{
171+ FS9p *aux = (void*)fs->aux;
172+
173+ aux->f = F9rename;
174+
175+ return -1;
176+}
177+
178+static void
179+fs9pr(C9ctx *ctx, C9r *r)
180+{
181+ FSstat *st;
182+ C9tag tag;
183+ FS9p *aux;
184+ FS *fs;
185+
186+ fs = ctx->paux;
187+ aux = (void*)fs->aux;
188+
189+ switch(r->type){
190+ case Rversion:
191+ c9attach(ctx, &tag, FS9proot, C9nofid, "none", NULL); /* FIXME those need to be configurable */
192+ break;
193+
194+ case Rauth:
195+ break;
196+
197+ case Rattach:
198+ aux->flags |= Flinit;
199+ break;
200+
201+ case Rerror:
202+ fs->error(fs, "%s: %s", f2s[aux->f], r->error);
203+ aux->flags |= Flerror;
204+ break;
205+
206+ case Rflush:
207+ break;
208+
209+ case Rwalk:
210+ break;
211+
212+ case Ropen:
213+ break;
214+
215+ case Rcreate:
216+ break;
217+
218+ case Rread:
219+ break;
220+
221+ case Rwrite:
222+ break;
223+
224+ case Rclunk:
225+ break;
226+
227+ case Rremove:
228+ break;
229+
230+ case Rstat:
231+ if(aux->f == F9stat || aux->f == F9fstat){
232+ st = aux->p;
233+ st->size = r->stat.size;
234+ st->name = r->stat.name;
235+ st->uid = r->stat.uid;
236+ st->gid = r->stat.gid;
237+ st->mode = r->stat.mode;
238+ st->mtime = r->stat.mtime;
239+ aux->f = -aux->f;
240+ }
241+ break;
242+
243+ case Rwstat:
244+ break;
245+ }
246+}
247+
248+int
249+fs9pinit(FS *fs, uint32_t msize)
250+{
251+ FS9p *aux = (void*)fs->aux;
252+ C9error err;
253+ C9tag tag;
254+ int i;
255+
256+ if(fs->error == NULL)
257+ return -1;
258+ if(aux == NULL || aux->ctx == NULL || aux->fds == NULL || aux->numfds < 1){
259+ fs->error(fs, "fs9pinit: invalid aux");
260+ return -1;
261+ }
262+ if(aux->ctx == NULL){
263+ fs->error(fs, "fs9pinit: invalid ctx");
264+ return -1;
265+ }
266+
267+ fs->chdir = fs9pchdir;
268+ fs->open = fs9popen;
269+ fs->create = fs9pcreate;
270+ fs->read = fs9pread;
271+ fs->readdir = fs9preaddir;
272+ fs->write = fs9pwrite;
273+ fs->seek = fs9pseek;
274+ fs->stat = fs9pstat;
275+ fs->fstat = fs9pfstat;
276+ fs->close = fs9pclose;
277+ fs->remove = fs9premove;
278+ fs->rename = fs9prename;
279+
280+ aux->root.offset = 0;
281+ for(i = 0; i < aux->numfds; i++)
282+ aux->fds[i] = freefd;
283+
284+ aux->ctx->r = fs9pr;
285+ aux->ctx->paux = fs;
286+ aux->f = F9init;
287+ aux->flags = 0;
288+
289+ if((err = c9version(aux->ctx, &tag, msize)) != 0)
290+ return err;
291+ for(;;){
292+ if((err = c9proc(aux->ctx)) != 0)
293+ return err;
294+ if(aux->flags & Flerror)
295+ return -1;
296+ if(aux->flags & Flinit)
297+ break;
298+ }
299+
300+ return 0;
301+}
include/c9/fs.h+48−0
@@ -0,0 +1,48 @@
1+typedef struct FS FS;
2+typedef struct FSstat FSstat;
3+
4+typedef enum
5+{
6+ FS_OREAD,
7+ FS_OWRITE,
8+ FS_ORDWR,
9+ FS_OEXEC,
10+ FS_OTRUNC = 0x10,
11+ FS_ORCLOSE = 0x40,
12+ FS_OEXCL = 0x1000,
13+ FS_DIR = 0x80000000U, /* open/create: it's supposed to be a directory. */
14+}FSmode;
15+
16+struct FSstat
17+{
18+ uint64_t size; /* Size of the file (in bytes). */
19+ char *name; /* Name of the file. */
20+ char *uid; /* Owner of the file. */
21+ char *gid; /* Group of the file. */
22+ uint32_t mode; /* Permissions. See C9st* and C9perm. */
23+ uint64_t mtime; /* Last modification time. Nanoseconds since epoch. */
24+};
25+
26+struct FSaux;
27+
28+struct FS
29+{
30+ /* Callback for error messages. */
31+ void (*error)(FS *fs, const char *fmt, ...) __attribute__((nonnull(1), format(printf, 2, 3)));
32+
33+ /* Do not set these. */
34+ int (*chdir)(FS *fs, const char *path) __attribute__((nonnull(1, 2)));
35+ int (*open)(FS *fs, const char *name, FSmode mode) __attribute__((nonnull(1, 2)));
36+ int (*create)(FS *fs, const char *path, int perm) __attribute__((nonnull(1, 2)));
37+ int64_t (*read)(FS *fs, int fd, void *buf, uint64_t count) __attribute__((nonnull(1, 3)));
38+ int64_t (*readdir)(FS *fs, int fd, FSstat *st, int nst);
39+ int64_t (*write)(FS *fs, int fd, const void *buf, uint64_t count) __attribute__((nonnull(1, 3)));
40+ int64_t (*seek)(FS *fs, int fd, int64_t offset, int whence) __attribute__((nonnull(1)));
41+ int (*stat)(FS *fs, const char *path, FSstat *st) __attribute__((nonnull(1, 2, 3)));
42+ int (*fstat)(FS *fs, int fd, FSstat *st) __attribute__((nonnull(1, 3)));
43+ int (*close)(FS *fs, int fd) __attribute__((nonnull(1)));
44+ int (*remove)(FS *fs, const char *name) __attribute__((nonnull(1, 2)));
45+ int (*rename)(FS *fs, const char *oldpath, const char *newpath) __attribute__((nonnull(1, 2, 3)));
46+
47+ struct FSaux *aux;
48+};
include/c9/fs9p.h+31−0
@@ -0,0 +1,31 @@
1+enum
2+{
3+ FS9proot = 3, /* '/' file descriptor. */
4+};
5+
6+typedef struct FS9p FS9p;
7+typedef struct FS9pfd FS9pfd;
8+
9+struct FS9pfd
10+{
11+ uint64_t offset;
12+};
13+
14+struct FS9p
15+{
16+ /* The following three fields need to be set before calling fs9pinit. */
17+
18+ C9ctx *ctx; /* Set to a full set up context, except "r" and "t" fields. */
19+ FS9pfd *fds; /* Point at the allocated fds array. */
20+ int numfds; /* Set to the number of entries available in fds array. */
21+ void *aux; /* Optional, user-supplied aux value. */
22+
23+ /* Private, do not touch. */
24+ FS9pfd root;
25+ FS9pfd cwd;
26+ int f;
27+ void *p;
28+ int flags;
29+};
30+
31+extern int fs9pinit(FS *fs, uint32_t msize) __attribute__((nonnull(1)));
include/c9/proto.hrenamed from c9/c9.h+7−11
@@ -1,12 +1,3 @@
1−#ifdef __plan9__
2−typedef u64int uint64_t;
3−typedef u32int uint32_t;
4−typedef u16int uint16_t;
5−typedef u8int uint8_t;
6−#define __attribute__(...)
7−#define NULL nil
8−#endif
9−
101 struct C9aux;
112
123 typedef struct C9r C9r;
@@ -151,12 +142,12 @@ struct C9qid · 138 unmodified lines
151142 */
152143 struct C9stat
153144 {
145+ C9qid qid; /* Same as qid[0]. */
154146 uint64_t size; /* Size of the file (in bytes). */
155147 char *name; /* Name of the file. */
156148 char *uid; /* Owner of the file. */
157149 char *gid; /* Group of the file. */
158150 char *muid; /* The user who modified the file last. */
159− C9qid qid; /* Same as qid[0]. */
160151 uint32_t mode; /* Permissions. See C9st* and C9perm. */
161152 uint32_t atime; /* Last access time. */
162153 uint32_t mtime; /* Last modification time. */
@@ -295,12 +286,13 @@ struct C9ctx · 132 unmodified lines
295286 void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2)));
296287
297288 /* Callback for error messages. */
298− void (*error)(const char *fmt, ...) __attribute__((nonnull(1), format(printf, 1, 2)));
289+ void (*error)(C9ctx *ctx, const char *fmt, ...) __attribute__((nonnull(1), format(printf, 2, 3)));
299290
300291 /* Auxiliary data, can be used by any of above callbacks. */
301292 struct C9aux *aux;
302293
303294 /* private stuff */
295+ void *paux;
304296 uint32_t msize;
305297 #ifndef C9_NO_CLIENT
306298 uint32_t flush[C9maxflush];
@@ -308,8 +300,12 @@ struct C9ctx · 1 unmodified line
308300 #endif
309301 union
310302 {
303+#ifndef C9_NO_CLIENT
311304 C9tag lowfreetag;
305+#endif
306+#ifndef C9_NO_SERVER
312307 uint16_t svflags;
308+#endif
313309 };
314310 };
315311
nixos/README.md+0−61
@@ -1,61 +0,0 @@
1−# Read-only 9pex in NixOS
2−
3−```
4−sudo mkdir -p /etc/nixos/pkgs
5−sudo cp ninepex.nix /etc/nixos/pkgs/
6−```
7−
8−And change `/etc/nixos/configuration.nix`:
9−
10−```
11−{ config, pkgs, ... }:
12−
13−let
14− ninepex = pkgs.callPackage ./pkgs/ninepex.nix {};
15−in
16−{
17−
18−.................
19−
20− services.xinetd = {
21− enable = true;
22− services = [
23− {
24− name = "9pfs";
25− port = 564;
26− user = "none";
27− server = "${ninepex}/bin/9pex";
28− serverArgs = "/home/9";
29− flags = "KEEPALIVE REUSE";
30− extraConfig = ''
31− instances = UNLIMITED
32− '';
33− }
34− ];
35− };
36−
37−.................
38−
39− users.users.none = {
40− isNormalUser = false;
41− isSystemUser = false;
42− createHome = true;
43− home = "/home/9";
44− shell = "${pkgs.shadow}/bin/nologin";
45− };
46−
47−```
48−
49−Files go to `/home/9`. With `rsync` it can be done as so:
50−
51−```
52−rsync -iCaR \
53− --exclude='.git*' \
54− --exclude='*.out' \
55− --exclude='*.[0125678vqki]' \
56− --exclude='[0125678vqki].*' \
57− --delete-excluded --delete \
58− --chown none:nogroup \
59− dir1 dir2 dir3 \
60− root@host:/home/9/
61−```
nixos/ninepex.nix+0−16
@@ -1,16 +0,0 @@
1−{ stdenv }:
2−
3−stdenv.mkDerivation rec {
4− name = "9pex";
5− src = fetchGit {
6− url = "https://git.sr.ht/~ft/9pro";
7− ref = "master";
8− rev = "f9ddf46b12b9ee88e3b81f35f80e374722ca6f9d";
9− };
10−
11− installPhase = ''
12− ./build.sh
13− mkdir -p "$out/bin"
14− cp 9pex "$out/bin/"
15− '';
16−}
parg/.gitignore+0−14
@@ -1,14 +0,0 @@
1−*.o
2−*.obj
3−
4−*.a
5−*.lib
6−
7−*.so
8−*.so.*
9−*.dll
10−
11−*.exe
12−
13−/build/
14−/doc/
parg/.travis.yml+0−22
@@ -1,22 +0,0 @@
1−language: c
2−
3−compiler: gcc
4−
5−sudo: false
6−
7−before_install:
8− - pip install --user codecov
9−
10−before_script:
11− - mkdir build
12− - cd build
13− - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_COVERAGE=ON ..
14−
15−script: cmake --build . && ctest -V .
16−
17−after_success:
18− - gcov CMakeFiles/parg.dir/parg.c.gcno -o CMakeFiles/parg.dir
19− - codecov -X gcov
20−
21−notifications:
22− email: false
parg/CMakeLists.txt+0−56
@@ -1,56 +0,0 @@
1−cmake_minimum_required(VERSION 2.8.6)
2−
3−project(parg C)
4−
5−include(CheckCCompilerFlag)
6−include(CTest)
7−
8−option(BUILD_COVERAGE "Code coverage" OFF)
9−
10−message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
11−
12−function(_add_c_compiler_flags var)
13− foreach(flag ${ARGN})
14− string(REGEX REPLACE "[^a-zA-Z0-9]+" "_" flag_var "CFLAG_${flag}")
15− check_c_compiler_flag("${flag}" ${flag_var})
16− if(${flag_var})
17− set(${var} "${${var}} ${flag}")
18− endif()
19− endforeach()
20− set(${var} "${${var}}" PARENT_SCOPE)
21−endfunction()
22−
23−if(MSVC)
24− _add_c_compiler_flags(CMAKE_C_FLAGS "/analyze")
25− add_definitions(/D_CRT_SECURE_NO_WARNINGS)
26−elseif(CMAKE_COMPILER_IS_GNUCC)
27− _add_c_compiler_flags(CMAKE_C_FLAGS
28− -Wall
29− -Wextra
30− -pedantic
31− -Wshadow
32− -Wpointer-arith
33− -Wcast-qual
34− -Wcast-align
35− -Wstrict-prototypes
36− -Wmissing-prototypes
37− )
38−elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
39− _add_c_compiler_flags(CMAKE_C_FLAGS "-Weverything;-pedantic")
40−endif()
41−
42−if(BUILD_COVERAGE)
43− set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
44−endif()
45−
46−include_directories(${PROJECT_SOURCE_DIR})
47−
48−add_library(parg parg.c parg.h)
49−
50−if(BUILD_TESTING)
51− add_executable(test_parg test/test_parg.c test/greatest.h parg.h)
52− target_link_libraries(test_parg parg)
53−
54− enable_testing()
55− add_test(test_parg test_parg)
56−endif()
parg/COPYING+0−113
@@ -1,113 +0,0 @@
1−CC0 1.0 Universal
2−
3−Statement of Purpose
4−
5−The laws of most jurisdictions throughout the world automatically confer
6−exclusive Copyright and Related Rights (defined below) upon the creator
7−and subsequent owner(s) (each and all, an "owner") of an original work of
8−authorship and/or a database (each, a "Work").
9−
10−Certain owners wish to permanently relinquish those rights to a Work for
11−the purpose of contributing to a commons of creative, cultural and
12−scientific works ("Commons") that the public can reliably and without fear
13−of later claims of infringement build upon, modify, incorporate in other
14−works, reuse and redistribute as freely as possible in any form whatsoever
15−and for any purposes, including without limitation commercial purposes.
16−These owners may contribute to the Commons to promote the ideal of a free
17−culture and the further production of creative, cultural and scientific
18−works, or to gain reputation or greater distribution for their Work in
19−part through the use and efforts of others.
20−
21−For these and/or other purposes and motivations, and without any
22−expectation of additional consideration or compensation, the person
23−associating CC0 with a Work (the "Affirmer"), to the extent that he or she
24−is an owner of Copyright and Related Rights in the Work, voluntarily
25−elects to apply CC0 to the Work and publicly distribute the Work under its
26−terms, with knowledge of his or her Copyright and Related Rights in the
27−Work and the meaning and intended legal effect of CC0 on those rights.
28−
29−1. Copyright and Related Rights. A Work made available under CC0 may be
30−protected by copyright and related or neighboring rights ("Copyright and
31−Related Rights"). Copyright and Related Rights include, but are not
32−limited to, the following:
33−
34− i. the right to reproduce, adapt, distribute, perform, display,
35− communicate, and translate a Work;
36− ii. moral rights retained by the original author(s) and/or performer(s);
37−iii. publicity and privacy rights pertaining to a person's image or
38− likeness depicted in a Work;
39− iv. rights protecting against unfair competition in regards to a Work,
40− subject to the limitations in paragraph 4(a), below;
41− v. rights protecting the extraction, dissemination, use and reuse of data
42− in a Work;
43− vi. database rights (such as those arising under Directive 96/9/EC of the
44− European Parliament and of the Council of 11 March 1996 on the legal
45− protection of databases, and under any national implementation
46− thereof, including any amended or successor version of such
47− directive); and
48−vii. other similar, equivalent or corresponding rights throughout the
49− world based on applicable law or treaty, and any national
50− implementations thereof.
51−
52−2. Waiver. To the greatest extent permitted by, but not in contravention
53−of, applicable law, Affirmer hereby overtly, fully, permanently,
54−irrevocably and unconditionally waives, abandons, and surrenders all of
55−Affirmer's Copyright and Related Rights and associated claims and causes
56−of action, whether now known or unknown (including existing as well as
57−future claims and causes of action), in the Work (i) in all territories
58−worldwide, (ii) for the maximum duration provided by applicable law or
59−treaty (including future time extensions), (iii) in any current or future
60−medium and for any number of copies, and (iv) for any purpose whatsoever,
61−including without limitation commercial, advertising or promotional
62−purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
63−member of the public at large and to the detriment of Affirmer's heirs and
64−successors, fully intending that such Waiver shall not be subject to
65−revocation, rescission, cancellation, termination, or any other legal or
66−equitable action to disrupt the quiet enjoyment of the Work by the public
67−as contemplated by Affirmer's express Statement of Purpose.
68−
69−3. Public License Fallback. Should any part of the Waiver for any reason
70−be judged legally invalid or ineffective under applicable law, then the
71−Waiver shall be preserved to the maximum extent permitted taking into
72−account Affirmer's express Statement of Purpose. In addition, to the
73−extent the Waiver is so judged Affirmer hereby grants to each affected
74−person a royalty-free, non transferable, non sublicensable, non exclusive,
75−irrevocable and unconditional license to exercise Affirmer's Copyright and
76−Related Rights in the Work (i) in all territories worldwide, (ii) for the
77−maximum duration provided by applicable law or treaty (including future
78−time extensions), (iii) in any current or future medium and for any number
79−of copies, and (iv) for any purpose whatsoever, including without
80−limitation commercial, advertising or promotional purposes (the
81−"License"). The License shall be deemed effective as of the date CC0 was
82−applied by Affirmer to the Work. Should any part of the License for any
83−reason be judged legally invalid or ineffective under applicable law, such
84−partial invalidity or ineffectiveness shall not invalidate the remainder
85−of the License, and in such case Affirmer hereby affirms that he or she
86−will not (i) exercise any of his or her remaining Copyright and Related
87−Rights in the Work or (ii) assert any associated claims and causes of
88−action with respect to the Work, in either case contrary to Affirmer's
89−express Statement of Purpose.
90−
91−4. Limitations and Disclaimers.
92−
93− a. No trademark or patent rights held by Affirmer are waived, abandoned,
94− surrendered, licensed or otherwise affected by this document.
95− b. Affirmer offers the Work as-is and makes no representations or
96− warranties of any kind concerning the Work, express, implied,
97− statutory or otherwise, including without limitation warranties of
98− title, merchantability, fitness for a particular purpose, non
99− infringement, or the absence of latent or other defects, accuracy, or
100− the present or absence of errors, whether or not discoverable, all to
101− the greatest extent permissible under applicable law.
102− c. Affirmer disclaims responsibility for clearing rights of other persons
103− that may apply to the Work or any use thereof, including without
104− limitation any person's Copyright and Related Rights in the Work.
105− Further, Affirmer disclaims responsibility for obtaining any necessary
106− consents, permissions or other rights required for any use of the
107− Work.
108− d. Affirmer understands and acknowledges that Creative Commons is not a
109− party to this document and has no duty or obligation with respect to
110− this CC0 or use of the Work.
111−
112−For more information, please see
113−<http://creativecommons.org/publicdomain/zero/1.0/>
parg/Doxyfile+0−10
@@ -1,10 +0,0 @@
1−# Doxyfile 1.8.6
2−PROJECT_NAME = "parg"
3−OUTPUT_DIRECTORY = doc
4−JAVADOC_AUTOBRIEF = YES
5−TAB_SIZE = 8
6−OPTIMIZE_OUTPUT_FOR_C = YES
7−EXTRACT_ALL = YES
8−INPUT = parg.h README.md
9−GENERATE_LATEX = NO
10−USE_MDFILE_AS_MAINPAGE = README.md
parg/README.md+0−205
@@ -1,205 +0,0 @@
1−
2−[![Build Status](https://travis-ci.org/jibsen/parg.svg?branch=master)](https://travis-ci.org/jibsen/parg) [![codecov.io](http://codecov.io/github/jibsen/parg/coverage.svg?branch=master)](http://codecov.io/github/jibsen/parg?branch=master)
3−
4−About
5−-----
6−
7−Most command-line programs have to parse options, so there are a lot of
8−different solutions to this problem. Some offer many features, while others
9−are more basic.
10−
11−One of the simpler solutions for C is the [getopt][] function, and its
12−extension `getopt_long`. They iterate over the options in `argv`, returning
13−them one at a time on successive calls.
14−
15−One nice thing about them is that they are available on most Unix-like
16−operating systems (and usually accompany GCC elsewhere, like Windows).
17−Unfortunately, some implementation details vary between platforms.
18−
19−A potential question is what license the version you get when you include
20−them is available under. Some are GPL, others LGPL. There are also ports of
21−`getopt` that use more liberal licenses.
22−
23−`parg` is a parser for `argv` that works similarly to `getopt`, but does not
24−aim to be a direct replacement. It attempts to make some choices about how to
25−handle the extensions and idiosyncrasies of other `getopt` implementations,
26−and document them.
27−
28−It consists of a single source and include file, written in portable ANSI C.
29−It is made available under [CC0][].
30−
31−[getopt]: https://en.wikipedia.org/wiki/Getopt
32−[CC0]: http://creativecommons.org/publicdomain/zero/1.0/
33−
34−
35−Usage
36−-----
37−
38−The include file `parg.h` contains documentation in the form of [doxygen][]
39−comments. A configuration file is included, so you can simply run `doxygen`
40−to generate documentation in HTML format.
41−
42−`parg` uses [CMake][] to generate build systems. To create one for the tools
43−on your platform, and build `parg`, use something along the lines of:
44−
45−~~~sh
46−mkdir build
47−cd build
48−cmake ..
49−cmake --build .
50−~~~
51−
52−You can also simply compile the source files and link them into your project.
53−CMake just provides an easy way to build and test across various platforms and
54−toolsets.
55−
56−[doxygen]: http://www.doxygen.org/
57−[CMake]: http://www.cmake.org/
58−
59−
60−Example
61−-------
62−
63−Here is an example that parses command-line options using `parg_getopt()`:
64−
65−~~~c
66−#include <stdio.h>
67−#include <stdlib.h>
68−
69−#include "parg.h"
70−
71−int main(int argc, char *argv[])
72−{
73− struct parg_state ps;
74− int c;
75−
76− parg_init(&ps);
77−
78− while ((c = parg_getopt(&ps, argc, argv, "hs:v")) != -1) {
79− switch (c) {
80− case 1:
81− printf("nonoption '%s'\n", ps.optarg);
82− break;
83− case 'h':
84− printf("Usage: testparg [-h] [-v] [-s STRING]\n");
85− return EXIT_SUCCESS;
86− break;
87− case 's':
88− printf("option -s with argument '%s'\n", ps.optarg);
89− break;
90− case 'v':
91− printf("testparg 1.0.0\n");
92− return EXIT_SUCCESS;
93− break;
94− case '?':
95− if (ps.optopt == 's') {
96− printf("option -s requires an argument\n");
97− }
98− else {
99− printf("unknown option -%c\n", ps.optopt);
100− }
101− return EXIT_FAILURE;
102− break;
103− default:
104− printf("error: unhandled option -%c\n", c);
105− return EXIT_FAILURE;
106− break;
107− }
108− }
109−
110− for (c = ps.optind; c < argc; ++c) {
111− printf("nonoption '%s'\n", argv[c]);
112− }
113−
114− return EXIT_SUCCESS;
115−}
116−~~~
117−
118−
119−Comparison to `getopt`
120−----------------------
121−
122−### Use of global variables
123−
124−`getopt` uses global variables to store its state between calls. `parg` uses
125−a struct `parg_state`, which you must pass with each call.
126−
127−### Handling of nonoptions
128−
129−POSIX and BSD `getopt` return `-1` on the first nonoption argument. GNU
130−`getopt` by default reorders `argv` (even though it is passed as const), so
131−all options come first.
132−
133−`parg` does not change `argv`, and returns each nonoption as the option
134−argument of an option with value `1` (like GNU `getopt`, if `optstring` were
135−prefixed by '`-`').
136−
137−If you wish to process all options first, and have the nonoptions ordered at
138−the end of `argv`, you can use `parg_reorder()`:
139−
140−~~~c
141− optend = parg_reorder(argc, argv, optstring, NULL);
142−
143− while ((c = parg_getopt(&ps, optend, argv, optstring)) != -1) {
144− /* ... */
145− }
146−
147− /* elements of argv[] from optend to argc are nonoptions */
148−~~~
149−
150−### Value of `optind` on error
151−
152−When there are multiple short options in one argument, `getopt` does not
153−increment `optind` until the last one is processed. This makes it harder to
154−tell which argument an unknown option came from (if `a` is an unknown option,
155−`-a` and `-ab` will return '`?`' with different values in `optind`).
156−
157−`parg` always increments the `optind` value in it's state so it points to the
158−next `argv` element to be processed. So when `parg` returns '`?`' (or '`:`'),
159−the element that contains the error is `argv[optind - 1]`.
160−
161−### Value of `optopt` on error
162−
163−With `getopt_long`, it varies what the values of `optopt` and `longindex` are
164−when an error is found with option arguments of long options. Sometimes these
165−values are not documented.
166−
167−`parg` sets `optopt` to `val` if `flag` is `NULL`, and `0` otherwise (which
168−equals the return value on successful match), and `longindex` is set to the
169−index of the entry in `longopts` that matched.
170−
171−### Return value on option argument error
172−
173−When the first character of `optstring` is '`:`', it varies what `getopt`
174−returns on extraneous option arguments.
175−
176−In this case, `parg` returns '`?`' if no option match is found, and '`:`' if
177−a match is found, but is missing a required argument, or has an extraneous
178−argument.
179−
180−
181−Alternatives
182−------------
183−
184−Some ports of `getopt`:
185−
186− - [Free Getopt](http://freegetopt.sourceforge.net/)
187− - [ya_getopt](http://github.com/kubo/ya_getopt/)
188− - [getopt_port](http://github.com/kimgr/getopt_port/)
189−
190−Other command-line parsing libraries that support C:
191−
192− - [Gengetopt](http://www.gnu.org/software/gengetopt/)
193− - [Argp](http://www.gnu.org/software/libc/manual/html_node/Argp.html)
194− - [popt](http://en.wikipedia.org/wiki/Popt)
195− - [argtable](http://argtable.sourceforge.net/)
196− - [optlist](http://michael.dipperstein.com/optlist/)
197− - [Arg_parser](http://www.nongnu.org/arg-parser/arg_parser.html)
198− - [Gopt](http://www.purposeful.co.uk/software/gopt/)
199− - [docopt](http://docopt.org/)
200−
201−A few C++ command-line parsing libraries:
202−
203− - [TCLAP](http://tclap.sourceforge.net/)
204− - [program_options](http://www.boost.org/doc/libs/1_58_0/doc/html/program_options.html)
205− - [CommandLine](http://llvm.org/docs/CommandLine.html)
parg/parg.c+0−354
@@ -1,354 +0,0 @@
1−/*
2− * parg - parse argv
3− *
4− * Written in 2015-2016 by Joergen Ibsen
5− *
6− * To the extent possible under law, the author(s) have dedicated all
7− * copyright and related and neighboring rights to this software to the
8− * public domain worldwide. This software is distributed without any
9− * warranty. <http://creativecommons.org/publicdomain/zero/1.0/>
10− */
11−
12−#include "parg.h"
13−
14−#include <assert.h>
15−#include <stdlib.h>
16−#include <string.h>
17−
18−/*
19− * Check if state is at end of argv.
20− */
21−static int
22−is_argv_end(const struct parg_state *ps, int argc, char *const argv[])
23−{
24− return ps->optind >= argc || argv[ps->optind] == NULL;
25−}
26−
27−/*
28− * Match nextchar against optstring.
29− */
30−static int
31−match_short(struct parg_state *ps, int argc, char *const argv[],
32− const char *optstring)
33−{
34− const char *p = strchr(optstring, *ps->nextchar);
35−
36− if (p == NULL) {
37− ps->optopt = *ps->nextchar++;
38− return '?';
39− }
40−
41− /* If no option argument, return option */
42− if (p[1] != ':') {
43− return *ps->nextchar++;
44− }
45−
46− /* If more characters, return as option argument */
47− if (ps->nextchar[1] != '\0') {
48− ps->optarg = &ps->nextchar[1];
49− ps->nextchar = NULL;
50− return *p;
51− }
52−
53− /* If option argument is optional, return option */
54− if (p[2] == ':') {
55− return *ps->nextchar++;
56− }
57−
58− /* Option argument required, so return next argv element */
59− if (is_argv_end(ps, argc, argv)) {
60− ps->optopt = *ps->nextchar++;
61− return optstring[0] == ':' ? ':' : '?';
62− }
63−
64− ps->optarg = argv[ps->optind++];
65− ps->nextchar = NULL;
66− return *p;
67−}
68−
69−/*
70− * Match string at nextchar against longopts.
71− */
72−static int
73−match_long(struct parg_state *ps, int argc, char *const argv[],
74− const char *optstring,
75− const struct parg_option *longopts, int *longindex)
76−{
77− size_t len;
78− int num_match = 0;
79− int match = -1;
80− int i;
81−
82− len = strcspn(ps->nextchar, "=");
83−
84− for (i = 0; longopts[i].name; ++i) {
85− if (strncmp(ps->nextchar, longopts[i].name, len) == 0) {
86− match = i;
87− num_match++;
88− /* Take if exact match */
89− if (longopts[i].name[len] == '\0') {
90− num_match = 1;
91− break;
92− }
93− }
94− }
95−
96− /* Return '?' on no or ambiguous match */
97− if (num_match != 1) {
98− ps->optopt = 0;
99− ps->nextchar = NULL;
100− return '?';
101− }
102−
103− assert(match != -1);
104−
105− if (longindex) {
106− *longindex = match;
107− }
108−
109− if (ps->nextchar[len] == '=') {
110− /* Option argument present, check if extraneous */
111− if (longopts[match].has_arg == PARG_NOARG) {
112− ps->optopt = longopts[match].flag ? 0 : longopts[match].val;
113− ps->nextchar = NULL;
114− return optstring[0] == ':' ? ':' : '?';
115− }
116− else {
117− ps->optarg = &ps->nextchar[len + 1];
118− }
119− }
120− else if (longopts[match].has_arg == PARG_REQARG) {
121− /* Option argument required, so return next argv element */
122− if (is_argv_end(ps, argc, argv)) {
123− ps->optopt = longopts[match].flag ? 0 : longopts[match].val;
124− ps->nextchar = NULL;
125− return optstring[0] == ':' ? ':' : '?';
126− }
127−
128− ps->optarg = argv[ps->optind++];
129− }
130−
131− ps->nextchar = NULL;
132−
133− if (longopts[match].flag != NULL) {
134− *longopts[match].flag = longopts[match].val;
135− return 0;
136− }
137−
138− return longopts[match].val;
139−}
140−
141−void
142−parg_init(struct parg_state *ps)
143−{
144− ps->optarg = NULL;
145− ps->optind = 1;
146− ps->optopt = '?';
147− ps->nextchar = NULL;
148−}
149−
150−int
151−parg_getopt(struct parg_state *ps, int argc, char *const argv[],
152− const char *optstring)
153−{
154− return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL);
155−}
156−
157−int
158−parg_getopt_long(struct parg_state *ps, int argc, char *const argv[],
159− const char *optstring,
160− const struct parg_option *longopts, int *longindex)
161−{
162− assert(ps != NULL);
163− assert(argv != NULL);
164− assert(optstring != NULL);
165−
166− ps->optarg = NULL;
167−
168− if (argc < 2) {
169− return -1;
170− }
171−
172− /* Advance to next element if needed */
173− if (ps->nextchar == NULL || *ps->nextchar == '\0') {
174− if (is_argv_end(ps, argc, argv)) {
175− return -1;
176− }
177−
178− ps->nextchar = argv[ps->optind++];
179−
180− /* Check for nonoption element (including '-') */
181− if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') {
182− ps->optarg = ps->nextchar;
183− ps->nextchar = NULL;
184− return 1;
185− }
186−
187− /* Check for '--' */
188− if (ps->nextchar[1] == '-') {
189− if (ps->nextchar[2] == '\0') {
190− ps->nextchar = NULL;
191− return -1;
192− }
193−
194− if (longopts != NULL) {
195− ps->nextchar += 2;
196−
197− return match_long(ps, argc, argv, optstring,
198− longopts, longindex);
199− }
200− }
201−
202− ps->nextchar++;
203− }
204−
205− /* Match nextchar */
206− return match_short(ps, argc, argv, optstring);
207−}
208−
209−/*
210− * Reverse elements of `v` from `i` to `j`.
211− */
212−static void
213−reverse(char *v[], int i, int j)
214−{
215− while (j - i > 1) {
216− char *tmp = v[i];
217− v[i] = v[j - 1];
218− v[j - 1] = tmp;
219− ++i;
220− --j;
221− }
222−}
223−
224−/*
225− * Reorder elements of `argv` with no special cases.
226− *
227− * This function assumes there is no `--` element, and the last element
228− * is not an option missing a required argument.
229− *
230− * The algorithm is described here:
231− * http://hardtoc.com/2016/11/07/reordering-arguments.html
232− */
233−static int
234−parg_reorder_simple(int argc, char *argv[],
235− const char *optstring,
236− const struct parg_option *longopts)
237−{
238− struct parg_state ps;
239− int change;
240− int l = 0;
241− int m = 0;
242− int r = 0;
243−
244− if (argc < 2) {
245− return argc;
246− }
247−
248− do {
249− int nextind;
250− int c;
251−
252− parg_init(&ps);
253−
254− nextind = ps.optind;
255−
256− /* Parse until end of argument */
257− do {
258− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
259− } while (ps.nextchar != NULL && *ps.nextchar != '\0');
260−
261− change = 0;
262−
263− do {
264− /* Find next non-option */
265− for (l = nextind; c != 1 && c != -1;) {
266− l = ps.optind;
267−
268− do {
269− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
270− } while (ps.nextchar != NULL && *ps.nextchar != '\0');
271− }
272−
273− /* Find next option */
274− for (m = l; c == 1;) {
275− m = ps.optind;
276−
277− do {
278− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
279− } while (ps.nextchar != NULL && *ps.nextchar != '\0');
280− }
281−
282− /* Find next non-option */
283− for (r = m; c != 1 && c != -1;) {
284− r = ps.optind;
285−
286− do {
287− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
288− } while (ps.nextchar != NULL && *ps.nextchar != '\0');
289− }
290−
291− /* Find next option */
292− for (nextind = r; c == 1;) {
293− nextind = ps.optind;
294−
295− do {
296− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
297− } while (ps.nextchar != NULL && *ps.nextchar != '\0');
298− }
299−
300− if (m < r) {
301− change = 1;
302− reverse(argv, l, m);
303− reverse(argv, m, r);
304− reverse(argv, l, r);
305− }
306− } while (c != -1);
307− } while (change != 0);
308−
309− return l + (r - m);
310−}
311−
312−int
313−parg_reorder(int argc, char *argv[],
314− const char *optstring,
315− const struct parg_option *longopts)
316−{
317− struct parg_state ps;
318− int lastind;
319− int optend;
320− int c;
321−
322− assert(argv != NULL);
323− assert(optstring != NULL);
324−
325− if (argc < 2) {
326− return argc;
327− }
328−
329− parg_init(&ps);
330−
331− /* Find end of normal arguments */
332− do {
333− lastind = ps.optind;
334−
335− c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
336−
337− /* Check for trailing option with error */
338− if ((c == '?' || c == ':') && is_argv_end(&ps, argc, argv)) {
339− lastind = ps.optind - 1;
340− break;
341− }
342− } while (c != -1);
343−
344− optend = parg_reorder_simple(lastind, argv, optstring, longopts);
345−
346− /* Rotate `--` or trailing option with error into position */
347− if (lastind < argc) {
348− reverse(argv, optend, lastind);
349− reverse(argv, optend, lastind + 1);
350− ++optend;
351− }
352−
353− return optend;
354−}
parg/parg.h+0−192
@@ -1,192 +0,0 @@
1−/*
2− * parg - parse argv
3− *
4− * Written in 2015-2016 by Joergen Ibsen
5− *
6− * To the extent possible under law, the author(s) have dedicated all
7− * copyright and related and neighboring rights to this software to the
8− * public domain worldwide. This software is distributed without any
9− * warranty. <http://creativecommons.org/publicdomain/zero/1.0/>
10− */
11−
12−#ifndef PARG_H_INCLUDED
13−#define PARG_H_INCLUDED
14−
15−#ifdef __cplusplus
16−extern "C" {
17−#endif
18−
19−#define PARG_VER_MAJOR 1 /**< Major version number */
20−#define PARG_VER_MINOR 0 /**< Minor version number */
21−#define PARG_VER_PATCH 2 /**< Patch version number */
22−#define PARG_VER_STRING "1.0.2" /**< Version number as a string */
23−
24−/**
25− * Structure containing state between calls to parser.
26− *
27− * @see parg_init
28− */
29−struct parg_state {
30− const char *optarg; /**< Pointer to option argument, if any */
31− int optind; /**< Next index in argv to process */
32− int optopt; /**< Option value resulting in error, if any */
33− const char *nextchar; /**< Next character to process */
34−};
35−
36−/**
37− * Structure for supplying long options to `parg_getopt_long()`.
38− *
39− * @see parg_getopt_long
40− */
41−struct parg_option {
42− const char *name; /**< Name of option */
43− int has_arg; /**< Option argument status */
44− int *flag; /**< Pointer to flag variable */
45− int val; /**< Value of option */
46−};
47−
48−/**
49− * Values for `has_arg` flag in `parg_option`.
50− *
51− * @see parg_option
52− */
53−typedef enum {
54− PARG_NOARG, /**< No argument */
55− PARG_REQARG, /**< Required argument */
56− PARG_OPTARG /**< Optional argument */
57−} parg_arg_num;
58−
59−/**
60− * Initialize `ps`.
61− *
62− * Must be called before using state with a parser.
63− *
64− * @see parg_state
65− *
66− * @param ps pointer to state
67− */
68−void
69−parg_init(struct parg_state *ps);
70−
71−/**
72− * Parse next short option in `argv`.
73− *
74− * Elements in `argv` that contain short options start with a single dash
75− * followed by one or more option characters, and optionally an option
76− * argument for the last option character. Examples are '`-d`', '`-ofile`',
77− * and '`-dofile`'.
78− *
79− * Consecutive calls to this function match the command-line arguments in
80− * `argv` against the short option characters in `optstring`.
81− *
82− * If an option character in `optstring` is followed by a colon, '`:`', the
83− * option requires an argument. If it is followed by two colons, the option
84− * may take an optional argument.
85− *
86− * If a match is found, `optarg` points to the option argument, if any, and
87− * the value of the option character is returned.
88− *
89− * If a match is found, but is missing a required option argument, `optopt`
90− * is set to the option character. If the first character in `optstring` is
91− * '`:`', then '`:`' is returned, otherwise '`?`' is returned.
92− *
93− * If no option character in `optstring` matches a short option, `optopt`
94− * is set to the option character, and '`?`' is returned.
95− *
96− * If an element of argv does not contain options (a nonoption element),
97− * `optarg` points to the element, and `1` is returned.
98− *
99− * An element consisting of a single dash, '`-`', is returned as a nonoption.
100− *
101− * Parsing stops and `-1` is returned, when the end of `argv` is reached, or
102− * if an element contains '`--`'.
103− *
104− * Works similarly to `getopt`, if `optstring` were prefixed by '`-`'.
105− *
106− * @param ps pointer to state
107− * @param argc number of elements in `argv`
108− * @param argv array of pointers to command-line arguments
109− * @param optstring string containing option characters
110− * @return option value on match, `1` on nonoption element, `-1` on end of
111− * arguments, '`?`' on unmatched option, '`?`' or '`:`' on option argument
112− * error
113− */
114−int
115−parg_getopt(struct parg_state *ps, int argc, char *const argv[],
116− const char *optstring);
117−
118−/**
119− * Parse next long or short option in `argv`.
120− *
121− * Elements in `argv` that contain a long option start with two dashes
122− * followed by a string, and optionally an equal sign and an option argument.
123− * Examples are '`--help`' and '`--size=5`'.
124− *
125− * If no exact match is found, an unambiguous prefix of a long option will
126− * match. For example, if '`foo`' and '`foobar`' are valid long options, then
127− * '`--fo`' is ambiguous and will not match, '`--foo`' matches exactly, and
128− * '`--foob`' is an unambiguous prefix and will match.
129− *
130− * If a long option match is found, and `flag` is `NULL`, `val` is returned.
131− *
132− * If a long option match is found, and `flag` is not `NULL`, `val` is stored
133− * in the variable `flag` points to, and `0` is returned.
134− *
135− * If a long option match is found, but is missing a required option argument,
136− * or has an option argument even though it takes none, `optopt` is set to
137− * `val` if `flag` is `NULL`, and `0` otherwise. If the first character in
138− * `optstring` is '`:`', then '`:`' is returned, otherwise '`?`' is returned.
139− *
140− * If `longindex` is not `NULL`, the index of the entry in `longopts` that
141− * matched is stored there.
142− *
143− * If no long option in `longopts` matches a long option, '`?`' is returned.
144− *
145− * Handling of nonoptions and short options is like `parg_getopt()`.
146− *
147− * If no short options are required, an empty string, `""`, should be passed
148− * as `optstring`.
149− *
150− * Works similarly to `getopt_long`, if `optstring` were prefixed by '`-`'.
151− *
152− * @see parg_getopt
153− *
154− * @param ps pointer to state
155− * @param argc number of elements in `argv`
156− * @param argv array of pointers to command-line arguments
157− * @param optstring string containing option characters
158− * @param longopts array of `parg_option` structures
159− * @param longindex pointer to variable to store index of matching option in
160− * @return option value on match, `0` for flag option, `1` on nonoption
161− * element, `-1` on end of arguments, '`?`' on unmatched or ambiguous option,
162− * '`?`' or '`:`' on option argument error
163− */
164−int
165−parg_getopt_long(struct parg_state *ps, int argc, char *const argv[],
166− const char *optstring,
167− const struct parg_option *longopts, int *longindex);
168−
169−/**
170− * Reorder elements of `argv` so options appear first.
171− *
172− * If there are no long options, `longopts` may be `NULL`.
173− *
174− * The return value can be used as `argc` parameter for `parg_getopt()` and
175− * `parg_getopt_long()`.
176− *
177− * @param argc number of elements in `argv`
178− * @param argv array of pointers to command-line arguments
179− * @param optstring string containing option characters
180− * @param longopts array of `parg_option` structures
181− * @return index of first nonoption in `argv` on success, `-1` on error
182− */
183−int
184−parg_reorder(int argc, char *argv[],
185− const char *optstring,
186− const struct parg_option *longopts);
187−
188−#ifdef __cplusplus
189−} /* extern "C" */
190−#endif
191−
192−#endif /* PARG_H_INCLUDED */
parg/test/greatest.h+0−1035
@@ -1,1035 +0,0 @@
1−/*
2− * Copyright (c) 2011-2016 Scott Vokes <vokes.s@gmail.com>
3− *
4− * Permission to use, copy, modify, and/or distribute this software for any
5− * purpose with or without fee is hereby granted, provided that the above
6− * copyright notice and this permission notice appear in all copies.
7− *
8− * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9− * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10− * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11− * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12− * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13− * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14− * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15− */
16−
17−#ifndef GREATEST_H
18−#define GREATEST_H
19−
20−#ifdef __cplusplus
21−extern "C" {
22−#endif
23−
24−/* 1.2.1 */
25−#define GREATEST_VERSION_MAJOR 1
26−#define GREATEST_VERSION_MINOR 2
27−#define GREATEST_VERSION_PATCH 1
28−
29−/* A unit testing system for C, contained in 1 file.
30− * It doesn't use dynamic allocation or depend on anything
31− * beyond ANSI C89.
32− *
33− * An up-to-date version can be found at:
34− * https://github.com/silentbicycle/greatest/
35− */
36−
37−
38−/*********************************************************************
39− * Minimal test runner template
40− *********************************************************************/
41−#if 0
42−
43−#include "greatest.h"
44−
45−TEST foo_should_foo(void) {
46− PASS();
47−}
48−
49−static void setup_cb(void *data) {
50− printf("setup callback for each test case\n");
51−}
52−
53−static void teardown_cb(void *data) {
54− printf("teardown callback for each test case\n");
55−}
56−
57−SUITE(suite) {
58− /* Optional setup/teardown callbacks which will be run before/after
59− * every test case. If using a test suite, they will be cleared when
60− * the suite finishes. */
61− SET_SETUP(setup_cb, voidp_to_callback_data);
62− SET_TEARDOWN(teardown_cb, voidp_to_callback_data);
63−
64− RUN_TEST(foo_should_foo);
65−}
66−
67−/* Add definitions that need to be in the test runner's main file. */
68−GREATEST_MAIN_DEFS();
69−
70−/* Set up, run suite(s) of tests, report pass/fail/skip stats. */
71−int run_tests(void) {
72− GREATEST_INIT(); /* init. greatest internals */
73− /* List of suites to run (if any). */
74− RUN_SUITE(suite);
75−
76− /* Tests can also be run directly, without using test suites. */
77− RUN_TEST(foo_should_foo);
78−
79− GREATEST_PRINT_REPORT(); /* display results */
80− return greatest_all_passed();
81−}
82−
83−/* main(), for a standalone command-line test runner.
84− * This replaces run_tests above, and adds command line option
85− * handling and exiting with a pass/fail status. */
86−int main(int argc, char **argv) {
87− GREATEST_MAIN_BEGIN(); /* init & parse command-line args */
88− RUN_SUITE(suite);
89− GREATEST_MAIN_END(); /* display results */
90−}
91−
92−#endif
93−/*********************************************************************/
94−
95−
96−#include <stdlib.h>
97−#include <stdio.h>
98−#include <string.h>
99−#include <ctype.h>
100−
101−/***********
102− * Options *
103− ***********/
104−
105−/* Default column width for non-verbose output. */
106−#ifndef GREATEST_DEFAULT_WIDTH
107−#define GREATEST_DEFAULT_WIDTH 72
108−#endif
109−
110−/* FILE *, for test logging. */
111−#ifndef GREATEST_STDOUT
112−#define GREATEST_STDOUT stdout
113−#endif
114−
115−/* Remove GREATEST_ prefix from most commonly used symbols? */
116−#ifndef GREATEST_USE_ABBREVS
117−#define GREATEST_USE_ABBREVS 1
118−#endif
119−
120−/* Set to 0 to disable all use of setjmp/longjmp. */
121−#ifndef GREATEST_USE_LONGJMP
122−#define GREATEST_USE_LONGJMP 1
123−#endif
124−
125−#if GREATEST_USE_LONGJMP
126−#include <setjmp.h>
127−#endif
128−
129−/* Set to 0 to disable all use of time.h / clock(). */
130−#ifndef GREATEST_USE_TIME
131−#define GREATEST_USE_TIME 1
132−#endif
133−
134−#if GREATEST_USE_TIME
135−#include <time.h>
136−#endif
137−
138−/* Floating point type, for ASSERT_IN_RANGE. */
139−#ifndef GREATEST_FLOAT
140−#define GREATEST_FLOAT double
141−#define GREATEST_FLOAT_FMT "%g"
142−#endif
143−
144−/*********
145− * Types *
146− *********/
147−
148−/* Info for the current running suite. */
149−typedef struct greatest_suite_info {
150− unsigned int tests_run;
151− unsigned int passed;
152− unsigned int failed;
153− unsigned int skipped;
154−
155−#if GREATEST_USE_TIME
156− /* timers, pre/post running suite and individual tests */
157− clock_t pre_suite;
158− clock_t post_suite;
159− clock_t pre_test;
160− clock_t post_test;
161−#endif
162−} greatest_suite_info;
163−
164−/* Type for a suite function. */
165−typedef void (greatest_suite_cb)(void);
166−
167−/* Types for setup/teardown callbacks. If non-NULL, these will be run
168− * and passed the pointer to their additional data. */
169−typedef void (greatest_setup_cb)(void *udata);
170−typedef void (greatest_teardown_cb)(void *udata);
171−
172−/* Type for an equality comparison between two pointers of the same type.
173− * Should return non-0 if equal, otherwise 0.
174− * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */
175−typedef int greatest_equal_cb(const void *exp, const void *got, void *udata);
176−
177−/* Type for a callback that prints a value pointed to by T.
178− * Return value has the same meaning as printf's.
179− * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */
180−typedef int greatest_printf_cb(const void *t, void *udata);
181−
182−/* Callbacks for an arbitrary type; needed for type-specific
183− * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/
184−typedef struct greatest_type_info {
185− greatest_equal_cb *equal;
186− greatest_printf_cb *print;
187−} greatest_type_info;
188−
189−typedef struct greatest_memory_cmp_env {
190− const unsigned char *exp;
191− const unsigned char *got;
192− size_t size;
193−} greatest_memory_cmp_env;
194−
195−/* Callbacks for string and raw memory types. */
196−extern greatest_type_info greatest_type_info_string;
197−extern greatest_type_info greatest_type_info_memory;
198−
199−typedef enum {
200− GREATEST_FLAG_FIRST_FAIL = 0x01,
201− GREATEST_FLAG_LIST_ONLY = 0x02
202−} greatest_flag_t;
203−
204−/* Struct containing all test runner state. */
205−typedef struct greatest_run_info {
206− unsigned char flags;
207− unsigned char verbosity;
208− unsigned int tests_run; /* total test count */
209−
210− /* overall pass/fail/skip counts */
211− unsigned int passed;
212− unsigned int failed;
213− unsigned int skipped;
214− unsigned int assertions;
215−
216− /* currently running test suite */
217− greatest_suite_info suite;
218−
219− /* info to print about the most recent failure */
220− const char *fail_file;
221− unsigned int fail_line;
222− const char *msg;
223−
224− /* current setup/teardown hooks and userdata */
225− greatest_setup_cb *setup;
226− void *setup_udata;
227− greatest_teardown_cb *teardown;
228− void *teardown_udata;
229−
230− /* formatting info for ".....s...F"-style output */
231− unsigned int col;
232− unsigned int width;
233−
234− /* only run a specific suite or test */
235− const char *suite_filter;
236− const char *test_filter;
237−
238−#if GREATEST_USE_TIME
239− /* overall timers */
240− clock_t begin;
241− clock_t end;
242−#endif
243−
244−#if GREATEST_USE_LONGJMP
245− jmp_buf jump_dest;
246−#endif
247−} greatest_run_info;
248−
249−struct greatest_report_t {
250− /* overall pass/fail/skip counts */
251− unsigned int passed;
252− unsigned int failed;
253− unsigned int skipped;
254− unsigned int assertions;
255−};
256−
257−/* Global var for the current testing context.
258− * Initialized by GREATEST_MAIN_DEFS(). */
259−extern greatest_run_info greatest_info;
260−
261−/* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */
262−typedef const char *greatest_enum_str_fun(int value);
263−
264−/**********************
265− * Exported functions *
266− **********************/
267−
268−/* These are used internally by greatest. */
269−void greatest_do_pass(const char *name);
270−void greatest_do_fail(const char *name);
271−void greatest_do_skip(const char *name);
272−int greatest_pre_test(const char *name);
273−void greatest_post_test(const char *name, int res);
274−void greatest_usage(const char *name);
275−int greatest_do_assert_equal_t(const void *exp, const void *got,
276− greatest_type_info *type_info, void *udata);
277−
278−/* These are part of the public greatest API. */
279−void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata);
280−void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata);
281−int greatest_all_passed(void);
282−void greatest_set_test_filter(const char *name);
283−void greatest_set_suite_filter(const char *name);
284−void greatest_get_report(struct greatest_report_t *report);
285−unsigned int greatest_get_verbosity(void);
286−void greatest_set_verbosity(unsigned int verbosity);
287−void greatest_set_flag(greatest_flag_t flag);
288−
289−
290−/********************
291−* Language Support *
292−********************/
293−
294−/* If __VA_ARGS__ (C99) is supported, allow parametric testing
295−* without needing to manually manage the argument struct. */
296−#if __STDC_VERSION__ >= 19901L || _MSC_VER >= 1800
297−#define GREATEST_VA_ARGS
298−#endif
299−
300−
301−/**********
302− * Macros *
303− **********/
304−
305−/* Define a suite. */
306−#define GREATEST_SUITE(NAME) void NAME(void); void NAME(void)
307−
308−/* Declare a suite, provided by another compilation unit. */
309−#define GREATEST_SUITE_EXTERN(NAME) void NAME(void)
310−
311−/* Start defining a test function.
312− * The arguments are not included, to allow parametric testing. */
313−#define GREATEST_TEST static enum greatest_test_res
314−
315−/* PASS/FAIL/SKIP result from a test. Used internally. */
316−typedef enum greatest_test_res {
317− GREATEST_TEST_RES_PASS = 0,
318− GREATEST_TEST_RES_FAIL = -1,
319− GREATEST_TEST_RES_SKIP = 1
320−} greatest_test_res;
321−
322−/* Run a suite. */
323−#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME)
324−
325−/* Run a test in the current suite. */
326−#define GREATEST_RUN_TEST(TEST) \
327− do { \
328− if (greatest_pre_test(#TEST) == 1) { \
329− enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \
330− if (res == GREATEST_TEST_RES_PASS) { \
331− res = TEST(); \
332− } \
333− greatest_post_test(#TEST, res); \
334− } else if (GREATEST_LIST_ONLY()) { \
335− fprintf(GREATEST_STDOUT, " %s\n", #TEST); \
336− } \
337− } while (0)
338−
339−/* Ignore a test, don't warn about it being unused. */
340−#define GREATEST_IGNORE_TEST(TEST) (void)TEST
341−
342−/* Run a test in the current suite with one void * argument,
343− * which can be a pointer to a struct with multiple arguments. */
344−#define GREATEST_RUN_TEST1(TEST, ENV) \
345− do { \
346− if (greatest_pre_test(#TEST) == 1) { \
347− int res = TEST(ENV); \
348− greatest_post_test(#TEST, res); \
349− } else if (GREATEST_LIST_ONLY()) { \
350− fprintf(GREATEST_STDOUT, " %s\n", #TEST); \
351− } \
352− } while (0)
353−
354−#ifdef GREATEST_VA_ARGS
355−#define GREATEST_RUN_TESTp(TEST, ...) \
356− do { \
357− if (greatest_pre_test(#TEST) == 1) { \
358− int res = TEST(__VA_ARGS__); \
359− greatest_post_test(#TEST, res); \
360− } else if (GREATEST_LIST_ONLY()) { \
361− fprintf(GREATEST_STDOUT, " %s\n", #TEST); \
362− } \
363− } while (0)
364−#endif
365−
366−
367−/* Check if the test runner is in verbose mode. */
368−#define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0)
369−#define GREATEST_LIST_ONLY() \
370− (greatest_info.flags & GREATEST_FLAG_LIST_ONLY)
371−#define GREATEST_FIRST_FAIL() \
372− (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL)
373−#define GREATEST_FAILURE_ABORT() \
374− (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL())
375−
376−/* Message-less forms of tests defined below. */
377−#define GREATEST_PASS() GREATEST_PASSm(NULL)
378−#define GREATEST_FAIL() GREATEST_FAILm(NULL)
379−#define GREATEST_SKIP() GREATEST_SKIPm(NULL)
380−#define GREATEST_ASSERT(COND) \
381− GREATEST_ASSERTm(#COND, COND)
382−#define GREATEST_ASSERT_OR_LONGJMP(COND) \
383− GREATEST_ASSERT_OR_LONGJMPm(#COND, COND)
384−#define GREATEST_ASSERT_FALSE(COND) \
385− GREATEST_ASSERT_FALSEm(#COND, COND)
386−#define GREATEST_ASSERT_EQ(EXP, GOT) \
387− GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT)
388−#define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \
389− GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT)
390−#define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \
391− GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL)
392−#define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \
393− GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA)
394−#define GREATEST_ASSERT_STR_EQ(EXP, GOT) \
395− GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT)
396−#define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \
397− GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE)
398−#define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \
399− GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE)
400−#define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \
401− GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR)
402−
403−/* The following forms take an additional message argument first,
404− * to be displayed by the test runner. */
405−
406−/* Fail if a condition is not true, with message. */
407−#define GREATEST_ASSERTm(MSG, COND) \
408− do { \
409− greatest_info.assertions++; \
410− if (!(COND)) { GREATEST_FAILm(MSG); } \
411− } while (0)
412−
413−/* Fail if a condition is not true, longjmping out of test. */
414−#define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \
415− do { \
416− greatest_info.assertions++; \
417− if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \
418− } while (0)
419−
420−/* Fail if a condition is not false, with message. */
421−#define GREATEST_ASSERT_FALSEm(MSG, COND) \
422− do { \
423− greatest_info.assertions++; \
424− if ((COND)) { GREATEST_FAILm(MSG); } \
425− } while (0)
426−
427−/* Fail if EXP != GOT (equality comparison by ==). */
428−#define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \
429− do { \
430− greatest_info.assertions++; \
431− if ((EXP) != (GOT)) { GREATEST_FAILm(MSG); } \
432− } while (0)
433−
434−/* Fail if EXP != GOT (equality comparison by ==).
435− * Warning: EXP and GOT will be evaluated more than once on failure. */
436−#define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \
437− do { \
438− const char *greatest_FMT = ( FMT ); \
439− greatest_info.assertions++; \
440− if ((EXP) != (GOT)) { \
441− fprintf(GREATEST_STDOUT, "\nExpected: "); \
442− fprintf(GREATEST_STDOUT, greatest_FMT, EXP); \
443− fprintf(GREATEST_STDOUT, "\n Got: "); \
444− fprintf(GREATEST_STDOUT, greatest_FMT, GOT); \
445− fprintf(GREATEST_STDOUT, "\n"); \
446− GREATEST_FAILm(MSG); \
447− } \
448− } while (0)
449−
450−/* Fail if EXP is not equal to GOT, printing enum IDs. */
451−#define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \
452− do { \
453− int greatest_EXP = (int)(EXP); \
454− int greatest_GOT = (int)(GOT); \
455− greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \
456− if (greatest_EXP != greatest_GOT) { \
457− fprintf(GREATEST_STDOUT, "\nExpected: %s", \
458− greatest_ENUM_STR(greatest_EXP)); \
459− fprintf(GREATEST_STDOUT, "\n Got: %s\n", \
460− greatest_ENUM_STR(greatest_GOT)); \
461− GREATEST_FAILm(MSG); \
462− } \
463− } while (0) \
464−
465−/* Fail if GOT not in range of EXP +|- TOL. */
466−#define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \
467− do { \
468− GREATEST_FLOAT greatest_EXP = (EXP); \
469− GREATEST_FLOAT greatest_GOT = (GOT); \
470− GREATEST_FLOAT greatest_TOL = (TOL); \
471− greatest_info.assertions++; \
472− if ((greatest_EXP > greatest_GOT && \
473− greatest_EXP - greatest_GOT > greatest_TOL) || \
474− (greatest_EXP < greatest_GOT && \
475− greatest_GOT - greatest_EXP > greatest_TOL)) { \
476− fprintf(GREATEST_STDOUT, \
477− "\nExpected: " GREATEST_FLOAT_FMT \
478− " +/- " GREATEST_FLOAT_FMT \
479− "\n Got: " GREATEST_FLOAT_FMT \
480− "\n", \
481− greatest_EXP, greatest_TOL, greatest_GOT); \
482− GREATEST_FAILm(MSG); \
483− } \
484− } while (0)
485−
486−/* Fail if EXP is not equal to GOT, according to strcmp. */
487−#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \
488− do { \
489− GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \
490− &greatest_type_info_string, NULL); \
491− } while (0) \
492−
493−/* Fail if EXP is not equal to GOT, according to strcmp. */
494−#define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \
495− do { \
496− size_t size = SIZE; \
497− GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \
498− &greatest_type_info_string, &size); \
499− } while (0) \
500−
501−/* Fail if EXP is not equal to GOT, according to memcmp. */
502−#define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \
503− do { \
504− greatest_memory_cmp_env env; \
505− env.exp = (const unsigned char *)EXP; \
506− env.got = (const unsigned char *)GOT; \
507− env.size = SIZE; \
508− GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \
509− &greatest_type_info_memory, &env); \
510− } while (0) \
511−
512−/* Fail if EXP is not equal to GOT, according to a comparison
513− * callback in TYPE_INFO. If they are not equal, optionally use a
514− * print callback in TYPE_INFO to print them. */
515−#define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \
516− do { \
517− greatest_type_info *type_info = (TYPE_INFO); \
518− greatest_info.assertions++; \
519− if (!greatest_do_assert_equal_t(EXP, GOT, \
520− type_info, UDATA)) { \
521− if (type_info == NULL || type_info->equal == NULL) { \
522− GREATEST_FAILm("type_info->equal callback missing!"); \
523− } else { \
524− GREATEST_FAILm(MSG); \
525− } \
526− } \
527− } while (0) \
528−
529−/* Pass. */
530−#define GREATEST_PASSm(MSG) \
531− do { \
532− greatest_info.msg = MSG; \
533− return GREATEST_TEST_RES_PASS; \
534− } while (0)
535−
536−/* Fail. */
537−#define GREATEST_FAILm(MSG) \
538− do { \
539− greatest_info.fail_file = __FILE__; \
540− greatest_info.fail_line = __LINE__; \
541− greatest_info.msg = MSG; \
542− return GREATEST_TEST_RES_FAIL; \
543− } while (0)
544−
545−/* Optional GREATEST_FAILm variant that longjmps. */
546−#if GREATEST_USE_LONGJMP
547−#define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL)
548−#define GREATEST_FAIL_WITH_LONGJMPm(MSG) \
549− do { \
550− greatest_info.fail_file = __FILE__; \
551− greatest_info.fail_line = __LINE__; \
552− greatest_info.msg = MSG; \
553− longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \
554− } while (0)
555−#endif
556−
557−/* Skip the current test. */
558−#define GREATEST_SKIPm(MSG) \
559− do { \
560− greatest_info.msg = MSG; \
561− return GREATEST_TEST_RES_SKIP; \
562− } while (0)
563−
564−/* Check the result of a subfunction using ASSERT, etc. */
565−#define GREATEST_CHECK_CALL(RES) \
566− do { \
567− enum greatest_test_res greatest_RES = RES; \
568− if (greatest_RES != GREATEST_TEST_RES_PASS) { \
569− return greatest_RES; \
570− } \
571− } while (0) \
572−
573−#if GREATEST_USE_TIME
574−#define GREATEST_SET_TIME(NAME) \
575− NAME = clock(); \
576− if (NAME == (clock_t) -1) { \
577− fprintf(GREATEST_STDOUT, \
578− "clock error: %s\n", #NAME); \
579− exit(EXIT_FAILURE); \
580− }
581−
582−#define GREATEST_CLOCK_DIFF(C1, C2) \
583− fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \
584− (long unsigned int) (C2) - (long unsigned int)(C1), \
585− (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC))
586−#else
587−#define GREATEST_SET_TIME(UNUSED)
588−#define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2)
589−#endif
590−
591−#if GREATEST_USE_LONGJMP
592−#define GREATEST_SAVE_CONTEXT() \
593− /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call */ \
594− /* so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \
595− ((enum greatest_test_res)(setjmp(greatest_info.jump_dest)))
596−#else
597−#define GREATEST_SAVE_CONTEXT() \
598− /*a no-op, since setjmp/longjmp aren't being used */ \
599− GREATEST_TEST_RES_PASS
600−#endif
601−
602−/* Include several function definitions in the main test file. */
603−#define GREATEST_MAIN_DEFS() \
604− \
605−/* Is FILTER a subset of NAME? */ \
606−static int greatest_name_match(const char *name, \
607− const char *filter) { \
608− size_t offset = 0; \
609− size_t filter_len = strlen(filter); \
610− while (name[offset] != '\0') { \
611− if (name[offset] == filter[0]) { \
612− if (0 == strncmp(&name[offset], filter, filter_len)) { \
613− return 1; \
614− } \
615− } \
616− offset++; \
617− } \
618− \
619− return 0; \
620−} \
621− \
622−int greatest_pre_test(const char *name) { \
623− if (!GREATEST_LIST_ONLY() \
624− && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \
625− && (greatest_info.test_filter == NULL || \
626− greatest_name_match(name, greatest_info.test_filter))) { \
627− GREATEST_SET_TIME(greatest_info.suite.pre_test); \
628− if (greatest_info.setup) { \
629− greatest_info.setup(greatest_info.setup_udata); \
630− } \
631− return 1; /* test should be run */ \
632− } else { \
633− return 0; /* skipped */ \
634− } \
635−} \
636− \
637−void greatest_post_test(const char *name, int res) { \
638− GREATEST_SET_TIME(greatest_info.suite.post_test); \
639− if (greatest_info.teardown) { \
640− void *udata = greatest_info.teardown_udata; \
641− greatest_info.teardown(udata); \
642− } \
643− \
644− if (res <= GREATEST_TEST_RES_FAIL) { \
645− greatest_do_fail(name); \
646− } else if (res >= GREATEST_TEST_RES_SKIP) { \
647− greatest_do_skip(name); \
648− } else if (res == GREATEST_TEST_RES_PASS) { \
649− greatest_do_pass(name); \
650− } \
651− greatest_info.suite.tests_run++; \
652− greatest_info.col++; \
653− if (GREATEST_IS_VERBOSE()) { \
654− GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \
655− greatest_info.suite.post_test); \
656− fprintf(GREATEST_STDOUT, "\n"); \
657− } else if (greatest_info.col % greatest_info.width == 0) { \
658− fprintf(GREATEST_STDOUT, "\n"); \
659− greatest_info.col = 0; \
660− } \
661− if (GREATEST_STDOUT == stdout) fflush(stdout); \
662−} \
663− \
664−static void report_suite(void) { \
665− if (greatest_info.suite.tests_run > 0) { \
666− fprintf(GREATEST_STDOUT, \
667− "\n%u test%s - %u passed, %u failed, %u skipped", \
668− greatest_info.suite.tests_run, \
669− greatest_info.suite.tests_run == 1 ? "" : "s", \
670− greatest_info.suite.passed, \
671− greatest_info.suite.failed, \
672− greatest_info.suite.skipped); \
673− GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \
674− greatest_info.suite.post_suite); \
675− fprintf(GREATEST_STDOUT, "\n"); \
676− } \
677−} \
678− \
679−static void update_counts_and_reset_suite(void) { \
680− greatest_info.setup = NULL; \
681− greatest_info.setup_udata = NULL; \
682− greatest_info.teardown = NULL; \
683− greatest_info.teardown_udata = NULL; \
684− greatest_info.passed += greatest_info.suite.passed; \
685− greatest_info.failed += greatest_info.suite.failed; \
686− greatest_info.skipped += greatest_info.suite.skipped; \
687− greatest_info.tests_run += greatest_info.suite.tests_run; \
688− memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \
689− greatest_info.col = 0; \
690−} \
691− \
692−static void greatest_run_suite(greatest_suite_cb *suite_cb, \
693− const char *suite_name) { \
694− if (greatest_info.suite_filter && \
695− !greatest_name_match(suite_name, greatest_info.suite_filter)) { \
696− return; \
697− } \
698− update_counts_and_reset_suite(); \
699− if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) { return; } \
700− fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \
701− GREATEST_SET_TIME(greatest_info.suite.pre_suite); \
702− suite_cb(); \
703− GREATEST_SET_TIME(greatest_info.suite.post_suite); \
704− report_suite(); \
705−} \
706− \
707−void greatest_do_pass(const char *name) { \
708− if (GREATEST_IS_VERBOSE()) { \
709− fprintf(GREATEST_STDOUT, "PASS %s: %s", \
710− name, greatest_info.msg ? greatest_info.msg : ""); \
711− } else { \
712− fprintf(GREATEST_STDOUT, "."); \
713− } \
714− greatest_info.suite.passed++; \
715−} \
716− \
717−void greatest_do_fail(const char *name) { \
718− if (GREATEST_IS_VERBOSE()) { \
719− fprintf(GREATEST_STDOUT, \
720− "FAIL %s: %s (%s:%u)", \
721− name, greatest_info.msg ? greatest_info.msg : "", \
722− greatest_info.fail_file, greatest_info.fail_line); \
723− } else { \
724− fprintf(GREATEST_STDOUT, "F"); \
725− greatest_info.col++; \
726− /* add linebreak if in line of '.'s */ \
727− if (greatest_info.col != 0) { \
728− fprintf(GREATEST_STDOUT, "\n"); \
729− greatest_info.col = 0; \
730− } \
731− fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \
732− name, \
733− greatest_info.msg ? greatest_info.msg : "", \
734− greatest_info.fail_file, greatest_info.fail_line); \
735− } \
736− greatest_info.suite.failed++; \
737−} \
738− \
739−void greatest_do_skip(const char *name) { \
740− if (GREATEST_IS_VERBOSE()) { \
741− fprintf(GREATEST_STDOUT, "SKIP %s: %s", \
742− name, \
743− greatest_info.msg ? \
744− greatest_info.msg : "" ); \
745− } else { \
746− fprintf(GREATEST_STDOUT, "s"); \
747− } \
748− greatest_info.suite.skipped++; \
749−} \
750− \
751−int greatest_do_assert_equal_t(const void *exp, const void *got, \
752− greatest_type_info *type_info, void *udata) { \
753− int eq = 0; \
754− if (type_info == NULL || type_info->equal == NULL) { \
755− return 0; \
756− } \
757− eq = type_info->equal(exp, got, udata); \
758− if (!eq) { \
759− if (type_info->print != NULL) { \
760− fprintf(GREATEST_STDOUT, "\nExpected: "); \
761− (void)type_info->print(exp, udata); \
762− fprintf(GREATEST_STDOUT, "\n Got: "); \
763− (void)type_info->print(got, udata); \
764− fprintf(GREATEST_STDOUT, "\n"); \
765− } else { \
766− fprintf(GREATEST_STDOUT, \
767− "GREATEST_ASSERT_EQUAL_T failure at %s:%u\n", \
768− greatest_info.fail_file, \
769− greatest_info.fail_line); \
770− } \
771− } \
772− return eq; \
773−} \
774− \
775−void greatest_usage(const char *name) { \
776− fprintf(GREATEST_STDOUT, \
777− "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \
778− " -h, --help print this Help\n" \
779− " -l List suites and their tests, then exit\n" \
780− " -f Stop runner after first failure\n" \
781− " -v Verbose output\n" \
782− " -s SUITE only run suites containing string SUITE\n" \
783− " -t TEST only run tests containing string TEST\n", \
784− name); \
785−} \
786− \
787−static void greatest_parse_args(int argc, char **argv) { \
788− int i = 0; \
789− for (i = 1; i < argc; i++) { \
790− if (0 == strncmp("-t", argv[i], 2)) { \
791− if (argc <= i + 1) { \
792− greatest_usage(argv[0]); \
793− exit(EXIT_FAILURE); \
794− } \
795− greatest_info.test_filter = argv[i+1]; \
796− i++; \
797− } else if (0 == strncmp("-s", argv[i], 2)) { \
798− if (argc <= i + 1) { \
799− greatest_usage(argv[0]); \
800− exit(EXIT_FAILURE); \
801− } \
802− greatest_info.suite_filter = argv[i+1]; \
803− i++; \
804− } else if (0 == strncmp("-f", argv[i], 2)) { \
805− greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \
806− } else if (0 == strncmp("-v", argv[i], 2)) { \
807− greatest_info.verbosity++; \
808− } else if (0 == strncmp("-l", argv[i], 2)) { \
809− greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \
810− } else if (0 == strncmp("-h", argv[i], 2) || \
811− 0 == strncmp("--help", argv[i], 6)) { \
812− greatest_usage(argv[0]); \
813− exit(EXIT_SUCCESS); \
814− } else if (0 == strncmp("--", argv[i], 2)) { \
815− break; \
816− } else { \
817− fprintf(GREATEST_STDOUT, \
818− "Unknown argument '%s'\n", argv[i]); \
819− greatest_usage(argv[0]); \
820− exit(EXIT_FAILURE); \
821− } \
822− } \
823−} \
824− \
825−int greatest_all_passed(void) { return (greatest_info.failed == 0); } \
826− \
827−void greatest_set_test_filter(const char *name) { \
828− greatest_info.test_filter = name; \
829−} \
830− \
831−void greatest_set_suite_filter(const char *name) { \
832− greatest_info.suite_filter = name; \
833−} \
834− \
835−void greatest_get_report(struct greatest_report_t *report) { \
836− if (report) { \
837− report->passed = greatest_info.passed; \
838− report->failed = greatest_info.failed; \
839− report->skipped = greatest_info.skipped; \
840− report->assertions = greatest_info.assertions; \
841− } \
842−} \
843− \
844−unsigned int greatest_get_verbosity(void) { \
845− return greatest_info.verbosity; \
846−} \
847− \
848−void greatest_set_verbosity(unsigned int verbosity) { \
849− greatest_info.verbosity = (unsigned char)verbosity; \
850−} \
851− \
852−void greatest_set_flag(greatest_flag_t flag) { \
853− greatest_info.flags |= flag; \
854−} \
855− \
856−void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \
857− greatest_info.setup = cb; \
858− greatest_info.setup_udata = udata; \
859−} \
860− \
861−void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \
862− void *udata) { \
863− greatest_info.teardown = cb; \
864− greatest_info.teardown_udata = udata; \
865−} \
866− \
867−static int greatest_string_equal_cb(const void *exp, const void *got, \
868− void *udata) { \
869− size_t *size = (size_t *)udata; \
870− return (size != NULL \
871− ? (0 == strncmp((const char *)exp, (const char *)got, *size)) \
872− : (0 == strcmp((const char *)exp, (const char *)got))); \
873−} \
874− \
875−static int greatest_string_printf_cb(const void *t, void *udata) { \
876− (void)udata; /* note: does not check \0 termination. */ \
877− return fprintf(GREATEST_STDOUT, "%s", (const char *)t); \
878−} \
879− \
880−greatest_type_info greatest_type_info_string = { \
881− greatest_string_equal_cb, \
882− greatest_string_printf_cb, \
883−}; \
884− \
885−static int greatest_memory_equal_cb(const void *exp, const void *got, \
886− void *udata) { \
887− greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \
888− return (0 == memcmp(exp, got, env->size)); \
889−} \
890− \
891−static int greatest_memory_printf_cb(const void *t, void *udata) { \
892− greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \
893− unsigned char *buf = (unsigned char *)t, diff_mark = ' '; \
894− FILE *out = GREATEST_STDOUT; \
895− size_t i, line_i, line_len = 0; \
896− int len = 0; /* format hexdump with differences highlighted */ \
897− for (i = 0; i < env->size; i+= line_len) { \
898− diff_mark = ' '; \
899− line_len = env->size - i; \
900− if (line_len > 16) { line_len = 16; } \
901− for (line_i = i; line_i < i + line_len; line_i++) { \
902− if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \
903− } \
904− len += fprintf(out, "\n%04x %c ", (unsigned int)i, diff_mark); \
905− for (line_i = i; line_i < i + line_len; line_i++) { \
906− int m = env->exp[line_i] == env->got[line_i]; /* match? */ \
907− len += fprintf(out, "%02x%c", buf[line_i], m ? ' ' : '<'); \
908− } \
909− for (line_i = 0; line_i < 16 - line_len; line_i++) { \
910− len += fprintf(out, " "); \
911− } \
912− fprintf(out, " "); \
913− for (line_i = i; line_i < i + line_len; line_i++) { \
914− unsigned char c = buf[line_i]; \
915− len += fprintf(out, "%c", isprint(c) ? c : '.'); \
916− } \
917− } \
918− len += fprintf(out, "\n"); \
919− return len; \
920−} \
921− \
922−greatest_type_info greatest_type_info_memory = { \
923− greatest_memory_equal_cb, \
924− greatest_memory_printf_cb, \
925−}; \
926− \
927−greatest_run_info greatest_info
928−
929−/* Init internals. */
930−#define GREATEST_INIT() \
931− do { \
932− /* Suppress unused function warning if features aren't used */ \
933− (void)greatest_run_suite; \
934− (void)greatest_parse_args; \
935− \
936− memset(&greatest_info, 0, sizeof(greatest_info)); \
937− greatest_info.width = GREATEST_DEFAULT_WIDTH; \
938− GREATEST_SET_TIME(greatest_info.begin); \
939− } while (0) \
940−
941−/* Handle command-line arguments, etc. */
942−#define GREATEST_MAIN_BEGIN() \
943− do { \
944− GREATEST_INIT(); \
945− greatest_parse_args(argc, argv); \
946− } while (0)
947−
948−/* Report passes, failures, skipped tests, the number of
949− * assertions, and the overall run time. */
950−#define GREATEST_PRINT_REPORT() \
951− do { \
952− if (!GREATEST_LIST_ONLY()) { \
953− update_counts_and_reset_suite(); \
954− GREATEST_SET_TIME(greatest_info.end); \
955− fprintf(GREATEST_STDOUT, \
956− "\nTotal: %u test%s", \
957− greatest_info.tests_run, \
958− greatest_info.tests_run == 1 ? "" : "s"); \
959− GREATEST_CLOCK_DIFF(greatest_info.begin, \
960− greatest_info.end); \
961− fprintf(GREATEST_STDOUT, ", %u assertion%s\n", \
962− greatest_info.assertions, \
963− greatest_info.assertions == 1 ? "" : "s"); \
964− fprintf(GREATEST_STDOUT, \
965− "Pass: %u, fail: %u, skip: %u.\n", \
966− greatest_info.passed, \
967− greatest_info.failed, greatest_info.skipped); \
968− } \
969− } while (0)
970−
971−/* Report results, exit with exit status based on results. */
972−#define GREATEST_MAIN_END() \
973− do { \
974− GREATEST_PRINT_REPORT(); \
975− return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \
976− } while (0)
977−
978−/* Make abbreviations without the GREATEST_ prefix for the
979− * most commonly used symbols. */
980−#if GREATEST_USE_ABBREVS
981−#define TEST GREATEST_TEST
982−#define SUITE GREATEST_SUITE
983−#define SUITE_EXTERN GREATEST_SUITE_EXTERN
984−#define RUN_TEST GREATEST_RUN_TEST
985−#define RUN_TEST1 GREATEST_RUN_TEST1
986−#define RUN_SUITE GREATEST_RUN_SUITE
987−#define IGNORE_TEST GREATEST_IGNORE_TEST
988−#define ASSERT GREATEST_ASSERT
989−#define ASSERTm GREATEST_ASSERTm
990−#define ASSERT_FALSE GREATEST_ASSERT_FALSE
991−#define ASSERT_EQ GREATEST_ASSERT_EQ
992−#define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT
993−#define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE
994−#define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T
995−#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ
996−#define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ
997−#define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ
998−#define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ
999−#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm
1000−#define ASSERT_EQm GREATEST_ASSERT_EQm
1001−#define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm
1002−#define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm
1003−#define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm
1004−#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm
1005−#define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm
1006−#define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm
1007−#define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm
1008−#define PASS GREATEST_PASS
1009−#define FAIL GREATEST_FAIL
1010−#define SKIP GREATEST_SKIP
1011−#define PASSm GREATEST_PASSm
1012−#define FAILm GREATEST_FAILm
1013−#define SKIPm GREATEST_SKIPm
1014−#define SET_SETUP GREATEST_SET_SETUP_CB
1015−#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB
1016−#define CHECK_CALL GREATEST_CHECK_CALL
1017−
1018−#ifdef GREATEST_VA_ARGS
1019−#define RUN_TESTp GREATEST_RUN_TESTp
1020−#endif
1021−
1022−#if GREATEST_USE_LONGJMP
1023−#define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP
1024−#define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm
1025−#define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP
1026−#define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm
1027−#endif
1028−
1029−#endif /* USE_ABBREVS */
1030−
1031−#ifdef __cplusplus
1032−}
1033−#endif
1034−
1035−#endif
parg/test/test_parg.c+0−703
@@ -1,703 +0,0 @@
1−/*
2− * test_parg - parg unit test
3− *
4− * Written in 2015-2016 by Joergen Ibsen
5− *
6− * To the extent possible under law, the author(s) have dedicated all
7− * copyright and related and neighboring rights to this software to the
8− * public domain worldwide. This software is distributed without any
9− * warranty. <http://creativecommons.org/publicdomain/zero/1.0/>
10− */
11−
12−#include "parg.h"
13−
14−#include <assert.h>
15−#include <stdio.h>
16−#include <stdlib.h>
17−
18−#include "greatest.h"
19−
20−#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
21−
22−static const char *os_def = ":no::r:";
23−
24−static const struct parg_option po_def[] = {
25− { "noarg", PARG_NOARG, NULL, 'n' },
26− { "optarg", PARG_OPTARG, NULL, 'o' },
27− { "reqarg", PARG_REQARG, NULL, 'r' },
28− { "foo", PARG_NOARG, NULL, 'f' },
29− { "foobar", PARG_NOARG, NULL, 'b' },
30− { 0, 0, 0, 0 }
31−};
32−
33−/*
34− * parg_getopt tests
35− */
36−
37−TEST parg_getopt_app_only(void)
38−{
39− struct parg_state ps;
40− char *argv[] = { "app" };
41−
42− parg_init(&ps);
43−
44− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
45− ASSERT_EQ(NULL, ps.optarg);
46− ASSERT_EQ(1, ps.optind);
47−
48− PASS();
49−}
50−
51−TEST parg_getopt_dash(void)
52−{
53− struct parg_state ps;
54− char *argv[] = { "app", "-" };
55−
56− parg_init(&ps);
57−
58− ASSERT_EQ(1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
59− ASSERT_EQ(argv[1], ps.optarg);
60− ASSERT_EQ(2, ps.optind);
61−
62− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
63− ASSERT_EQ(NULL, ps.optarg);
64− ASSERT_EQ(2, ps.optind);
65−
66− PASS();
67−}
68−
69−TEST parg_getopt_double_dash(void)
70−{
71− struct parg_state ps;
72− char *argv[] = { "app", "--" };
73−
74− parg_init(&ps);
75−
76− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
77− ASSERT_EQ(NULL, ps.optarg);
78− ASSERT_EQ(2, ps.optind);
79−
80− PASS();
81−}
82−
83−TEST parg_getopt_nonopt(void)
84−{
85− struct parg_state ps;
86− char *argv[] = { "app", "foo" };
87−
88− parg_init(&ps);
89−
90− ASSERT_EQ(1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
91− ASSERT_EQ(argv[1], ps.optarg);
92− ASSERT_EQ(2, ps.optind);
93−
94− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
95− ASSERT_EQ(NULL, ps.optarg);
96− ASSERT_EQ(2, ps.optind);
97−
98− PASS();
99−}
100−
101−TEST parg_getopt_no_match(void)
102−{
103− struct parg_state ps;
104− char *argv[] = { "app", "-u" };
105−
106− parg_init(&ps);
107−
108− ASSERT_EQ('?', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
109− ASSERT_EQ('u', ps.optopt);
110− ASSERT_EQ(2, ps.optind);
111−
112− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
113− ASSERT_EQ(NULL, ps.optarg);
114− ASSERT_EQ(2, ps.optind);
115−
116− PASS();
117−}
118−
119−TEST parg_getopt_noarg(void)
120−{
121− struct parg_state ps;
122− char *argv[] = { "app", "-n" };
123−
124− parg_init(&ps);
125−
126− ASSERT_EQ('n', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
127− ASSERT_EQ(NULL, ps.optarg);
128− ASSERT_EQ(2, ps.optind);
129−
130− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
131− ASSERT_EQ(NULL, ps.optarg);
132− ASSERT_EQ(2, ps.optind);
133−
134− PASS();
135−}
136−
137−TEST parg_getopt_optarg_missing(void)
138−{
139− struct parg_state ps;
140− char *argv[] = { "app", "-o" };
141−
142− parg_init(&ps);
143−
144− ASSERT_EQ('o', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
145− ASSERT_EQ(NULL, ps.optarg);
146− ASSERT_EQ(2, ps.optind);
147−
148− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
149− ASSERT_EQ(NULL, ps.optarg);
150− ASSERT_EQ(2, ps.optind);
151−
152− PASS();
153−}
154−
155−TEST parg_getopt_optarg_inline(void)
156−{
157− struct parg_state ps;
158− char *argv[] = { "app", "-oarg" };
159−
160− parg_init(&ps);
161−
162− ASSERT_EQ('o', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
163− ASSERT_EQ(&argv[1][2], ps.optarg);
164− ASSERT_EQ(2, ps.optind);
165−
166− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
167− ASSERT_EQ(NULL, ps.optarg);
168− ASSERT_EQ(2, ps.optind);
169−
170− PASS();
171−}
172−
173−TEST parg_getopt_reqarg_missing(void)
174−{
175− struct parg_state ps;
176− char *argv[] = { "app", "-r" };
177−
178− parg_init(&ps);
179−
180− ASSERT_EQ(':', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
181− ASSERT_EQ(NULL, ps.optarg);
182− ASSERT_EQ('r', ps.optopt);
183− ASSERT_EQ(2, ps.optind);
184−
185− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
186− ASSERT_EQ(NULL, ps.optarg);
187− ASSERT_EQ(2, ps.optind);
188−
189− PASS();
190−}
191−
192−TEST parg_getopt_reqarg_inline(void)
193−{
194− struct parg_state ps;
195− char *argv[] = { "app", "-rarg" };
196−
197− parg_init(&ps);
198−
199− ASSERT_EQ('r', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
200− ASSERT_EQ(&argv[1][2], ps.optarg);
201− ASSERT_EQ(2, ps.optind);
202−
203− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
204− ASSERT_EQ(NULL, ps.optarg);
205− ASSERT_EQ(2, ps.optind);
206−
207− PASS();
208−}
209−
210−TEST parg_getopt_reqarg_nextarg(void)
211−{
212− struct parg_state ps;
213− char *argv[] = { "app", "-r", "arg" };
214−
215− parg_init(&ps);
216−
217− ASSERT_EQ('r', parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
218− ASSERT_EQ(argv[2], ps.optarg);
219− ASSERT_EQ(3, ps.optind);
220−
221− ASSERT_EQ(-1, parg_getopt(&ps, ARRAY_SIZE(argv), argv, os_def));
222− ASSERT_EQ(NULL, ps.optarg);
223− ASSERT_EQ(3, ps.optind);
224−
225− PASS();
226−}
227−
228−/*
229− * parg_getopt_long tests
230− */
231−
232−TEST parg_getopt_long_app_only(void)
233−{
234− struct parg_state ps;
235− char *argv[] = { "app" };
236− int li = -1;
237−
238− parg_init(&ps);
239−
240− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
241− ASSERT_EQ(NULL, ps.optarg);
242− ASSERT_EQ(1, ps.optind);
243−
244− PASS();
245−}
246−
247−TEST parg_getopt_long_dash(void)
248−{
249− struct parg_state ps;
250− char *argv[] = { "app", "-" };
251− int li = -1;
252−
253− parg_init(&ps);
254−
255− ASSERT_EQ(1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
256− ASSERT_EQ(argv[1], ps.optarg);
257− ASSERT_EQ(2, ps.optind);
258−
259− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
260− ASSERT_EQ(NULL, ps.optarg);
261− ASSERT_EQ(2, ps.optind);
262−
263− PASS();
264−}
265−
266−TEST parg_getopt_long_double_dash(void)
267−{
268− struct parg_state ps;
269− char *argv[] = { "app", "--" };
270− int li = -1;
271−
272− parg_init(&ps);
273−
274− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
275− ASSERT_EQ(NULL, ps.optarg);
276− ASSERT_EQ(2, ps.optind);
277−
278− PASS();
279−}
280−
281−TEST parg_getopt_long_nonopt(void)
282−{
283− struct parg_state ps;
284− char *argv[] = { "app", "foo" };
285− int li = -1;
286−
287− parg_init(&ps);
288−
289− ASSERT_EQ(1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
290− ASSERT_EQ(argv[1], ps.optarg);
291− ASSERT_EQ(2, ps.optind);
292−
293− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
294− ASSERT_EQ(NULL, ps.optarg);
295− ASSERT_EQ(2, ps.optind);
296−
297− PASS();
298−}
299−
300−TEST parg_getopt_long_no_match(void)
301−{
302− struct parg_state ps;
303− char *argv[] = { "app", "--unknown" };
304− int li = -1;
305−
306− parg_init(&ps);
307−
308− ASSERT_EQ('?', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
309− ASSERT_EQ(2, ps.optind);
310−
311− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
312− ASSERT_EQ(NULL, ps.optarg);
313− ASSERT_EQ(2, ps.optind);
314−
315− PASS();
316−}
317−
318−TEST parg_getopt_long_flag(void)
319−{
320− struct parg_state ps;
321− char *argv[] = { "app", "--flag" };
322− int li = -1;
323− int flag = 0;
324− struct parg_option po_flag[] = {
325− { "flag", PARG_NOARG, 0, 1 },
326− { 0, 0, 0, 0 }
327− };
328−
329− po_flag[0].flag = &flag;
330−
331− parg_init(&ps);
332−
333− ASSERT_EQ(0, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li));
334− ASSERT_EQ(0, li);
335− ASSERT_EQ(1, flag);
336− ASSERT_EQ(2, ps.optind);
337−
338− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li));
339− ASSERT_EQ(NULL, ps.optarg);
340− ASSERT_EQ(2, ps.optind);
341−
342− PASS();
343−}
344−
345−TEST parg_getopt_long_noarg(void)
346−{
347− struct parg_state ps;
348− char *argv[] = { "app", "--noarg" };
349− int li = -1;
350−
351− parg_init(&ps);
352−
353− ASSERT_EQ('n', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
354− ASSERT_EQ(0, li);
355− ASSERT_EQ(NULL, ps.optarg);
356− ASSERT_EQ(2, ps.optind);
357−
358− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
359− ASSERT_EQ(NULL, ps.optarg);
360− ASSERT_EQ(2, ps.optind);
361−
362− PASS();
363−}
364−
365−TEST parg_getopt_long_noarg_inline(void)
366−{
367− struct parg_state ps;
368− char *argv[] = { "app", "--noarg=arg" };
369− int li = -1;
370−
371− parg_init(&ps);
372−
373− ASSERT_EQ(':', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
374− ASSERT_EQ(0, li);
375− ASSERT_EQ(NULL, ps.optarg);
376− ASSERT_EQ('n', ps.optopt);
377− ASSERT_EQ(2, ps.optind);
378−
379− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
380− ASSERT_EQ(NULL, ps.optarg);
381− ASSERT_EQ(2, ps.optind);
382−
383− PASS();
384−}
385−
386−TEST parg_getopt_long_optarg_missing(void)
387−{
388− struct parg_state ps;
389− char *argv[] = { "app", "--optarg" };
390− int li = -1;
391−
392− parg_init(&ps);
393−
394− ASSERT_EQ('o', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
395− ASSERT_EQ(1, li);
396− ASSERT_EQ(NULL, ps.optarg);
397− ASSERT_EQ(2, ps.optind);
398−
399− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
400− ASSERT_EQ(NULL, ps.optarg);
401− ASSERT_EQ(2, ps.optind);
402−
403− PASS();
404−}
405−
406−TEST parg_getopt_long_optarg_inline(void)
407−{
408− struct parg_state ps;
409− char *argv[] = { "app", "--optarg=arg" };
410− int li = -1;
411−
412− parg_init(&ps);
413−
414− ASSERT_EQ('o', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
415− ASSERT_EQ(1, li);
416− ASSERT_EQ(&argv[1][9], ps.optarg);
417− ASSERT_EQ(2, ps.optind);
418−
419− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
420− ASSERT_EQ(NULL, ps.optarg);
421− ASSERT_EQ(2, ps.optind);
422−
423− PASS();
424−}
425−
426−TEST parg_getopt_long_reqarg_missing(void)
427−{
428− struct parg_state ps;
429− char *argv[] = { "app", "--reqarg" };
430− int li = -1;
431−
432− parg_init(&ps);
433−
434− ASSERT_EQ(':', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
435− ASSERT_EQ(2, li);
436− ASSERT_EQ(NULL, ps.optarg);
437− ASSERT_EQ('r', ps.optopt);
438− ASSERT_EQ(2, ps.optind);
439−
440− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
441− ASSERT_EQ(NULL, ps.optarg);
442− ASSERT_EQ(2, ps.optind);
443−
444− PASS();
445−}
446−
447−TEST parg_getopt_long_reqarg_inline(void)
448−{
449− struct parg_state ps;
450− char *argv[] = { "app", "--reqarg=arg" };
451− int li = -1;
452−
453− parg_init(&ps);
454−
455− ASSERT_EQ('r', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
456− ASSERT_EQ(2, li);
457− ASSERT_EQ(&argv[1][9], ps.optarg);
458− ASSERT_EQ(2, ps.optind);
459−
460− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
461− ASSERT_EQ(NULL, ps.optarg);
462− ASSERT_EQ(2, ps.optind);
463−
464− PASS();
465−}
466−
467−TEST parg_getopt_long_reqarg_nextarg(void)
468−{
469− struct parg_state ps;
470− char *argv[] = { "app", "--reqarg", "arg" };
471− int li = -1;
472−
473− parg_init(&ps);
474−
475− ASSERT_EQ('r', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
476− ASSERT_EQ(2, li);
477− ASSERT_EQ(argv[2], ps.optarg);
478− ASSERT_EQ(3, ps.optind);
479−
480− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
481− ASSERT_EQ(NULL, ps.optarg);
482− ASSERT_EQ(3, ps.optind);
483−
484− PASS();
485−}
486−
487−TEST parg_getopt_long_prefix_ambiguous(void)
488−{
489− struct parg_state ps;
490− char *argv[] = { "app", "--fo" };
491− int li = -1;
492−
493− parg_init(&ps);
494−
495− ASSERT_EQ('?', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
496− ASSERT_EQ(NULL, ps.optarg);
497− ASSERT_EQ(2, ps.optind);
498−
499− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
500− ASSERT_EQ(NULL, ps.optarg);
501− ASSERT_EQ(2, ps.optind);
502−
503− PASS();
504−}
505−
506−TEST parg_getopt_long_prefix_exact(void)
507−{
508− struct parg_state ps;
509− char *argv[] = { "app", "--foo" };
510− int li = -1;
511−
512− parg_init(&ps);
513−
514− ASSERT_EQ('f', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
515− ASSERT_EQ(3, li);
516− ASSERT_EQ(NULL, ps.optarg);
517− ASSERT_EQ(2, ps.optind);
518−
519− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
520− ASSERT_EQ(NULL, ps.optarg);
521− ASSERT_EQ(2, ps.optind);
522−
523− PASS();
524−}
525−
526−TEST parg_getopt_long_prefix_unambiguous(void)
527−{
528− struct parg_state ps;
529− char *argv[] = { "app", "--foob" };
530− int li = -1;
531−
532− parg_init(&ps);
533−
534− ASSERT_EQ('b', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
535− ASSERT_EQ(4, li);
536− ASSERT_EQ(NULL, ps.optarg);
537− ASSERT_EQ(2, ps.optind);
538−
539− ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li));
540− ASSERT_EQ(NULL, ps.optarg);
541− ASSERT_EQ(2, ps.optind);
542−
543− PASS();
544−}
545−
546−/*
547− * parg_reorder tests
548− */
549−
550−TEST parg_reorder_short(void)
551−{
552− char *argv[] = {
553− "app", "first", "-r", "--", "foo", "-u", "bar", "-o", "--",
554− "-n", "last"
555− };
556− char *argv_expected[] = {
557− "app", "-r", "--", "-u", "-o", "--", "first", "foo", "bar",
558− "-n", "last"
559− };
560− size_t i;
561−
562− ASSERT_EQ(6, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL));
563−
564− for (i = 0; i < ARRAY_SIZE(argv); ++i) {
565− ASSERT_STR_EQ(argv_expected[i], argv[i]);
566− }
567−
568− PASS();
569−}
570−
571−TEST parg_reorder_long(void)
572−{
573− char *argv[] = {
574− "app", "first", "--reqarg", "--", "foo", "--unknown", "bar",
575− "--optarg", "--", "--noarg", "last"
576− };
577− char *argv_expected[] = {
578− "app", "--reqarg", "--", "--unknown", "--optarg", "--",
579− "first", "foo", "bar", "--noarg", "last"
580− };
581− size_t i;
582−
583− ASSERT_EQ(6, parg_reorder(ARRAY_SIZE(argv), argv, "", po_def));
584−
585− for (i = 0; i < ARRAY_SIZE(argv); ++i) {
586− ASSERT_STR_EQ(argv_expected[i], argv[i]);
587− }
588−
589− PASS();
590−}
591−
592−TEST parg_reorder_app_only(void)
593−{
594− char *argv[] = { "app" };
595−
596− ASSERT_EQ(1, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL));
597−
598− PASS();
599−}
600−
601−TEST parg_reorder_double_dash_last(void)
602−{
603− char *argv[] = { "app", "foo", "-n", "--" };
604− char *argv_expected[] = { "app", "-n", "--", "foo" };
605− size_t i;
606−
607− ASSERT_EQ(3, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL));
608−
609− for (i = 0; i < ARRAY_SIZE(argv); ++i) {
610− ASSERT_STR_EQ(argv_expected[i], argv[i]);
611− }
612−
613− PASS();
614−}
615−
616−TEST parg_reorder_missing_arg_last(void)
617−{
618− char *argv[] = { "app", "foo", "-r" };
619− char *argv_expected[] = { "app", "-r", "foo" };
620− size_t i;
621−
622− ASSERT_EQ(2, parg_reorder(ARRAY_SIZE(argv), argv, os_def, NULL));
623−
624− for (i = 0; i < ARRAY_SIZE(argv); ++i) {
625− ASSERT_STR_EQ(argv_expected[i], argv[i]);
626− }
627−
628− PASS();
629−}
630−
631−SUITE(parg_getopt_tests)
632−{
633− RUN_TEST(parg_getopt_app_only);
634−
635− RUN_TEST(parg_getopt_dash);
636−
637− RUN_TEST(parg_getopt_double_dash);
638−
639− RUN_TEST(parg_getopt_nonopt);
640−
641− RUN_TEST(parg_getopt_no_match);
642−
643− RUN_TEST(parg_getopt_noarg);
644−
645− RUN_TEST(parg_getopt_optarg_missing);
646− RUN_TEST(parg_getopt_optarg_inline);
647−
648− RUN_TEST(parg_getopt_reqarg_missing);
649− RUN_TEST(parg_getopt_reqarg_inline);
650− RUN_TEST(parg_getopt_reqarg_nextarg);
651−}
652−
653−SUITE(parg_getopt_long_tests)
654−{
655− RUN_TEST(parg_getopt_long_app_only);
656−
657− RUN_TEST(parg_getopt_long_dash);
658−
659− RUN_TEST(parg_getopt_long_double_dash);
660−
661− RUN_TEST(parg_getopt_long_nonopt);
662−
663− RUN_TEST(parg_getopt_long_no_match);
664−
665− RUN_TEST(parg_getopt_long_flag);
666−
667− RUN_TEST(parg_getopt_long_noarg);
668− RUN_TEST(parg_getopt_long_noarg_inline);
669−
670− RUN_TEST(parg_getopt_long_optarg_missing);
671− RUN_TEST(parg_getopt_long_optarg_inline);
672−
673− RUN_TEST(parg_getopt_long_reqarg_missing);
674− RUN_TEST(parg_getopt_long_reqarg_inline);
675− RUN_TEST(parg_getopt_long_reqarg_nextarg);
676−
677− RUN_TEST(parg_getopt_long_prefix_ambiguous);
678− RUN_TEST(parg_getopt_long_prefix_exact);
679− RUN_TEST(parg_getopt_long_prefix_unambiguous);
680−}
681−
682−SUITE(parg_reorder_tests)
683−{
684− RUN_TEST(parg_reorder_short);
685− RUN_TEST(parg_reorder_long);
686−
687− RUN_TEST(parg_reorder_app_only);
688−
689− RUN_TEST(parg_reorder_double_dash_last);
690−
691− RUN_TEST(parg_reorder_missing_arg_last);
692−}
693−
694−GREATEST_MAIN_DEFS();
695−
696−int main(int argc, char *argv[])
697−{
698− GREATEST_MAIN_BEGIN();
699− RUN_SUITE(parg_getopt_tests);
700− RUN_SUITE(parg_getopt_long_tests);
701− RUN_SUITE(parg_reorder_tests);
702− GREATEST_MAIN_END();
703−}
proto.crenamed from c9/c9.c+40−52
@@ -1,18 +1,6 @@
1−/*
2− * This is 9p client and server implementation which aims to be
3− * correct, small and secure. It's the lowest level implementation.
4− * It doesn't have much comments, mostly because it doesn't make
5− * any sense to copy-paste protocol documentation, which
6− * you can read at http://man.cat-v.org/plan_9/5/, see 'intro'.
7− */
8−#ifdef __plan9__
9−#include <u.h>
10−#include <libc.h>
11−#else
121 #include <stdint.h>
132 #include <string.h>
14−#endif
15−#include "c9.h"
3+#include "c9/proto.h"
164
175 enum
186 {
@@ -138,7 +126,7 @@ newtag(C9ctx *c, C9ttype type, C9tag *tag) · 119 unmodified lines
138126 }
139127 }
140128
141− c->error("newtag: no free tags");
129+ c->error(c, "newtag: no free tags");
142130 return C9Etag;
143131 }
144132
@@ -148,11 +136,11 @@ freetag(C9ctx *c, C9tag tag) · 3 unmodified lines
148136 if(tag != 0xffff){
149137 uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits;
150138 if(tag >= C9maxtags){
151− c->error("freetag: invalid tag %ud", (uint32_t)tag);
139+ c->error(c, "freetag: invalid tag %ud", (uint32_t)tag);
152140 return -1;
153141 }
154142 if((c->tags[d] & 1<<m) != 0){
155− c->error("freetag: double free for tag %ud", (uint32_t)tag);
143+ c->error(c, "freetag: double free for tag %ud", (uint32_t)tag);
156144 return -1;
157145 }
158146 if(c->lowfreetag > tag)
@@ -168,12 +156,12 @@ T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err) · 9 unmodified lines
168156 uint8_t *p = NULL;
169157
170158 if(size > c->msize-4-1-2){
171− c->error("T: invalid size %ud", size);
159+ c->error(c, "T: invalid size %ud", size);
172160 *err = C9Esize;
173161 }else if((*err = newtag(c, type, tag)) == 0){
174162 size += 4+1+2;
175163 if((p = c->begin(c, size)) == NULL){
176− c->error("T: no buffer for %ud bytes", size);
164+ c->error(c, "T: no buffer for %ud bytes", size);
177165 freetag(c, *tag);
178166 *err = C9Ebuf;
179167 }else{
@@ -193,7 +181,7 @@ c9version(C9ctx *c, C9tag *tag, uint32_t msize) · 13 unmodified lines
193181 C9error err;
194182
195183 if(msize < C9minmsize){
196− c->error("c9version: msize too small: %ud", msize);
184+ c->error(c, "c9version: msize too small: %ud", msize);
197185 return C9Einit;
198186 }
199187 memset(c->tags, 0xff, sizeof(c->tags));
@@ -217,7 +205,7 @@ c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) · 17 unmodified lines
217205 C9error err;
218206
219207 if(ulen > C9maxstr || alen > C9maxstr){
220− c->error("c9auth: string too long: %ud chars", ulen > alen ? ulen : alen);
208+ c->error(c, "c9auth: string too long: %ud chars", ulen > alen ? ulen : alen);
221209 return C9Estr;
222210 }
223211 if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){
@@ -238,7 +226,7 @@ c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) · 14 unmodified lines
238226
239227 for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++);
240228 if(i == C9maxflush){
241− c->error("c9flush: no free flush slots");
229+ c->error(c, "c9flush: no free flush slots");
242230 return C9Eflush;
243231 }
244232 if((b = T(c, 2, Tflush, tag, &err)) != NULL){
@@ -258,7 +246,7 @@ c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const c · 13 unmodified lines
258246 C9error err;
259247
260248 if(ulen > C9maxstr || alen > C9maxstr){
261− c->error("c9attach: string too long: %ud chars", ulen > alen ? ulen : alen);
249+ c->error(c, "c9attach: string too long: %ud chars", ulen > alen ? ulen : alen);
262250 return C9Estr;
263251 }
264252 if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){
@@ -282,13 +270,13 @@ c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) · 17 unmodified lines
282270 for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){
283271 len[i] = safestrlen(path[i]);
284272 if(len[i] == 0 || len[i] > C9maxstr){
285− c->error("c9walk: invalid path element: %ud chars", len[i]);
273+ c->error(c, "c9walk: invalid path element: %ud chars", len[i]);
286274 return C9Epath;
287275 }
288276 sz += 2 + len[i];
289277 }
290278 if(path[i] != NULL){
291− c->error("c9walk: invalid elements !(0 <= %ud <= %ud)", i, C9maxpathel);
279+ c->error(c, "c9walk: invalid elements !(0 <= %ud <= %ud)", i, C9maxpathel);
292280 return C9Epath;
293281 }
294282
@@ -325,7 +313,7 @@ c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mod · 30 unmodified lines
325313 C9error err;
326314
327315 if(nlen == 0 || nlen > C9maxstr){
328− c->error("c9create: invalid name: %ud chars", nlen);
316+ c->error(c, "c9create: invalid name: %ud chars", nlen);
329317 return C9Epath;
330318 }
331319 if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){
@@ -423,11 +411,11 @@ c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) · 91 unmodified lines
423411 C9error err;
424412
425413 if(nlen == 0 || nlen > C9maxstr){
426− c->error("c9wstat: invalid name: %ud chars", nlen);
414+ c->error(c, "c9wstat: invalid name: %ud chars", nlen);
427415 return C9Epath;
428416 }
429417 if(ulen > C9maxstr || glen > C9maxstr){
430− c->error("c9wstat: string too long: %ud chars", ulen > glen ? ulen : glen);
418+ c->error(c, "c9wstat: string too long: %ud chars", ulen > glen ? ulen : glen);
431419 return C9Estr;
432420 }
433421 if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){
@@ -460,20 +448,20 @@ c9proc(C9ctx *c) · 26 unmodified lines
460448 err = -1;
461449 if((b = c->read(c, 4, &err)) == NULL){
462450 if(err != 0)
463− c->error("c9proc: short read");
451+ c->error(c, "c9proc: short read");
464452 return err == 0 ? 0 : C9Epkt;
465453 }
466454
467455 sz = r32(&b);
468456 if(sz < 7 || sz > c->msize){
469− c->error("c9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize);
457+ c->error(c, "c9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize);
470458 return C9Epkt;
471459 }
472460 sz -= 4;
473461 err = -1;
474462 if((b = c->read(c, sz, &err)) == NULL){
475463 if(err != 0)
476− c->error("c9proc: short read");
464+ c->error(c, "c9proc: short read");
477465 return err == 0 ? 0 : C9Epkt;
478466 }
479467
@@ -481,7 +469,7 @@ c9proc(C9ctx *c) · 1 unmodified line
481469 r.tag = r16(&b);
482470 if(r.type != Rversion){
483471 if(r.tag >= C9maxtags){
484− c->error("c9proc: invalid tag %ud", (uint32_t)r.tag);
472+ c->error(c, "c9proc: invalid tag %ud", (uint32_t)r.tag);
485473 return C9Epkt;
486474 }
487475 if(freetag(c, r.tag) != 0)
@@ -510,7 +498,7 @@ c9proc(C9ctx *c) · 22 unmodified lines
510498 if(sz < 2 || (cnt = r16(&b))*13 > sz-2)
511499 goto error;
512500 if(cnt > C9maxpathel){
513− c->error("c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel);
501+ c->error(c, "c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel);
514502 return C9Epath;
515503 }
516504 for(i = 0; i < cnt; i++){
@@ -580,7 +568,7 @@ c9proc(C9ctx *c) · 63 unmodified lines
580568 if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2)
581569 goto error;
582570 if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){
583− c->error("invalid version");
571+ c->error(c, "invalid version");
584572 return C9Ever;
585573 }
586574 if(msize < c->msize)
@@ -593,7 +581,7 @@ c9proc(C9ctx *c) · 6 unmodified lines
593581 }
594582 return 0;
595583 error:
596− c->error("c9proc: invalid packet type %ud", r.type);
584+ c->error(c, "c9proc: invalid packet type %ud", r.type);
597585 return C9Epkt;
598586 }
599587
@@ -633,7 +621,7 @@ c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size) · 33 unmodified lines
633621 *t += sz;
634622 return 0;
635623 error:
636− c->error("c9parsedir: invalid size: size=%ud sz=%ud", *size, sz);
624+ c->error(c, "c9parsedir: invalid size: size=%ud sz=%ud", *size, sz);
637625 return C9Epkt;
638626 }
639627
@@ -645,12 +633,12 @@ R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err) · 5 unmodified lines
645633 uint8_t *p = NULL;
646634
647635 if(size > c->msize-4-1-2){
648− c->error("R: invalid size %ud", size);
636+ c->error(c, "R: invalid size %ud", size);
649637 *err = C9Esize;
650638 }else{
651639 size += 4+1+2;
652640 if((p = c->begin(c, size)) == NULL){
653− c->error("R: no buffer for %ud bytes", size);
641+ c->error(c, "R: no buffer for %ud bytes", size);
654642 *err = C9Ebuf;
655643 }else{
656644 *err = 0;
@@ -699,7 +687,7 @@ s9error(C9ctx *c, C9tag tag, const char *ename) · 42 unmodified lines
699687 C9error err;
700688
701689 if(len > C9maxstr){
702− c->error("s9error: invalid ename: %ud chars", len);
690+ c->error(c, "s9error: invalid ename: %ud chars", len);
703691 return C9Estr;
704692 }
705693 if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){
@@ -743,7 +731,7 @@ s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) · 37 unmodified lines
743731
744732 for(n = 0; n < C9maxpathel && qids[n] != NULL; n++);
745733 if(n > C9maxpathel){
746− c->error("s9walk: invalid elements !(0 <= %ud <= %ud)", n, C9maxpathel);
734+ c->error(c, "s9walk: invalid elements !(0 <= %ud <= %ud)", n, C9maxpathel);
747735 return C9Epath;
748736 }
749737
@@ -839,13 +827,13 @@ s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_ · 89 unmodified lines
839827 mulen = safestrlen(s->muid);
840828
841829 if(nlen == 0 || nlen > C9maxstr){
842− c->error("s9readdir: invalid name: %ud chars", nlen);
830+ c->error(c, "s9readdir: invalid name: %ud chars", nlen);
843831 return C9Epath;
844832 }
845833 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
846834 ulen = ulen > glen ? ulen : glen;
847835 ulen = ulen > mulen ? ulen : mulen;
848− c->error("s9readdir: string too long: %ud chars", ulen);
836+ c->error(c, "s9readdir: string too long: %ud chars", ulen);
849837 return C9Estr;
850838 }
851839
@@ -916,13 +904,13 @@ s9stat(C9ctx *c, C9tag tag, const C9stat *s) · 64 unmodified lines
916904 C9error err;
917905
918906 if(nlen == 0 || nlen > C9maxstr){
919− c->error("s9stat: invalid name: %ud chars", nlen);
907+ c->error(c, "s9stat: invalid name: %ud chars", nlen);
920908 return C9Epath;
921909 }
922910 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
923911 ulen = ulen > glen ? ulen : glen;
924912 ulen = ulen > mulen ? ulen : mulen;
925− c->error("s9stat: string too long: %ud chars", ulen);
913+ c->error(c, "s9stat: string too long: %ud chars", ulen);
926914 return C9Estr;
927915 }
928916
@@ -969,20 +957,20 @@ s9proc(C9ctx *c) · 40 unmodified lines
969957 readerr = -1;
970958 if((b = c->read(c, 4, &readerr)) == NULL){
971959 if(readerr != 0)
972− c->error("s9proc: short read");
960+ c->error(c, "s9proc: short read");
973961 return readerr == 0 ? 0 : C9Epkt;
974962 }
975963
976964 sz = r32(&b);
977965 if(sz < 7 || sz > c->msize){
978− c->error("s9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize);
966+ c->error(c, "s9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize);
979967 return C9Epkt;
980968 }
981969 sz -= 4;
982970 readerr = -1;
983971 if((b = c->read(c, sz, &readerr)) == NULL){
984972 if(readerr != 0)
985− c->error("s9proc: short read");
973+ c->error(c, "s9proc: short read");
986974 return readerr == 0 ? 0 : C9Epkt;
987975 }
988976
@@ -991,7 +979,7 @@ s9proc(C9ctx *c) · 2 unmodified lines
991979 sz -= 3;
992980
993981 if((c->svflags & Svver) == 0 && t.type != Tversion){
994− c->error("s9proc: expected Tversion, got %ud", t.type);
982+ c->error(c, "s9proc: expected Tversion, got %ud", t.type);
995983 return C9Epkt;
996984 }
997985
@@ -1035,7 +1023,7 @@ s9proc(C9ctx *c) · 37 unmodified lines
10351023 t.fid = r32(&b);
10361024 t.walk.newfid = r32(&b);
10371025 if((n = r16(&b)) > 16){
1038− c->error("s9proc: Twalk !(%ud <= 16)", n);
1026+ c->error(c, "s9proc: Twalk !(%ud <= 16)", n);
10391027 return C9Epath;
10401028 }
10411029 sz -= 4+4+2;
@@ -1044,7 +1032,7 @@ s9proc(C9ctx *c) · 2 unmodified lines
10441032 if(sz < 2 || (cnt = r16(&b)) > sz-2)
10451033 goto error;
10461034 if(cnt < 1){
1047− c->error("s9proc: Twalk invalid element [%ud]", i);
1035+ c->error(c, "s9proc: Twalk invalid element [%ud]", i);
10481036 return C9Epath;
10491037 }
10501038 b[-2] = 0;
@@ -1076,7 +1064,7 @@ s9proc(C9ctx *c) · 25 unmodified lines
10761064 if((cnt = r16(&b)) > sz-4)
10771065 goto error;
10781066 if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){
1079− c->error("s9proc");
1067+ c->error(c, "s9proc");
10801068 return err;
10811069 }
10821070 c->t(c, &t);
@@ -1111,7 +1099,7 @@ s9proc(C9ctx *c) · 28 unmodified lines
11111099 w32(&b, 0);
11121100 wcs(&b, "unknown", 7);
11131101 err = c->end(c);
1114− c->error("s9proc: invalid version");
1102+ c->error(c, "s9proc: invalid version");
11151103 }
11161104 return C9Ever;
11171105 }
@@ -1169,7 +1157,7 @@ s9proc(C9ctx *c) · 51 unmodified lines
11691157 }
11701158 return 0;
11711159 error:
1172− c->error("s9proc: invalid packet (type=%ud)", t.type);
1160+ c->error(c, "s9proc: invalid packet (type=%ud)", t.type);
11731161 return C9Epkt;
11741162 }
11751163
pull_subtree.sh+0−2
@@ -1,2 +0,0 @@
1−#!/bin/sh
2−git subtree pull --prefix c9 git@git.sr.ht:~ft/c9 master --squash

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t