1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1992-2010 AT&T Intellectual Property * 5* and is licensed under the * 6* Common Public License, Version 1.0 * 7* by AT&T Intellectual Property * 8* * 9* A copy of the License is available at * 10* http://www.opensource.org/licenses/cpl1.0.txt * 11* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12* * 13* Information and Software Systems Research * 14* AT&T Research * 15* Florham Park NJ * 16* * 17* Glenn Fowler <gsf@research.att.com> * 18* David Korn <dgk@research.att.com> * 19* * 20***********************************************************************/ 21#pragma prototyped 22/* 23 * Glenn Fowler 24 * AT&T Research 25 * 26 * cp/ln/mv -- copy/link/move files 27 */ 28 29static const char usage_head[] = 30"[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]" 31USAGE_LICENSE 32; 33 34static const char usage_cp[] = 35"[+NAME?cp - copy files]" 36"[+DESCRIPTION?If the last argument names an existing directory, \bcp\b" 37" copies each \afile\a into a file with the same name in that" 38" directory. Otherwise, if only two files are given, \bcp\b copies" 39" the first onto the second. It is an error if the last argument is" 40" not a directory and more than two files are given. By default" 41" directories are not copied.]" 42 43"[a:archive?Preserve as much as possible of the structure and attributes of" 44" the original files in the copy. Equivalent to \b--physical\b" 45" \b--preserve\b \b--recursive\b.]" 46"[p:preserve?Preserve file owner, group, permissions and timestamps.]" 47"[h:hierarchy|parents?Form the name of each destination file by appending" 48" to the target directory a slash and the specified source file name." 49" The last argument must be an existing directory. Missing destination" 50" directories are created.]" 51"[H:metaphysical?Follow command argument symbolic links, otherwise don't" 52" follow.]" 53"[l:link?Make hard links to destination files instead of copies.]" 54"[L:logical|dereference?Follow symbolic links and copy the files" 55" they point to.]" 56"[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic" 57" rather than the files they point to.]" 58; 59 60static const char usage_ln[] = 61"[+NAME?ln - link files]" 62"[+DESCRIPTION?If the last argument names an existing directory, \bln\b" 63" links each \afile\a into a file with the same name in that" 64" directory. Otherwise, if only two files are given, \bln\b links" 65" the first onto the second. It is an error if the last argument is" 66" not a directory and more than two files are given. By default" 67" directories are not linked.]" 68; 69 70static const char usage_mv[] = 71"[+NAME?mv - rename files]" 72"[+DESCRIPTION?If the last argument names an existing directory, \bmv\b" 73" renames each \afile\a into a file with the same name in that" 74" directory. Otherwise, if only two files are given, \bmv\b renames" 75" the first onto the second. It is an error if the last argument is" 76" not a directory and more than two files are given. If a source and" 77" destination file reside on different filesystems then \bmv\b copies" 78" the file contents to the destination and then deletes the source" 79" file.]" 80; 81 82static const char usage_tail[] = 83"[f:force?Replace existing destination files.]" 84"[i:interactive|prompt?Prompt whether to replace existing destination files." 85" An affirmative response (\by\b or \bY\b) replaces the file, a quit" 86" response (\bq\b or \bQ\b) exits immediately, and all other" 87" responses skip the file.]" 88"[r|R:recursive?Operate on the contents of directories recursively.]" 89"[s:symlink|symbolic-link?Make symbolic links to destination files.]" 90"[u:update?Replace a destination file only if its modification time is older" 91" than the corresponding source file modification time.]" 92"[v:verbose?Print the name of each file before operating on it.]" 93"[b:backup?Make backups of files that are about to be replaced. See" 94" \b--suffix\b and \b--version-control\b for more information.]" 95"[F:fsync|sync?\bfsync\b(2) each file after it is copied.]" 96"[S:backup-suffix|suffix?A backup file is made by renaming the file to the" 97" same name with the backup suffix appended. The backup suffix is" 98" determined in this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b," 99" environment variable, or the default value \b~\b.]:[suffix]" 100"[V:backup-type|version-control?The backup type is determined in this order:" 101" this option, the \bVERSION_CONTROL\b environment variable, or the" 102" default value \bexisting\b. \atype\a may be one of:]:[type]{" 103" [+numbered|t?Always make numbered backups. The numbered backup" 104" suffix is \b.\aSNS\a, where \aS\a is the" 105" \bbackup-suffix\b and \aN\a is the version number," 106" starting at 1, incremented with each version.]" 107" [+existing|nil?Make numbered backups of files that already" 108" have them, otherwise simple backups.]" 109" [+simple|never?Always make simple backups.]" 110"}" 111"[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in" 112" different filesystems than their parents.]" 113 114"\n" 115"\nsource destination\n" 116"file ... directory\n" 117"\n" 118 119"[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2)," 120" \bremove\b(3)]" 121; 122 123#include <cmd.h> 124#include <ls.h> 125#include <times.h> 126#include <fts_fix.h> 127#include <fs3d.h> 128#include <hashkey.h> 129#include <stk.h> 130#include <tmx.h> 131 132#define PATH_CHUNK 256 133 134#define CP 1 135#define LN 2 136#define MV 3 137 138#define BAK_replace 0 /* no backup -- just replace */ 139#define BAK_existing 1 /* number if already else simple*/ 140#define BAK_number 2 /* append .suffix number suffix */ 141#define BAK_simple 3 /* append suffix */ 142 143typedef struct State_s /* program state */ 144{ 145 void* context; /* builtin context */ 146 int backup; /* BAK_* type */ 147 int directory; /* destination is directory */ 148 int flags; /* FTS_* flags */ 149 int force; /* force approval */ 150 int fs3d; /* 3d fs enabled */ 151 int hierarchy; /* preserve hierarchy */ 152 int interactive; /* prompt for approval */ 153 int missmode; /* default missing dir mode */ 154 int official; /* move to next view */ 155 int op; /* {CP,LN,MV} */ 156 int perm; /* permissions to preserve */ 157 int postsiz; /* state.path post index */ 158 int presiz; /* state.path pre index */ 159 int preserve; /* preserve { id mode time } */ 160 int recursive; /* subtrees too */ 161 int suflen; /* strlen(state.suffix) */ 162 int sync; /* fsync() each file after copy */ 163 int uid; /* caller uid */ 164 int update; /* replace only if newer */ 165 int verbose; /* list each file before op */ 166 int wflags; /* open() for write flags */ 167 168 int (*link)(const char*, const char*); /* link */ 169 int (*stat)(const char*, struct stat*); /* stat */ 170 171#define INITSTATE pathsiz /* (re)init state before this */ 172 int pathsiz; /* state.path buffer size */ 173 174 175 char* path; /* to pathname buffer */ 176 char* opname; /* state.op message string */ 177 char* suffix; /* backup suffix */ 178 179 Sfio_t* tmp; /* tmp string stream */ 180 181 char text[PATH_MAX]; /* link text buffer */ 182} State_t; 183 184static const char dot[2] = { '.' }; 185 186/* 187 * preserve support 188 */ 189 190static void 191preserve(State_t* state, const char* path, struct stat* ns, struct stat* os) 192{ 193 int n; 194 195 if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0)) 196 error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path); 197 n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid); 198 if (n && chown(state->path, os->st_uid, os->st_gid)) 199 switch (n) 200 { 201 case 01: 202 error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid)); 203 break; 204 case 02: 205 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid)); 206 break; 207 case 03: 208 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid)); 209 break; 210 } 211} 212 213/* 214 * visit a single file and state.op to the destination 215 */ 216 217static int 218visit(State_t* state, register FTSENT* ent) 219{ 220 register char* base; 221 register int n; 222 register int len; 223 int rm; 224 int rfd; 225 int wfd; 226 int m; 227 int v; 228 char* s; 229 char* e; 230 char* protection; 231 Sfio_t* ip; 232 Sfio_t* op; 233 FTS* fts; 234 FTSENT* sub; 235 struct stat st; 236 237 if (ent->fts_info == FTS_DC) 238 { 239 error(2, "%s: directory causes cycle", ent->fts_path); 240 fts_set(NiL, ent, FTS_SKIP); 241 return 0; 242 } 243 if (ent->fts_level == 0) 244 { 245 base = ent->fts_name; 246 len = ent->fts_namelen; 247 if (state->hierarchy) 248 state->presiz = -1; 249 else 250 { 251 state->presiz = ent->fts_pathlen; 252 while (*base == '.' && *(base + 1) == '/') 253 for (base += 2; *base == '/'; base++); 254 if (*base == '.' && !*(base + 1)) 255 state->presiz--; 256 else if (*base) 257 state->presiz -= base - ent->fts_name; 258 base = ent->fts_name + len; 259 while (base > ent->fts_name && *(base - 1) == '/') 260 base--; 261 while (base > ent->fts_name && *(base - 1) != '/') 262 base--; 263 len -= base - ent->fts_name; 264 if (state->directory) 265 state->presiz -= len + 1; 266 } 267 } 268 else 269 { 270 base = ent->fts_path + state->presiz + 1; 271 len = ent->fts_pathlen - state->presiz - 1; 272 } 273 len++; 274 if (state->directory) 275 { 276 if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0))) 277 error(ERROR_SYSTEM|3, "out of space"); 278 if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/')) 279 { 280 s = state->path + state->postsiz; 281 memcpy(s, base, len); 282 while (e = strchr(s, '/')) 283 { 284 *e = 0; 285 if (access(state->path, F_OK)) 286 { 287 st.st_mode = state->missmode; 288 if (s = strrchr(s, '/')) 289 { 290 *s = 0; 291 stat(state->path, &st); 292 *s = '/'; 293 } 294 if (mkdir(state->path, st.st_mode & S_IPERM)) 295 { 296 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 297 fts_set(NiL, ent, FTS_SKIP); 298 return 0; 299 } 300 } 301 *e++ = '/'; 302 s = e; 303 } 304 } 305 } 306 switch (ent->fts_info) 307 { 308 case FTS_DP: 309 if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU) 310 { 311 if (len && ent->fts_level > 0) 312 memcpy(state->path + state->postsiz, base, len); 313 else 314 state->path[state->postsiz] = 0; 315 if (stat(state->path, &st)) 316 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 317 else 318 { 319 if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM)) 320 error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1); 321 if (state->preserve) 322 preserve(state, state->path, &st, ent->fts_statp); 323 } 324 } 325 return 0; 326 case FTS_DNR: 327 case FTS_DNX: 328 case FTS_D: 329 if (!state->recursive) 330 { 331 fts_set(NiL, ent, FTS_SKIP); 332 if (state->op == CP) 333 error(1, "%s: directory -- copying as plain file", ent->fts_path); 334 else if (state->link == link && !state->force) 335 { 336 error(2, "%s: cannot link directory", ent->fts_path); 337 return 0; 338 } 339 } 340 else switch (ent->fts_info) 341 { 342 case FTS_DNR: 343 error(2, "%s: cannot read directory", ent->fts_path); 344 return 0; 345 case FTS_DNX: 346 error(2, "%s: cannot search directory", ent->fts_path); 347 fts_set(NiL, ent, FTS_SKIP); 348 349 /*FALLTHROUGH*/ 350 case FTS_D: 351 if (state->directory) 352 memcpy(state->path + state->postsiz, base, len); 353 if (!(*state->stat)(state->path, &st)) 354 { 355 if (!S_ISDIR(st.st_mode)) 356 { 357 error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path); 358 return 0; 359 } 360 } 361 else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0))) 362 { 363 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 364 fts_set(NiL, ent, FTS_SKIP); 365 } 366 if (!state->directory) 367 { 368 state->directory = 1; 369 state->path[state->postsiz++] = '/'; 370 state->presiz--; 371 } 372 return 0; 373 } 374 break; 375 case FTS_ERR: 376 case FTS_NS: 377 case FTS_SLNONE: 378 if (state->link != pathsetlink) 379 { 380 error(2, "%s: not found", ent->fts_path); 381 return 0; 382 } 383 break; 384#if 0 385 case FTS_SL: 386 if (state->op == CP) 387 { 388 error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path); 389 return 0; 390 } 391 break; 392#endif 393 } 394 if (state->directory) 395 memcpy(state->path + state->postsiz, base, len); 396 if ((*state->stat)(state->path, &st)) 397 st.st_mode = 0; 398 else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime) 399 { 400 fts_set(NiL, ent, FTS_SKIP); 401 return 0; 402 } 403 else if (!state->fs3d || !iview(&st)) 404 { 405 /* 406 * target is in top 3d view 407 */ 408 409 if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino) 410 { 411 if (state->op == MV) 412 { 413 /* 414 * let rename() handle it 415 */ 416 417 if (state->verbose) 418 sfputr(sfstdout, state->path, '\n'); 419 goto operate; 420 } 421 if (!state->official) 422 error(2, "%s: identical to %s", state->path, ent->fts_path); 423 return 0; 424 } 425 if (S_ISDIR(st.st_mode)) 426 { 427 error(2, "%s: cannot %s existing directory", state->path, state->opname); 428 return 0; 429 } 430 if (state->verbose) 431 sfputr(sfstdout, state->path, '\n'); 432 rm = state->op == LN || ent->fts_info == FTS_SL; 433 if (!rm || !state->force) 434 { 435 if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0) 436 { 437 close(n); 438 if (state->force) 439 /* ok */; 440 else if (state->interactive) 441 { 442 if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context)) 443 return 0; 444 } 445 else if (state->op == LN) 446 { 447 error(2, "%s: cannot %s existing file", state->path, state->opname); 448 return 0; 449 } 450 } 451 else if (state->force) 452 rm = 1; 453 else 454 { 455 protection = 456#ifdef ETXTBSY 457 errno == ETXTBSY ? "``running program''" : 458#endif 459 st.st_uid != state->uid ? "``not owner''" : 460 fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1; 461 if (state->interactive) 462 { 463 if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context)) 464 return 0; 465 rm = 1; 466 } 467 else if (!rm) 468 { 469 error(2, "%s: cannot %s %s protection", state->path, state->opname, protection); 470 return 0; 471 } 472 } 473 } 474 switch (state->backup) 475 { 476 case BAK_existing: 477 case BAK_number: 478 v = 0; 479 if (s = strrchr(state->path, '/')) 480 { 481 e = state->path; 482 *s++ = 0; 483 } 484 else 485 { 486 e = (char*)dot; 487 s = state->path; 488 } 489 n = strlen(s); 490 if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL)) 491 { 492 while (sub = fts_read(fts)) 493 { 494 if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v) 495 v = m; 496 if (sub->fts_level) 497 fts_set(NiL, sub, FTS_SKIP); 498 } 499 fts_close(fts); 500 } 501 if (s != state->path) 502 *--s = '/'; 503 if (v || state->backup == BAK_number) 504 { 505 sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix); 506 goto backup; 507 } 508 /*FALLTHROUGH*/ 509 case BAK_simple: 510 sfprintf(state->tmp, "%s%s", state->path, state->suffix); 511 backup: 512 if (!(s = sfstruse(state->tmp))) 513 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 514 if (rename(state->path, s)) 515 { 516 error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s); 517 return 0; 518 } 519 break; 520 default: 521 if (rm && remove(state->path)) 522 { 523 error(ERROR_SYSTEM|2, "%s: cannot remove", state->path); 524 return 0; 525 } 526 break; 527 } 528 } 529 operate: 530 switch (state->op) 531 { 532 case MV: 533 for (;;) 534 { 535 if (!rename(ent->fts_path, state->path)) 536 return 0; 537 if (errno == ENOENT) 538 rm = 1; 539 else if (!rm && st.st_mode && !remove(state->path)) 540 { 541 rm = 1; 542 continue; 543 } 544 if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode))) 545 { 546 error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path); 547 return 0; 548 } 549 else 550 break; 551 } 552 /*FALLTHROUGH*/ 553 case CP: 554 if (S_ISLNK(ent->fts_statp->st_mode)) 555 { 556 if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0) 557 { 558 error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path); 559 return 0; 560 } 561 state->text[n] = 0; 562 if (pathsetlink(state->text, state->path)) 563 { 564 error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path); 565 return 0; 566 } 567 } 568 else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode)) 569 { 570 if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0) 571 { 572 error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path); 573 return 0; 574 } 575 else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0) 576 { 577 error(ERROR_SYSTEM|2, "%s: cannot write", state->path); 578 if (ent->fts_statp->st_size > 0) 579 close(rfd); 580 return 0; 581 } 582 else if (ent->fts_statp->st_size > 0) 583 { 584 if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ))) 585 { 586 error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path); 587 close(rfd); 588 close(wfd); 589 } 590 else 591 { 592 n = 0; 593 if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE))) 594 { 595 error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path); 596 close(wfd); 597 sfclose(ip); 598 } 599 else 600 { 601 if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0) 602 n |= 3; 603 if (!sfeof(ip)) 604 n |= 1; 605 if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op)) 606 n |= 2; 607 if (sfclose(ip)) 608 n |= 1; 609 if (n) 610 error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io")); 611 } 612 } 613 } 614 else 615 close(wfd); 616 } 617 else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode)) 618 { 619 if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp))) 620 { 621 error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path); 622 return 0; 623 } 624 } 625 else 626 { 627 error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode)); 628 return 0; 629 } 630 if (state->preserve) 631 { 632 if (ent->fts_info != FTS_SL) 633 { 634 if (stat(state->path, &st)) 635 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 636 else 637 { 638 if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm)) 639 error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1); 640 preserve(state, state->path, &st, ent->fts_statp); 641 } 642 } 643 if (state->op == MV && remove(ent->fts_path)) 644 error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path); 645 } 646 break; 647 case LN: 648 if ((*state->link)(ent->fts_path, state->path)) 649 error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path); 650 break; 651 } 652 return 0; 653} 654 655int 656b_cp(int argc, register char** argv, void* context) 657{ 658 register char* file; 659 register char* s; 660 char** v; 661 char* backup_type; 662 FTS* fts; 663 FTSENT* ent; 664 const char* usage; 665 int path_resolve; 666 int standard; 667 struct stat st; 668 State_t* state; 669 Shbltin_t* sh; 670 void* cleanup = context; 671 672 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 673 if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr)) 674 { 675 if (!(state = newof(0, State_t, 1, 0))) 676 error(ERROR_SYSTEM|3, "out of space"); 677 if (sh) 678 sh->ptr = state; 679 } 680 else 681 memset(state, 0, offsetof(State_t, INITSTATE)); 682 state->context = context; 683 state->presiz = -1; 684 backup_type = 0; 685 state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR; 686 state->uid = geteuid(); 687 state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; 688 if (!state->tmp && !(state->tmp = sfstropen())) 689 error(ERROR_SYSTEM|3, "out of space [tmp string]"); 690 sfputr(state->tmp, usage_head, -1); 691 standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"); 692 switch (error_info.id[0]) 693 { 694 case 'c': 695 case 'C': 696 sfputr(state->tmp, usage_cp, -1); 697 state->op = CP; 698 state->stat = stat; 699 path_resolve = -1; 700 break; 701 case 'l': 702 case 'L': 703 sfputr(state->tmp, usage_ln, -1); 704 state->op = LN; 705 state->flags |= FTS_PHYSICAL; 706 state->link = link; 707 state->stat = lstat; 708 path_resolve = 1; 709 break; 710 case 'm': 711 case 'M': 712 sfputr(state->tmp, usage_mv, -1); 713 state->op = MV; 714 state->flags |= FTS_PHYSICAL; 715 state->preserve = 1; 716 state->stat = lstat; 717 path_resolve = 1; 718 break; 719 default: 720 error(3, "not implemented"); 721 break; 722 } 723 sfputr(state->tmp, usage_tail, -1); 724 if (!(usage = sfstruse(state->tmp))) 725 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 726 state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace"); 727 for (;;) 728 { 729 switch (optget(argv, usage)) 730 { 731 case 'a': 732 state->flags |= FTS_PHYSICAL; 733 state->preserve = 1; 734 state->recursive = 1; 735 path_resolve = 1; 736 continue; 737 case 'b': 738 state->backup = 1; 739 continue; 740 case 'f': 741 state->force = 1; 742 if (state->op != CP || !standard) 743 state->interactive = 0; 744 continue; 745 case 'h': 746 state->hierarchy = 1; 747 continue; 748 case 'i': 749 state->interactive = 1; 750 if (state->op != CP || !standard) 751 state->force = 0; 752 continue; 753 case 'l': 754 state->op = LN; 755 state->link = link; 756 state->stat = lstat; 757 continue; 758 case 'p': 759 state->preserve = 1; 760 continue; 761 case 'r': 762 state->recursive = 1; 763 if (path_resolve < 0) 764 path_resolve = 0; 765 continue; 766 case 's': 767 state->op = LN; 768 state->link = pathsetlink; 769 state->stat = lstat; 770 continue; 771 case 'u': 772 state->update = 1; 773 continue; 774 case 'v': 775 state->verbose = 1; 776 continue; 777 case 'x': 778 state->flags |= FTS_XDEV; 779 continue; 780 case 'F': 781#if _lib_fsync 782 state->sync = 1; 783#else 784 error(1, "%s not implemented on this system", opt_info.name); 785#endif 786 continue; 787 case 'H': 788 state->flags |= FTS_META|FTS_PHYSICAL; 789 path_resolve = 1; 790 continue; 791 case 'L': 792 state->flags &= ~FTS_PHYSICAL; 793 path_resolve = 1; 794 continue; 795 case 'P': 796 state->flags &= ~FTS_META; 797 state->flags |= FTS_PHYSICAL; 798 path_resolve = 1; 799 continue; 800 case 'R': 801 state->recursive = 1; 802 state->flags &= ~FTS_META; 803 state->flags |= FTS_PHYSICAL; 804 path_resolve = 1; 805 continue; 806 case 'S': 807 state->suffix = opt_info.arg; 808 continue; 809 case 'V': 810 backup_type = opt_info.arg; 811 continue; 812 case '?': 813 error(ERROR_USAGE|4, "%s", opt_info.arg); 814 continue; 815 case ':': 816 error(2, "%s", opt_info.arg); 817 continue; 818 } 819 break; 820 } 821 argc -= opt_info.index + 1; 822 argv += opt_info.index; 823 if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--")) 824 { 825 argc--; 826 argv++; 827 } 828 if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*)))) 829 error(ERROR_SYSTEM|3, "out of space"); 830 memcpy(v, argv, (argc + 1) * sizeof(char*)); 831 argv = v; 832 if (!standard) 833 { 834 state->wflags |= O_EXCL; 835 if (!argc) 836 { 837 argc++; 838 argv[1] = (char*)dot; 839 } 840 } 841 if (state->backup) 842 { 843 if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL"))) 844 state->backup = BAK_existing; 845 else 846 switch (strkey(backup_type)) 847 { 848 case HASHKEY6('e','x','i','s','t','i'): 849 case HASHKEY5('e','x','i','s','t'): 850 case HASHKEY4('e','x','i','s'): 851 case HASHKEY3('e','x','i'): 852 case HASHKEY2('e','x'): 853 case HASHKEY1('e'): 854 case HASHKEY3('n','i','l'): 855 case HASHKEY2('n','i'): 856 state->backup = BAK_existing; 857 break; 858 case HASHKEY5('n','e','v','e','r'): 859 case HASHKEY4('n','e','v','e'): 860 case HASHKEY3('n','e','v'): 861 case HASHKEY2('n','e'): 862 case HASHKEY6('s','i','m','p','l','e'): 863 case HASHKEY5('s','i','m','p','l'): 864 case HASHKEY4('s','i','m','p'): 865 case HASHKEY3('s','i','m'): 866 case HASHKEY2('s','i'): 867 case HASHKEY1('s'): 868 state->backup = BAK_simple; 869 break; 870 case HASHKEY6('n','u','m','b','e','r'): 871 case HASHKEY5('n','u','m','b','e'): 872 case HASHKEY4('n','u','m','b'): 873 case HASHKEY3('n','u','m'): 874 case HASHKEY2('n','u'): 875 case HASHKEY1('t'): 876 state->backup = BAK_number; 877 break; 878 default: 879 if (file) 880 error(2, "%s: unknown backup type", backup_type); 881 break; 882 } 883 if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX"))) 884 state->suffix = "~"; 885 state->suflen = strlen(state->suffix); 886 } 887 if (argc <= 0 || error_info.errors) 888 error(ERROR_USAGE|4, "%s", optusage(NiL)); 889 if (!path_resolve) 890 state->flags |= fts_flags(); 891 file = argv[argc]; 892 argv[argc] = 0; 893 if (s = strrchr(file, '/')) 894 { 895 while (*s == '/') 896 s++; 897 if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s))) 898 s = 0; 899 } 900 if (file != (char*)dot) 901 pathcanon(file, 0); 902 if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1) 903 error(ERROR_USAGE|4, "%s", optusage(NiL)); 904 if (s && !state->directory) 905 error(3, "%s: not a directory", file); 906 if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*")) 907 state->official = 1; 908 state->postsiz = strlen(file); 909 if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0))) 910 error(ERROR_SYSTEM|3, "out of space"); 911 memcpy(state->path, file, state->postsiz + 1); 912 if (state->directory && state->path[state->postsiz - 1] != '/') 913 state->path[state->postsiz++] = '/'; 914 if (state->hierarchy) 915 { 916 if (!state->directory) 917 error(3, "%s: last argument must be a directory", file); 918 state->missmode = st.st_mode; 919 } 920 state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX); 921 if (!state->recursive) 922 state->flags |= FTS_TOP; 923 if (fts = fts_open(argv, state->flags, NiL)) 924 { 925 while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent)); 926 fts_close(fts); 927 } 928 else if (state->link != pathsetlink) 929 switch (state->op) 930 { 931 case CP: 932 error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]); 933 break; 934 case LN: 935 error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]); 936 break; 937 case MV: 938 error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]); 939 break; 940 } 941 else if ((*state->link)(*argv, state->path)) 942 error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path); 943 if (cleanup && !sh) 944 { 945 if (state->path) 946 free(state->path); 947 free(state); 948 } 949 return error_info.errors != 0; 950} 951