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 | 7 | A lightweight library for implmeneting 9p clients and servers in C. |
| 6 | * 9gc - a very simple stdin/stdout 9gridchan client | ||
| 7 | 8 | ||
| 8 | This is all _WIP_ still. 9pex is working in read-only mode so far but | 9 | This is 9p client and server implementation which aims to be correct, |
| 9 | lacks proper auth, async IO, some more error control etc. | 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 | ``` | 15 | `fs.h`: higher level, blocking file API based on POSIX. |
| 14 | CC=musl-gcc CFLAGS="-Os -s -g0 -static" ./build.sh | ||
| 15 | ``` | ||
| 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 | 23 | * portability: zero external dependencies, no POSIX, just C |
| 22 | 24 | * no dynamic memory allocation | |
| 23 | * 9pex: to allow "escaping the root" by following symlinks you have to pass `-e` option | 25 | * security: no crashes due to incoming data |
| 24 | * 9pex: chrooting with musl makes `realpath` not work as it requires /proc to be mounted, get rid of `realpath`? | ||
| 25 | * 9gc: you can run it in termux on Android like so: `rlwrap -a'XXXXXXwth' -s 0 -t dumb 9gc -e NICKNAME` | ||
| @@ -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 | struct C9aux; | 1 | struct C9aux; |
| 11 | 2 | ||
| 12 | typedef struct C9r C9r; | 3 | typedef struct C9r C9r; |
| @@ -151,12 +142,12 @@ struct C9qid · 138 unmodified lines | |||
| 151 | */ | 142 | */ |
| 152 | struct C9stat | 143 | struct C9stat |
| 153 | { | 144 | { |
| 145 | C9qid qid; /* Same as qid[0]. */ | ||
| 154 | uint64_t size; /* Size of the file (in bytes). */ | 146 | uint64_t size; /* Size of the file (in bytes). */ |
| 155 | char *name; /* Name of the file. */ | 147 | char *name; /* Name of the file. */ |
| 156 | char *uid; /* Owner of the file. */ | 148 | char *uid; /* Owner of the file. */ |
| 157 | char *gid; /* Group of the file. */ | 149 | char *gid; /* Group of the file. */ |
| 158 | char *muid; /* The user who modified the file last. */ | 150 | char *muid; /* The user who modified the file last. */ |
| 159 | C9qid qid; /* Same as qid[0]. */ | ||
| 160 | uint32_t mode; /* Permissions. See C9st* and C9perm. */ | 151 | uint32_t mode; /* Permissions. See C9st* and C9perm. */ |
| 161 | uint32_t atime; /* Last access time. */ | 152 | uint32_t atime; /* Last access time. */ |
| 162 | uint32_t mtime; /* Last modification time. */ | 153 | uint32_t mtime; /* Last modification time. */ |
| @@ -295,12 +286,13 @@ struct C9ctx · 132 unmodified lines | |||
| 295 | void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2))); | 286 | void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2))); |
| 296 | 287 | ||
| 297 | /* Callback for error messages. */ | 288 | /* Callback for error messages. */ |
| 298 | void (*error)(const char *fmt, ...) __attribute__((nonnull(1), format(printf, 1, 2))); | 289 | void (*error)(C9ctx *ctx, const char *fmt, ...) __attribute__((nonnull(1), format(printf, 2, 3))); |
| 299 | 290 | ||
| 300 | /* Auxiliary data, can be used by any of above callbacks. */ | 291 | /* Auxiliary data, can be used by any of above callbacks. */ |
| 301 | struct C9aux *aux; | 292 | struct C9aux *aux; |
| 302 | 293 | ||
| 303 | /* private stuff */ | 294 | /* private stuff */ |
| 295 | void *paux; | ||
| 304 | uint32_t msize; | 296 | uint32_t msize; |
| 305 | #ifndef C9_NO_CLIENT | 297 | #ifndef C9_NO_CLIENT |
| 306 | uint32_t flush[C9maxflush]; | 298 | uint32_t flush[C9maxflush]; |
| @@ -308,8 +300,12 @@ struct C9ctx · 1 unmodified line | |||
| 308 | #endif | 300 | #endif |
| 309 | union | 301 | union |
| 310 | { | 302 | { |
| 303 | #ifndef C9_NO_CLIENT | ||
| 311 | C9tag lowfreetag; | 304 | C9tag lowfreetag; |
| 305 | #endif | ||
| 306 | #ifndef C9_NO_SERVER | ||
| 312 | uint16_t svflags; | 307 | uint16_t svflags; |
| 308 | #endif | ||
| 313 | }; | 309 | }; |
| 314 | }; | 310 | }; |
| 315 | 311 | ||
| @@ -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 | #include <stdint.h> | 1 | #include <stdint.h> |
| 13 | #include <string.h> | 2 | #include <string.h> |
| 14 | #endif | 3 | #include "c9/proto.h" |
| 15 | #include "c9.h" | ||
| 16 | 4 | ||
| 17 | enum | 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 | return C9Etag; | 130 | return C9Etag; |
| 143 | } | 131 | } |
| 144 | 132 | ||
| @@ -148,11 +136,11 @@ freetag(C9ctx *c, C9tag tag) · 3 unmodified lines | |||
| 148 | if(tag != 0xffff){ | 136 | if(tag != 0xffff){ |
| 149 | uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits; | 137 | uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits; |
| 150 | if(tag >= C9maxtags){ | 138 | if(tag >= C9maxtags){ |
| 151 | c->error("freetag: invalid tag %ud", (uint32_t)tag); | 139 | c->error(c, "freetag: invalid tag %ud", (uint32_t)tag); |
| 152 | return -1; | 140 | return -1; |
| 153 | } | 141 | } |
| 154 | if((c->tags[d] & 1<<m) != 0){ | 142 | if((c->tags[d] & 1<<m) != 0){ |
| 155 | c->error("freetag: double free for tag %ud", (uint32_t)tag); | 143 | c->error(c, "freetag: double free for tag %ud", (uint32_t)tag); |
| 156 | return -1; | 144 | return -1; |
| 157 | } | 145 | } |
| 158 | if(c->lowfreetag > tag) | 146 | if(c->lowfreetag > tag) |
| @@ -168,12 +156,12 @@ T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err) · 9 unmodified lines | |||
| 168 | uint8_t *p = NULL; | 156 | uint8_t *p = NULL; |
| 169 | 157 | ||
| 170 | if(size > c->msize-4-1-2){ | 158 | if(size > c->msize-4-1-2){ |
| 171 | c->error("T: invalid size %ud", size); | 159 | c->error(c, "T: invalid size %ud", size); |
| 172 | *err = C9Esize; | 160 | *err = C9Esize; |
| 173 | }else if((*err = newtag(c, type, tag)) == 0){ | 161 | }else if((*err = newtag(c, type, tag)) == 0){ |
| 174 | size += 4+1+2; | 162 | size += 4+1+2; |
| 175 | if((p = c->begin(c, size)) == NULL){ | 163 | if((p = c->begin(c, size)) == NULL){ |
| 176 | c->error("T: no buffer for %ud bytes", size); | 164 | c->error(c, "T: no buffer for %ud bytes", size); |
| 177 | freetag(c, *tag); | 165 | freetag(c, *tag); |
| 178 | *err = C9Ebuf; | 166 | *err = C9Ebuf; |
| 179 | }else{ | 167 | }else{ |
| @@ -193,7 +181,7 @@ c9version(C9ctx *c, C9tag *tag, uint32_t msize) · 13 unmodified lines | |||
| 193 | C9error err; | 181 | C9error err; |
| 194 | 182 | ||
| 195 | if(msize < C9minmsize){ | 183 | if(msize < C9minmsize){ |
| 196 | c->error("c9version: msize too small: %ud", msize); | 184 | c->error(c, "c9version: msize too small: %ud", msize); |
| 197 | return C9Einit; | 185 | return C9Einit; |
| 198 | } | 186 | } |
| 199 | memset(c->tags, 0xff, sizeof(c->tags)); | 187 | memset(c->tags, 0xff, sizeof(c->tags)); |
| @@ -217,7 +205,7 @@ c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) · 17 unmodified lines | |||
| 217 | C9error err; | 205 | C9error err; |
| 218 | 206 | ||
| 219 | if(ulen > C9maxstr || alen > C9maxstr){ | 207 | if(ulen > C9maxstr || alen > C9maxstr){ |
| 220 | c->error("c9auth: string too long: %ud chars", ulen > alen ? ulen : alen); | 208 | c->error(c, "c9auth: string too long: %ud chars", ulen > alen ? ulen : alen); |
| 221 | return C9Estr; | 209 | return C9Estr; |
| 222 | } | 210 | } |
| 223 | if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){ | 211 | if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){ |
| @@ -238,7 +226,7 @@ c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) · 14 unmodified lines | |||
| 238 | 226 | ||
| 239 | for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++); | 227 | for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++); |
| 240 | if(i == C9maxflush){ | 228 | if(i == C9maxflush){ |
| 241 | c->error("c9flush: no free flush slots"); | 229 | c->error(c, "c9flush: no free flush slots"); |
| 242 | return C9Eflush; | 230 | return C9Eflush; |
| 243 | } | 231 | } |
| 244 | if((b = T(c, 2, Tflush, tag, &err)) != NULL){ | 232 | if((b = T(c, 2, Tflush, tag, &err)) != NULL){ |
| @@ -258,7 +246,7 @@ c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const c · 13 unmodified lines | |||
| 258 | C9error err; | 246 | C9error err; |
| 259 | 247 | ||
| 260 | if(ulen > C9maxstr || alen > C9maxstr){ | 248 | if(ulen > C9maxstr || alen > C9maxstr){ |
| 261 | c->error("c9attach: string too long: %ud chars", ulen > alen ? ulen : alen); | 249 | c->error(c, "c9attach: string too long: %ud chars", ulen > alen ? ulen : alen); |
| 262 | return C9Estr; | 250 | return C9Estr; |
| 263 | } | 251 | } |
| 264 | if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){ | 252 | if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){ |
| @@ -282,13 +270,13 @@ c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) · 17 unmodified lines | |||
| 282 | for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){ | 270 | for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){ |
| 283 | len[i] = safestrlen(path[i]); | 271 | len[i] = safestrlen(path[i]); |
| 284 | if(len[i] == 0 || len[i] > C9maxstr){ | 272 | if(len[i] == 0 || len[i] > C9maxstr){ |
| 285 | c->error("c9walk: invalid path element: %ud chars", len[i]); | 273 | c->error(c, "c9walk: invalid path element: %ud chars", len[i]); |
| 286 | return C9Epath; | 274 | return C9Epath; |
| 287 | } | 275 | } |
| 288 | sz += 2 + len[i]; | 276 | sz += 2 + len[i]; |
| 289 | } | 277 | } |
| 290 | if(path[i] != NULL){ | 278 | if(path[i] != NULL){ |
| 291 | c->error("c9walk: invalid elements !(0 <= %ud <= %ud)", i, C9maxpathel); | 279 | c->error(c, "c9walk: invalid elements !(0 <= %ud <= %ud)", i, C9maxpathel); |
| 292 | return C9Epath; | 280 | return C9Epath; |
| 293 | } | 281 | } |
| 294 | 282 | ||
| @@ -325,7 +313,7 @@ c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mod · 30 unmodified lines | |||
| 325 | C9error err; | 313 | C9error err; |
| 326 | 314 | ||
| 327 | if(nlen == 0 || nlen > C9maxstr){ | 315 | if(nlen == 0 || nlen > C9maxstr){ |
| 328 | c->error("c9create: invalid name: %ud chars", nlen); | 316 | c->error(c, "c9create: invalid name: %ud chars", nlen); |
| 329 | return C9Epath; | 317 | return C9Epath; |
| 330 | } | 318 | } |
| 331 | if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){ | 319 | if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){ |
| @@ -423,11 +411,11 @@ c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) · 91 unmodified lines | |||
| 423 | C9error err; | 411 | C9error err; |
| 424 | 412 | ||
| 425 | if(nlen == 0 || nlen > C9maxstr){ | 413 | if(nlen == 0 || nlen > C9maxstr){ |
| 426 | c->error("c9wstat: invalid name: %ud chars", nlen); | 414 | c->error(c, "c9wstat: invalid name: %ud chars", nlen); |
| 427 | return C9Epath; | 415 | return C9Epath; |
| 428 | } | 416 | } |
| 429 | if(ulen > C9maxstr || glen > C9maxstr){ | 417 | if(ulen > C9maxstr || glen > C9maxstr){ |
| 430 | c->error("c9wstat: string too long: %ud chars", ulen > glen ? ulen : glen); | 418 | c->error(c, "c9wstat: string too long: %ud chars", ulen > glen ? ulen : glen); |
| 431 | return C9Estr; | 419 | return C9Estr; |
| 432 | } | 420 | } |
| 433 | if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){ | 421 | if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){ |
| @@ -460,20 +448,20 @@ c9proc(C9ctx *c) · 26 unmodified lines | |||
| 460 | err = -1; | 448 | err = -1; |
| 461 | if((b = c->read(c, 4, &err)) == NULL){ | 449 | if((b = c->read(c, 4, &err)) == NULL){ |
| 462 | if(err != 0) | 450 | if(err != 0) |
| 463 | c->error("c9proc: short read"); | 451 | c->error(c, "c9proc: short read"); |
| 464 | return err == 0 ? 0 : C9Epkt; | 452 | return err == 0 ? 0 : C9Epkt; |
| 465 | } | 453 | } |
| 466 | 454 | ||
| 467 | sz = r32(&b); | 455 | sz = r32(&b); |
| 468 | if(sz < 7 || sz > c->msize){ | 456 | if(sz < 7 || sz > c->msize){ |
| 469 | c->error("c9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize); | 457 | c->error(c, "c9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize); |
| 470 | return C9Epkt; | 458 | return C9Epkt; |
| 471 | } | 459 | } |
| 472 | sz -= 4; | 460 | sz -= 4; |
| 473 | err = -1; | 461 | err = -1; |
| 474 | if((b = c->read(c, sz, &err)) == NULL){ | 462 | if((b = c->read(c, sz, &err)) == NULL){ |
| 475 | if(err != 0) | 463 | if(err != 0) |
| 476 | c->error("c9proc: short read"); | 464 | c->error(c, "c9proc: short read"); |
| 477 | return err == 0 ? 0 : C9Epkt; | 465 | return err == 0 ? 0 : C9Epkt; |
| 478 | } | 466 | } |
| 479 | 467 | ||
| @@ -481,7 +469,7 @@ c9proc(C9ctx *c) · 1 unmodified line | |||
| 481 | r.tag = r16(&b); | 469 | r.tag = r16(&b); |
| 482 | if(r.type != Rversion){ | 470 | if(r.type != Rversion){ |
| 483 | if(r.tag >= C9maxtags){ | 471 | if(r.tag >= C9maxtags){ |
| 484 | c->error("c9proc: invalid tag %ud", (uint32_t)r.tag); | 472 | c->error(c, "c9proc: invalid tag %ud", (uint32_t)r.tag); |
| 485 | return C9Epkt; | 473 | return C9Epkt; |
| 486 | } | 474 | } |
| 487 | if(freetag(c, r.tag) != 0) | 475 | if(freetag(c, r.tag) != 0) |
| @@ -510,7 +498,7 @@ c9proc(C9ctx *c) · 22 unmodified lines | |||
| 510 | if(sz < 2 || (cnt = r16(&b))*13 > sz-2) | 498 | if(sz < 2 || (cnt = r16(&b))*13 > sz-2) |
| 511 | goto error; | 499 | goto error; |
| 512 | if(cnt > C9maxpathel){ | 500 | if(cnt > C9maxpathel){ |
| 513 | c->error("c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel); | 501 | c->error(c, "c9proc: Rwalk !(%ud <= %ud)", cnt, C9maxpathel); |
| 514 | return C9Epath; | 502 | return C9Epath; |
| 515 | } | 503 | } |
| 516 | for(i = 0; i < cnt; i++){ | 504 | for(i = 0; i < cnt; i++){ |
| @@ -580,7 +568,7 @@ c9proc(C9ctx *c) · 63 unmodified lines | |||
| 580 | if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) | 568 | if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) |
| 581 | goto error; | 569 | goto error; |
| 582 | if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){ | 570 | if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){ |
| 583 | c->error("invalid version"); | 571 | c->error(c, "invalid version"); |
| 584 | return C9Ever; | 572 | return C9Ever; |
| 585 | } | 573 | } |
| 586 | if(msize < c->msize) | 574 | if(msize < c->msize) |
| @@ -593,7 +581,7 @@ c9proc(C9ctx *c) · 6 unmodified lines | |||
| 593 | } | 581 | } |
| 594 | return 0; | 582 | return 0; |
| 595 | error: | 583 | error: |
| 596 | c->error("c9proc: invalid packet type %ud", r.type); | 584 | c->error(c, "c9proc: invalid packet type %ud", r.type); |
| 597 | return C9Epkt; | 585 | return C9Epkt; |
| 598 | } | 586 | } |
| 599 | 587 | ||
| @@ -633,7 +621,7 @@ c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size) · 33 unmodified lines | |||
| 633 | *t += sz; | 621 | *t += sz; |
| 634 | return 0; | 622 | return 0; |
| 635 | error: | 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 | return C9Epkt; | 625 | return C9Epkt; |
| 638 | } | 626 | } |
| 639 | 627 | ||
| @@ -645,12 +633,12 @@ R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err) · 5 unmodified lines | |||
| 645 | uint8_t *p = NULL; | 633 | uint8_t *p = NULL; |
| 646 | 634 | ||
| 647 | if(size > c->msize-4-1-2){ | 635 | if(size > c->msize-4-1-2){ |
| 648 | c->error("R: invalid size %ud", size); | 636 | c->error(c, "R: invalid size %ud", size); |
| 649 | *err = C9Esize; | 637 | *err = C9Esize; |
| 650 | }else{ | 638 | }else{ |
| 651 | size += 4+1+2; | 639 | size += 4+1+2; |
| 652 | if((p = c->begin(c, size)) == NULL){ | 640 | if((p = c->begin(c, size)) == NULL){ |
| 653 | c->error("R: no buffer for %ud bytes", size); | 641 | c->error(c, "R: no buffer for %ud bytes", size); |
| 654 | *err = C9Ebuf; | 642 | *err = C9Ebuf; |
| 655 | }else{ | 643 | }else{ |
| 656 | *err = 0; | 644 | *err = 0; |
| @@ -699,7 +687,7 @@ s9error(C9ctx *c, C9tag tag, const char *ename) · 42 unmodified lines | |||
| 699 | C9error err; | 687 | C9error err; |
| 700 | 688 | ||
| 701 | if(len > C9maxstr){ | 689 | if(len > C9maxstr){ |
| 702 | c->error("s9error: invalid ename: %ud chars", len); | 690 | c->error(c, "s9error: invalid ename: %ud chars", len); |
| 703 | return C9Estr; | 691 | return C9Estr; |
| 704 | } | 692 | } |
| 705 | if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){ | 693 | if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){ |
| @@ -743,7 +731,7 @@ s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) · 37 unmodified lines | |||
| 743 | 731 | ||
| 744 | for(n = 0; n < C9maxpathel && qids[n] != NULL; n++); | 732 | for(n = 0; n < C9maxpathel && qids[n] != NULL; n++); |
| 745 | if(n > C9maxpathel){ | 733 | if(n > C9maxpathel){ |
| 746 | c->error("s9walk: invalid elements !(0 <= %ud <= %ud)", n, C9maxpathel); | 734 | c->error(c, "s9walk: invalid elements !(0 <= %ud <= %ud)", n, C9maxpathel); |
| 747 | return C9Epath; | 735 | return C9Epath; |
| 748 | } | 736 | } |
| 749 | 737 | ||
| @@ -839,13 +827,13 @@ s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_ · 89 unmodified lines | |||
| 839 | mulen = safestrlen(s->muid); | 827 | mulen = safestrlen(s->muid); |
| 840 | 828 | ||
| 841 | if(nlen == 0 || nlen > C9maxstr){ | 829 | if(nlen == 0 || nlen > C9maxstr){ |
| 842 | c->error("s9readdir: invalid name: %ud chars", nlen); | 830 | c->error(c, "s9readdir: invalid name: %ud chars", nlen); |
| 843 | return C9Epath; | 831 | return C9Epath; |
| 844 | } | 832 | } |
| 845 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ | 833 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ |
| 846 | ulen = ulen > glen ? ulen : glen; | 834 | ulen = ulen > glen ? ulen : glen; |
| 847 | ulen = ulen > mulen ? ulen : mulen; | 835 | ulen = ulen > mulen ? ulen : mulen; |
| 848 | c->error("s9readdir: string too long: %ud chars", ulen); | 836 | c->error(c, "s9readdir: string too long: %ud chars", ulen); |
| 849 | return C9Estr; | 837 | return C9Estr; |
| 850 | } | 838 | } |
| 851 | 839 | ||
| @@ -916,13 +904,13 @@ s9stat(C9ctx *c, C9tag tag, const C9stat *s) · 64 unmodified lines | |||
| 916 | C9error err; | 904 | C9error err; |
| 917 | 905 | ||
| 918 | if(nlen == 0 || nlen > C9maxstr){ | 906 | if(nlen == 0 || nlen > C9maxstr){ |
| 919 | c->error("s9stat: invalid name: %ud chars", nlen); | 907 | c->error(c, "s9stat: invalid name: %ud chars", nlen); |
| 920 | return C9Epath; | 908 | return C9Epath; |
| 921 | } | 909 | } |
| 922 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ | 910 | if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ |
| 923 | ulen = ulen > glen ? ulen : glen; | 911 | ulen = ulen > glen ? ulen : glen; |
| 924 | ulen = ulen > mulen ? ulen : mulen; | 912 | ulen = ulen > mulen ? ulen : mulen; |
| 925 | c->error("s9stat: string too long: %ud chars", ulen); | 913 | c->error(c, "s9stat: string too long: %ud chars", ulen); |
| 926 | return C9Estr; | 914 | return C9Estr; |
| 927 | } | 915 | } |
| 928 | 916 | ||
| @@ -969,20 +957,20 @@ s9proc(C9ctx *c) · 40 unmodified lines | |||
| 969 | readerr = -1; | 957 | readerr = -1; |
| 970 | if((b = c->read(c, 4, &readerr)) == NULL){ | 958 | if((b = c->read(c, 4, &readerr)) == NULL){ |
| 971 | if(readerr != 0) | 959 | if(readerr != 0) |
| 972 | c->error("s9proc: short read"); | 960 | c->error(c, "s9proc: short read"); |
| 973 | return readerr == 0 ? 0 : C9Epkt; | 961 | return readerr == 0 ? 0 : C9Epkt; |
| 974 | } | 962 | } |
| 975 | 963 | ||
| 976 | sz = r32(&b); | 964 | sz = r32(&b); |
| 977 | if(sz < 7 || sz > c->msize){ | 965 | if(sz < 7 || sz > c->msize){ |
| 978 | c->error("s9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize); | 966 | c->error(c, "s9proc: invalid packet size !(7 <= %ud <= %ud)", sz, c->msize); |
| 979 | return C9Epkt; | 967 | return C9Epkt; |
| 980 | } | 968 | } |
| 981 | sz -= 4; | 969 | sz -= 4; |
| 982 | readerr = -1; | 970 | readerr = -1; |
| 983 | if((b = c->read(c, sz, &readerr)) == NULL){ | 971 | if((b = c->read(c, sz, &readerr)) == NULL){ |
| 984 | if(readerr != 0) | 972 | if(readerr != 0) |
| 985 | c->error("s9proc: short read"); | 973 | c->error(c, "s9proc: short read"); |
| 986 | return readerr == 0 ? 0 : C9Epkt; | 974 | return readerr == 0 ? 0 : C9Epkt; |
| 987 | } | 975 | } |
| 988 | 976 | ||
| @@ -991,7 +979,7 @@ s9proc(C9ctx *c) · 2 unmodified lines | |||
| 991 | sz -= 3; | 979 | sz -= 3; |
| 992 | 980 | ||
| 993 | if((c->svflags & Svver) == 0 && t.type != Tversion){ | 981 | if((c->svflags & Svver) == 0 && t.type != Tversion){ |
| 994 | c->error("s9proc: expected Tversion, got %ud", t.type); | 982 | c->error(c, "s9proc: expected Tversion, got %ud", t.type); |
| 995 | return C9Epkt; | 983 | return C9Epkt; |
| 996 | } | 984 | } |
| 997 | 985 | ||
| @@ -1035,7 +1023,7 @@ s9proc(C9ctx *c) · 37 unmodified lines | |||
| 1035 | t.fid = r32(&b); | 1023 | t.fid = r32(&b); |
| 1036 | t.walk.newfid = r32(&b); | 1024 | t.walk.newfid = r32(&b); |
| 1037 | if((n = r16(&b)) > 16){ | 1025 | if((n = r16(&b)) > 16){ |
| 1038 | c->error("s9proc: Twalk !(%ud <= 16)", n); | 1026 | c->error(c, "s9proc: Twalk !(%ud <= 16)", n); |
| 1039 | return C9Epath; | 1027 | return C9Epath; |
| 1040 | } | 1028 | } |
| 1041 | sz -= 4+4+2; | 1029 | sz -= 4+4+2; |
| @@ -1044,7 +1032,7 @@ s9proc(C9ctx *c) · 2 unmodified lines | |||
| 1044 | if(sz < 2 || (cnt = r16(&b)) > sz-2) | 1032 | if(sz < 2 || (cnt = r16(&b)) > sz-2) |
| 1045 | goto error; | 1033 | goto error; |
| 1046 | if(cnt < 1){ | 1034 | if(cnt < 1){ |
| 1047 | c->error("s9proc: Twalk invalid element [%ud]", i); | 1035 | c->error(c, "s9proc: Twalk invalid element [%ud]", i); |
| 1048 | return C9Epath; | 1036 | return C9Epath; |
| 1049 | } | 1037 | } |
| 1050 | b[-2] = 0; | 1038 | b[-2] = 0; |
| @@ -1076,7 +1064,7 @@ s9proc(C9ctx *c) · 25 unmodified lines | |||
| 1076 | if((cnt = r16(&b)) > sz-4) | 1064 | if((cnt = r16(&b)) > sz-4) |
| 1077 | goto error; | 1065 | goto error; |
| 1078 | if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){ | 1066 | if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){ |
| 1079 | c->error("s9proc"); | 1067 | c->error(c, "s9proc"); |
| 1080 | return err; | 1068 | return err; |
| 1081 | } | 1069 | } |
| 1082 | c->t(c, &t); | 1070 | c->t(c, &t); |
| @@ -1111,7 +1099,7 @@ s9proc(C9ctx *c) · 28 unmodified lines | |||
| 1111 | w32(&b, 0); | 1099 | w32(&b, 0); |
| 1112 | wcs(&b, "unknown", 7); | 1100 | wcs(&b, "unknown", 7); |
| 1113 | err = c->end(c); | 1101 | err = c->end(c); |
| 1114 | c->error("s9proc: invalid version"); | 1102 | c->error(c, "s9proc: invalid version"); |
| 1115 | } | 1103 | } |
| 1116 | return C9Ever; | 1104 | return C9Ever; |
| 1117 | } | 1105 | } |
| @@ -1169,7 +1157,7 @@ s9proc(C9ctx *c) · 51 unmodified lines | |||
| 1169 | } | 1157 | } |
| 1170 | return 0; | 1158 | return 0; |
| 1171 | error: | 1159 | error: |
| 1172 | c->error("s9proc: invalid packet (type=%ud)", t.type); | 1160 | c->error(c, "s9proc: invalid packet (type=%ud)", t.type); |
| 1173 | return C9Epkt; | 1161 | return C9Epkt; |
| 1174 | } | 1162 | } |
| 1175 | 1163 | ||
| @@ -1,2 +0,0 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | git subtree pull --prefix c9 git@git.sr.ht:~ft/c9 master --squash | ||