1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 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 29 static const char usage_head[] = 30 "[-?@(#)$Id: cp (AT&T Research) 2007-12-13 $\n]" 31 USAGE_LICENSE 32 ; 33 34 static 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 60 static 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 70 static 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 82 static 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.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 143 typedef struct State_s /* program state */ 144 { 145 int backup; /* BAK_* type */ 146 int directory; /* destination is directory */ 147 int flags; /* FTS_* flags */ 148 int force; /* force approval */ 149 int fs3d; /* 3d fs enabled */ 150 int hierarchy; /* preserve hierarchy */ 151 int interactive; /* prompt for approval */ 152 int missmode; /* default missing dir mode */ 153 int official; /* move to next view */ 154 int op; /* {CP,LN,MV} */ 155 int perm; /* permissions to preserve */ 156 int postsiz; /* state.path post index */ 157 int presiz; /* state.path pre index */ 158 int preserve; /* preserve { id mode time } */ 159 int recursive; /* subtrees too */ 160 int suflen; /* strlen(state.suffix) */ 161 int sync; /* fsync() each file after copy */ 162 int uid; /* caller uid */ 163 int update; /* replace only if newer */ 164 int verbose; /* list each file before op */ 165 int wflags; /* open() for write flags */ 166 167 int (*link)(const char*, const char*); /* link */ 168 int (*stat)(const char*, struct stat*); /* stat */ 169 170 #define INITSTATE pathsiz /* (re)init state before this */ 171 int pathsiz; /* state.path buffer size */ 172 173 174 char* path; /* to pathname buffer */ 175 char* opname; /* state.op message string */ 176 char* suffix; /* backup suffix */ 177 178 Sfio_t* tmp; /* tmp string stream */ 179 180 char text[PATH_MAX]; /* link text buffer */ 181 } State_t; 182 183 static const char dot[2] = { '.' }; 184 185 /* 186 * preserve support 187 */ 188 189 static void 190 preserve(State_t* state, const char* path, struct stat* ns, struct stat* os) 191 { 192 int n; 193 194 if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0)) 195 error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path); 196 n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid); 197 if (n && chown(state->path, os->st_uid, os->st_gid)) 198 switch (n) 199 { 200 case 01: 201 error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid)); 202 break; 203 case 02: 204 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid)); 205 break; 206 case 03: 207 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid)); 208 break; 209 } 210 } 211 212 /* 213 * visit a single file and state.op to the destination 214 */ 215 216 static int 217 visit(State_t* state, register FTSENT* ent) 218 { 219 register char* base; 220 register int n; 221 register int len; 222 int rm; 223 int rfd; 224 int wfd; 225 int m; 226 int v; 227 char* s; 228 char* e; 229 char* protection; 230 Sfio_t* ip; 231 Sfio_t* op; 232 FTS* fts; 233 FTSENT* sub; 234 struct stat st; 235 236 if (ent->fts_info == FTS_DC) 237 { 238 error(2, "%s: directory causes cycle", ent->fts_path); 239 fts_set(NiL, ent, FTS_SKIP); 240 return 0; 241 } 242 if (ent->fts_level == 0) 243 { 244 base = ent->fts_name; 245 len = ent->fts_namelen; 246 if (state->hierarchy) 247 state->presiz = -1; 248 else 249 { 250 state->presiz = ent->fts_pathlen; 251 while (*base == '.' && *(base + 1) == '/') 252 for (base += 2; *base == '/'; base++); 253 if (*base == '.' && !*(base + 1)) 254 state->presiz--; 255 else if (*base) 256 state->presiz -= base - ent->fts_name; 257 base = ent->fts_name + len; 258 while (base > ent->fts_name && *(base - 1) == '/') 259 base--; 260 while (base > ent->fts_name && *(base - 1) != '/') 261 base--; 262 len -= base - ent->fts_name; 263 if (state->directory) 264 state->presiz -= len + 1; 265 } 266 } 267 else 268 { 269 base = ent->fts_path + state->presiz + 1; 270 len = ent->fts_pathlen - state->presiz - 1; 271 } 272 len++; 273 if (state->directory) 274 { 275 if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0))) 276 error(3, "out of space"); 277 if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/')) 278 { 279 s = state->path + state->postsiz; 280 memcpy(s, base, len); 281 while (e = strchr(s, '/')) 282 { 283 *e = 0; 284 if (access(state->path, F_OK)) 285 { 286 st.st_mode = state->missmode; 287 if (s = strrchr(s, '/')) 288 { 289 *s = 0; 290 stat(state->path, &st); 291 *s = '/'; 292 } 293 if (mkdir(state->path, st.st_mode & S_IPERM)) 294 { 295 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 296 fts_set(NiL, ent, FTS_SKIP); 297 return 0; 298 } 299 } 300 *e++ = '/'; 301 s = e; 302 } 303 } 304 } 305 switch (ent->fts_info) 306 { 307 case FTS_DP: 308 if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU) 309 { 310 if (len && ent->fts_level > 0) 311 memcpy(state->path + state->postsiz, base, len); 312 else 313 state->path[state->postsiz] = 0; 314 if (stat(state->path, &st)) 315 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 316 else 317 { 318 if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM)) 319 error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1); 320 if (state->preserve) 321 preserve(state, state->path, &st, ent->fts_statp); 322 } 323 } 324 return 0; 325 case FTS_DNR: 326 case FTS_DNX: 327 case FTS_D: 328 if (!state->recursive) 329 { 330 fts_set(NiL, ent, FTS_SKIP); 331 if (state->op == CP) 332 error(1, "%s: directory -- copying as plain file", ent->fts_path); 333 else if (state->link == link && !state->force) 334 { 335 error(2, "%s: cannot link directory", ent->fts_path); 336 return 0; 337 } 338 } 339 else switch (ent->fts_info) 340 { 341 case FTS_DNR: 342 error(2, "%s: cannot read directory", ent->fts_path); 343 return 0; 344 case FTS_DNX: 345 error(2, "%s: cannot search directory", ent->fts_path); 346 fts_set(NiL, ent, FTS_SKIP); 347 348 /*FALLTHROUGH*/ 349 case FTS_D: 350 if (state->directory) 351 memcpy(state->path + state->postsiz, base, len); 352 if (!(*state->stat)(state->path, &st)) 353 { 354 if (!S_ISDIR(st.st_mode)) 355 { 356 error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path); 357 return 0; 358 } 359 } 360 else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0))) 361 { 362 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 363 fts_set(NiL, ent, FTS_SKIP); 364 } 365 if (!state->directory) 366 { 367 state->directory = 1; 368 state->path[state->postsiz++] = '/'; 369 state->presiz--; 370 } 371 return 0; 372 } 373 break; 374 case FTS_ERR: 375 case FTS_NS: 376 case FTS_SLNONE: 377 if (state->link != pathsetlink) 378 { 379 error(2, "%s: not found", ent->fts_path); 380 return 0; 381 } 382 break; 383 #if 0 384 case FTS_SL: 385 if (state->op == CP) 386 { 387 error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path); 388 return 0; 389 } 390 break; 391 #endif 392 } 393 if (state->directory) 394 memcpy(state->path + state->postsiz, base, len); 395 if ((*state->stat)(state->path, &st)) 396 st.st_mode = 0; 397 else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime) 398 { 399 fts_set(NiL, ent, FTS_SKIP); 400 return 0; 401 } 402 else if (!state->fs3d || !iview(&st)) 403 { 404 /* 405 * target is in top 3d view 406 */ 407 408 if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino) 409 { 410 if (state->op == MV) 411 { 412 /* 413 * let rename() handle it 414 */ 415 416 if (state->verbose) 417 sfputr(sfstdout, state->path, '\n'); 418 goto operate; 419 } 420 if (!state->official) 421 error(2, "%s: identical to %s", state->path, ent->fts_path); 422 return 0; 423 } 424 if (S_ISDIR(st.st_mode)) 425 { 426 error(2, "%s: cannot %s existing directory", state->path, state->opname); 427 return 0; 428 } 429 if (state->verbose) 430 sfputr(sfstdout, state->path, '\n'); 431 rm = state->op == LN || ent->fts_info == FTS_SL; 432 if (!rm || !state->force) 433 { 434 if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0) 435 { 436 close(n); 437 if (state->force) 438 /* ok */; 439 else if (state->interactive) 440 { 441 if (astquery(-1, "%s %s? ", state->opname, state->path)) 442 return 0; 443 } 444 else if (state->op == LN) 445 { 446 error(2, "%s: cannot %s existing file", state->path, state->opname); 447 return 0; 448 } 449 } 450 else if (state->force) 451 rm = 1; 452 else 453 { 454 protection = 455 #ifdef ETXTBSY 456 errno == ETXTBSY ? "``running program''" : 457 #endif 458 st.st_uid != state->uid ? "``not owner''" : 459 fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1; 460 if (state->interactive) 461 { 462 if (astquery(-1, "override protection %s for %s? ", protection, state->path)) 463 return 0; 464 rm = 1; 465 } 466 else if (!rm) 467 { 468 error(2, "%s: cannot %s %s protection", state->path, state->opname, protection); 469 return 0; 470 } 471 } 472 } 473 switch (state->backup) 474 { 475 case BAK_existing: 476 case BAK_number: 477 v = 0; 478 if (s = strrchr(state->path, '/')) 479 { 480 e = state->path; 481 *s++ = 0; 482 } 483 else 484 { 485 e = (char*)dot; 486 s = state->path; 487 } 488 n = strlen(s); 489 if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL)) 490 { 491 while (sub = fts_read(fts)) 492 { 493 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) 494 v = m; 495 if (sub->fts_level) 496 fts_set(NiL, sub, FTS_SKIP); 497 } 498 fts_close(fts); 499 } 500 if (s != state->path) 501 *--s = '/'; 502 if (v || state->backup == BAK_number) 503 { 504 sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix); 505 goto backup; 506 } 507 /*FALLTHROUGH*/ 508 case BAK_simple: 509 sfprintf(state->tmp, "%s%s", state->path, state->suffix); 510 backup: 511 if (!(s = sfstruse(state->tmp))) 512 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 513 if (rename(state->path, s)) 514 { 515 error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s); 516 return 0; 517 } 518 break; 519 default: 520 if (rm && remove(state->path)) 521 { 522 error(ERROR_SYSTEM|2, "%s: cannot remove", state->path); 523 return 0; 524 } 525 break; 526 } 527 } 528 operate: 529 switch (state->op) 530 { 531 case MV: 532 for (;;) 533 { 534 if (!rename(ent->fts_path, state->path)) 535 return 0; 536 if (errno == ENOENT) 537 rm = 1; 538 else if (!rm && st.st_mode && !remove(state->path)) 539 { 540 rm = 1; 541 continue; 542 } 543 if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode))) 544 { 545 error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path); 546 return 0; 547 } 548 else 549 break; 550 } 551 /*FALLTHROUGH*/ 552 case CP: 553 if (S_ISLNK(ent->fts_statp->st_mode)) 554 { 555 if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0) 556 { 557 error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path); 558 return 0; 559 } 560 state->text[n] = 0; 561 if (pathsetlink(state->text, state->path)) 562 { 563 error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path); 564 return 0; 565 } 566 } 567 else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode)) 568 { 569 if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0) 570 { 571 error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path); 572 return 0; 573 } 574 else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0) 575 { 576 error(ERROR_SYSTEM|2, "%s: cannot write", state->path); 577 if (ent->fts_statp->st_size > 0) 578 close(rfd); 579 return 0; 580 } 581 else if (ent->fts_statp->st_size > 0) 582 { 583 if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ))) 584 { 585 error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path); 586 close(rfd); 587 close(wfd); 588 } 589 else 590 { 591 n = 0; 592 if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE))) 593 { 594 error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path); 595 close(wfd); 596 sfclose(ip); 597 } 598 else 599 { 600 if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0) 601 n |= 3; 602 if (!sfeof(ip)) 603 n |= 1; 604 if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op)) 605 n |= 2; 606 if (sfclose(ip)) 607 n |= 1; 608 if (n) 609 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")); 610 } 611 } 612 } 613 else 614 close(wfd); 615 } 616 else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode)) 617 { 618 if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp))) 619 { 620 error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path); 621 return 0; 622 } 623 } 624 else 625 { 626 error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode)); 627 return 0; 628 } 629 if (state->preserve) 630 { 631 if (ent->fts_info != FTS_SL) 632 { 633 if (stat(state->path, &st)) 634 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 635 else 636 { 637 if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm)) 638 error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1); 639 preserve(state, state->path, &st, ent->fts_statp); 640 } 641 } 642 if (state->op == MV && remove(ent->fts_path)) 643 error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path); 644 } 645 break; 646 case LN: 647 if ((*state->link)(ent->fts_path, state->path)) 648 error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path); 649 break; 650 } 651 return 0; 652 } 653 654 int 655 b_cp(int argc, register char** argv, void* context) 656 { 657 register char* file; 658 register char* s; 659 char** v; 660 char* backup_type; 661 FTS* fts; 662 FTSENT* ent; 663 const char* usage; 664 int path_resolve; 665 int standard; 666 struct stat st; 667 State_t* state; 668 Shbltin_t* sh; 669 670 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 671 if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr)) 672 { 673 if (!(state = newof(0, State_t, 1, 0))) 674 error(ERROR_SYSTEM|3, "out of space"); 675 if (sh) 676 sh->ptr = state; 677 } 678 else 679 memset(state, 0, offsetof(State_t, INITSTATE)); 680 state->presiz = -1; 681 backup_type = 0; 682 state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR; 683 state->uid = geteuid(); 684 state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; 685 if (!state->tmp && !(state->tmp = sfstropen())) 686 error(ERROR_SYSTEM|3, "out of space [tmp string]"); 687 sfputr(state->tmp, usage_head, -1); 688 standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"); 689 switch (error_info.id[0]) 690 { 691 case 'c': 692 case 'C': 693 sfputr(state->tmp, usage_cp, -1); 694 state->op = CP; 695 state->stat = stat; 696 path_resolve = -1; 697 break; 698 case 'l': 699 case 'L': 700 sfputr(state->tmp, usage_ln, -1); 701 state->op = LN; 702 state->flags |= FTS_PHYSICAL; 703 state->link = link; 704 state->stat = lstat; 705 path_resolve = 1; 706 break; 707 case 'm': 708 case 'M': 709 sfputr(state->tmp, usage_mv, -1); 710 state->op = MV; 711 state->flags |= FTS_PHYSICAL; 712 state->preserve = 1; 713 state->stat = lstat; 714 path_resolve = 1; 715 break; 716 default: 717 error(3, "not implemented"); 718 break; 719 } 720 sfputr(state->tmp, usage_tail, -1); 721 if (!(usage = sfstruse(state->tmp))) 722 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 723 state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace"); 724 for (;;) 725 { 726 switch (optget(argv, usage)) 727 { 728 case 'a': 729 state->flags |= FTS_PHYSICAL; 730 state->preserve = 1; 731 state->recursive = 1; 732 path_resolve = 1; 733 continue; 734 case 'b': 735 state->backup = 1; 736 continue; 737 case 'f': 738 state->force = 1; 739 if (state->op != CP || !standard) 740 state->interactive = 0; 741 continue; 742 case 'h': 743 state->hierarchy = 1; 744 continue; 745 case 'i': 746 state->interactive = 1; 747 if (state->op != CP || !standard) 748 state->force = 0; 749 continue; 750 case 'l': 751 state->op = LN; 752 state->link = link; 753 state->stat = lstat; 754 continue; 755 case 'p': 756 state->preserve = 1; 757 continue; 758 case 'r': 759 state->recursive = 1; 760 if (path_resolve < 0) 761 path_resolve = 0; 762 continue; 763 case 's': 764 state->op = LN; 765 state->link = pathsetlink; 766 state->stat = lstat; 767 continue; 768 case 'u': 769 state->update = 1; 770 continue; 771 case 'v': 772 state->verbose = 1; 773 continue; 774 case 'x': 775 state->flags |= FTS_XDEV; 776 continue; 777 case 'F': 778 #if _lib_fsync 779 state->sync = 1; 780 #else 781 error(1, "%s not implemented on this system", opt_info.name); 782 #endif 783 continue; 784 case 'H': 785 state->flags |= FTS_META|FTS_PHYSICAL; 786 path_resolve = 1; 787 continue; 788 case 'L': 789 state->flags &= ~FTS_PHYSICAL; 790 path_resolve = 1; 791 continue; 792 case 'P': 793 state->flags &= ~FTS_META; 794 state->flags |= FTS_PHYSICAL; 795 path_resolve = 1; 796 continue; 797 case 'R': 798 state->recursive = 1; 799 state->flags &= ~FTS_META; 800 state->flags |= FTS_PHYSICAL; 801 path_resolve = 1; 802 continue; 803 case 'S': 804 state->suffix = opt_info.arg; 805 continue; 806 case 'V': 807 backup_type = opt_info.arg; 808 continue; 809 case '?': 810 error(ERROR_USAGE|4, "%s", opt_info.arg); 811 continue; 812 case ':': 813 error(2, "%s", opt_info.arg); 814 continue; 815 } 816 break; 817 } 818 argc -= opt_info.index + 1; 819 argv += opt_info.index; 820 if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--")) 821 { 822 argc--; 823 argv++; 824 } 825 if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*)))) 826 error(3, "out of space"); 827 memcpy(v, argv, (argc + 1) * sizeof(char*)); 828 argv = v; 829 if (!standard) 830 { 831 state->wflags |= O_EXCL; 832 if (!argc) 833 { 834 argc++; 835 argv[1] = (char*)dot; 836 } 837 } 838 if (state->backup) 839 { 840 if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL"))) 841 state->backup = BAK_existing; 842 else 843 switch (strkey(backup_type)) 844 { 845 case HASHKEY6('e','x','i','s','t','i'): 846 case HASHKEY5('e','x','i','s','t'): 847 case HASHKEY4('e','x','i','s'): 848 case HASHKEY3('e','x','i'): 849 case HASHKEY2('e','x'): 850 case HASHKEY1('e'): 851 case HASHKEY3('n','i','l'): 852 case HASHKEY2('n','i'): 853 state->backup = BAK_existing; 854 break; 855 case HASHKEY5('n','e','v','e','r'): 856 case HASHKEY4('n','e','v','e'): 857 case HASHKEY3('n','e','v'): 858 case HASHKEY2('n','e'): 859 case HASHKEY6('s','i','m','p','l','e'): 860 case HASHKEY5('s','i','m','p','l'): 861 case HASHKEY4('s','i','m','p'): 862 case HASHKEY3('s','i','m'): 863 case HASHKEY2('s','i'): 864 case HASHKEY1('s'): 865 state->backup = BAK_simple; 866 break; 867 case HASHKEY6('n','u','m','b','e','r'): 868 case HASHKEY5('n','u','m','b','e'): 869 case HASHKEY4('n','u','m','b'): 870 case HASHKEY3('n','u','m'): 871 case HASHKEY2('n','u'): 872 case HASHKEY1('t'): 873 state->backup = BAK_number; 874 break; 875 default: 876 if (file) 877 error(2, "%s: unknown backup type", backup_type); 878 break; 879 } 880 if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX"))) 881 state->suffix = "~"; 882 state->suflen = strlen(state->suffix); 883 } 884 if (argc <= 0 || error_info.errors) 885 error(ERROR_USAGE|4, "%s", optusage(NiL)); 886 if (!path_resolve) 887 state->flags |= fts_flags(); 888 file = argv[argc]; 889 argv[argc] = 0; 890 if (s = strrchr(file, '/')) 891 { 892 while (*s == '/') 893 s++; 894 if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s))) 895 s = 0; 896 } 897 if (file != (char*)dot) 898 pathcanon(file, 0); 899 if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1) 900 error(ERROR_USAGE|4, "%s", optusage(NiL)); 901 if (s && !state->directory) 902 error(3, "%s: not a directory", file); 903 if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*")) 904 state->official = 1; 905 state->postsiz = strlen(file); 906 if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0))) 907 error(3, "out of space"); 908 memcpy(state->path, file, state->postsiz + 1); 909 if (state->directory && state->path[state->postsiz - 1] != '/') 910 state->path[state->postsiz++] = '/'; 911 if (state->hierarchy) 912 { 913 if (!state->directory) 914 error(3, "%s: last argument must be a directory", file); 915 state->missmode = st.st_mode; 916 } 917 state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX); 918 if (!state->recursive) 919 state->flags |= FTS_TOP; 920 if (fts = fts_open(argv, state->flags, NiL)) 921 { 922 while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent)); 923 fts_close(fts); 924 } 925 else if (state->link != pathsetlink) 926 switch (state->op) 927 { 928 case CP: 929 error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]); 930 break; 931 case LN: 932 error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]); 933 break; 934 case MV: 935 error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]); 936 break; 937 } 938 else if ((*state->link)(*argv, state->path)) 939 error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path); 940 return error_info.errors != 0; 941 } 942