wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

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