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 29 static const char usage_head[] = 30 "[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\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_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 143 typedef 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 184 static const char dot[2] = { '.' }; 185 186 /* 187 * preserve support 188 */ 189 190 static void 191 preserve(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 217 static int 218 visit(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 655 int 656 b_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