commonMain.core.Cp.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kommandline-jvm Show documentation
Show all versions of kommandline-jvm Show documentation
Kotlin DSL for popular CLI commands.
The newest version!
package pl.mareklangiewicz.kommand.core
import okio.Path
import pl.mareklangiewicz.annotations.DelicateApi
import pl.mareklangiewicz.kground.namelowords
import pl.mareklangiewicz.kommand.*
import pl.mareklangiewicz.kommand.core.CpOpt.*
import pl.mareklangiewicz.udata.strf
/**
* Copy single [src] to [dst].
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
@OptIn(DelicateApi::class)
fun cpSingle(
src: Path,
dst: Path,
vararg useNamedArgs: Unit,
force: Boolean = false,
verbose: Boolean = false,
) = cp(src, dst, force = force, verbose = verbose) { -TargetDirNone }
/**
* Copy each [srcPaths] into the specified [targetDir], using the sources names.
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
@OptIn(DelicateApi::class)
fun cpInto(
targetDir: Path,
vararg srcPaths: Path?,
force: Boolean = false,
verbose: Boolean = false,
) = cp(*srcPaths, force = force, verbose = verbose) { -TargetDir(targetDir) }
/**
* Note1: As [Path] kdoc says: The only path that ends with "/" is the file system root "/".
* So we avoid POSIX gotcha with dereferencing symlinks, so we don't have to use [StripTrailingSlashes].
* Details: https://www.gnu.org/software/coreutils/manual/html_node/Trailing-slashes.html
*
* Note2: This version is delicate because last Path can be conditionally interpreted as "target dir"
* (if it happens to exist and is dir/symlink-to-dir). It's popular use-case, but can cause race conditions etc.
* Details:[GNU CoreUtils Target Dir](https://www.gnu.org/software/coreutils/manual/html_node/Target-directory.html)
* Make sure to use option [TargetDir] / [cpInto] / [cpSingle] to avoid potential issues.
*/
@DelicateApi
fun cp(
vararg paths: Path?,
force: Boolean = false,
verbose: Boolean = false,
init: Cp.() -> Unit,
) = Cp(nonopts = paths.mapNotNull { it?.strf }.toMutableList())
.apply { if (force) -Force; if (verbose) -Verbose; init() }
/**
* The [Cp] kommand copies files (or, optionally, directories). The copy is completely independent of the original.
* You can either copy one file to another, or copy arbitrarily many files to a destination directory.
*
* [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
* [linux man](https://man7.org/linux/man-pages/man1/cp.1.html)
* [backup opts](https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html)
*
* Generally, files are written just as they are read. For exceptions, see the [Sparse] option below.
*
* By default, [Cp] does not copy directories. However, [Recursive] or [Archive] cause cp to copy recursively
* by descending into source directories and copying files to corresponding destination directories.
*
* When copying from a symbolic link, cp normally follows the link
* only when NOT copying recursively or when [MakeHardLinks] is used. This default can be overridden
* with the [Archive], [CopySymLinks], [CopyAllLinks], [FollowAllSymLinks], [FollowCmdSymLinks] options.
* If more than one of these options is specified, the last one silently overrides the others.
*
* When copying to a symbolic link, cp follows the link only when it refers to an existing regular file.
* However, when copying to a dangling symbolic link, cp refuses by default, and fails with a diagnostic,
* since the operation is inherently dangerous. This behavior is contrary to historical practice and to POSIX.
* Set POSIXLY_CORRECT to make cp attempt to create the target of a dangling destination symlink,
* in spite of the possible risk.
* Also, when an options like [BackupExisting] or [MakeHardLinks] acts to rename or remove the destination
* before copying, cp renames or removes the symbolic link rather than the file it points to.
*
* By default, cp copies the contents of special files only when not copying recursively.
* This default can be overridden with the [CopyContents] option.
*
* cp generally refuses to copy a file onto itself, with the following exception:
* If [Force] and [BackupExisting] is specified with source and dest identical, and referring to a regular file,
* cp will make a backup file, either regular or numbered, as specified in the usual ways (see Backup options).
* This is useful when you simply want to make a backup of an existing file before changing it.
* Example: `cp --backup --force --preserve=all -- "$fname" "$fname"`
*/
@DelicateApi
data class Cp(
override val opts: MutableList = mutableListOf(),
override val nonopts: MutableList = mutableListOf(),
) : KommandTypical {
override val name get() = "cp"
}
@DelicateApi
interface CpOpt : KOptTypical {
// region [GNU Common Opts]
// https://www.gnu.org/software/coreutils/manual/html_node/Common-options.html
data object Help : KOptLN(), CpOpt // Don't risk short -h (ambiguity: sudo -h host; ls -h (human-readable), etc.)
data object Version : KOptLN(), CpOpt // Don't risk short -v (ambiguity with "verbose" for many commands)
data object EOOpt : KOptL(""), CpOpt
// endregion [GNU Common Opts]
// region [GNU Backup Opts]
// https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
/**
* Make numbered backups of files that already have them, simple backups of the others.
* Using -b is equivalent to using --backup=existing; -b does not accept any argument.
*/
data object BackupExisting : KOptS("b"), CpOpt
@Deprecated("Use BackupExisting which works the same and is short.", ReplaceWith("BackupExisting"))
data object BackupExistingExplicit : KOptL("backup", "existing"), CpOpt
@Deprecated("Use BackupExisting which works the same and is short.", ReplaceWith("BackupExisting"))
data object BackupNilExplicit : KOptL("backup", "nil"), CpOpt
/** Never make backups */
data object BackupOff : KOptL("backup", "off"), CpOpt
@Deprecated("Use BackupOff which works the same.", ReplaceWith("BackupOff"))
data object BackupNone : KOptL("backup", "none"), CpOpt
/** Always make numbered backups. */
data object BackupNumbered : KOptL("backup", "t"), CpOpt
@Deprecated("Use BackupNumbered which works the same and is a bit shorter.", ReplaceWith("BackupNumbered"))
data object BackupNumberedExplicit : KOptL("backup", "numbered"), CpOpt
/** Always make simple (never numbered) backups. */
data object BackupSimple : KOptL("backup", "simple"), CpOpt
@Deprecated("Use BackupSimple which works the same and doesn't have confusing name.", ReplaceWith("BackupSimple"))
data object BackupNeverConfusing : KOptL("backup", "never"), CpOpt
/**
* No method here, so it's a special case where the value of the VERSION_CONTROL environment variable is used.
* And if VERSION_CONTROL is not set, the default backup type is ‘existing’.
*/
data object BackupDefaultEnv : KOptL("backup"), CpOpt
/**
* Append suffix to each backup file made with -b.
* If this option is not specified, the value of the SIMPLE_BACKUP_SUFFIX environment variable is used.
* And if SIMPLE_BACKUP_SUFFIX is not set, the default is '~', just as in Emacs.
*/
data class BackupSuffix(val suffix: String) : KOptS("S", suffix), CpOpt
// endregion [GNU Backup Opts]
// region [GNU Target Dir Opts]
/**
* @param dir will be target dir to put files into. If null there will be NO target dir.
* (prevents traps/races with treating last arg as target dir if happen to exist and is dir or symlink to dir)
* Details:[GNU CoreUtils Target Dir](https://www.gnu.org/software/coreutils/manual/html_node/Target-directory.html)
*/
data class TargetDir(val dir: Path?) : KOptS(dir?.let { "t" } ?: "T", dir?.strf), CpOpt
/** Same as TargetDir(null). See [TargetDir] */
data object TargetDirNone : KOptS("T"), CpOpt
// endregion [GNU Target Dir Opts]
/**
* Preserve as much as possible of the structure and attributes of the original files in the copy
* (but do not attempt to preserve internal directory structure;
* i.e., ‘ls -U’ may list the entries in a copied directory in a different order).
* Try to preserve SELinux security context and extended attributes (xattr),
* but ignore any failure to do that and print no corresponding diagnostic.
* Equivalent to -dR --preserve=all with the reduced diagnostics.
*/
data object Archive : KOptS("a"), CpOpt
/**
* Copy only the specified attributes of the source file to the destination.
* If the destination already exists, do not alter its contents.
* See the --preserve option for controlling which attributes to copy.
*/
data object AttributesOnly : KOptLN(), CpOpt
/**
* If copying recursively, copy the contents of any special files (e.g., FIFOs and device files)
* as if they were regular files. This means trying to read the data in each source file
* and writing it to the destination. It is usually a mistake to use this option,
* as it normally has undesirable effects on special files like FIFOs and the ones
* typically found in the /dev directory. In most cases, cp -R --copy-contents will hang indefinitely
* trying to read from FIFOs and special files like /dev/console,
* and it will fill up your destination file system if you use it to copy /dev/zero.
* This option has no effect unless copying recursively,
* and it does not affect the copying of symbolic links.
*/
@DelicateApi("Especially delicate as in many cases it can hang indefinitely or fill entire FS with generated bytes.")
data object CopyContents : KOptLN(), CpOpt
/**
* Copy symbolic links as symbolic links rather than copying the files that they point to,
* and preserve hard links between source files in the copies.
* Equivalent to --no-dereference --preserve=links.
*/
data object CopyAllLinks : KOptS("d"), CpOpt
/**
* Copy symbolic links as symbolic links rather than copying the files that they point to.
* This option affects only symbolic links in the source;
* symbolic links in the destination are always followed if possible.
*/
data object CopySymLinks : KOptS("P"), CpOpt
/** Print the name of each file before moving it. */
data object Verbose : KOptLN(), CpOpt // Don't risk short -v (ambiguity with "version")
/** Print extra information to stdout, explaining how files are copied. This option implies [Verbose]. */
data object VerboseDebug : KOptL("debug"), CpOpt
/**
* When copying without this option and an existing destination file cannot be opened for writing, the copy fails.
* However, with --force, when a destination file cannot be opened,
* cp then tries to recreate the file by first removing it.
* The --force option alone will not remove dangling symlinks.
* When this option is combined with --link (-l) or --symbolic-link (-s), the destination link is replaced,
* and unless --backup (-b) is also given there is no brief moment when the destination does not exist.
* Also see the description of --remove-destination.
*
* This option is independent of the --interactive or -i option: neither cancels the effect of the other.
*
* This option is ignored when the --no-clobber or -n option is also used.
*/
data object Force : KOptLN(), CpOpt // Don't risk short -f (better to be explicit with FORCE)
/** Remove each existing destination file before attempting to open it (contrast with [Force] above). */
data object ForceRemoval : KOptL("remove-destination"), CpOpt
/**
* If a command line argument specifies a symbolic link,
* then copy the file it points to rather than the symbolic link itself.
* However, copy (preserving its nature) any symbolic link that is encountered via recursive traversal.
*/
data object FollowCmdSymLinks : KOptS("H"), CpOpt
/**
* Follow symbolic links when copying from them. With this option, cp cannot create a symbolic link.
* For example, a symlink (to regular file) in the source tree
* will be copied to a regular file in the destination tree.
*/
data object FollowAllSymLinks : KOptS("L"), CpOpt
/**
* When copying a file other than a directory, prompt whether to overwrite an existing destination file,
* and fail if the response is not affirmative. The -i option overrides a previous -n option.
*/
data object Interactive : KOptS("i"), CpOpt
/** Make hard links instead of copies of non-directories. */
data object MakeHardLinks : KOptS("l"), CpOpt
/**
* Make symbolic links instead of copies of non-directories.
* All source file names must be absolute (starting with ‘/’) unless the destination files
* are in the current directory. This option merely results in an error message on systems
* that do not support symbolic links.
*/
data object MakeSymLinks : KOptS("s"), CpOpt
/**
* Do not overwrite an existing file; silently skip instead. This option overrides a previous -i option.
* This option is mutually exclusive with -b or --backup option.
* This option is deprecated due to having a different exit status from other platforms.
* See also the --update option which will give more control over how to deal with existing files in the destination,
* and over the exit status in particular.
*/
@Deprecated("Deprecated due to having a different exit status from other platforms")
data object NoClobber : KOptS("n"), CpOpt
/**
* Preserve the specified attributes [FileAttrs] of the original files.
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
data class Preserve(val attrs: List) : KOptLN(attrs.joinToString(",") { it.namelowords() }), CpOpt {
constructor(vararg attrs: FileAttrs) : this(attrs.toList())
}
/** Works the same as Preserve(Mode, Ownership, Timestamp), but uses short notation. [Preserve] */
data object PreserveBasic : KOptS("p"), CpOpt
/**
* Do not preserve the specified attributes. The attribute list has the same form as for [Preserve]
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
data class NoPreserve(val attrs: List) : KOptLN(attrs.joinToString(",") { it.namelowords() }), CpOpt {
constructor(vararg attrs: FileAttrs) : this(attrs.toList())
}
enum class FileAttrs { Mode, Ownership, Timestamps, Links, Context, Xattr, All }
/**
* Form the name of each destination file by appending to the target directory a slash and the specified name
* of the source file. The last argument given to cp must be the name of an existing directory.
* For example, the command: `cp --parents a/b/c existing_dir` copies the file `a/b/c` to `existing_dir/a/b/c`,
* creating any missing intermediate directories.
*/
data object Parents : KOptLN(), CpOpt
/**
* Copy directories recursively. By default, do not follow symbolic links in the source
* unless used together with the --link (-l) option;
* see the --archive (-a), -d, --dereference (-L), --no-dereference (-P), and -H options.
* Special files are copied by creating a destination file of the same type as the source;
* see the --copy-contents option. It is not portable to use -r to copy symbolic links or special files.
* On some non-GNU systems, -r implies the equivalent of -L and --copy-contents for historical reasons.
* Also, it is not portable to use -R to copy symbolic links unless you also specify -P,
* as POSIX allows implementations that dereference symbolic links by default.
*/
data object Recursive : KOptLN(), CpOpt // Don't risk short -r or -R (better to be explicit about RECURSIVE)
/**
* Perform a lightweight, copy-on-write (COW) copy, if supported by the file system.
* Once it has succeeded, beware that the source and destination files share the same data blocks
* as long as they remain unmodified. Thus, if an I/O error affects data blocks of one of the files,
* the other suffers the same fate.
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
data class Reflink(val reflinkWhen: ReflinkWhen) : KOptLN(reflinkWhen.namelowords()), CpOpt
enum class ReflinkWhen { Always, Auto, Never }
/**
* A sparse file contains holes – a sequence of zero bytes that does not occupy any file system blocks;
* the ‘read’ system call reads these as zeros. This can both save considerable space and increase speed,
* since many binary files contain lots of consecutive zero bytes.
* By default, cp detects holes in input source files via a crude heuristic
* and makes the corresponding output file sparse as well. Only regular files may be sparse.
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
data class Sparse(val sparseWhen: SparseWhen) : KOptLN(sparseWhen.namelowords()), CpOpt
enum class SparseWhen { Always, Auto, Never }
/**
* Remove any trailing slashes from each source argument (avoids POSIX gotcha with dereferencing symlinks).
* Details: https://www.gnu.org/software/coreutils/manual/html_node/Trailing-slashes.html
* Note: when using [Path] it's not a problem because [Path] doesn't use trailing slashes except for unix root path.
*/
data object StripTrailingSlashes : KOptLN(), CpOpt
/**
* Conditionally skip some files without failing. Exact behavior depends on [UpdateWhich].
* Details: [gnu cp invocation](https://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html)
*/
data class Update(val which: UpdateWhich) : KOptLN(which.namelowords("-")), CpOpt
enum class UpdateWhich { All, None, NoneFail, Older }
/** Same as Update(UpdateWhich.Older) [Update], but using short representation. */
data object UpdateOlder : KOptS("u"), CpOpt
/**
* Skip subdirectories that are on different file systems from the one that the copy started on.
* However, mount point directories are copied.
*/
data object OneFileSystem : KOptLN(), CpOpt
/**
* Without a specified context, adjust the SELinux security context according to the system default type
* for destination files, similarly to the restorecon command.
* The long form of this option with a specific context specified,
* will set the context for newly created files only.
* With a specified context, if both SELinux and SMACK are disabled,a warning is issued.
* This option is mutually exclusive with the --preserve=context option,
* and overrides the --preserve=all and -a options.
*/
data class SEContext(val context: String?) : KOptL("context", context), CpOpt
/**
* Works the same as SEContext(null) but using short notation.
* Adjusts the SELinux security context according to the system default type for destination files,
* similarly to the restorecon command.
*/
data object SEContextDefault : KOptS("Z"), CpOpt
}