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