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 @@
19pex
29gc
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
18enum {
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
30typedef struct C9aux C9aux;
31typedef struct REntry REntry;
32
33struct 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
42static const char *nick;
43static int printjoin;
44static uint64_t chatoff, skipuntil;
45//static char *regsrv = "tcp!registry.9p.zone!6675";
46static char *chatsrv = "tcp!chat.9p.zone!9990";
47static const char *channel = "chat";
48
49static uint8_t *
50ctxread(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
71static int
72wrsend(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
91static uint8_t *
92ctxbegin(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
108static int
109ctxend(C9ctx *ctx)
110{
111 (void)ctx;
112 return 0;
113}
114
115static int
116dial(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
158static void
159output(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
171static void
172ctxchatR(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
212static void
213ctxregistryR(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;
248fallback:
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)))
275static void
276ctxerror(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
286static C9aux *
287srv(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
305static C9aux *
306registry(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
337static int
338chatrw(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
391int
392main(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
30uint32_t crc32(const void *data, int len);
31
32enum
33{
34 Canrd = 1<<0,
35 Canwr = 1<<1,
36};
37
38typedef 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
54typedef struct
55{
56 C9tag tag;
57}Tag;
58
59typedef struct
60{
61 char *name;
62 uint32_t id;
63}Id;
64
65static 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
81static char *modes[] = {
82 "read", "write", "rdwr", "exec",
83};
84
85static char *Enoauth = "authentication not required";
86static char *Eunknownfid = "unknown fid";
87static char *Enowstat = "wstat prohibited";
88static char *Eperm = "permission denied";
89static char *Enowrite = "write prohibited";
90static char *Enomem = "out of memory";
91static char *Edupfid = "duplicate fid";
92static char *Ewalknodir = "walk in non-directory";
93static char *Enotfound = "file not found";
94static char *Eduptag = "duplicate tag";
95static char *Ebotch = "9P protocol botch";
96static char *Enocreate = "create prohibited";
97static char *Eisdir = "is a directory";
98static char *Ebadoffset = "bad offset";
99
100static int in, out, eof;
101static C9ctx ctx;
102static int debug, rootescape;
103static Fid **fids;
104static int numfids;
105static Tag **tags;
106static int numtags;
107static char *rootpath;
108static size_t rootlen;
109static C9qid walkqids[C9maxpathel];
110static uint8_t *rdbuf;
111static uint8_t *wrbuf;
112static uint32_t wroff, wrend, wrbufsz;
113static Id *uids, *gids;
114static int numuids, numgids;
115
116__attribute__ ((format (printf, 1, 2)))
117static void
118trace(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
130static int
131canrw(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
166static int
167wrsend(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
192static int
193hastag(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
205static int
206statpath(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
225static void
226stat2qid(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
242static Fid *
243newfid(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
294static Fid *
295findfid(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
310static int
311delfid(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
335static int
336hasperm(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
357static Fid *
358walk(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
428static int
429mode2unix(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
452static int
453openfid(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;
478error:
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
492static char *
493uid2str(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
518static char *
519gid2str(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
544static int
545stat2c9stat(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
570static int
571statfid(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
588static uint8_t *
589ctxread(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
615static uint8_t *
616ctxbegin(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
631static int
632ctxend(C9ctx *c)
633{
634 used(c);
635 wrend = wroff;
636 return 0;
637}
638
639__attribute__ ((format (printf, 1, 2)))
640static void
641ctxerror(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
651static int
652s9do(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
675static int
676readf(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
758done:
759 for (i = 0; i < num; i++)
760 free(c9stp[i]->name);
761 return res;
762}
763
764static void
765ctxt(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
877static void
878sigdebug(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
908int
909main(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# 9pro1# c9
2 2
3Plan9-related tools for Unix-like operating systems.3NOTE (2022-06-27): the library's (yep, it's going to be a library)
4API is getting completely revamped, and examples are being added.
5Use either an older version, or expect API breakage.
4 6
5 * 9pex - share a directory over stdin/stdout, can be used with socat/inetd7A lightweight library for implmeneting 9p clients and servers in C.
6 * 9gc - a very simple stdin/stdout 9gridchan client
7 8
8This is all _WIP_ still. 9pex is working in read-only mode so far but9This is 9p client and server implementation which aims to be correct,
9lacks proper auth, async IO, some more error control etc.10small and secure, targetting mainly low-resource MCUs.
10 11
11To 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.
12 14
13```15`fs.h`: higher level, blocking file API based on POSIX.
14CC=musl-gcc CFLAGS="-Os -s -g0 -static" ./build.sh
15```
16 16
17# Why17`fs9p.[ch]`: blocking file API implementation for 9p - having a
18context created with `proto.[ch]` one can use POSIX-like file API to
19work with a remote filesystem.
18 20
19We can do better.21Goals:
20 22
21# Notes/todo23* portability: zero external dependencies, no POSIX, just C
22 24* no dynamic memory allocation
23 * 9pex: to allow "escaping the root" by following symlinks you have to pass `-e` option25* security: no crashes due to incoming data
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`
build.sh+0−5
@@ -1,5 +0,0 @@
1#!/bin/sh
2set -e
3set -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 @@
1Public domain.
c9/README.md+0−8
@@ -1,8 +0,0 @@
1# c9
2
3Low level 9p client and server.
4
5## Examples
6
7Until I have time to write a minimal example you could take a look at
8https://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
4static 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
39uint32_t
40crc32(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
8int
9dial(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
8int dial(const char *s);
9C9ctx *init9p(int f);
10
11static FS9pfd fds[8];
12
13static void
14fserror(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
25int
26main(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
9enum {
10 Msize = 8192,
11
12 Disconnected = 1<<0,
13};
14
15typedef struct C9aux C9aux;
16
17struct 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
26static uint8_t *
27ctxread(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
48static int
49wrsend(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
68static uint8_t *
69ctxbegin(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
85static int
86ctxend(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
98static void
99ctxerror(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
110C9aux *
111init9p(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
7enum
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
27static const FS9pfd freefd =
28{
29 .offset = ~0ULL,
30};
31
32#define isfree(fd) ((fd).offset == ~0ULL)
33
34static 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
51static int
52fs9pchdir(FS *fs, const char *path)
53{
54 FS9p *aux = (void*)fs->aux;
55
56 aux->f = F9chdir;
57
58 return -1;
59}
60
61static int
62fs9popen(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
71static int
72fs9pcreate(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
81static int64_t
82fs9pread(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
91static int64_t
92fs9preaddir(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
101static int64_t
102fs9pwrite(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
111static int64_t
112fs9pseek(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
121static int
122fs9pstat(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
131static int
132fs9pfstat(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
148static int
149fs9pclose(FS *fs, int fd)
150{
151 FS9p *aux = (void*)fs->aux;
152
153 aux->f = F9close;
154
155 return -1;
156}
157
158static int
159fs9premove(FS *fs, const char *name)
160{
161 FS9p *aux = (void*)fs->aux;
162
163 aux->f = F9remove;
164
165 return -1;
166}
167
168static int
169fs9prename(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
178static void
179fs9pr(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
248int
249fs9pinit(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 @@
1typedef struct FS FS;
2typedef struct FSstat FSstat;
3
4typedef 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
16struct 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
26struct FSaux;
27
28struct 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 @@
1enum
2{
3 FS9proot = 3, /* '/' file descriptor. */
4};
5
6typedef struct FS9p FS9p;
7typedef struct FS9pfd FS9pfd;
8
9struct FS9pfd
10{
11 uint64_t offset;
12};
13
14struct 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
31extern 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__
2typedef u64int uint64_t;
3typedef u32int uint32_t;
4typedef u16int uint16_t;
5typedef u8int uint8_t;
6#define __attribute__(...)
7#define NULL nil
8#endif
9
10struct C9aux;1struct C9aux;
11 2
12typedef struct C9r C9r;3typedef struct C9r C9r;
@@ -151,12 +142,12 @@ struct C9qid · 138 unmodified lines
151 */142 */
152struct C9stat143struct C9stat
153{144{
145 C9qid qid; /* Same as qid[0]. */
154 uint64_t size; /* Size of the file (in bytes). */146 uint64_t size; /* Size of the file (in bytes). */
155 char *name; /* Name of the file. */147 char *name; /* Name of the file. */
156 char *uid; /* Owner of the file. */148 char *uid; /* Owner of the file. */
157 char *gid; /* Group of the file. */149 char *gid; /* Group of the file. */
158 char *muid; /* The user who modified the file last. */150 char *muid; /* The user who modified the file last. */
159 C9qid qid; /* Same as qid[0]. */
160 uint32_t mode; /* Permissions. See C9st* and C9perm. */151 uint32_t mode; /* Permissions. See C9st* and C9perm. */
161 uint32_t atime; /* Last access time. */152 uint32_t atime; /* Last access time. */
162 uint32_t mtime; /* Last modification time. */153 uint32_t mtime; /* Last modification time. */
@@ -295,12 +286,13 @@ struct C9ctx · 132 unmodified lines
295 void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2)));286 void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2)));
296 287
297 /* Callback for error messages. */288 /* 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)));
299 290
300 /* Auxiliary data, can be used by any of above callbacks. */291 /* Auxiliary data, can be used by any of above callbacks. */
301 struct C9aux *aux;292 struct C9aux *aux;
302 293
303 /* private stuff */294 /* private stuff */
295 void *paux;
304 uint32_t msize;296 uint32_t msize;
305#ifndef C9_NO_CLIENT297#ifndef C9_NO_CLIENT
306 uint32_t flush[C9maxflush];298 uint32_t flush[C9maxflush];
@@ -308,8 +300,12 @@ struct C9ctx · 1 unmodified line
308#endif300#endif
309 union301 union
310 {302 {
303#ifndef C9_NO_CLIENT
311 C9tag lowfreetag;304 C9tag lowfreetag;
305#endif
306#ifndef C9_NO_SERVER
312 uint16_t svflags;307 uint16_t svflags;
308#endif
313 };309 };
314};310};
315 311
nixos/README.md+0−61
@@ -1,61 +0,0 @@
1# Read-only 9pex in NixOS
2
3```
4sudo mkdir -p /etc/nixos/pkgs
5sudo cp ninepex.nix /etc/nixos/pkgs/
6```
7
8And change `/etc/nixos/configuration.nix`:
9
10```
11{ config, pkgs, ... }:
12
13let
14 ninepex = pkgs.callPackage ./pkgs/ninepex.nix {};
15in
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
49Files go to `/home/9`. With `rsync` it can be done as so:
50
51```
52rsync -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
3stdenv.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 @@
1language: c
2
3compiler: gcc
4
5sudo: false
6
7before_install:
8 - pip install --user codecov
9
10before_script:
11 - mkdir build
12 - cd build
13 - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_COVERAGE=ON ..
14
15script: cmake --build . && ctest -V .
16
17after_success:
18 - gcov CMakeFiles/parg.dir/parg.c.gcno -o CMakeFiles/parg.dir
19 - codecov -X gcov
20
21notifications:
22 email: false
parg/CMakeLists.txt+0−56
@@ -1,56 +0,0 @@
1cmake_minimum_required(VERSION 2.8.6)
2
3project(parg C)
4
5include(CheckCCompilerFlag)
6include(CTest)
7
8option(BUILD_COVERAGE "Code coverage" OFF)
9
10message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
11
12function(_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)
21endfunction()
22
23if(MSVC)
24 _add_c_compiler_flags(CMAKE_C_FLAGS "/analyze")
25 add_definitions(/D_CRT_SECURE_NO_WARNINGS)
26elseif(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 )
38elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
39 _add_c_compiler_flags(CMAKE_C_FLAGS "-Weverything;-pedantic")
40endif()
41
42if(BUILD_COVERAGE)
43 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
44endif()
45
46include_directories(${PROJECT_SOURCE_DIR})
47
48add_library(parg parg.c parg.h)
49
50if(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)
56endif()
parg/COPYING+0−113
@@ -1,113 +0,0 @@
1CC0 1.0 Universal
2
3Statement of Purpose
4
5The laws of most jurisdictions throughout the world automatically confer
6exclusive Copyright and Related Rights (defined below) upon the creator
7and subsequent owner(s) (each and all, an "owner") of an original work of
8authorship and/or a database (each, a "Work").
9
10Certain owners wish to permanently relinquish those rights to a Work for
11the purpose of contributing to a commons of creative, cultural and
12scientific works ("Commons") that the public can reliably and without fear
13of later claims of infringement build upon, modify, incorporate in other
14works, reuse and redistribute as freely as possible in any form whatsoever
15and for any purposes, including without limitation commercial purposes.
16These owners may contribute to the Commons to promote the ideal of a free
17culture and the further production of creative, cultural and scientific
18works, or to gain reputation or greater distribution for their Work in
19part through the use and efforts of others.
20
21For these and/or other purposes and motivations, and without any
22expectation of additional consideration or compensation, the person
23associating CC0 with a Work (the "Affirmer"), to the extent that he or she
24is an owner of Copyright and Related Rights in the Work, voluntarily
25elects to apply CC0 to the Work and publicly distribute the Work under its
26terms, with knowledge of his or her Copyright and Related Rights in the
27Work and the meaning and intended legal effect of CC0 on those rights.
28
291. Copyright and Related Rights. A Work made available under CC0 may be
30protected by copyright and related or neighboring rights ("Copyright and
31Related Rights"). Copyright and Related Rights include, but are not
32limited 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);
37iii. 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
48vii. other similar, equivalent or corresponding rights throughout the
49 world based on applicable law or treaty, and any national
50 implementations thereof.
51
522. Waiver. To the greatest extent permitted by, but not in contravention
53of, applicable law, Affirmer hereby overtly, fully, permanently,
54irrevocably and unconditionally waives, abandons, and surrenders all of
55Affirmer's Copyright and Related Rights and associated claims and causes
56of action, whether now known or unknown (including existing as well as
57future claims and causes of action), in the Work (i) in all territories
58worldwide, (ii) for the maximum duration provided by applicable law or
59treaty (including future time extensions), (iii) in any current or future
60medium and for any number of copies, and (iv) for any purpose whatsoever,
61including without limitation commercial, advertising or promotional
62purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
63member of the public at large and to the detriment of Affirmer's heirs and
64successors, fully intending that such Waiver shall not be subject to
65revocation, rescission, cancellation, termination, or any other legal or
66equitable action to disrupt the quiet enjoyment of the Work by the public
67as contemplated by Affirmer's express Statement of Purpose.
68
693. Public License Fallback. Should any part of the Waiver for any reason
70be judged legally invalid or ineffective under applicable law, then the
71Waiver shall be preserved to the maximum extent permitted taking into
72account Affirmer's express Statement of Purpose. In addition, to the
73extent the Waiver is so judged Affirmer hereby grants to each affected
74person a royalty-free, non transferable, non sublicensable, non exclusive,
75irrevocable and unconditional license to exercise Affirmer's Copyright and
76Related Rights in the Work (i) in all territories worldwide, (ii) for the
77maximum duration provided by applicable law or treaty (including future
78time extensions), (iii) in any current or future medium and for any number
79of copies, and (iv) for any purpose whatsoever, including without
80limitation commercial, advertising or promotional purposes (the
81"License"). The License shall be deemed effective as of the date CC0 was
82applied by Affirmer to the Work. Should any part of the License for any
83reason be judged legally invalid or ineffective under applicable law, such
84partial invalidity or ineffectiveness shall not invalidate the remainder
85of the License, and in such case Affirmer hereby affirms that he or she
86will not (i) exercise any of his or her remaining Copyright and Related
87Rights in the Work or (ii) assert any associated claims and causes of
88action with respect to the Work, in either case contrary to Affirmer's
89express Statement of Purpose.
90
914. 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
112For 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
2PROJECT_NAME = "parg"
3OUTPUT_DIRECTORY = doc
4JAVADOC_AUTOBRIEF = YES
5TAB_SIZE = 8
6OPTIMIZE_OUTPUT_FOR_C = YES
7EXTRACT_ALL = YES
8INPUT = parg.h README.md
9GENERATE_LATEX = NO
10USE_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
4About
5-----
6
7Most command-line programs have to parse options, so there are a lot of
8different solutions to this problem. Some offer many features, while others
9are more basic.
10
11One of the simpler solutions for C is the [getopt][] function, and its
12extension `getopt_long`. They iterate over the options in `argv`, returning
13them one at a time on successive calls.
14
15One nice thing about them is that they are available on most Unix-like
16operating systems (and usually accompany GCC elsewhere, like Windows).
17Unfortunately, some implementation details vary between platforms.
18
19A potential question is what license the version you get when you include
20them 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
24aim to be a direct replacement. It attempts to make some choices about how to
25handle the extensions and idiosyncrasies of other `getopt` implementations,
26and document them.
27
28It consists of a single source and include file, written in portable ANSI C.
29It 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
35Usage
36-----
37
38The include file `parg.h` contains documentation in the form of [doxygen][]
39comments. A configuration file is included, so you can simply run `doxygen`
40to generate documentation in HTML format.
41
42`parg` uses [CMake][] to generate build systems. To create one for the tools
43on your platform, and build `parg`, use something along the lines of:
44
45~~~sh
46mkdir build
47cd build
48cmake ..
49cmake --build .
50~~~
51
52You can also simply compile the source files and link them into your project.
53CMake just provides an easy way to build and test across various platforms and
54toolsets.
55
56[doxygen]: http://www.doxygen.org/
57[CMake]: http://www.cmake.org/
58
59
60Example
61-------
62
63Here 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
71int 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
119Comparison to `getopt`
120----------------------
121
122### Use of global variables
123
124`getopt` uses global variables to store its state between calls. `parg` uses
125a struct `parg_state`, which you must pass with each call.
126
127### Handling of nonoptions
128
129POSIX 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
131all options come first.
132
133`parg` does not change `argv`, and returns each nonoption as the option
134argument of an option with value `1` (like GNU `getopt`, if `optstring` were
135prefixed by '`-`').
136
137If you wish to process all options first, and have the nonoptions ordered at
138the 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
152When there are multiple short options in one argument, `getopt` does not
153increment `optind` until the last one is processed. This makes it harder to
154tell 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
158next `argv` element to be processed. So when `parg` returns '`?`' (or '`:`'),
159the element that contains the error is `argv[optind - 1]`.
160
161### Value of `optopt` on error
162
163With `getopt_long`, it varies what the values of `optopt` and `longindex` are
164when an error is found with option arguments of long options. Sometimes these
165values are not documented.
166
167`parg` sets `optopt` to `val` if `flag` is `NULL`, and `0` otherwise (which
168equals the return value on successful match), and `longindex` is set to the
169index of the entry in `longopts` that matched.
170
171### Return value on option argument error
172
173When the first character of `optstring` is '`:`', it varies what `getopt`
174returns on extraneous option arguments.
175
176In this case, `parg` returns '`?`' if no option match is found, and '`:`' if
177a match is found, but is missing a required argument, or has an extraneous
178argument.
179
180
181Alternatives
182------------
183
184Some 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
190Other 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
201A 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 */
21static int
22is_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 */
30static int
31match_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 */
72static int
73match_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
141void
142parg_init(struct parg_state *ps)
143{
144 ps->optarg = NULL;
145 ps->optind = 1;
146 ps->optopt = '?';
147 ps->nextchar = NULL;
148}
149
150int
151parg_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
157int
158parg_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 */
212static void
213reverse(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 */
233static int
234parg_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
312int
313parg_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
16extern "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 */
29struct 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 */
41struct 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 */
53typedef 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 */
68void
69parg_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 */
114int
115parg_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 */
164int
165parg_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 */
183int
184parg_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
21extern "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
45TEST foo_should_foo(void) {
46 PASS();
47}
48
49static void setup_cb(void *data) {
50 printf("setup callback for each test case\n");
51}
52
53static void teardown_cb(void *data) {
54 printf("teardown callback for each test case\n");
55}
56
57SUITE(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. */
68GREATEST_MAIN_DEFS();
69
70/* Set up, run suite(s) of tests, report pass/fail/skip stats. */
71int 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. */
86int 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. */
149typedef 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. */
165typedef 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. */
169typedef void (greatest_setup_cb)(void *udata);
170typedef 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]. */
175typedef 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]. */
180typedef 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].*/
184typedef struct greatest_type_info {
185 greatest_equal_cb *equal;
186 greatest_printf_cb *print;
187} greatest_type_info;
188
189typedef 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. */
196extern greatest_type_info greatest_type_info_string;
197extern greatest_type_info greatest_type_info_memory;
198
199typedef 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. */
205typedef 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
249struct 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(). */
259extern greatest_run_info greatest_info;
260
261/* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */
262typedef const char *greatest_enum_str_fun(int value);
263
264/**********************
265 * Exported functions *
266 **********************/
267
268/* These are used internally by greatest. */
269void greatest_do_pass(const char *name);
270void greatest_do_fail(const char *name);
271void greatest_do_skip(const char *name);
272int greatest_pre_test(const char *name);
273void greatest_post_test(const char *name, int res);
274void greatest_usage(const char *name);
275int 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. */
279void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata);
280void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata);
281int greatest_all_passed(void);
282void greatest_set_test_filter(const char *name);
283void greatest_set_suite_filter(const char *name);
284void greatest_get_report(struct greatest_report_t *report);
285unsigned int greatest_get_verbosity(void);
286void greatest_set_verbosity(unsigned int verbosity);
287void 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. */
316typedef 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? */ \
606static 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 \
622int 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 \
637void 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 \
664static 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 \
679static 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 \
692static 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 \
707void 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 \
717void 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 \
739void 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 \
751int 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 \
775void 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 \
787static 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 \
825int greatest_all_passed(void) { return (greatest_info.failed == 0); } \
826 \
827void greatest_set_test_filter(const char *name) { \
828 greatest_info.test_filter = name; \
829} \
830 \
831void greatest_set_suite_filter(const char *name) { \
832 greatest_info.suite_filter = name; \
833} \
834 \
835void 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 \
844unsigned int greatest_get_verbosity(void) { \
845 return greatest_info.verbosity; \
846} \
847 \
848void greatest_set_verbosity(unsigned int verbosity) { \
849 greatest_info.verbosity = (unsigned char)verbosity; \
850} \
851 \
852void greatest_set_flag(greatest_flag_t flag) { \
853 greatest_info.flags |= flag; \
854} \
855 \
856void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \
857 greatest_info.setup = cb; \
858 greatest_info.setup_udata = udata; \
859} \
860 \
861void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \
862 void *udata) { \
863 greatest_info.teardown = cb; \
864 greatest_info.teardown_udata = udata; \
865} \
866 \
867static 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 \
875static 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 \
880greatest_type_info greatest_type_info_string = { \
881 greatest_string_equal_cb, \
882 greatest_string_printf_cb, \
883}; \
884 \
885static 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 \
891static 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 \
922greatest_type_info greatest_type_info_memory = { \
923 greatest_memory_equal_cb, \
924 greatest_memory_printf_cb, \
925}; \
926 \
927greatest_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
22static const char *os_def = ":no::r:";
23
24static 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
37TEST 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
51TEST 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
69TEST 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
83TEST 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
101TEST 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
119TEST 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
137TEST 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
155TEST 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
173TEST 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
192TEST 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
210TEST 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
232TEST 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
247TEST 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
266TEST 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
281TEST 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
300TEST 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
318TEST 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
345TEST 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
365TEST 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
386TEST 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
406TEST 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
426TEST 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
447TEST 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
467TEST 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
487TEST 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
506TEST 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
526TEST 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
550TEST 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
571TEST 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
592TEST 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
601TEST 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
616TEST 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
631SUITE(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
653SUITE(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
682SUITE(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
694GREATEST_MAIN_DEFS();
695
696int 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
12#include <stdint.h>1#include <stdint.h>
13#include <string.h>2#include <string.h>
14#endif3#include "c9/proto.h"
15#include "c9.h"
16 4
17enum5enum
18{6{
@@ -138,7 +126,7 @@ newtag(C9ctx *c, C9ttype type, C9tag *tag) · 119 unmodified lines
138 }126 }
139 }127 }
140 128
141 c->error("newtag: no free tags");129 c->error(c, "newtag: no free tags");
142 return C9Etag;130 return C9Etag;
143}131}
144 132
@@ -148,11 +136,11 @@ freetag(C9ctx *c, C9tag tag) · 3 unmodified lines
148 if(tag != 0xffff){136 if(tag != 0xffff){
149 uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits;137 uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits;
150 if(tag >= C9maxtags){138 if(tag >= C9maxtags){
151 c->error("freetag: invalid tag %ud", (uint32_t)tag);139 c->error(c, "freetag: invalid tag %ud", (uint32_t)tag);
152 return -1;140 return -1;
153 }141 }
154 if((c->tags[d] & 1<<m) != 0){142 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);
156 return -1;144 return -1;
157 }145 }
158 if(c->lowfreetag > tag)146 if(c->lowfreetag > tag)
@@ -168,12 +156,12 @@ T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err) · 9 unmodified lines
168 uint8_t *p = NULL;156 uint8_t *p = NULL;
169 157
170 if(size > c->msize-4-1-2){158 if(size > c->msize-4-1-2){
171 c->error("T: invalid size %ud", size);159 c->error(c, "T: invalid size %ud", size);
172 *err = C9Esize;160 *err = C9Esize;
173 }else if((*err = newtag(c, type, tag)) == 0){161 }else if((*err = newtag(c, type, tag)) == 0){
174 size += 4+1+2;162 size += 4+1+2;
175 if((p = c->begin(c, size)) == NULL){163 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);
177 freetag(c, *tag);165 freetag(c, *tag);
178 *err = C9Ebuf;166 *err = C9Ebuf;
179 }else{167 }else{
@@ -193,7 +181,7 @@ c9version(C9ctx *c, C9tag *tag, uint32_t msize) · 13 unmodified lines
193 C9error err;181 C9error err;
194 182
195 if(msize < C9minmsize){183 if(msize < C9minmsize){
196 c->error("c9version: msize too small: %ud", msize);184 c->error(c, "c9version: msize too small: %ud", msize);
197 return C9Einit;185 return C9Einit;
198 }186 }
199 memset(c->tags, 0xff, sizeof(c->tags));187 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
217 C9error err;205 C9error err;
218 206
219 if(ulen > C9maxstr || alen > C9maxstr){207 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);
221 return C9Estr;209 return C9Estr;
222 }210 }
223 if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){211 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
238 226
239 for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++);227 for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++);
240 if(i == C9maxflush){228 if(i == C9maxflush){
241 c->error("c9flush: no free flush slots");229 c->error(c, "c9flush: no free flush slots");
242 return C9Eflush;230 return C9Eflush;
243 }231 }
244 if((b = T(c, 2, Tflush, tag, &err)) != NULL){232 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
258 C9error err;246 C9error err;
259 247
260 if(ulen > C9maxstr || alen > C9maxstr){248 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);
262 return C9Estr;250 return C9Estr;
263 }251 }
264 if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){252 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
282 for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){270 for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){
283 len[i] = safestrlen(path[i]);271 len[i] = safestrlen(path[i]);
284 if(len[i] == 0 || len[i] > C9maxstr){272 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]);
286 return C9Epath;274 return C9Epath;
287 }275 }
288 sz += 2 + len[i];276 sz += 2 + len[i];
289 }277 }
290 if(path[i] != NULL){278 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);
292 return C9Epath;280 return C9Epath;
293 }281 }
294 282
@@ -325,7 +313,7 @@ c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mod · 30 unmodified lines
325 C9error err;313 C9error err;
326 314
327 if(nlen == 0 || nlen > C9maxstr){315 if(nlen == 0 || nlen > C9maxstr){
328 c->error("c9create: invalid name: %ud chars", nlen);316 c->error(c, "c9create: invalid name: %ud chars", nlen);
329 return C9Epath;317 return C9Epath;
330 }318 }
331 if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){319 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
423 C9error err;411 C9error err;
424 412
425 if(nlen == 0 || nlen > C9maxstr){413 if(nlen == 0 || nlen > C9maxstr){
426 c->error("c9wstat: invalid name: %ud chars", nlen);414 c->error(c, "c9wstat: invalid name: %ud chars", nlen);
427 return C9Epath;415 return C9Epath;
428 }416 }
429 if(ulen > C9maxstr || glen > C9maxstr){417 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);
431 return C9Estr;419 return C9Estr;
432 }420 }
433 if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){421 if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){
@@ -460,20 +448,20 @@ c9proc(C9ctx *c) · 26 unmodified lines
460 err = -1;448 err = -1;
461 if((b = c->read(c, 4, &err)) == NULL){449 if((b = c->read(c, 4, &err)) == NULL){
462 if(err != 0)450 if(err != 0)
463 c->error("c9proc: short read");451 c->error(c, "c9proc: short read");
464 return err == 0 ? 0 : C9Epkt;452 return err == 0 ? 0 : C9Epkt;
465 }453 }
466 454
467 sz = r32(&b);455 sz = r32(&b);
468 if(sz < 7 || sz > c->msize){456 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);
470 return C9Epkt;458 return C9Epkt;
471 }459 }
472 sz -= 4;460 sz -= 4;
473 err = -1;461 err = -1;
474 if((b = c->read(c, sz, &err)) == NULL){462 if((b = c->read(c, sz, &err)) == NULL){
475 if(err != 0)463 if(err != 0)
476 c->error("c9proc: short read");464 c->error(c, "c9proc: short read");
477 return err == 0 ? 0 : C9Epkt;465 return err == 0 ? 0 : C9Epkt;
478 }466 }
479 467
@@ -481,7 +469,7 @@ c9proc(C9ctx *c) · 1 unmodified line
481 r.tag = r16(&b);469 r.tag = r16(&b);
482 if(r.type != Rversion){470 if(r.type != Rversion){
483 if(r.tag >= C9maxtags){471 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);
485 return C9Epkt;473 return C9Epkt;
486 }474 }
487 if(freetag(c, r.tag) != 0)475 if(freetag(c, r.tag) != 0)
@@ -510,7 +498,7 @@ c9proc(C9ctx *c) · 22 unmodified lines
510 if(sz < 2 || (cnt = r16(&b))*13 > sz-2)498 if(sz < 2 || (cnt = r16(&b))*13 > sz-2)
511 goto error;499 goto error;
512 if(cnt > C9maxpathel){500 if(cnt > C9maxpathel){
513 c->error("c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel);501 c->error(c, "c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel);
514 return C9Epath;502 return C9Epath;
515 }503 }
516 for(i = 0; i < cnt; i++){504 for(i = 0; i < cnt; i++){
@@ -580,7 +568,7 @@ c9proc(C9ctx *c) · 63 unmodified lines
580 if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2)568 if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2)
581 goto error;569 goto error;
582 if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){570 if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){
583 c->error("invalid version");571 c->error(c, "invalid version");
584 return C9Ever;572 return C9Ever;
585 }573 }
586 if(msize < c->msize)574 if(msize < c->msize)
@@ -593,7 +581,7 @@ c9proc(C9ctx *c) · 6 unmodified lines
593 }581 }
594 return 0;582 return 0;
595error:583error:
596 c->error("c9proc: invalid packet type %ud", r.type);584 c->error(c, "c9proc: invalid packet type %ud", r.type);
597 return C9Epkt;585 return C9Epkt;
598}586}
599 587
@@ -633,7 +621,7 @@ c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size) · 33 unmodified lines
633 *t += sz;621 *t += sz;
634 return 0;622 return 0;
635error:623error:
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);
637 return C9Epkt;625 return C9Epkt;
638}626}
639 627
@@ -645,12 +633,12 @@ R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err) · 5 unmodified lines
645 uint8_t *p = NULL;633 uint8_t *p = NULL;
646 634
647 if(size > c->msize-4-1-2){635 if(size > c->msize-4-1-2){
648 c->error("R: invalid size %ud", size);636 c->error(c, "R: invalid size %ud", size);
649 *err = C9Esize;637 *err = C9Esize;
650 }else{638 }else{
651 size += 4+1+2;639 size += 4+1+2;
652 if((p = c->begin(c, size)) == NULL){640 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);
654 *err = C9Ebuf;642 *err = C9Ebuf;
655 }else{643 }else{
656 *err = 0;644 *err = 0;
@@ -699,7 +687,7 @@ s9error(C9ctx *c, C9tag tag, const char *ename) · 42 unmodified lines
699 C9error err;687 C9error err;
700 688
701 if(len > C9maxstr){689 if(len > C9maxstr){
702 c->error("s9error: invalid ename: %ud chars", len);690 c->error(c, "s9error: invalid ename: %ud chars", len);
703 return C9Estr;691 return C9Estr;
704 }692 }
705 if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){693 if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){
@@ -743,7 +731,7 @@ s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) · 37 unmodified lines
743 731
744 for(n = 0; n < C9maxpathel && qids[n] != NULL; n++);732 for(n = 0; n < C9maxpathel && qids[n] != NULL; n++);
745 if(n > C9maxpathel){733 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);
747 return C9Epath;735 return C9Epath;
748 }736 }
749 737
@@ -839,13 +827,13 @@ s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_ · 89 unmodified lines
839 mulen = safestrlen(s->muid);827 mulen = safestrlen(s->muid);
840 828
841 if(nlen == 0 || nlen > C9maxstr){829 if(nlen == 0 || nlen > C9maxstr){
842 c->error("s9readdir: invalid name: %ud chars", nlen);830 c->error(c, "s9readdir: invalid name: %ud chars", nlen);
843 return C9Epath;831 return C9Epath;
844 }832 }
845 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){833 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
846 ulen = ulen > glen ? ulen : glen;834 ulen = ulen > glen ? ulen : glen;
847 ulen = ulen > mulen ? ulen : mulen;835 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);
849 return C9Estr;837 return C9Estr;
850 }838 }
851 839
@@ -916,13 +904,13 @@ s9stat(C9ctx *c, C9tag tag, const C9stat *s) · 64 unmodified lines
916 C9error err;904 C9error err;
917 905
918 if(nlen == 0 || nlen > C9maxstr){906 if(nlen == 0 || nlen > C9maxstr){
919 c->error("s9stat: invalid name: %ud chars", nlen);907 c->error(c, "s9stat: invalid name: %ud chars", nlen);
920 return C9Epath;908 return C9Epath;
921 }909 }
922 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){910 if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
923 ulen = ulen > glen ? ulen : glen;911 ulen = ulen > glen ? ulen : glen;
924 ulen = ulen > mulen ? ulen : mulen;912 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);
926 return C9Estr;914 return C9Estr;
927 }915 }
928 916
@@ -969,20 +957,20 @@ s9proc(C9ctx *c) · 40 unmodified lines
969 readerr = -1;957 readerr = -1;
970 if((b = c->read(c, 4, &readerr)) == NULL){958 if((b = c->read(c, 4, &readerr)) == NULL){
971 if(readerr != 0)959 if(readerr != 0)
972 c->error("s9proc: short read");960 c->error(c, "s9proc: short read");
973 return readerr == 0 ? 0 : C9Epkt;961 return readerr == 0 ? 0 : C9Epkt;
974 }962 }
975 963
976 sz = r32(&b);964 sz = r32(&b);
977 if(sz < 7 || sz > c->msize){965 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);
979 return C9Epkt;967 return C9Epkt;
980 }968 }
981 sz -= 4;969 sz -= 4;
982 readerr = -1;970 readerr = -1;
983 if((b = c->read(c, sz, &readerr)) == NULL){971 if((b = c->read(c, sz, &readerr)) == NULL){
984 if(readerr != 0)972 if(readerr != 0)
985 c->error("s9proc: short read");973 c->error(c, "s9proc: short read");
986 return readerr == 0 ? 0 : C9Epkt;974 return readerr == 0 ? 0 : C9Epkt;
987 }975 }
988 976
@@ -991,7 +979,7 @@ s9proc(C9ctx *c) · 2 unmodified lines
991 sz -= 3;979 sz -= 3;
992 980
993 if((c->svflags & Svver) == 0 && t.type != Tversion){981 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);
995 return C9Epkt;983 return C9Epkt;
996 }984 }
997 985
@@ -1035,7 +1023,7 @@ s9proc(C9ctx *c) · 37 unmodified lines
1035 t.fid = r32(&b);1023 t.fid = r32(&b);
1036 t.walk.newfid = r32(&b);1024 t.walk.newfid = r32(&b);
1037 if((n = r16(&b)) > 16){1025 if((n = r16(&b)) > 16){
1038 c->error("s9proc: Twalk !(%ud <= 16)", n);1026 c->error(c, "s9proc: Twalk !(%ud <= 16)", n);
1039 return C9Epath;1027 return C9Epath;
1040 }1028 }
1041 sz -= 4+4+2;1029 sz -= 4+4+2;
@@ -1044,7 +1032,7 @@ s9proc(C9ctx *c) · 2 unmodified lines
1044 if(sz < 2 || (cnt = r16(&b)) > sz-2)1032 if(sz < 2 || (cnt = r16(&b)) > sz-2)
1045 goto error;1033 goto error;
1046 if(cnt < 1){1034 if(cnt < 1){
1047 c->error("s9proc: Twalk invalid element [%ud]", i);1035 c->error(c, "s9proc: Twalk invalid element [%ud]", i);
1048 return C9Epath;1036 return C9Epath;
1049 }1037 }
1050 b[-2] = 0;1038 b[-2] = 0;
@@ -1076,7 +1064,7 @@ s9proc(C9ctx *c) · 25 unmodified lines
1076 if((cnt = r16(&b)) > sz-4)1064 if((cnt = r16(&b)) > sz-4)
1077 goto error;1065 goto error;
1078 if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){1066 if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){
1079 c->error("s9proc");1067 c->error(c, "s9proc");
1080 return err;1068 return err;
1081 }1069 }
1082 c->t(c, &t);1070 c->t(c, &t);
@@ -1111,7 +1099,7 @@ s9proc(C9ctx *c) · 28 unmodified lines
1111 w32(&b, 0);1099 w32(&b, 0);
1112 wcs(&b, "unknown", 7);1100 wcs(&b, "unknown", 7);
1113 err = c->end(c);1101 err = c->end(c);
1114 c->error("s9proc: invalid version");1102 c->error(c, "s9proc: invalid version");
1115 }1103 }
1116 return C9Ever;1104 return C9Ever;
1117 }1105 }
@@ -1169,7 +1157,7 @@ s9proc(C9ctx *c) · 51 unmodified lines
1169 }1157 }
1170 return 0;1158 return 0;
1171error:1159error:
1172 c->error("s9proc: invalid packet (type=%ud)", t.type);1160 c->error(c, "s9proc: invalid packet (type=%ud)", t.type);
1173 return C9Epkt;1161 return C9Epkt;
1174}1162}
1175 1163
pull_subtree.sh+0−2
@@ -1,2 +0,0 @@
1#!/bin/sh
2git 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