1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2009 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) 2009-06-18 $\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 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(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 671 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 672 if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr)) 673 { 674 if (!(state = newof(0, State_t, 1, 0))) 675 error(ERROR_SYSTEM|3, "out of space"); 676 if (sh) 677 sh->ptr = state; 678 } 679 else 680 memset(state, 0, offsetof(State_t, INITSTATE)); 681 state->context = context; 682 state->presiz = -1; 683 backup_type = 0; 684 state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR; 685 state->uid = geteuid(); 686 state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; 687 if (!state->tmp && !(state->tmp = sfstropen())) 688 error(ERROR_SYSTEM|3, "out of space [tmp string]"); 689 sfputr(state->tmp, usage_head, -1); 690 standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"); 691 switch (error_info.id[0]) 692 { 693 case 'c': 694 case 'C': 695 sfputr(state->tmp, usage_cp, -1); 696 state->op = CP; 697 state->stat = stat; 698 path_resolve = -1; 699 break; 700 case 'l': 701 case 'L': 702 sfputr(state->tmp, usage_ln, -1); 703 state->op = LN; 704 state->flags |= FTS_PHYSICAL; 705 state->link = link; 706 state->stat = lstat; 707 path_resolve = 1; 708 break; 709 case 'm': 710 case 'M': 711 sfputr(state->tmp, usage_mv, -1); 712 state->op = MV; 713 state->flags |= FTS_PHYSICAL; 714 state->preserve = 1; 715 state->stat = lstat; 716 path_resolve = 1; 717 break; 718 default: 719 error(3, "not implemented"); 720 break; 721 } 722 sfputr(state->tmp, usage_tail, -1); 723 if (!(usage = sfstruse(state->tmp))) 724 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 725 state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace"); 726 for (;;) 727 { 728 switch (optget(argv, usage)) 729 { 730 case 'a': 731 state->flags |= FTS_PHYSICAL; 732 state->preserve = 1; 733 state->recursive = 1; 734 path_resolve = 1; 735 continue; 736 case 'b': 737 state->backup = 1; 738 continue; 739 case 'f': 740 state->force = 1; 741 if (state->op != CP || !standard) 742 state->interactive = 0; 743 continue; 744 case 'h': 745 state->hierarchy = 1; 746 continue; 747 case 'i': 748 state->interactive = 1; 749 if (state->op != CP || !standard) 750 state->force = 0; 751 continue; 752 case 'l': 753 state->op = LN; 754 state->link = link; 755 state->stat = lstat; 756 continue; 757 case 'p': 758 state->preserve = 1; 759 continue; 760 case 'r': 761 state->recursive = 1; 762 if (path_resolve < 0) 763 path_resolve = 0; 764 continue; 765 case 's': 766 state->op = LN; 767 state->link = pathsetlink; 768 state->stat = lstat; 769 continue; 770 case 'u': 771 state->update = 1; 772 continue; 773 case 'v': 774 state->verbose = 1; 775 continue; 776 case 'x': 777 state->flags |= FTS_XDEV; 778 continue; 779 case 'F': 780 #if _lib_fsync 781 state->sync = 1; 782 #else 783 error(1, "%s not implemented on this system", opt_info.name); 784 #endif 785 continue; 786 case 'H': 787 state->flags |= FTS_META|FTS_PHYSICAL; 788 path_resolve = 1; 789 continue; 790 case 'L': 791 state->flags &= ~FTS_PHYSICAL; 792 path_resolve = 1; 793 continue; 794 case 'P': 795 state->flags &= ~FTS_META; 796 state->flags |= FTS_PHYSICAL; 797 path_resolve = 1; 798 continue; 799 case 'R': 800 state->recursive = 1; 801 state->flags &= ~FTS_META; 802 state->flags |= FTS_PHYSICAL; 803 path_resolve = 1; 804 continue; 805 case 'S': 806 state->suffix = opt_info.arg; 807 continue; 808 case 'V': 809 backup_type = opt_info.arg; 810 continue; 811 case '?': 812 error(ERROR_USAGE|4, "%s", opt_info.arg); 813 continue; 814 case ':': 815 error(2, "%s", opt_info.arg); 816 continue; 817 } 818 break; 819 } 820 argc -= opt_info.index + 1; 821 argv += opt_info.index; 822 if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--")) 823 { 824 argc--; 825 argv++; 826 } 827 if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*)))) 828 error(3, "out of space"); 829 memcpy(v, argv, (argc + 1) * sizeof(char*)); 830 argv = v; 831 if (!standard) 832 { 833 state->wflags |= O_EXCL; 834 if (!argc) 835 { 836 argc++; 837 argv[1] = (char*)dot; 838 } 839 } 840 if (state->backup) 841 { 842 if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL"))) 843 state->backup = BAK_existing; 844 else 845 switch (strkey(backup_type)) 846 { 847 case HASHKEY6('e','x','i','s','t','i'): 848 case HASHKEY5('e','x','i','s','t'): 849 case HASHKEY4('e','x','i','s'): 850 case HASHKEY3('e','x','i'): 851 case HASHKEY2('e','x'): 852 case HASHKEY1('e'): 853 case HASHKEY3('n','i','l'): 854 case HASHKEY2('n','i'): 855 state->backup = BAK_existing; 856 break; 857 case HASHKEY5('n','e','v','e','r'): 858 case HASHKEY4('n','e','v','e'): 859 case HASHKEY3('n','e','v'): 860 case HASHKEY2('n','e'): 861 case HASHKEY6('s','i','m','p','l','e'): 862 case HASHKEY5('s','i','m','p','l'): 863 case HASHKEY4('s','i','m','p'): 864 case HASHKEY3('s','i','m'): 865 case HASHKEY2('s','i'): 866 case HASHKEY1('s'): 867 state->backup = BAK_simple; 868 break; 869 case HASHKEY6('n','u','m','b','e','r'): 870 case HASHKEY5('n','u','m','b','e'): 871 case HASHKEY4('n','u','m','b'): 872 case HASHKEY3('n','u','m'): 873 case HASHKEY2('n','u'): 874 case HASHKEY1('t'): 875 state->backup = BAK_number; 876 break; 877 default: 878 if (file) 879 error(2, "%s: unknown backup type", backup_type); 880 break; 881 } 882 if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX"))) 883 state->suffix = "~"; 884 state->suflen = strlen(state->suffix); 885 } 886 if (argc <= 0 || error_info.errors) 887 error(ERROR_USAGE|4, "%s", optusage(NiL)); 888 if (!path_resolve) 889 state->flags |= fts_flags(); 890 file = argv[argc]; 891 argv[argc] = 0; 892 if (s = strrchr(file, '/')) 893 { 894 while (*s == '/') 895 s++; 896 if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s))) 897 s = 0; 898 } 899 if (file != (char*)dot) 900 pathcanon(file, 0); 901 if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1) 902 error(ERROR_USAGE|4, "%s", optusage(NiL)); 903 if (s && !state->directory) 904 error(3, "%s: not a directory", file); 905 if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*")) 906 state->official = 1; 907 state->postsiz = strlen(file); 908 if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0))) 909 error(3, "out of space"); 910 memcpy(state->path, file, state->postsiz + 1); 911 if (state->directory && state->path[state->postsiz - 1] != '/') 912 state->path[state->postsiz++] = '/'; 913 if (state->hierarchy) 914 { 915 if (!state->directory) 916 error(3, "%s: last argument must be a directory", file); 917 state->missmode = st.st_mode; 918 } 919 state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX); 920 if (!state->recursive) 921 state->flags |= FTS_TOP; 922 if (fts = fts_open(argv, state->flags, NiL)) 923 { 924 while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent)); 925 fts_close(fts); 926 } 927 else if (state->link != pathsetlink) 928 switch (state->op) 929 { 930 case CP: 931 error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]); 932 break; 933 case LN: 934 error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]); 935 break; 936 case MV: 937 error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]); 938 break; 939 } 940 else if ((*state->link)(*argv, state->path)) 941 error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path); 942 return error_info.errors != 0; 943 } 944