wmiir.c (10521B)
1 /* Copyight ©2007-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #define IXP_NO_P9_ 5 #define IXP_P9_STRUCTS 6 #include <dirent.h> 7 #include <errno.h> 8 #include <limits.h> 9 #include <locale.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <sys/signal.h> 14 #include <time.h> 15 #include <unistd.h> 16 #include <wchar.h> 17 18 #include <ixp.h> 19 #include <stuff/util.h> 20 #include <bio.h> 21 #include <fmt.h> 22 23 static IxpClient* client; 24 static Biobuf* outbuf; 25 static bool binary; 26 27 static void 28 usage(void) { 29 lprint(1, 30 "usage: %s [-a <address>] [-b] {create | ls [-dlp] | read | remove | write} <file>\n" 31 " %s [-a <address>] xwrite <file> <data>\n" 32 " %s -v\n", argv0, argv0, argv0); 33 exit(1); 34 } 35 36 static int 37 errfmt(Fmt *f) { 38 return fmtstrcpy(f, ixp_errbuf()); 39 } 40 41 static bool 42 flush(IxpCFid *fid, char *in, int len, bool binary) { 43 static mbstate_t state; 44 static char buf[IXP_MAX_MSG]; 45 static char *out = buf, *outend = buf + sizeof buf; 46 char *inend; 47 wchar_t w; 48 Rune r; 49 int res; 50 51 if(binary) 52 return ixp_write(fid, in, len) == len; 53 54 inend = in + len; 55 do { 56 if(in == nil || out + UTFmax > outend) { 57 if(ixp_write(fid, buf, out - buf) != out - buf) 58 return false; 59 out = buf; 60 } 61 if(in == nil) { 62 state = (mbstate_t){0}; 63 return true; 64 } 65 66 switch((res = mbrtowc(&w, in, inend - in, &state))) { 67 case -1: 68 return false; 69 case 0: 70 case -2: 71 return true; 72 default: 73 in += res; 74 r = w < Runemax ? w : Runesync; 75 out += runetochar(out, &r); 76 } 77 } while(in < inend); 78 return true; 79 } 80 81 static bool 82 unflush(int fd, char *in, int len, bool binary) { 83 static mbstate_t state; 84 static char buf[IXP_MAX_MSG], extra[UTFmax]; 85 static char *out = buf, *outend = buf + sizeof buf; 86 static int nextra; 87 char *start; 88 Rune r; 89 int res, n; 90 91 if(binary) 92 return write(fd, in, len) == len; 93 94 if(in) { 95 if((n = nextra)) { 96 nextra = 0; 97 while(len > 0 && n < UTFmax && !fullrune(extra, n)) { 98 extra[n++] = *in++; 99 len--; 100 } 101 unflush(fd, extra, n, binary); 102 } 103 n = utfnlen(in, len); 104 } 105 106 start = in; 107 do { 108 if(in == nil || out + MB_LEN_MAX > outend) { 109 if(write(fd, buf, out - buf) != out - buf) 110 return false; 111 out = buf; 112 } 113 if(in == nil || n == 0) { 114 state = (mbstate_t){0}; 115 return true; 116 } 117 118 in += chartorune(&r, in); 119 n--; 120 res = wcrtomb(out, r, &state); 121 if(res == -1) 122 *out++ = '?'; 123 else 124 out += res; 125 } while(n > 0); 126 127 if(in < start + len) { 128 nextra = min(sizeof extra, len - (in - start)); 129 memcpy(extra, in, nextra); 130 } 131 return true; 132 } 133 134 /* Utility Functions */ 135 static void 136 write_data(IxpCFid *fid, char *name, bool binary) { 137 char buf[IXP_MAX_MSG]; 138 int len; 139 140 while((len = read(0, buf, fid->iounit)) > 0) 141 if(!flush(fid, buf, len, binary)) 142 fatal("cannot write file %q\n", name); 143 144 if(!binary) 145 flush(fid, nil, 0, binary); 146 } 147 148 static int 149 comp_stat(const void *s1, const void *s2) { 150 Stat *st1, *st2; 151 152 st1 = (Stat*)s1; 153 st2 = (Stat*)s2; 154 return strcmp(st1->name, st2->name); 155 } 156 157 static void 158 setrwx(long m, char *s) { 159 static char *modes[] = { 160 "---", "--x", "-w-", 161 "-wx", "r--", "r-x", 162 "rw-", "rwx", 163 }; 164 strncpy(s, modes[m], 3); 165 } 166 167 static char * 168 modestr(uint mode) { 169 static char buf[16]; 170 171 buf[0]='-'; 172 if(mode & P9_DMDIR) 173 buf[0]='d'; 174 buf[1]='-'; 175 setrwx((mode >> 6) & 7, &buf[2]); 176 setrwx((mode >> 3) & 7, &buf[5]); 177 setrwx((mode >> 0) & 7, &buf[8]); 178 buf[11] = 0; 179 return buf; 180 } 181 182 static char* 183 timestr(time_t val) { 184 static char buf[32]; 185 186 strftime(buf, sizeof buf, "%Y-%m-%d %H:%M", localtime(&val)); 187 return buf; 188 } 189 190 static void 191 print_stat(Stat *s, int lflag, char *file, int pflag) { 192 char *slash; 193 194 slash = ""; 195 if(pflag) 196 slash = "/"; 197 else 198 file = ""; 199 200 if(lflag) 201 Blprint(outbuf, "%s %s %s %5llud %s %s%s%s\n", 202 modestr(s->mode), s->uid, s->gid, s->length, 203 timestr(s->mtime), file, slash, s->name); 204 else { 205 if((s->mode&P9_DMDIR) && strcmp(s->name, "/")) 206 Blprint(outbuf, "%s%s%s/\n", file, slash, s->name); 207 else 208 Blprint(outbuf, "%s%s%s\n", file, slash, s->name); 209 } 210 } 211 212 /* Service Functions */ 213 static int 214 xwrite(int argc, char *argv[]) { 215 IxpCFid *fid; 216 char *file; 217 218 ARGBEGIN{ 219 default: 220 usage(); 221 }ARGEND; 222 223 file = EARGF(usage()); 224 fid = ixp_open(client, file, P9_OWRITE); 225 if(fid == nil) 226 fatal("Can't open file '%s': %r\n", file); 227 228 write_data(fid, file, binary); 229 ixp_close(fid); 230 return 0; 231 } 232 233 static int 234 xawrite(int argc, char *argv[]) { 235 IxpCFid *fid; 236 char *file, *buf; 237 int nbuf, i; 238 239 ARGBEGIN{ 240 default: 241 usage(); 242 }ARGEND; 243 244 file = EARGF(usage()); 245 fid = ixp_open(client, file, P9_OWRITE); 246 if(fid == nil) 247 fatal("Can't open file '%s': %r\n", file); 248 249 nbuf = 1; 250 for(i=0; i < argc; i++) 251 nbuf += strlen(argv[i]) + (i > 0); 252 buf = emalloc(nbuf); 253 buf[0] = '\0'; 254 while(argc) { 255 strcat(buf, ARGF()); 256 if(argc) 257 strcat(buf, " "); 258 } 259 260 if(!(flush(fid, buf, nbuf, binary) && (binary || flush(fid, 0, 0, binary)))) 261 fatal("cannot write file '%s': %r\n", file); 262 ixp_close(fid); 263 free(buf); 264 return 0; 265 } 266 267 static int 268 xcreate(int argc, char *argv[]) { 269 IxpCFid *fid; 270 char *file; 271 272 ARGBEGIN{ 273 default: 274 usage(); 275 }ARGEND; 276 277 file = EARGF(usage()); 278 fid = ixp_create(client, file, 0777, P9_OWRITE); 279 if(fid == nil) 280 fatal("Can't create file '%s': %r\n", file); 281 282 if((fid->qid.type&P9_DMDIR) == 0) 283 write_data(fid, file, binary); 284 ixp_close(fid); 285 return 0; 286 } 287 288 static int 289 xremove(int argc, char *argv[]) { 290 char *file; 291 292 ARGBEGIN{ 293 default: 294 usage(); 295 }ARGEND; 296 297 file = EARGF(usage()); 298 do { 299 if(!ixp_remove(client, file)) 300 lprint(2, "%s: Can't remove file '%s': %r\n", argv0, file); 301 }while((file = ARGF())); 302 return 0; 303 } 304 305 static int 306 xread(int argc, char *argv[]) { 307 IxpCFid *fid; 308 char *file, *buf; 309 int count; 310 311 ARGBEGIN{ 312 default: 313 usage(); 314 }ARGEND; 315 316 if(argc == 0) 317 usage(); 318 file = EARGF(usage()); 319 do { 320 fid = ixp_open(client, file, P9_OREAD); 321 if(fid == nil) 322 fatal("Can't open file '%s': %r\n", file); 323 324 buf = emalloc(fid->iounit); 325 while((count = ixp_read(fid, buf, fid->iounit)) > 0) { 326 unflush(1, buf, count, binary); 327 if (!binary && count < fid->iounit) 328 unflush(1, 0, 0, binary); 329 } 330 if(!binary) 331 unflush(1, 0, 0, binary); 332 ixp_close(fid); 333 334 if(count == -1) 335 lprint(2, "%s: cannot read file '%s': %r\n", argv0, file); 336 } while((file = ARGF())); 337 338 return 0; 339 } 340 341 static int 342 xls(int argc, char *argv[]) { 343 IxpMsg m; 344 Stat *stat; 345 IxpCFid *fid; 346 char *file; 347 char *buf; 348 int lflag, dflag, pflag; 349 int count, nstat, mstat, i; 350 351 lflag = dflag = pflag = 0; 352 353 ARGBEGIN{ 354 case 'l': 355 lflag++; 356 break; 357 case 'd': 358 dflag++; 359 break; 360 case 'p': 361 pflag++; 362 break; 363 default: 364 usage(); 365 }ARGEND; 366 367 count = 0; 368 file = EARGF(usage()); 369 do { 370 stat = ixp_stat(client, file); 371 if(stat == nil) 372 fatal("cannot stat file '%s': %r\n", file); 373 374 i = strlen(file); 375 if(file[i-1] == '/') { 376 file[i-1] = '\0'; 377 if(!(stat->mode&P9_DMDIR)) 378 fatal("%s: not a directory", file); 379 } 380 if(dflag || (stat->mode&P9_DMDIR) == 0) { 381 print_stat(stat, lflag, file, pflag); 382 ixp_freestat(stat); 383 continue; 384 } 385 ixp_freestat(stat); 386 387 fid = ixp_open(client, file, P9_OREAD); 388 if(fid == nil) 389 fatal("Can't open file '%s': %r\n", file); 390 391 nstat = 0; 392 mstat = 16; 393 stat = emalloc(mstat * sizeof *stat); 394 buf = emalloc(fid->iounit); 395 while((count = ixp_read(fid, buf, fid->iounit)) > 0) { 396 m = ixp_message(buf, count, MsgUnpack); 397 while(m.pos < m.end) { 398 if(nstat == mstat) { 399 mstat <<= 1; 400 stat = erealloc(stat, mstat * sizeof *stat); 401 } 402 ixp_pstat(&m, &stat[nstat++]); 403 } 404 } 405 ixp_close(fid); 406 407 qsort(stat, nstat, sizeof *stat, comp_stat); 408 for(i = 0; i < nstat; i++) { 409 print_stat(&stat[i], lflag, file, pflag); 410 ixp_freestat(&stat[i]); 411 } 412 free(stat); 413 } while((file = ARGF())); 414 415 if(count == -1) 416 fatal("cannot read directory '%s': %r\n", file); 417 return 0; 418 } 419 420 static int 421 xnamespace(int argc, char *argv[]) { 422 char *path; 423 424 ARGBEGIN{ 425 default: 426 usage(); 427 }ARGEND; 428 429 path = ixp_namespace(); 430 if(path == nil) 431 fatal("can't find namespace: %r\n"); 432 Blprint(outbuf, "%s", path); 433 return 0; 434 } 435 436 static int 437 xproglist(int argc, char *argv[]) { 438 DIR *d; 439 struct dirent *de; 440 struct stat st; 441 char *dir, *cwd; 442 int i; 443 444 quotefmtinstall(); 445 446 ARGBEGIN{ 447 default: 448 usage(); 449 }ARGEND; 450 451 i = 7, cwd = nil; 452 do 453 cwd = erealloc(cwd, 1<<i); 454 while(!getcwd(cwd, 1<<i) && errno == ERANGE); 455 456 while((dir = ARGF())) 457 /* Don't use Blprint. wimenu expects UTF-8. */ 458 if(!chdir(cwd) && !chdir(dir) && (d = opendir(dir))) { 459 while((de = readdir(d))) { 460 stat(de->d_name, &st); 461 if(S_ISREG(st.st_mode) && !access(de->d_name, X_OK)) 462 Bprint(outbuf, "%q\n", de->d_name); 463 } 464 closedir(d); 465 } 466 467 return 0; /* NOTREACHED */ 468 } 469 470 static int 471 xsetsid(int argc, char *argv[]) { 472 char *av0; 473 bool dofork; 474 475 av0 = nil; 476 dofork = false; 477 ARGBEGIN{ 478 case '0': 479 av0 = EARGF(usage()); 480 break; 481 case 'f': 482 dofork = true; 483 break; 484 default: 485 usage(); 486 }ARGEND; 487 if(av0 == nil) 488 av0 = argv[0]; 489 if(av0 == nil) 490 return 1; 491 492 setsid(); 493 if(dofork) 494 switch(fork()) { 495 case 0: break; 496 case -1: fatal("can't fork: %r\n"); 497 default: return 0; 498 } 499 500 execvp(av0, argv); 501 fatal("setsid: can't exec: %r"); 502 return 1; /* NOTREACHED */ 503 } 504 505 typedef struct exectab exectab; 506 struct exectab { 507 char *cmd; 508 int (*fn)(int, char**); 509 } fstab[] = { 510 {"cat", xread}, 511 {"create", xcreate}, 512 {"ls", xls}, 513 {"read", xread}, 514 {"remove", xremove}, 515 {"rm", xremove}, 516 {"write", xwrite}, 517 {"xwrite", xawrite}, 518 {0, } 519 }, utiltab[] = { 520 {"namespace", xnamespace}, 521 {"ns", xnamespace}, 522 {"proglist", xproglist}, 523 {"setsid", xsetsid}, 524 {0, } 525 }; 526 527 int 528 main(int argc, char *argv[]) { 529 char *address; 530 exectab *tab; 531 int ret; 532 533 IXP_ASSERT_VERSION; 534 535 setlocale(LC_ALL, ""); 536 binary = utf8locale(); 537 538 quotefmtinstall(); 539 fmtinstall('r', errfmt); 540 541 address = getenv("WMII_ADDRESS"); 542 543 ARGBEGIN{ 544 case 'b': 545 binary = true; 546 break; 547 case 'v': 548 lprint(1, "%s-" VERSION ", " COPYRIGHT "\n", argv0); 549 exit(0); 550 case 'a': 551 address = EARGF(usage()); 552 break; 553 default: 554 usage(); 555 }ARGEND; 556 557 if(argc < 1) 558 usage(); 559 560 outbuf = Bfdopen(1, OWRITE); 561 562 for(tab=utiltab; tab->cmd; tab++) 563 if(!strcmp(*argv, tab->cmd)) { 564 ret = tab->fn(argc, argv); 565 goto done; 566 } 567 568 if(address && *address) 569 client = ixp_mount(address); 570 else 571 client = ixp_nsmount("wmii"); 572 if(client == nil) 573 fatal("can't mount: %r\n"); 574 575 signal(SIGPIPE, SIG_DFL); 576 577 for(tab=fstab; tab->cmd; tab++) 578 if(strcmp(*argv, tab->cmd) == 0) break; 579 if(tab->cmd == 0) 580 usage(); 581 582 ret = tab->fn(argc, argv); 583 584 ixp_unmount(client); 585 done: 586 Bterm(outbuf); 587 return ret; 588 } 589