Skip to content

Implement --one-top-level#2881

Open
dzwdz wants to merge 4 commits into
libarchive:masterfrom
dzwdz:one-top-level
Open

Implement --one-top-level#2881
dzwdz wants to merge 4 commits into
libarchive:masterfrom
dzwdz:one-top-level

Conversation

@dzwdz
Copy link
Copy Markdown

@dzwdz dzwdz commented Mar 1, 2026

Closes #2354.

This also implements support for optional arguments in bsdtar, as splitting it into a separate PR wouldn't make sense.

I think it would be nice to have an additional option that aborts the extraction if the directory exists, but I haven't implemented that here (yet?). For me, the point of --one-top-level is not to mess with the files/directories I already have - so it's kinda annoying that, in this one specific case, tar will extract files into an existing directory anyways. Sadly, GNU tar made that mistake before us, so I had to copy it.

dzwdz added 4 commits March 1, 2026 22:07
It would be pointless to implement optional arguments before having any
option that can actually accept them, so that's why I did this first.

Putting archive_basename() into creation_set.c feels wrong, but that file
has all the required static functions.

I didn't test my CMakeLists.txt changes: I don't have it installed, and
I've only added a single line.  I assume it'll be fine... and if it's
not, that's what CI is for :p
In the previous commit, `bsdtar -x --one-top-level` would only show the
error after receiving some input.  Not sure how I would test that.
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Mar 1, 2026

@kientzle
Copy link
Copy Markdown
Contributor

kientzle commented Mar 2, 2026

A couple of questions:

  1. Should you be able to specify multiple such options? (--one-top-directory=foo --one-top-directory=bar)?
  2. Should --one-top-level refuse to use a pre-existing directory? We don't necessarily have to mimic GNU tar in this respect if there's a compelling reason not to.
  3. Should this be order-sensitive with respect to -C? E.g., should --one-top-level=foo -C=bar mean the same thing as -C=bar --one-top-level=foo? (In particular, if --one-top-level will use a pre-existing directory, then it's very similar to -C when the directory exists.)

@kientzle
Copy link
Copy Markdown
Contributor

kientzle commented Mar 2, 2026

I'll also point out that there is a simple way to achieve this already:

mkdir foo && tar xvf foo.tar -C foo

Not every problem has to be solved within tar itself.

There is some merit in supporting GNU tar options when those options are widely used by shell scripts. I've not personally seen folks rely on --one-top-level, so I'm not yet convinced this meets that bar.

@DHowett
Copy link
Copy Markdown
Collaborator

DHowett commented Mar 2, 2026

I fear GNU may have missed an opportunity to improve tar; they could have designed "one-top-level" to act as mkdir foo && tar -x -C foo with an optional implicit --strip-components=1 if the archive seems like it needs it (ergo, if the archive already has one top level directory.)

Otherwise, why call it that? Extracting an archive that contains a single top-level directory using "one-top-level" will produce a single top-level directory containing a single directory.

That's just -C but worse in every way.

@DHowett
Copy link
Copy Markdown
Collaborator

DHowett commented Mar 2, 2026

Ah, it looks like @dzwdz had the same thought in #2882! :)

@dzwdz
Copy link
Copy Markdown
Author

dzwdz commented Mar 2, 2026

Should you be able to specify multiple such options?

I think this mostly makes sense in the context of alias tar="bsdtar --one-top-level, and then specifying the exact name with another --one-top-level argument, or passing something like -no-one-top-level (not implemented, also feels a bit silly) to disable it. GNU tar uses the last argument passed, it doesn't combine them like -C.

Our handling of --one-top-level=1 --one-top-level might be different, I haven't checked yet. Oh, and I think right now there's a memory leak if you pass --one-top-level=1 --one-top-level=2?

Should --one-top-level refuse to use a pre-existing directory? We don't necessarily have to mimic GNU tar in this respect if there's a compelling reason not to.

Yeah, that'd be a footgun if you switch from bsdtar to GNU tar. I could picture myself in a few years using --one-top-level with GNU tar, thinking that it has the same "safety" feature, and finding out that it doesn't the hard way.

Should this be order-sensitive with respect to -C?

That wouldn't make sense if not for the unfortunate detail for ignoring existing directories (logically you can't chdir into a subdirectory of a newly created dir), so no. GNU tar doesn't seem to care about the order relative to -C.


Not every problem has to be solved within tar itself.

I agree, I've dealt with this using some wrapper scripts up until now1. I thought about writing my own extraction utility using libarchive too, but at this point I think it's better to just contribute the features I want to bsdtar, instead of having two libarchive-based extraction utilities with somewhat overlapping option sets.


That's just -C but worse in every way.

I mean, I think there's some merit to it. It's useful not to have to run mkdir separately, and the name inference is also nice.

Footnotes

  1. Otherwise this gets really annoying for e.g. Bandcamp albums. mkdir 'SONIC DRAGOLGO - MY SWEET HONEY BUNNY' && tar xvf 'SONIC DRAGOLGO - MY SWEET HONEY BUNNY.zip' -C 'SONIC DRAGOLGO - MY SWEET HONEY BUNNY', using a random one I had laying around as an example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider adding --one-top-level

3 participants