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