|
1 // files.c - file operation builtins |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1996-1997 Andrew Main |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Andrew Main or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Andrew Main and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Andrew Main and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Andrew Main and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 #include "files.mdh" |
|
32 |
|
33 #ifdef __SYMBIAN32__ |
|
34 #ifdef __WINSCW__ |
|
35 #pragma warn_unusedarg off |
|
36 #endif//__WINSCW__ |
|
37 #endif//__SYMBIAN32__ |
|
38 |
|
39 typedef int (*MoveFunc) _((char const *, char const *)); |
|
40 typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *)); |
|
41 |
|
42 #ifndef STDC_HEADERS |
|
43 extern int link _((const char *, const char *)); |
|
44 extern int symlink _((const char *, const char *)); |
|
45 extern int rename _((const char *, const char *)); |
|
46 #endif |
|
47 |
|
48 struct recursivecmd; |
|
49 |
|
50 #include "files.pro" |
|
51 |
|
52 /**/ |
|
53 static int |
|
54 ask(void) |
|
55 { |
|
56 int a = getchar(), c; |
|
57 for(c = a; c != EOF && c != '\n'; ) |
|
58 c = getchar(); |
|
59 return a == 'y' || a == 'Y'; |
|
60 } |
|
61 |
|
62 /* sync builtin */ |
|
63 |
|
64 /**/ |
|
65 static int |
|
66 bin_sync(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) |
|
67 { |
|
68 sync(); |
|
69 return 0; |
|
70 } |
|
71 |
|
72 /* mkdir builtin */ |
|
73 |
|
74 /**/ |
|
75 int |
|
76 bin_mkdir(char *nam, char **args, Options ops, UNUSED(int func)) |
|
77 { |
|
78 mode_t oumask = umask(0); |
|
79 mode_t mode = 0777 & ~oumask; |
|
80 int err = 0; |
|
81 |
|
82 umask(oumask); |
|
83 if(OPT_ISSET(ops,'m')) { |
|
84 char *str = OPT_ARG(ops,'m'), *ptr; |
|
85 |
|
86 mode = zstrtol(str, &ptr, 8); |
|
87 if(!*str || *ptr) { |
|
88 zwarnnam(nam, "invalid mode `%s'", str, 0); |
|
89 return 1; |
|
90 } |
|
91 } |
|
92 for(; *args; args++) { |
|
93 char *ptr = strchr(*args, 0); |
|
94 |
|
95 while(ptr > *args + (**args == '\\') && *--ptr == '\\') |
|
96 *ptr = 0; |
|
97 if(OPT_ISSET(ops,'p')) { |
|
98 char *ptr = *args; |
|
99 |
|
100 for(;;) { |
|
101 while(*ptr == '\\') |
|
102 ptr++; |
|
103 while(*ptr && *ptr != '\\') |
|
104 ptr++; |
|
105 if(!*ptr) { |
|
106 err |= domkdir(nam, *args, mode, 1); |
|
107 break; |
|
108 } else { |
|
109 int e; |
|
110 |
|
111 *ptr = 0; |
|
112 e = domkdir(nam, *args, mode | 0300, 1); |
|
113 if(e) { |
|
114 err = 1; |
|
115 break; |
|
116 } |
|
117 *ptr = '\\'; |
|
118 } |
|
119 } |
|
120 } else |
|
121 err |= domkdir(nam, *args, mode, 0); |
|
122 } |
|
123 return err; |
|
124 } |
|
125 |
|
126 /**/ |
|
127 static int |
|
128 domkdir(char *nam, char *path, mode_t mode, int p) |
|
129 { |
|
130 int err; |
|
131 mode_t oumask; |
|
132 char const *rpath = unmeta(path); |
|
133 |
|
134 if(p) { |
|
135 struct stat st; |
|
136 |
|
137 if(!lstat(rpath, &st) && S_ISDIR(st.st_mode)) |
|
138 return 0; |
|
139 } |
|
140 oumask = umask(0); |
|
141 err = mkdir(path, mode) ? errno : 0; |
|
142 umask(oumask); |
|
143 if(!err) |
|
144 return 0; |
|
145 zwarnnam(nam, "cannot make directory `%s': %e", path, err); |
|
146 return 1; |
|
147 } |
|
148 |
|
149 /* rmdir builtin */ |
|
150 |
|
151 /**/ |
|
152 int |
|
153 bin_rmdir(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) |
|
154 { |
|
155 int err = 0; |
|
156 |
|
157 for(; *args; args++) { |
|
158 char *rpath = unmeta(*args); |
|
159 |
|
160 if(!rpath) { |
|
161 zwarnnam(nam, "%s: %e", *args, ENAMETOOLONG); |
|
162 err = 1; |
|
163 } else if(rmdir(rpath)) { |
|
164 zwarnnam(nam, "cannot remove directory `%s': %e", *args, errno); |
|
165 err = 1; |
|
166 } |
|
167 } |
|
168 return err; |
|
169 } |
|
170 |
|
171 /* ln and mv builtins */ |
|
172 |
|
173 //#define BIN_LN 0 |
|
174 //#define BIN_MV 1 |
|
175 |
|
176 #define MV_NODIRS (1<<0) |
|
177 #define MV_FORCE (1<<1) |
|
178 #define MV_INTER (1<<2) |
|
179 #define MV_ASKNW (1<<3) |
|
180 #define MV_ATOMIC (1<<4) |
|
181 |
|
182 /* bin_ln actually does three related jobs: hard linking, symbolic * |
|
183 * linking, and renaming. If called as mv it renames, otherwise * |
|
184 * it looks at the -s option. If hard linking, it will refuse to * |
|
185 * attempt linking to a directory unless the -d option is given. */ |
|
186 |
|
187 /**/ |
|
188 int |
|
189 bin_ln(char *nam, char **args, Options ops, int func) |
|
190 { |
|
191 MoveFunc move; |
|
192 int flags, err = 0; |
|
193 char **a, *ptr, *rp, *buf; |
|
194 struct stat st; |
|
195 size_t blen; |
|
196 |
|
197 |
|
198 if(func == BIN_MV) { |
|
199 move = (MoveFunc) rename; |
|
200 flags = OPT_ISSET(ops,'f') ? 0 : MV_ASKNW; |
|
201 flags |= MV_ATOMIC; |
|
202 } else { |
|
203 flags = OPT_ISSET(ops,'f') ? MV_FORCE : 0; |
|
204 #ifdef HAVE_LSTAT |
|
205 if(OPT_ISSET(ops,'s')) |
|
206 move = (MoveFunc) symlink; |
|
207 else |
|
208 #endif |
|
209 { |
|
210 move = (MoveFunc) link; |
|
211 if(!OPT_ISSET(ops,'d')) |
|
212 flags |= MV_NODIRS; |
|
213 } |
|
214 } |
|
215 if(OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f')) |
|
216 flags |= MV_INTER; |
|
217 for(a = args; a[1]; a++) ; |
|
218 if(a != args) { |
|
219 rp = unmeta(*a); |
|
220 if(rp && !stat(rp, &st) && S_ISDIR(st.st_mode)) |
|
221 goto havedir; |
|
222 } |
|
223 if(a > args+1) { |
|
224 zwarnnam(nam, "last of many arguments must be a directory", NULL, 0); |
|
225 return 1; |
|
226 } |
|
227 if(!args[1]) { |
|
228 ptr = strrchr(args[0], '\\'); |
|
229 if(ptr) |
|
230 args[1] = ptr+1; |
|
231 else |
|
232 args[1] = args[0]; |
|
233 } |
|
234 return domove(nam, move, args[0], args[1], flags); |
|
235 havedir: |
|
236 buf = ztrdup(*a); |
|
237 *a = NULL; |
|
238 buf = appstr(buf, "\\"); |
|
239 blen = strlen(buf); |
|
240 for(; *args; args++) { |
|
241 |
|
242 ptr = strrchr(*args, '\\'); |
|
243 if(ptr) |
|
244 ptr++; |
|
245 else |
|
246 ptr = *args; |
|
247 |
|
248 buf[blen] = 0; |
|
249 buf = appstr(buf, ptr); |
|
250 err |= domove(nam, move, *args, buf, flags); |
|
251 } |
|
252 zsfree(buf); |
|
253 return err; |
|
254 } |
|
255 |
|
256 /**/ |
|
257 static int |
|
258 domove(char *nam, MoveFunc move, char *p, char *q, int flags) |
|
259 { |
|
260 struct stat st; |
|
261 char *pbuf, *qbuf; |
|
262 |
|
263 pbuf = ztrdup(unmeta(p)); |
|
264 qbuf = unmeta(q); |
|
265 if(flags & MV_NODIRS) { |
|
266 errno = EISDIR; |
|
267 if(lstat(pbuf, &st) || S_ISDIR(st.st_mode)) { |
|
268 zwarnnam(nam, "%s: %e", p, errno); |
|
269 zsfree(pbuf); |
|
270 return 1; |
|
271 } |
|
272 } |
|
273 if(!lstat(qbuf, &st)) { |
|
274 int doit = flags & MV_FORCE; |
|
275 struct stat file_stat; |
|
276 if(S_ISDIR(st.st_mode)) { |
|
277 zwarnnam(nam, "%s: cannot overwrite directory", q, 0); |
|
278 zsfree(pbuf); |
|
279 return 1; |
|
280 }else if(lstat(pbuf, &file_stat)){ |
|
281 zwarnnam(nam, "%e: %s", q, errno); |
|
282 zsfree(pbuf); |
|
283 return 1; |
|
284 }else if(flags & MV_INTER) { |
|
285 nicezputs(nam, stderr); |
|
286 fputs(": replace `", stderr); |
|
287 nicezputs(q, stderr); |
|
288 fputs("'? ", stderr); |
|
289 fflush(stderr); |
|
290 if(!ask()) { |
|
291 zsfree(pbuf); |
|
292 return 0; |
|
293 } |
|
294 doit = 1; |
|
295 } else if((flags & MV_ASKNW) && |
|
296 !S_ISLNK(st.st_mode) && |
|
297 access(qbuf, W_OK)) { |
|
298 nicezputs(nam, stderr); |
|
299 fputs(": replace `", stderr); |
|
300 nicezputs(q, stderr); |
|
301 fprintf(stderr, "', overriding mode %04o? ", |
|
302 mode_to_octal(st.st_mode)); |
|
303 fflush(stderr); |
|
304 if(!ask()) { |
|
305 zsfree(pbuf); |
|
306 return 0; |
|
307 } |
|
308 doit = 1; |
|
309 } |
|
310 if(doit && !(flags & MV_ATOMIC)) |
|
311 unlink(qbuf); |
|
312 } |
|
313 if(move(pbuf, qbuf)) { |
|
314 zwarnnam(nam, "%s: %e", p, errno); |
|
315 zsfree(pbuf); |
|
316 return 1; |
|
317 } |
|
318 zsfree(pbuf); |
|
319 return 0; |
|
320 } |
|
321 |
|
322 /* general recursion */ |
|
323 |
|
324 struct recursivecmd { |
|
325 char *nam; |
|
326 int opt_noerr; |
|
327 int opt_recurse; |
|
328 int opt_safe; |
|
329 RecurseFunc dirpre_func; |
|
330 RecurseFunc dirpost_func; |
|
331 RecurseFunc leaf_func; |
|
332 void *magic; |
|
333 }; |
|
334 |
|
335 /**/ |
|
336 static int |
|
337 recursivecmd(char *nam, int opt_noerr, int opt_recurse, int opt_safe, |
|
338 char **args, RecurseFunc dirpre_func, RecurseFunc dirpost_func, |
|
339 RecurseFunc leaf_func, void *magic) |
|
340 { |
|
341 int err = 0, len; |
|
342 char *rp, *s; |
|
343 struct dirsav ds; |
|
344 struct recursivecmd reccmd; |
|
345 |
|
346 reccmd.nam = nam; |
|
347 reccmd.opt_noerr = opt_noerr; |
|
348 reccmd.opt_recurse = opt_recurse; |
|
349 reccmd.opt_safe = opt_safe; |
|
350 reccmd.dirpre_func = dirpre_func; |
|
351 reccmd.dirpost_func = dirpost_func; |
|
352 reccmd.leaf_func = leaf_func; |
|
353 reccmd.magic = magic; |
|
354 ds.ino = ds.dev = 0; |
|
355 ds.dirname = NULL; |
|
356 ds.dirfd = ds.level = -1; |
|
357 if (opt_recurse || opt_safe) { |
|
358 if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 && |
|
359 zgetdir(&ds) && *ds.dirname != '\\') |
|
360 ds.dirfd = open("..", O_RDONLY|O_NOCTTY); |
|
361 } |
|
362 for(; !errflag && !(err & 2) && *args; args++) { |
|
363 rp = ztrdup(*args); |
|
364 unmetafy(rp, &len); |
|
365 if (opt_safe) { |
|
366 s = strrchr(rp, '\\'); |
|
367 if (s && !s[1]) { |
|
368 while (*s == '\\' && s > rp) |
|
369 *s-- = '\0'; |
|
370 while (*s != '\\' && s > rp) |
|
371 s--; |
|
372 } |
|
373 if (s && s[1]) { |
|
374 int e; |
|
375 |
|
376 *s = '\0'; |
|
377 e = lchdir(s > rp ? rp : "\\", &ds, 1); |
|
378 err |= -e; |
|
379 if (!e) { |
|
380 struct dirsav d; |
|
381 |
|
382 d.ino = d.dev = 0; |
|
383 d.dirname = NULL; |
|
384 d.dirfd = d.level = -1; |
|
385 err |= recursivecmd_doone(&reccmd, *args, s + 1, &d, 0); |
|
386 zsfree(d.dirname); |
|
387 if (restoredir(&ds)) |
|
388 err |= 2; |
|
389 } else if(!opt_noerr) |
|
390 zwarnnam(nam, "%s: %e", *args, errno); |
|
391 } else |
|
392 err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 0); |
|
393 } else |
|
394 err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 1); |
|
395 zfree(rp, len + 1); |
|
396 } |
|
397 if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) { |
|
398 zsfree(pwd); |
|
399 pwd = ztrdup("\\"); |
|
400 chdir(pwd); |
|
401 } |
|
402 if (ds.dirfd >= 0) |
|
403 close(ds.dirfd); |
|
404 zsfree(ds.dirname); |
|
405 return !!err; |
|
406 } |
|
407 |
|
408 /* rm builtin */ |
|
409 |
|
410 struct rmmagic { |
|
411 char *nam; |
|
412 int opt_force; |
|
413 int opt_interact; |
|
414 int opt_unlinkdir; |
|
415 }; |
|
416 |
|
417 /**/ |
|
418 static int |
|
419 recursivecmd_doone(struct recursivecmd const *reccmd, |
|
420 char *arg, char *rp, struct dirsav *ds, int first) |
|
421 { |
|
422 struct stat st, *sp = NULL; |
|
423 struct rmmagic *rmm = reccmd->magic; |
|
424 |
|
425 if( (reccmd->opt_recurse || rmm->opt_unlinkdir) && !lstat(rp, &st)) { |
|
426 if(S_ISDIR(st.st_mode)) |
|
427 return recursivecmd_dorec(reccmd, arg, rp, &st, ds, first); |
|
428 sp = &st; |
|
429 } |
|
430 return reccmd->leaf_func(arg, rp, sp, reccmd->magic); |
|
431 } |
|
432 |
|
433 /**/ |
|
434 static int |
|
435 recursivecmd_dorec(struct recursivecmd const *reccmd, |
|
436 char *arg, char *rp, struct stat const *sp, struct dirsav *ds, int first) |
|
437 { |
|
438 char *fn; |
|
439 DIR *d; |
|
440 int err, err1; |
|
441 struct dirsav dsav; |
|
442 char *files = NULL; |
|
443 int fileslen = 0; |
|
444 |
|
445 err1 = reccmd->dirpre_func(arg, rp, sp, reccmd->magic); |
|
446 if(err1 & 2) |
|
447 return 2; |
|
448 |
|
449 err = -lchdir(rp, ds, !first); |
|
450 if (err) { |
|
451 if(!reccmd->opt_noerr) |
|
452 zwarnnam(reccmd->nam, "%s: %e", arg, errno); |
|
453 return err; |
|
454 } |
|
455 err = err1; |
|
456 |
|
457 dsav.ino = dsav.dev = 0; |
|
458 dsav.dirname = NULL; |
|
459 dsav.dirfd = dsav.level = -1; |
|
460 d = opendir("."); |
|
461 if(!d) { |
|
462 if(!reccmd->opt_noerr) |
|
463 zwarnnam(reccmd->nam, "%s: %e", arg, errno); |
|
464 err = 1; |
|
465 } else { |
|
466 int arglen = strlen(arg) + 1; |
|
467 |
|
468 while (!errflag && (fn = zreaddir(d, 1))) { |
|
469 int l = strlen(fn) + 1; |
|
470 files = hrealloc(files, fileslen, fileslen + l); |
|
471 strcpy(files + fileslen, fn); |
|
472 fileslen += l; |
|
473 } |
|
474 closedir(d); |
|
475 for (fn = files; !errflag && !(err & 2) && fn < files + fileslen;) { |
|
476 int l = strlen(fn) + 1; |
|
477 int is_dir=0; |
|
478 |
|
479 VARARR(char, narg, arglen + l); |
|
480 strcpy(narg,arg); |
|
481 narg[arglen-1] = '\\'; |
|
482 strcpy(narg + arglen, fn); |
|
483 unmetafy(fn, NULL); |
|
484 #ifdef __SYMBIAN32__ |
|
485 { |
|
486 struct stat sta; |
|
487 if(stat(fn, &sta)==0 && S_ISDIR(sta.st_mode)) |
|
488 is_dir=1; |
|
489 } |
|
490 #endif |
|
491 err |= recursivecmd_doone(reccmd, narg, fn, &dsav, 0); |
|
492 |
|
493 if(is_dir) |
|
494 ds=&dsav; |
|
495 fn += l; |
|
496 } |
|
497 hrealloc(files, fileslen, 0); |
|
498 } |
|
499 zsfree(dsav.dirname); |
|
500 if (err & 2) |
|
501 return 2; |
|
502 if (restoredir(ds)) { |
|
503 if(!reccmd->opt_noerr) |
|
504 zwarnnam(reccmd->nam, "failed to return to previous directory: %e", |
|
505 NULL, errno); |
|
506 return 2; |
|
507 } |
|
508 return err | reccmd->dirpost_func(arg, rp, sp, reccmd->magic); |
|
509 } |
|
510 |
|
511 /**/ |
|
512 static int |
|
513 recurse_donothing(UNUSED(char *arg), UNUSED(char *rp), UNUSED(struct stat const *sp), UNUSED(void *magic)) |
|
514 { |
|
515 return 0; |
|
516 } |
|
517 |
|
518 |
|
519 /**/ |
|
520 static int |
|
521 rm_leaf(char *arg, char *rp, struct stat const *sp, void *magic) |
|
522 { |
|
523 struct rmmagic *rmm = magic; |
|
524 struct stat st; |
|
525 |
|
526 if(!rmm->opt_unlinkdir || !rmm->opt_force) { |
|
527 if(!sp) { |
|
528 if(!lstat(rp, &st)) |
|
529 sp = &st; |
|
530 } |
|
531 if(sp) { |
|
532 if(!rmm->opt_unlinkdir && S_ISDIR(sp->st_mode)) { |
|
533 #ifndef __SYMBIAN32__ |
|
534 if(rmm->opt_force) |
|
535 return 0; |
|
536 #endif |
|
537 zwarnnam(rmm->nam, "%s: %e", arg, EISDIR); |
|
538 return 1; |
|
539 } |
|
540 if(rmm->opt_interact) { |
|
541 nicezputs(rmm->nam, stderr); |
|
542 fputs(": remove `", stderr); |
|
543 nicezputs(arg, stderr); |
|
544 fputs("'? ", stderr); |
|
545 fflush(stderr); |
|
546 if(!ask()) |
|
547 return 0; |
|
548 } else if(!rmm->opt_force && |
|
549 !S_ISLNK(sp->st_mode) && |
|
550 access(rp, W_OK)) { |
|
551 nicezputs(rmm->nam, stderr); |
|
552 fputs(": remove `", stderr); |
|
553 nicezputs(arg, stderr); |
|
554 fprintf(stderr, "', overriding mode %04o? ", |
|
555 mode_to_octal(sp->st_mode)); |
|
556 fflush(stderr); |
|
557 if(!ask()) |
|
558 return 0; |
|
559 } |
|
560 } |
|
561 } |
|
562 if(unlink(rp) && !rmm->opt_force) { |
|
563 zwarnnam(rmm->nam, "%s: %e", arg, errno); |
|
564 return 1; |
|
565 } |
|
566 return 0; |
|
567 } |
|
568 |
|
569 /**/ |
|
570 static int |
|
571 rm_dirpost(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic) |
|
572 { |
|
573 struct rmmagic *rmm = magic; |
|
574 |
|
575 if(rmm->opt_interact) { |
|
576 nicezputs(rmm->nam, stderr); |
|
577 fputs(": remove `", stderr); |
|
578 nicezputs(arg, stderr); |
|
579 fputs("'? ", stderr); |
|
580 fflush(stderr); |
|
581 if(!ask()) |
|
582 return 0; |
|
583 } |
|
584 if(rmdir(rp) && !rmm->opt_force) { |
|
585 zwarnnam(rmm->nam, "%s: %e", arg, errno); |
|
586 return 1; |
|
587 } |
|
588 return 0; |
|
589 } |
|
590 |
|
591 /**/ |
|
592 int |
|
593 bin_rm(char *nam, char **args, Options ops, UNUSED(int func)) |
|
594 { |
|
595 struct rmmagic rmm; |
|
596 int err; |
|
597 |
|
598 rmm.nam = nam; |
|
599 rmm.opt_force = OPT_ISSET(ops,'f'); |
|
600 rmm.opt_interact = OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f'); |
|
601 rmm.opt_unlinkdir = OPT_ISSET(ops,'d'); |
|
602 err = recursivecmd(nam, OPT_ISSET(ops,'f'), |
|
603 OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'d'), |
|
604 OPT_ISSET(ops,'s'), |
|
605 args, recurse_donothing, rm_dirpost, rm_leaf, &rmm); |
|
606 return OPT_ISSET(ops,'f') ? 0 : err; |
|
607 } |
|
608 |
|
609 /* chown builtin */ |
|
610 |
|
611 struct chownmagic { |
|
612 char *nam; |
|
613 uid_t uid; |
|
614 gid_t gid; |
|
615 }; |
|
616 |
|
617 /**/ |
|
618 static int |
|
619 chown_dochown(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic) |
|
620 { |
|
621 struct chownmagic *chm = magic; |
|
622 |
|
623 if(lchown(rp, chm->uid, chm->gid)) { |
|
624 zwarnnam(chm->nam, "%s: %e", arg, errno); |
|
625 return 1; |
|
626 } |
|
627 return 0; |
|
628 } |
|
629 |
|
630 /**/ |
|
631 static unsigned long getnumeric(char *p, int *errp) |
|
632 { |
|
633 unsigned long ret; |
|
634 |
|
635 if(*p < '0' || *p > '9') { |
|
636 *errp = 1; |
|
637 return 0; |
|
638 } |
|
639 ret = strtoul(p, &p, 10); |
|
640 *errp = !!*p; |
|
641 return ret; |
|
642 } |
|
643 |
|
644 //enum { BIN_CHOWN, BIN_CHGRP }; |
|
645 |
|
646 /**/ |
|
647 int |
|
648 bin_chown(char *nam, char **args, Options ops, int func) |
|
649 { |
|
650 struct chownmagic chm; |
|
651 char *uspec = ztrdup(*args), *p = uspec; |
|
652 char *end; |
|
653 |
|
654 chm.nam = nam; |
|
655 if(func == BIN_CHGRP) { |
|
656 chm.uid = (uid_t)-1; |
|
657 goto dogroup; |
|
658 } |
|
659 end = strchr(uspec, ':'); |
|
660 if(!end) |
|
661 end = strchr(uspec, '.'); |
|
662 if(end == uspec) { |
|
663 chm.uid = (uid_t)-1; |
|
664 p++; |
|
665 goto dogroup; |
|
666 } else { |
|
667 struct passwd *pwd; |
|
668 if(end) |
|
669 *end = 0; |
|
670 pwd = getpwnam(p); |
|
671 if(pwd) |
|
672 chm.uid = pwd->pw_uid; |
|
673 else { |
|
674 int err; |
|
675 chm.uid = getnumeric(p, &err); |
|
676 if(err) { |
|
677 zwarnnam(nam, "%s: no such user", p, 0); |
|
678 free(uspec); |
|
679 return 1; |
|
680 } |
|
681 } |
|
682 if(end) { |
|
683 p = end+1; |
|
684 if(!*p) { |
|
685 if(!pwd && !(pwd = getpwuid(chm.uid))) { |
|
686 zwarnnam(nam, "%s: no such user", uspec, 0); |
|
687 free(uspec); |
|
688 return 1; |
|
689 } |
|
690 chm.gid = pwd->pw_gid; |
|
691 } else if(p[0] == ':' && !p[1]) { |
|
692 chm.gid = (gid_t)-1; |
|
693 } else { |
|
694 struct group *grp; |
|
695 dogroup: |
|
696 grp = getgrnam(p); |
|
697 if(grp) |
|
698 chm.gid = grp->gr_gid; |
|
699 else { |
|
700 int err; |
|
701 chm.gid = getnumeric(p, &err); |
|
702 if(err) { |
|
703 zwarnnam(nam, "%s: no such group", p, 0); |
|
704 free(uspec); |
|
705 return 1; |
|
706 } |
|
707 } |
|
708 } |
|
709 } else |
|
710 chm.gid = (gid_t)-1; |
|
711 } |
|
712 free(uspec); |
|
713 return recursivecmd(nam, 0, OPT_ISSET(ops,'R'), OPT_ISSET(ops,'s'), |
|
714 args + 1, chown_dochown, recurse_donothing, chown_dochown, &chm); |
|
715 } |
|
716 |
|
717 /* module paraphernalia */ |
|
718 |
|
719 #ifdef HAVE_LSTAT |
|
720 # define LN_OPTS "dfis" |
|
721 #else |
|
722 # define LN_OPTS "dfi" |
|
723 #endif |
|
724 |
|
725 static struct builtin bintab[] = { |
|
726 BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "Rs", NULL), |
|
727 BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "Rs", NULL), |
|
728 BUILTIN("ln", 0, bin_ln, 1, -1, BIN_LN, LN_OPTS, NULL), |
|
729 // BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0, "pm:", NULL), |
|
730 BUILTIN("mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL), |
|
731 BUILTIN("rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL), |
|
732 BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL), |
|
733 BUILTIN("sync", 0, bin_sync, 0, 0, 0, NULL, NULL), |
|
734 }; |
|
735 |
|
736 /**/ |
|
737 int |
|
738 setup_(UNUSED(Module m)) |
|
739 { |
|
740 return 0; |
|
741 } |
|
742 |
|
743 /**/ |
|
744 int |
|
745 boot_(Module m) |
|
746 { |
|
747 return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); |
|
748 } |
|
749 |
|
750 /**/ |
|
751 int |
|
752 cleanup_(Module m) |
|
753 { |
|
754 deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); |
|
755 return 0; |
|
756 } |
|
757 |
|
758 /**/ |
|
759 int |
|
760 finish_(UNUSED(Module m)) |
|
761 { |
|
762 return 0; |
|
763 } |