Full Code of muesli/duf for AI

master 4636deb4a7b7 cached
35 files
101.4 KB
33.9k tokens
226 symbols
1 requests
Download .txt
Repository: muesli/duf
Branch: master
Commit: 4636deb4a7b7
Files: 35
Total size: 101.4 KB

Directory structure:
gitextract_nb2n7rrk/

├── .github/
│   ├── FUNDING.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── build.yml
│       ├── goreleaser.yml
│       ├── lint-soft.yml
│       ├── lint.yml
│       └── manpage.yml
├── .gitignore
├── .golangci-soft.yml
├── .golangci.yml
├── .goreleaser.yml
├── LICENSE
├── README.md
├── duf.1
├── filesystems.go
├── filesystems_darwin.go
├── filesystems_freebsd.go
├── filesystems_linux.go
├── filesystems_openbsd.go
├── filesystems_windows.go
├── go.mod
├── go.sum
├── groups.go
├── main.go
├── man.go
├── mounts.go
├── mounts_darwin.go
├── mounts_freebsd.go
├── mounts_linux.go
├── mounts_linux_test.go
├── mounts_openbsd.go
├── mounts_windows.go
├── style.go
├── table.go
└── themes.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: muesli


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "daily"
    labels:
      - "dependencies"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"
    labels:
      - "dependencies"


================================================
FILE: .github/workflows/build.yml
================================================
name: build
on: [push, pull_request]

jobs:
  build:
    strategy:
      matrix:
        go-version: [~1.23, ^1]
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    env:
      GO111MODULE: "on"
    steps:
      - name: Install Go
        uses: actions/setup-go@v6.0.0
        with:
          go-version: ${{ matrix.go-version }}

      - name: Checkout code
        uses: actions/checkout@v5

      - name: Download Go modules
        run: go mod download

      - name: Build
        run: go build -v ./...

      - name: Test
        run: go test ./...


================================================
FILE: .github/workflows/goreleaser.yml
================================================
name: goreleaser

on:
  pull_request:
  push:

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - name: Set up Go
        uses: actions/setup-go@v6.0.0
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          version: latest
          args: release --snapshot --skip publish --skip sign --clean


================================================
FILE: .github/workflows/lint-soft.yml
================================================
name: lint-soft
on:
  push:
    branches:
      - master
  pull_request:

permissions:
  contents: read
  # Optional: allow read access to pull request. Use with `only-new-issues` option.
  pull-requests: read

jobs:
  golangci:
    name: lint-soft
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-go@v6.0.0
        with:
          go-version: stable
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          # Optional: golangci-lint command line arguments.
          args: --config .golangci-soft.yml --issues-exit-code=0
          # Optional: show only new issues if it's a pull request. The default value is `false`.
          only-new-issues: true


================================================
FILE: .github/workflows/lint.yml
================================================
name: lint
on:
  push:
    branches:
      - master
  pull_request:

permissions:
  contents: read
  # Optional: allow read access to pull request. Use with `only-new-issues` option.
  pull-requests: read

jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-go@v6.0.0
        with:
          go-version: stable
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          # Optional: golangci-lint command line arguments.
          #args:
          # Optional: show only new issues if it's a pull request. The default value is `false`.
          only-new-issues: true


================================================
FILE: .github/workflows/manpage.yml
================================================
name: manpage

on:
  push:
    branches:
      - master

jobs:
  manpage:
    runs-on: ubuntu-latest
    steps:
      - name: Install Go
        uses: actions/setup-go@v6.0.0
        with:
          go-version: 1.23

      - name: Checkout code
        uses: actions/checkout@v5

      - name: Download Go modules
        run: go mod download

      - name: Build
        run: go build -v -tags mango

      - name: Generate man-page
        run: ./duf > duf.1

      - name: Commit
        uses: stefanzweifel/git-auto-commit-action@v4
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          commit_message: "docs: update man page"
          branch: master
          commit_user_name: mango 🤖
          commit_user_email: actions@github.com
          commit_author: mango 🤖 <actions@github.com>


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

duf

dist/


================================================
FILE: .golangci-soft.yml
================================================
version: "2"
run:
  tests: false
linters:
  enable:
    - exhaustive
    - goconst
    - godot
    - godox
    - gomoddirectives
    - goprintffuncname
    - misspell
    - mnd
    - nakedret
    - nestif
    - noctx
    - nolintlint
    - prealloc
    - wrapcheck
  disable:
    - errcheck
    - govet
    - ineffassign
    - staticcheck
    - unused
  exclusions:
    generated: lax
    presets:
      - common-false-positives
issues:
  max-issues-per-linter: 0
  max-same-issues: 0
formatters:
  exclusions:
    generated: lax


================================================
FILE: .golangci.yml
================================================
version: "2"
run:
  tests: false
linters:
  enable:
    - bodyclose
    - gosec
    - nilerr
    - predeclared
    - revive
    - rowserrcheck
    - sqlclosecheck
    - tparallel
    - unconvert
    - unparam
    - whitespace
  exclusions:
    generated: lax
    presets:
      - common-false-positives
issues:
  max-issues-per-linter: 0
  max-same-issues: 0
formatters:
  enable:
    - goimports
  exclusions:
    generated: lax


================================================
FILE: .goreleaser.yml
================================================
version: 2

env:
  - CGO_ENABLED=0

before:
  hooks:
    - go mod tidy

builds:
  - binary: duf
    flags:
      - -trimpath
    ldflags: -s -w -X main.Version={{ .Version }} -X main.CommitSHA={{ .Commit }}
    goos:
      - linux
      - freebsd
      - openbsd
      - darwin
      - windows
    goarch:
      - amd64
      - arm64
      - 386
      - arm
      - ppc64le
    goarm:
      - 6
      - 7
    ignore:
      - goos: windows
        goarm: "6"
      - goos: windows
        goarm: "7"

archives:
  - format_overrides:
      - goos: windows
        formats: ['zip']
    name_template: >-
      {{- .ProjectName }}_
      {{- .Version }}_
      {{- .Os }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else }}{{ .Arch }}{{ end }}
      {{- if .Arm }}v{{ .Arm }}{{ end -}}
    files:
      - duf.1

nfpms:
  - ids:
      - duf
    vendor: muesli
    homepage: "https://fribbledom.com/"
    maintainer: "Christian Muehlhaeuser <muesli@gmail.com>"
    description: "Disk Usage/Free Utility"
    license: MIT
    formats:
      - apk
      - deb
      - rpm
    bindir: /usr/bin

homebrew_casks:
  - repository:
      owner: muesli
      name: homebrew-tap
    commit_author:
      name: "Christian Muehlhaeuser"
      email: "muesli@gmail.com"
    homepage: "https://fribbledom.com/"
    description: "Disk Usage/Free Utility"
    manpages:
      - duf.1
    skip_upload: true

signs:
  - artifacts: checksum

checksum:
  name_template: "checksums.txt"
snapshot:
  version_template: "{{ .Tag }}-next"
changelog:
  sort: asc
  filters:
    exclude:
      - "^docs:"
      - "^test:"


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Christian Muehlhaeuser

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

---

Portions of duf's code are copied and modified from
https://github.com/shirou/gopsutil.

gopsutil is distributed under BSD license reproduced below.

Copyright (c) 2014, WAKAYAMA Shirou
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 * Neither the name of the gopsutil authors nor the names of its contributors
   may be used to endorse or promote products derived from this software without
   specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# duf

[![Latest Release](https://img.shields.io/github/release/muesli/duf.svg?style=for-the-badge)](https://github.com/muesli/duf/releases)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](https://pkg.go.dev/github.com/muesli/duf)
[![Software License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](/LICENSE)
[![Build Status](https://img.shields.io/github/actions/workflow/status/muesli/duf/build.yml?style=for-the-badge&branch=master)](https://github.com/muesli/duf/actions)
[![Go ReportCard](https://goreportcard.com/badge/github.com/muesli/duf?style=for-the-badge)](https://goreportcard.com/report/muesli/duf)

Disk Usage/Free Utility (Linux, BSD, macOS & Windows)

![duf](/duf.png)

## Features

- [x] User-friendly, colorful output
- [x] Adjusts to your terminal's theme & width
- [x] Sort the results according to your needs
- [x] Groups & filters devices
- [x] Can conveniently output JSON

## Installation

### Packages

#### Linux
- Arch Linux: `pacman -S duf`
- Ubuntu (22.04 and later) / Debian (12 and later): `apt install duf`
- Fedora Linux: `dnf install duf`
- Nix: `nix-env -iA nixpkgs.duf`
- Void Linux: `xbps-install -S duf`
- Gentoo Linux: `emerge sys-fs/duf`
- Solus: `eopkg it duf`
- [Packages](https://github.com/muesli/duf/releases) in Alpine, Debian & RPM formats

#### BSD
- FreeBSD: `pkg install duf`
- OpenBSD: `pkg_add duf`

#### macOS
- with [Homebrew](https://brew.sh/): `brew install duf`
- with [MacPorts](https://www.macports.org): `sudo port selfupdate && sudo port install duf`

#### Windows
- with [Chocolatey](https://chocolatey.org/): `choco install duf`
- with [scoop](https://scoop.sh/): `scoop install duf`

#### Android
- Android (via termux): `pkg install duf`

### Binaries
- [Binaries](https://github.com/muesli/duf/releases) for Linux, FreeBSD, OpenBSD, macOS, Windows

### From source

Make sure you have a working Go environment (Go 1.23 or higher is required).
See the [install instructions](https://golang.org/doc/install.html).

Compiling duf is easy, simply run:

    git clone https://github.com/muesli/duf.git
    cd duf
    go build

## Usage

You can simply start duf without any command-line arguments:

    duf

If you supply arguments, duf will only list specific devices & mount points:

    duf /home /some/file

If you want to list everything (including pseudo, duplicate, inaccessible file systems):

    duf --all

### Filtering

You can show and hide specific tables:

    duf --only local,network,fuse,special,loops,binds
    duf --hide local,network,fuse,special,loops,binds

You can also show and hide specific filesystems:

    duf --only-fs tmpfs,vfat
    duf --hide-fs tmpfs,vfat

...or specific mount points:

    duf --only-mp /,/home,/dev
    duf --hide-mp /,/home,/dev

Wildcards inside quotes work:

    duf --only-mp '/sys/*,/dev/*'

### Display options

Sort the output:

    duf --sort size

Valid keys are: `mountpoint`, `size`, `used`, `avail`, `usage`, `inodes`,
`inodes_used`, `inodes_avail`, `inodes_usage`, `type`, `filesystem`.

Show or hide specific columns:

    duf --output mountpoint,size,usage

Valid keys are: `mountpoint`, `size`, `used`, `avail`, `usage`, `inodes`,
`inodes_used`, `inodes_avail`, `inodes_usage`, `type`, `filesystem`.

List inode information instead of block usage:

    duf --inodes

If duf doesn't detect your terminal's colors correctly, you can set a theme:

    duf --theme light

### Color-coding & Thresholds

duf highlights the availability & usage columns in red, green, or yellow,
depending on how much space is still available. You can set your own thresholds:

    duf --avail-threshold="10G,1G"
    duf --usage-threshold="0.5,0.9"

### Bonus

If you prefer your output as JSON:

    duf --json

## Troubleshooting

Users of `oh-my-zsh` should be aware that it already defines an alias called
`duf`, which you will have to remove in order to use `duf`:

    unalias duf

## Feedback

Got some feedback or suggestions? Please open an issue or drop me a note!

* [Twitter](https://twitter.com/mueslix)
* [The Fediverse](https://mastodon.social/@fribbledom)


================================================
FILE: duf.1
================================================
.TH DUF 1 "2025-09-30" "duf" "Disk Usage/Free Utility"
.SH NAME
duf - Disk Usage/Free Utility
.SH SYNOPSIS
\fBduf\fP [\fIoptions\&.\&.\&.\fP] [\fIargument\&.\&.\&.\fP]
.SH DESCRIPTION
Simple Disk Usage/Free Utility\&.
.PP
Features:
.PP
.RS
.IP \(bu 3
User-friendly, colorful output\&.
.IP \(bu 3
Adjusts to your terminal's theme & width\&.
.IP \(bu 3
Sort the results according to your needs\&.
.IP \(bu 3
Groups & filters devices\&.
.IP \(bu 3
Can conveniently output JSON\&.
.SH OPTIONS
.TP
\fB--all\fP
include pseudo, duplicate, inaccessible file systems
.TP
\fB--avail-threshold\fP
specifies the coloring threshold (yellow, red) of the avail column, must be integer with optional SI prefixes
.TP
\fB--hide\fP
hide specific devices, separated with commas: local, network, fuse, special, loops, binds
.TP
\fB--hide-fs\fP
hide specific filesystems, separated with commas
.TP
\fB--hide-mp\fP
hide specific mount points, separated with commas (supports wildcards)
.TP
\fB-h, --human-readable\fP
ignored, just for df compatibility
.TP
\fB--inodes\fP
list inode information instead of block usage
.TP
\fB--json\fP
output all devices in JSON format
.TP
\fB--only\fP
show only specific devices, separated with commas: local, network, fuse, special, loops, binds
.TP
\fB--only-fs\fP
only specific filesystems, separated with commas
.TP
\fB--only-mp\fP
only specific mount points, separated with commas (supports wildcards)
.TP
\fB--output\fP
output fields: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem
.TP
\fB--sort\fP
sort output by: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem
.TP
\fB--style\fP
style: unicode, ascii
.TP
\fB--theme\fP
color themes: dark, light, ansi
.TP
\fB--usage-threshold\fP
specifies the coloring threshold (yellow, red) of the usage bars as a floating point number from 0 to 1
.TP
\fB--version\fP
display version
.TP
\fB--warnings\fP
output all warnings to STDERR
.TP
\fB--width\fP
max output width
.SH USAGE
You can simply start duf without any command-line arguments:
.PP
.PP
  $ duf
.PP
.PP
If you supply arguments, duf will only list specific devices & mount points:
.PP
.PP
  $ duf /home /some/file
.PP
.PP
If you want to list everything (including pseudo, duplicate, inaccessible file systems):
.PP
.PP
  $ duf --all
.PP
.PP
You can show and hide specific tables:
.PP
.PP
  $ duf --only local,network,fuse,special,loops,binds
.PP
  $ duf --hide local,network,fuse,special,loops,binds
.PP
.PP
You can also show and hide specific filesystems:
.PP
.PP
  $ duf --only-fs tmpfs,vfat
.PP
  $ duf --hide-fs tmpfs,vfat
.PP
.PP
\&.\&.\&.or specific mount points:
.PP
.PP
  $ duf --only-mp /,/home,/dev
.PP
  $ duf --hide-mp /,/home,/dev
.PP
.PP
Wildcards inside quotes work:
.PP
.PP
  $ duf --only-mp '/sys/*,/dev/*'
.PP
.PP
Sort the output:
.PP
.PP
  $ duf --sort size
.PP
.PP
Valid keys are: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem\&.
.PP
.PP
Show or hide specific columns:
.PP
.PP
  $ duf --output mountpoint,size,usage
.PP
.PP
Valid keys are: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem\&.
.PP
.PP
List inode information instead of block usage:
.PP
.PP
  $ duf --inodes
.PP
.PP
If duf doesn't detect your terminal's colors correctly, you can set a theme:
.PP
.PP
  $ duf --theme light
.PP
.PP
duf highlights the availability & usage columns in red, green, or yellow, depending on how much space is still available\&. You can set your own thresholds:
.PP
.PP
  $ duf --avail-threshold="10G,1G"
.PP
  $ duf --usage-threshold="0\&.5,0\&.9"
.PP
.PP
If you prefer your output as JSON:
.PP
.PP
  $ duf --json
.PP
.SH NOTES
Portions of duf's code are copied and modified from https://github\&.com/shirou/gopsutil\&.
.PP
gopsutil was written by WAKAYAMA Shirou and is distributed under BSD-3-Clause\&.
.SH AUTHORS
duf was written by Christian Muehlhaeuser <https://github\&.com/muesli/duf>
.SH COPYRIGHT
Copyright (C) 2020-2022 Christian Muehlhaeuser <https://github\&.com/muesli>
.PP
Released under MIT license\&.


================================================
FILE: filesystems.go
================================================
package main

import (
	"os"
	"path/filepath"
	"strings"
)

func findMounts(mounts []Mount, path string) ([]Mount, error) {
	var err error
	path, err = filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	path, err = filepath.EvalSymlinks(path)
	if err != nil {
		return nil, err
	}

	_, err = os.Stat(path)
	if err != nil {
		return nil, err
	}

	var m []Mount
	for _, v := range mounts {
		if path == v.Device {
			return []Mount{v}, nil
		}

		if strings.HasPrefix(path, v.Mountpoint) {
			var nm []Mount

			// keep all entries that are as close or closer to the target
			for _, mv := range m {
				if len(mv.Mountpoint) >= len(v.Mountpoint) {
					nm = append(nm, mv)
				}
			}
			m = nm

			// add entry only if we didn't already find something closer
			if len(nm) == 0 || len(v.Mountpoint) >= len(nm[0].Mountpoint) {
				m = append(m, v)
			}
		}
	}

	return m, nil
}

func deviceType(m Mount) string {
	if isNetworkFs(m) {
		return networkDevice
	}
	if isSpecialFs(m) {
		return specialDevice
	}
	if isFuseFs(m) {
		return fuseDevice
	}

	return localDevice
}

// remote: [ "nfs", "smbfs", "cifs", "ncpfs", "afs", "coda", "ftpfs", "mfs", "sshfs", "fuse.sshfs", "nfs4" ]
// special: [ "tmpfs", "devpts", "devtmpfs", "proc", "sysfs", "usbfs", "devfs", "fdescfs", "linprocfs" ]


================================================
FILE: filesystems_darwin.go
================================================
//go:build darwin
// +build darwin

package main

func isFuseFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isNetworkFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isSpecialFs(m Mount) bool {
	return m.Fstype == "devfs"
}

func isHiddenFs(m Mount) bool {
	return false
}


================================================
FILE: filesystems_freebsd.go
================================================
//go:build freebsd
// +build freebsd

package main

func isFuseFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isNetworkFs(m Mount) bool {
	fs := []string{"nfs", "smbfs"}

	for _, v := range fs {
		if m.Fstype == v {
			return true
		}
	}

	return false
}

func isSpecialFs(m Mount) bool {
	fs := []string{"devfs", "tmpfs", "linprocfs", "linsysfs", "fdescfs", "procfs"}

	for _, v := range fs {
		if m.Fstype == v {
			return true
		}
	}

	return false
}

func isHiddenFs(m Mount) bool {
	return false
}


================================================
FILE: filesystems_linux.go
================================================
//go:build linux
// +build linux

package main

import "strings"

//nolint:revive
const (
	// man statfs
	ADFS_SUPER_MAGIC      = 0xadf5
	AFFS_SUPER_MAGIC      = 0xADFF
	AUTOFS_SUPER_MAGIC    = 0x0187
	BDEVFS_MAGIC          = 0x62646576
	BEFS_SUPER_MAGIC      = 0x42465331
	BFS_MAGIC             = 0x1BADFACE
	BINFMTFS_MAGIC        = 0x42494e4d
	BPF_FS_MAGIC          = 0xcafe4a11
	BTRFS_SUPER_MAGIC     = 0x9123683E
	CGROUP_SUPER_MAGIC    = 0x27e0eb
	CGROUP2_SUPER_MAGIC   = 0x63677270
	CIFS_MAGIC_NUMBER     = 0xFF534D42
	CODA_SUPER_MAGIC      = 0x73757245
	COH_SUPER_MAGIC       = 0x012FF7B7
	CONFIGFS_MAGIC        = 0x62656570
	CRAMFS_MAGIC          = 0x28cd3d45
	DEBUGFS_MAGIC         = 0x64626720
	DEVFS_SUPER_MAGIC     = 0x1373
	DEVPTS_SUPER_MAGIC    = 0x1cd1
	EFIVARFS_MAGIC        = 0xde5e81e4
	EFS_SUPER_MAGIC       = 0x00414A53
	EXT_SUPER_MAGIC       = 0x137D
	EXT2_OLD_SUPER_MAGIC  = 0xEF51
	EXT2_SUPER_MAGIC      = 0xEF53
	EXT3_SUPER_MAGIC      = 0xEF53
	EXT4_SUPER_MAGIC      = 0xEF53
	FUSE_SUPER_MAGIC      = 0x65735546
	FUTEXFS_SUPER_MAGIC   = 0xBAD1DEA
	HFS_SUPER_MAGIC       = 0x4244
	HFSPLUS_SUPER_MAGIC   = 0x482b
	HOSTFS_SUPER_MAGIC    = 0x00c0ffee
	HPFS_SUPER_MAGIC      = 0xF995E849
	HUGETLBFS_MAGIC       = 0x958458f6
	ISOFS_SUPER_MAGIC     = 0x9660
	JFFS2_SUPER_MAGIC     = 0x72b6
	JFS_SUPER_MAGIC       = 0x3153464a
	MINIX_SUPER_MAGIC     = 0x137F /* orig. minix */
	MINIX_SUPER_MAGIC2    = 0x138F /* 30 char minix */
	MINIX2_SUPER_MAGIC    = 0x2468 /* minix V2 */
	MINIX2_SUPER_MAGIC2   = 0x2478 /* minix V2, 30 char names */
	MINIX3_SUPER_MAGIC    = 0x4d5a /* minix V3 fs, 60 char names */
	MQUEUE_MAGIC          = 0x19800202
	MSDOS_SUPER_MAGIC     = 0x4d44
	NCP_SUPER_MAGIC       = 0x564c
	NFS_SUPER_MAGIC       = 0x6969
	NILFS_SUPER_MAGIC     = 0x3434
	NTFS_SB_MAGIC         = 0x5346544e
	OCFS2_SUPER_MAGIC     = 0x7461636f
	OPENPROM_SUPER_MAGIC  = 0x9fa1
	PIPEFS_MAGIC          = 0x50495045
	PROC_SUPER_MAGIC      = 0x9fa0
	PSTOREFS_MAGIC        = 0x6165676C
	QNX4_SUPER_MAGIC      = 0x002f
	QNX6_SUPER_MAGIC      = 0x68191122
	RAMFS_MAGIC           = 0x858458f6
	REISERFS_SUPER_MAGIC  = 0x52654973
	ROMFS_MAGIC           = 0x7275
	SELINUX_MAGIC         = 0xf97cff8c
	SMACK_MAGIC           = 0x43415d53
	SMB_SUPER_MAGIC       = 0x517B
	SMB2_MAGIC_NUMBER     = 0xfe534d42
	SOCKFS_MAGIC          = 0x534F434B
	SQUASHFS_MAGIC        = 0x73717368
	SYSFS_MAGIC           = 0x62656572
	SYSV2_SUPER_MAGIC     = 0x012FF7B6
	SYSV4_SUPER_MAGIC     = 0x012FF7B5
	TMPFS_MAGIC           = 0x01021994
	TRACEFS_MAGIC         = 0x74726163
	UDF_SUPER_MAGIC       = 0x15013346
	UFS_MAGIC             = 0x00011954
	USBDEVICE_SUPER_MAGIC = 0x9fa2
	V9FS_MAGIC            = 0x01021997
	VXFS_SUPER_MAGIC      = 0xa501FCF5
	XENFS_SUPER_MAGIC     = 0xabba1974
	XENIX_SUPER_MAGIC     = 0x012FF7B4
	XFS_SUPER_MAGIC       = 0x58465342
	_XIAFS_SUPER_MAGIC    = 0x012FD16D

	AFS_SUPER_MAGIC             = 0x5346414F
	AUFS_SUPER_MAGIC            = 0x61756673
	ANON_INODE_FS_SUPER_MAGIC   = 0x09041934
	CEPH_SUPER_MAGIC            = 0x00C36400
	ECRYPTFS_SUPER_MAGIC        = 0xF15F
	FAT_SUPER_MAGIC             = 0x4006
	FHGFS_SUPER_MAGIC           = 0x19830326
	FUSEBLK_SUPER_MAGIC         = 0x65735546
	FUSECTL_SUPER_MAGIC         = 0x65735543
	GFS_SUPER_MAGIC             = 0x1161970
	GPFS_SUPER_MAGIC            = 0x47504653
	MTD_INODE_FS_SUPER_MAGIC    = 0x11307854
	INOTIFYFS_SUPER_MAGIC       = 0x2BAD1DEA
	ISOFS_R_WIN_SUPER_MAGIC     = 0x4004
	ISOFS_WIN_SUPER_MAGIC       = 0x4000
	JFFS_SUPER_MAGIC            = 0x07C0
	KAFS_SUPER_MAGIC            = 0x6B414653
	LUSTRE_SUPER_MAGIC          = 0x0BD00BD0
	NFSD_SUPER_MAGIC            = 0x6E667364
	PANFS_SUPER_MAGIC           = 0xAAD7AAEA
	RPC_PIPEFS_SUPER_MAGIC      = 0x67596969
	SECURITYFS_SUPER_MAGIC      = 0x73636673
	UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100
	VMHGFS_SUPER_MAGIC          = 0xBACBACBC
	VZFS_SUPER_MAGIC            = 0x565A4653
	ZFS_SUPER_MAGIC             = 0x2FC12FC1
)

// coreutils/src/stat.c
var fsTypeMap = map[int64]string{
	ADFS_SUPER_MAGIC:            "adfs",                /* 0xADF5 local */
	AFFS_SUPER_MAGIC:            "affs",                /* 0xADFF local */
	AFS_SUPER_MAGIC:             "afs",                 /* 0x5346414F remote */
	ANON_INODE_FS_SUPER_MAGIC:   "anon-inode FS",       /* 0x09041934 local */
	AUFS_SUPER_MAGIC:            "aufs",                /* 0x61756673 remote */
	AUTOFS_SUPER_MAGIC:          "autofs",              /* 0x0187 local */
	BEFS_SUPER_MAGIC:            "befs",                /* 0x42465331 local */
	BDEVFS_MAGIC:                "bdevfs",              /* 0x62646576 local */
	BFS_MAGIC:                   "bfs",                 /* 0x1BADFACE local */
	BINFMTFS_MAGIC:              "binfmt_misc",         /* 0x42494E4D local */
	BTRFS_SUPER_MAGIC:           "btrfs",               /* 0x9123683E local */
	CEPH_SUPER_MAGIC:            "ceph",                /* 0x00C36400 remote */
	CGROUP_SUPER_MAGIC:          "cgroupfs",            /* 0x0027E0EB local */
	CIFS_MAGIC_NUMBER:           "cifs",                /* 0xFF534D42 remote */
	CODA_SUPER_MAGIC:            "coda",                /* 0x73757245 remote */
	COH_SUPER_MAGIC:             "coh",                 /* 0x012FF7B7 local */
	CRAMFS_MAGIC:                "cramfs",              /* 0x28CD3D45 local */
	DEBUGFS_MAGIC:               "debugfs",             /* 0x64626720 local */
	DEVFS_SUPER_MAGIC:           "devfs",               /* 0x1373 local */
	DEVPTS_SUPER_MAGIC:          "devpts",              /* 0x1CD1 local */
	ECRYPTFS_SUPER_MAGIC:        "ecryptfs",            /* 0xF15F local */
	EFS_SUPER_MAGIC:             "efs",                 /* 0x00414A53 local */
	EXT_SUPER_MAGIC:             "ext",                 /* 0x137D local */
	EXT2_SUPER_MAGIC:            "ext2/ext3",           /* 0xEF53 local */
	EXT2_OLD_SUPER_MAGIC:        "ext2",                /* 0xEF51 local */
	FAT_SUPER_MAGIC:             "fat",                 /* 0x4006 local */
	FHGFS_SUPER_MAGIC:           "fhgfs",               /* 0x19830326 remote */
	FUSEBLK_SUPER_MAGIC:         "fuseblk",             /* 0x65735546 remote */
	FUSECTL_SUPER_MAGIC:         "fusectl",             /* 0x65735543 remote */
	FUTEXFS_SUPER_MAGIC:         "futexfs",             /* 0x0BAD1DEA local */
	GFS_SUPER_MAGIC:             "gfs/gfs2",            /* 0x1161970 remote */
	GPFS_SUPER_MAGIC:            "gpfs",                /* 0x47504653 remote */
	HFS_SUPER_MAGIC:             "hfs",                 /* 0x4244 local */
	HFSPLUS_SUPER_MAGIC:         "hfsplus",             /* 0x482b local */
	HPFS_SUPER_MAGIC:            "hpfs",                /* 0xF995E849 local */
	HUGETLBFS_MAGIC:             "hugetlbfs",           /* 0x958458F6 local */
	MTD_INODE_FS_SUPER_MAGIC:    "inodefs",             /* 0x11307854 local */
	INOTIFYFS_SUPER_MAGIC:       "inotifyfs",           /* 0x2BAD1DEA local */
	ISOFS_SUPER_MAGIC:           "isofs",               /* 0x9660 local */
	ISOFS_R_WIN_SUPER_MAGIC:     "isofs",               /* 0x4004 local */
	ISOFS_WIN_SUPER_MAGIC:       "isofs",               /* 0x4000 local */
	JFFS_SUPER_MAGIC:            "jffs",                /* 0x07C0 local */
	JFFS2_SUPER_MAGIC:           "jffs2",               /* 0x72B6 local */
	JFS_SUPER_MAGIC:             "jfs",                 /* 0x3153464A local */
	KAFS_SUPER_MAGIC:            "k-afs",               /* 0x6B414653 remote */
	LUSTRE_SUPER_MAGIC:          "lustre",              /* 0x0BD00BD0 remote */
	MINIX_SUPER_MAGIC:           "minix",               /* 0x137F local */
	MINIX_SUPER_MAGIC2:          "minix (30 char.)",    /* 0x138F local */
	MINIX2_SUPER_MAGIC:          "minix v2",            /* 0x2468 local */
	MINIX2_SUPER_MAGIC2:         "minix v2 (30 char.)", /* 0x2478 local */
	MINIX3_SUPER_MAGIC:          "minix3",              /* 0x4D5A local */
	MQUEUE_MAGIC:                "mqueue",              /* 0x19800202 local */
	MSDOS_SUPER_MAGIC:           "msdos",               /* 0x4D44 local */
	NCP_SUPER_MAGIC:             "novell",              /* 0x564C remote */
	NFS_SUPER_MAGIC:             "nfs",                 /* 0x6969 remote */
	NFSD_SUPER_MAGIC:            "nfsd",                /* 0x6E667364 remote */
	NILFS_SUPER_MAGIC:           "nilfs",               /* 0x3434 local */
	NTFS_SB_MAGIC:               "ntfs",                /* 0x5346544E local */
	OPENPROM_SUPER_MAGIC:        "openprom",            /* 0x9FA1 local */
	OCFS2_SUPER_MAGIC:           "ocfs2",               /* 0x7461636f remote */
	PANFS_SUPER_MAGIC:           "panfs",               /* 0xAAD7AAEA remote */
	PIPEFS_MAGIC:                "pipefs",              /* 0x50495045 remote */
	PROC_SUPER_MAGIC:            "proc",                /* 0x9FA0 local */
	PSTOREFS_MAGIC:              "pstorefs",            /* 0x6165676C local */
	QNX4_SUPER_MAGIC:            "qnx4",                /* 0x002F local */
	QNX6_SUPER_MAGIC:            "qnx6",                /* 0x68191122 local */
	RAMFS_MAGIC:                 "ramfs",               /* 0x858458F6 local */
	REISERFS_SUPER_MAGIC:        "reiserfs",            /* 0x52654973 local */
	ROMFS_MAGIC:                 "romfs",               /* 0x7275 local */
	RPC_PIPEFS_SUPER_MAGIC:      "rpc_pipefs",          /* 0x67596969 local */
	SECURITYFS_SUPER_MAGIC:      "securityfs",          /* 0x73636673 local */
	SELINUX_MAGIC:               "selinux",             /* 0xF97CFF8C local */
	SMB_SUPER_MAGIC:             "smb",                 /* 0x517B remote */
	SMB2_MAGIC_NUMBER:           "smb2",                /* 0xfe534d42 remote */
	SOCKFS_MAGIC:                "sockfs",              /* 0x534F434B local */
	SQUASHFS_MAGIC:              "squashfs",            /* 0x73717368 local */
	SYSFS_MAGIC:                 "sysfs",               /* 0x62656572 local */
	SYSV2_SUPER_MAGIC:           "sysv2",               /* 0x012FF7B6 local */
	SYSV4_SUPER_MAGIC:           "sysv4",               /* 0x012FF7B5 local */
	TMPFS_MAGIC:                 "tmpfs",               /* 0x01021994 local */
	UDF_SUPER_MAGIC:             "udf",                 /* 0x15013346 local */
	UFS_MAGIC:                   "ufs",                 /* 0x00011954 local */
	UFS_BYTESWAPPED_SUPER_MAGIC: "ufs",                 /* 0x54190100 local */
	USBDEVICE_SUPER_MAGIC:       "usbdevfs",            /* 0x9FA2 local */
	V9FS_MAGIC:                  "v9fs",                /* 0x01021997 local */
	VMHGFS_SUPER_MAGIC:          "vmhgfs",              /* 0xBACBACBC remote */
	VXFS_SUPER_MAGIC:            "vxfs",                /* 0xA501FCF5 local */
	VZFS_SUPER_MAGIC:            "vzfs",                /* 0x565A4653 local */
	XENFS_SUPER_MAGIC:           "xenfs",               /* 0xABBA1974 local */
	XENIX_SUPER_MAGIC:           "xenix",               /* 0x012FF7B4 local */
	XFS_SUPER_MAGIC:             "xfs",                 /* 0x58465342 local */
	_XIAFS_SUPER_MAGIC:          "xia",                 /* 0x012FD16D local */
	ZFS_SUPER_MAGIC:             "zfs",                 /* 0x2FC12FC1 local */
}

/*
var localMap = map[int64]bool{
	AFS_SUPER_MAGIC:      true,
	BTRFS_SUPER_MAGIC:    true,
	EXT_SUPER_MAGIC:      true,
	EXT2_OLD_SUPER_MAGIC: true,
	EXT2_SUPER_MAGIC:     true,
	FAT_SUPER_MAGIC:      true,
	HPFS_SUPER_MAGIC:     true,
	MSDOS_SUPER_MAGIC:    true,
	NTFS_SB_MAGIC:        true,
	REISERFS_SUPER_MAGIC: true,
	UDF_SUPER_MAGIC:      true,
	XFS_SUPER_MAGIC:      true,
	ZFS_SUPER_MAGIC:      true,
}
*/

var networkMap = map[int64]bool{
	CIFS_MAGIC_NUMBER: true,
	NFS_SUPER_MAGIC:   true,
	SMB_SUPER_MAGIC:   true,
	SMB2_MAGIC_NUMBER: true,
}

var specialMap = map[int64]bool{
	AUTOFS_SUPER_MAGIC:     true,
	BINFMTFS_MAGIC:         true,
	BPF_FS_MAGIC:           true,
	CGROUP_SUPER_MAGIC:     true,
	CGROUP2_SUPER_MAGIC:    true,
	CONFIGFS_MAGIC:         true,
	DEBUGFS_MAGIC:          true,
	DEVPTS_SUPER_MAGIC:     true,
	EFIVARFS_MAGIC:         true,
	FUSECTL_SUPER_MAGIC:    true,
	HUGETLBFS_MAGIC:        true,
	MQUEUE_MAGIC:           true,
	PROC_SUPER_MAGIC:       true,
	PSTOREFS_MAGIC:         true,
	SECURITYFS_SUPER_MAGIC: true,
	SYSFS_MAGIC:            true,
	TMPFS_MAGIC:            true,
	TRACEFS_MAGIC:          true,
}

/*
func isLocalFs(m Mount) bool {
	return localMap[int64(m.Stat().Type)] //nolint:unconvert
}
*/

func isFuseFs(m Mount) bool {
	return m.Stat().Type == FUSEBLK_SUPER_MAGIC ||
		m.Stat().Type == FUSE_SUPER_MAGIC
}

func isNetworkFs(m Mount) bool {
	return networkMap[int64(m.Stat().Type)] //nolint:unconvert
}

func isSpecialFs(m Mount) bool {
	if m.Device == "nsfs" {
		return true
	}

	return specialMap[int64(m.Stat().Type)] //nolint:unconvert
}

func isHiddenFs(m Mount) bool {
	switch m.Device {
	case "shm":
		return true
	case "overlay":
		return true
	}

	switch m.Fstype {
	case "autofs":
		return true
	case "squashfs":
		if strings.HasPrefix(m.Mountpoint, "/snap") {
			return true
		}
	}

	return false
}


================================================
FILE: filesystems_openbsd.go
================================================
//go:build openbsd
// +build openbsd

package main

func isFuseFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isNetworkFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isSpecialFs(m Mount) bool {
	return m.Fstype == "devfs"
}

func isHiddenFs(m Mount) bool {
	return false
}


================================================
FILE: filesystems_windows.go
================================================
//go:build windows
// +build windows

package main

import (
	"golang.org/x/sys/windows/registry"
)

const (
	WindowsSandboxMountPointRegistryPath = `Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\CPC\LocalMOF`
)

var windowsSandboxMountPoints = loadRegisteredWindowsSandboxMountPoints()

func loadRegisteredWindowsSandboxMountPoints() (ret map[string]struct{}) {
	ret = make(map[string]struct{})
	key, err := registry.OpenKey(registry.CURRENT_USER, WindowsSandboxMountPointRegistryPath, registry.READ)
	if err != nil {
		return
	}

	keyInfo, err := key.Stat()
	if err != nil {
		return
	}

	mountPoints, err := key.ReadValueNames(int(keyInfo.ValueCount))
	if err != nil {
		return
	}

	for _, val := range mountPoints {
		ret[val] = struct{}{}
	}
	return ret
}

func isFuseFs(m Mount) bool {
	//FIXME: implement
	return false
}

func isNetworkFs(m Mount) bool {
	_, ok := m.Metadata.(*NetResource)
	return ok
}

func isSpecialFs(m Mount) bool {
	_, ok := windowsSandboxMountPoints[m.Mountpoint]
	return ok
}

func isHiddenFs(m Mount) bool {
	return false
}


================================================
FILE: go.mod
================================================
module github.com/muesli/duf

go 1.23.0

require (
	github.com/IGLOU-EU/go-wildcard v1.0.3
	github.com/jedib0t/go-pretty/v6 v6.6.8
	github.com/mattn/go-runewidth v0.0.19
	github.com/muesli/mango v0.2.0
	github.com/muesli/mango-pflag v0.2.0
	github.com/muesli/roff v0.1.0
	github.com/muesli/termenv v0.16.0
	github.com/spf13/pflag v1.0.10
	golang.org/x/sys v0.35.0
	golang.org/x/term v0.34.0
)

require (
	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
	github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/rivo/uniseg v0.4.7 // indirect
	golang.org/x/text v0.22.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/IGLOU-EU/go-wildcard v1.0.3 h1:r8T46+8/9V1STciXJomTWRpPEv4nGJATDbJkdU0Nou0=
github.com/IGLOU-EU/go-wildcard v1.0.3/go.mod h1:/qeV4QLmydCbwH0UMQJmXDryrFKJknWi/jjO8IiuQfY=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
github.com/muesli/mango-pflag v0.2.0 h1:QViokgKDZQCzKhYe1zH8D+UlPJzBSGoP9yx0hBG0t5k=
github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y=
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: groups.go
================================================
package main

import (
	"strings"
)

const (
	localDevice   = "local"
	networkDevice = "network"
	fuseDevice    = "fuse"
	specialDevice = "special"
	loopsDevice   = "loops"
	bindsMount    = "binds"
)

// FilterOptions contains all filters.
type FilterOptions struct {
	HiddenDevices map[string]struct{}
	OnlyDevices   map[string]struct{}

	HiddenFilesystems map[string]struct{}
	OnlyFilesystems   map[string]struct{}

	HiddenMountPoints map[string]struct{}
	OnlyMountPoints   map[string]struct{}
}

// renderTables renders all tables.
func renderTables(m []Mount, filters FilterOptions, opts TableOptions) {
	deviceMounts := make(map[string][]Mount)
	hasOnlyDevices := len(filters.OnlyDevices) != 0

	_, hideLocal := filters.HiddenDevices[localDevice]
	_, hideNetwork := filters.HiddenDevices[networkDevice]
	_, hideFuse := filters.HiddenDevices[fuseDevice]
	_, hideSpecial := filters.HiddenDevices[specialDevice]
	_, hideLoops := filters.HiddenDevices[loopsDevice]
	_, hideBinds := filters.HiddenDevices[bindsMount]

	_, onlyLocal := filters.OnlyDevices[localDevice]
	_, onlyNetwork := filters.OnlyDevices[networkDevice]
	_, onlyFuse := filters.OnlyDevices[fuseDevice]
	_, onlySpecial := filters.OnlyDevices[specialDevice]
	_, onlyLoops := filters.OnlyDevices[loopsDevice]
	_, onlyBinds := filters.OnlyDevices[bindsMount]

	// sort/filter devices
	for _, v := range m {
		if len(filters.OnlyFilesystems) != 0 {
			// skip not onlyFs
			if _, ok := filters.OnlyFilesystems[strings.ToLower(v.Fstype)]; !ok {
				continue
			}
		} else {
			// skip hideFs
			if _, ok := filters.HiddenFilesystems[strings.ToLower(v.Fstype)]; ok {
				continue
			}
		}

		// skip hidden devices
		if isHiddenFs(v) && !*all {
			continue
		}

		// skip bind-mounts
		if strings.Contains(v.Opts, "bind") {
			if (hasOnlyDevices && !onlyBinds) || (hideBinds && !*all) {
				continue
			}
		}

		// skip loop devices
		if strings.HasPrefix(v.Device, "/dev/loop") {
			if (hasOnlyDevices && !onlyLoops) || (hideLoops && !*all) {
				continue
			}
		}

		// skip special devices
		if v.Blocks == 0 && !*all {
			continue
		}

		// skip zero size devices
		if v.BlockSize == 0 && !*all {
			continue
		}

		// skip not only mount point
		if len(filters.OnlyMountPoints) != 0 {
			if !findInKey(v.Mountpoint, filters.OnlyMountPoints) {
				continue
			}
		}

		// skip hidden mount point
		if len(filters.HiddenMountPoints) != 0 {
			if findInKey(v.Mountpoint, filters.HiddenMountPoints) {
				continue
			}
		}

		t := deviceType(v)
		deviceMounts[t] = append(deviceMounts[t], v)
	}

	// print tables
	for _, devType := range groups {
		mounts := deviceMounts[devType]

		shouldPrint := *all
		if !shouldPrint {
			switch devType {
			case localDevice:
				shouldPrint = (hasOnlyDevices && onlyLocal) || (!hasOnlyDevices && !hideLocal)
			case networkDevice:
				shouldPrint = (hasOnlyDevices && onlyNetwork) || (!hasOnlyDevices && !hideNetwork)
			case fuseDevice:
				shouldPrint = (hasOnlyDevices && onlyFuse) || (!hasOnlyDevices && !hideFuse)
			case specialDevice:
				shouldPrint = (hasOnlyDevices && onlySpecial) || (!hasOnlyDevices && !hideSpecial)
			}
		}

		if shouldPrint {
			printTable(devType, mounts, opts)
		}
	}
}


================================================
FILE: main.go
================================================
package main

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"time"

	wildcard "github.com/IGLOU-EU/go-wildcard"
	"github.com/jedib0t/go-pretty/v6/table"
	"github.com/muesli/termenv"
	flag "github.com/spf13/pflag"
	"golang.org/x/term"
)

var (
	// Version contains the application version number. It's set via ldflags
	// when building.
	Version = ""

	// CommitSHA contains the SHA of the commit that this application was built
	// against. It's set via ldflags when building.
	CommitSHA = ""

	env   = termenv.EnvColorProfile()
	theme Theme

	groups        = []string{localDevice, networkDevice, fuseDevice, specialDevice, loopsDevice, bindsMount}
	allowedValues = strings.Join(groups, ", ")

	all         = flag.Bool("all", false, "include pseudo, duplicate, inaccessible file systems")
	hideDevices = flag.String("hide", "", "hide specific devices, separated with commas:\n"+allowedValues)
	hideFs      = flag.String("hide-fs", "", "hide specific filesystems, separated with commas")
	hideMp      = flag.String("hide-mp", "", "hide specific mount points, separated with commas (supports wildcards)")
	onlyDevices = flag.String("only", "", "show only specific devices, separated with commas:\n"+allowedValues)
	onlyFs      = flag.String("only-fs", "", "only specific filesystems, separated with commas")
	onlyMp      = flag.String("only-mp", "", "only specific mount points, separated with commas (supports wildcards)")

	output   = flag.String("output", "", "output fields: "+strings.Join(columnIDs(), ", "))
	sortBy   = flag.String("sort", "mountpoint", "sort output by: "+strings.Join(columnIDs(), ", "))
	width    = flag.Uint("width", 0, "max output width")
	themeOpt = flag.String("theme", defaultThemeName(), "color themes: dark, light, ansi")
	styleOpt = flag.String("style", defaultStyleName(), "style: unicode, ascii")

	availThreshold = flag.String("avail-threshold", "10G,1G", "specifies the coloring threshold (yellow, red) of the avail column, must be integer with optional SI prefixes")
	usageThreshold = flag.String("usage-threshold", "0.5,0.9", "specifies the coloring threshold (yellow, red) of the usage bars as a floating point number from 0 to 1")

	_          = flag.BoolP("human-readable", "h", false, "ignored, just for df compatibility")
	inodes     = flag.Bool("inodes", false, "list inode information instead of block usage")
	jsonOutput = flag.Bool("json", false, "output all devices in JSON format")
	warns      = flag.Bool("warnings", false, "output all warnings to STDERR")
	version    = flag.Bool("version", false, "display version")
)

// renderJSON encodes the JSON output and prints it.
func renderJSON(m []Mount) error {
	output, err := json.MarshalIndent(m, "", " ")
	if err != nil {
		return fmt.Errorf("error formatting the json output: %s", err)
	}

	fmt.Println(string(output))
	return nil
}

// parseColumns parses the supplied output flag into a slice of column indices.
func parseColumns(cols string) ([]int, error) {
	var i []int

	s := strings.Split(cols, ",")
	for _, v := range s {
		v = strings.TrimSpace(v)
		if len(v) == 0 {
			continue
		}

		col, err := stringToColumn(v)
		if err != nil {
			return nil, err
		}

		i = append(i, col)
	}

	return i, nil
}

// parseStyle converts user-provided style option into a table.Style.
func parseStyle(styleOpt string) (table.Style, error) {
	switch styleOpt {
	case "unicode":
		return table.StyleRounded, nil
	case "ascii":
		return table.StyleDefault, nil
	default:
		return table.Style{}, fmt.Errorf("unknown style option: %s", styleOpt)
	}
}

// parseCommaSeparatedValues parses comma separated string into a map.
func parseCommaSeparatedValues(values string) map[string]struct{} {
	m := make(map[string]struct{})
	for _, v := range strings.Split(values, ",") {
		v = strings.TrimSpace(v)
		if len(v) == 0 {
			continue
		}

		v = strings.ToLower(v)
		m[v] = struct{}{}
	}
	return m
}

// validateGroups validates the parsed group maps.
func validateGroups(m map[string]struct{}) error {
	for k := range m {
		found := false
		for _, g := range groups {
			if g == k {
				found = true
				break
			}
		}

		if !found {
			return fmt.Errorf("unknown device group: %s", k)
		}
	}

	return nil
}

// findInKey parse a slice of pattern to match the given key.
func findInKey(str string, km map[string]struct{}) bool {
	for p := range km {
		if wildcard.Match(p, str) {
			return true
		}
	}

	return false
}

func printVersion() {
	info, ok := debug.ReadBuildInfo()
	var buildTime time.Time
	var modified bool
	if ok {
		if len(Version) == 0 {
			vs := strings.Split(info.Main.Version, "-")
			if len(vs) >= 1 {
				Version = vs[0]
			}
		}

		for _, setting := range info.Settings {
			switch setting.Key {
			case "vcs.revision":
				if len(CommitSHA) == 0 {
					CommitSHA = setting.Value
					if len(CommitSHA) > 12 {
						CommitSHA = CommitSHA[:12]
					}
				}
			case "vcs.time":
				buildTime, _ = time.Parse(time.RFC3339, setting.Value)
			case "vcs.modified":
				modified, _ = strconv.ParseBool(setting.Value)
			}
		}
	}

	if Version == "" || Version == "(devel)" {
		Version = "(built from source)"
	}

	fmt.Printf("duf %s", Version)
	if len(CommitSHA) > 0 {
		if modified {
			CommitSHA += "+modified"
		}
		fmt.Printf(" (%s)", CommitSHA)
	}
	if !buildTime.IsZero() {
		fmt.Printf(" (built on %s)", buildTime.Format("2006-01-02"))
	}

	fmt.Println()
}

func main() {
	// hide -h from help, it's just for df compatibility
	_ = flag.CommandLine.MarkHidden("human-readable")
	flag.Parse()

	if *version {
		printVersion()
		os.Exit(0)
	}

	// read mount table
	m, warnings, err := mounts()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// print JSON
	if *jsonOutput {
		if err = renderJSON(m); err != nil {
			fmt.Fprintln(os.Stderr, err)
		}
		return
	}

	// validate theme
	theme, err = loadTheme(*themeOpt)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if env == termenv.ANSI {
		// enforce ANSI theme for limited color support
		theme, err = loadTheme("ansi")
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	// validate style
	style, err := parseStyle(*styleOpt)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// validate output columns
	columns, err := parseColumns(*output)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if len(columns) == 0 {
		// no columns supplied, use defaults
		if *inodes {
			columns = []int{1, 6, 7, 8, 9, 10, 11}
		} else {
			columns = []int{1, 2, 3, 4, 5, 10, 11}
		}
	}

	// validate sort column
	sortCol, err := stringToSortIndex(*sortBy)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// validate filters
	filters := FilterOptions{
		HiddenDevices:     parseCommaSeparatedValues(*hideDevices),
		OnlyDevices:       parseCommaSeparatedValues(*onlyDevices),
		HiddenFilesystems: parseCommaSeparatedValues(*hideFs),
		OnlyFilesystems:   parseCommaSeparatedValues(*onlyFs),
		HiddenMountPoints: parseCommaSeparatedValues(*hideMp),
		OnlyMountPoints:   parseCommaSeparatedValues(*onlyMp),
	}
	err = validateGroups(filters.HiddenDevices)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = validateGroups(filters.OnlyDevices)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// validate arguments
	if len(flag.Args()) > 0 {
		var mounts []Mount
		vis := map[string]struct{}{}

		for _, v := range flag.Args() {
			var fm []Mount
			fm, err = findMounts(m, v)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			// de-duplicate
			for _, v := range fm {
				if _, ok := vis[v.Mountpoint]; !ok {
					mounts = append(mounts, v)
					vis[v.Mountpoint] = struct{}{}
				}
			}
		}

		m = mounts
	}

	// validate availability thresholds
	availbilityThresholds := strings.Split(*availThreshold, ",")
	if len(availbilityThresholds) != 2 {
		fmt.Fprintln(os.Stderr, fmt.Errorf("error parsing avail-threshold: invalid option '%s'", *availThreshold))
		os.Exit(1)
	}
	for _, threshold := range availbilityThresholds {
		_, err = stringToSize(threshold)
		if err != nil {
			fmt.Fprintln(os.Stderr, "error parsing avail-threshold:", err)
			os.Exit(1)
		}
	}

	// validate usage thresholds
	usageThresholds := strings.Split(*usageThreshold, ",")
	if len(usageThresholds) != 2 {
		fmt.Fprintln(os.Stderr, fmt.Errorf("error parsing usage-threshold: invalid option '%s'", *usageThreshold))
		os.Exit(1)
	}
	for _, threshold := range usageThresholds {
		_, err = strconv.ParseFloat(threshold, 64)
		if err != nil {
			fmt.Fprintln(os.Stderr, "error parsing usage-threshold:", err)
			os.Exit(1)
		}
	}

	// print out warnings
	if *warns {
		for _, warning := range warnings {
			fmt.Fprintln(os.Stderr, warning)
		}
	}

	// detect terminal width
	isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
	if isTerminal && *width == 0 {
		w, _, err := term.GetSize(int(os.Stdout.Fd()))
		if err == nil {
			*width = uint(w)
		}
	}
	if *width == 0 {
		*width = 80
	}

	// print tables
	renderTables(m, filters, TableOptions{
		Columns:   columns,
		SortBy:    sortCol,
		Style:     style,
		StyleName: *styleOpt,
	})
}


================================================
FILE: man.go
================================================
//go:build mango
// +build mango

package main

import (
	"fmt"
	"os"

	"github.com/muesli/mango"
	mpflag "github.com/muesli/mango-pflag"
	"github.com/muesli/roff"
	flag "github.com/spf13/pflag"
)

func init() {
	usage := `You can simply start duf without any command-line arguments:

  $ duf

If you supply arguments, duf will only list specific devices & mount points:

  $ duf /home /some/file

If you want to list everything (including pseudo, duplicate, inaccessible file systems):

  $ duf --all

You can show and hide specific tables:

  $ duf --only local,network,fuse,special,loops,binds
  $ duf --hide local,network,fuse,special,loops,binds

You can also show and hide specific filesystems:

  $ duf --only-fs tmpfs,vfat
  $ duf --hide-fs tmpfs,vfat

...or specific mount points:

  $ duf --only-mp /,/home,/dev
  $ duf --hide-mp /,/home,/dev

Wildcards inside quotes work:

  $ duf --only-mp '/sys/*,/dev/*'

Sort the output:

  $ duf --sort size

Valid keys are: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem.

Show or hide specific columns:

  $ duf --output mountpoint,size,usage

Valid keys are: mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem.

List inode information instead of block usage:

  $ duf --inodes

If duf doesn't detect your terminal's colors correctly, you can set a theme:

  $ duf --theme light

duf highlights the availability & usage columns in red, green, or yellow, depending on how much space is still available. You can set your own thresholds:

  $ duf --avail-threshold="10G,1G"
  $ duf --usage-threshold="0.5,0.9"

If you prefer your output as JSON:

  $ duf --json
`

	manPage := mango.NewManPage(1, "duf", "Disk Usage/Free Utility").
		WithLongDescription("Simple Disk Usage/Free Utility.\n"+
			"Features:\n"+
			"* User-friendly, colorful output.\n"+
			"* Adjusts to your terminal's theme & width.\n"+
			"* Sort the results according to your needs.\n"+
			"* Groups & filters devices.\n"+
			"* Can conveniently output JSON.").
		WithSection("Usage", usage).
		WithSection("Notes", "Portions of duf's code are copied and modified from https://github.com/shirou/gopsutil.\n"+
			"gopsutil was written by WAKAYAMA Shirou and is distributed under BSD-3-Clause.").
		WithSection("Authors", "duf was written by Christian Muehlhaeuser <https://github.com/muesli/duf>").
		WithSection("Copyright", "Copyright (C) 2020-2022 Christian Muehlhaeuser <https://github.com/muesli>\n"+
			"Released under MIT license.")

	flag.VisitAll(mpflag.PFlagVisitor(manPage))
	fmt.Println(manPage.Build(roff.NewDocument()))
	os.Exit(0)
}


================================================
FILE: mounts.go
================================================
package main

import (
	"bufio"
	"os"
	"strconv"
)

// Mount contains all metadata for a single filesystem mount.
type Mount struct {
	Device     string      `json:"device"`
	DeviceType string      `json:"device_type"`
	Mountpoint string      `json:"mount_point"`
	Fstype     string      `json:"fs_type"`
	Type       string      `json:"type"`
	Opts       string      `json:"opts"`
	Total      uint64      `json:"total"`
	Free       uint64      `json:"free"`
	Used       uint64      `json:"used"`
	Inodes     uint64      `json:"inodes"`
	InodesFree uint64      `json:"inodes_free"`
	InodesUsed uint64      `json:"inodes_used"`
	Blocks     uint64      `json:"blocks"`
	BlockSize  uint64      `json:"block_size"`
	Metadata   interface{} `json:"-"`
}

func readLines(filename string) ([]string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close() //nolint:errcheck // ignore error

	scanner := bufio.NewScanner(file)
	var s []string
	for scanner.Scan() {
		s = append(s, scanner.Text())
	}

	return s, scanner.Err()
}

func unescapeFstab(path string) string {
	escaped, err := strconv.Unquote(`"` + path + `"`)
	if err != nil {
		return path
	}
	return escaped
}

//nolint:unused // used on BSD
func byteToString(orig []byte) string {
	n := -1
	l := -1
	for i, b := range orig {
		// skip left side null
		if l == -1 && b == 0 {
			continue
		}
		if l == -1 {
			l = i
		}

		if b == 0 {
			break
		}
		n = i + 1
	}
	if n == -1 {
		return string(orig)
	}
	return string(orig[l:n])
}

//nolint:unused // used on OpenBSD
func intToString(orig []int8) string {
	ret := make([]byte, len(orig))
	size := -1
	for i, o := range orig {
		if o == 0 {
			size = i
			break
		}
		ret[i] = byte(o)
	}
	if size == -1 {
		size = len(orig)
	}

	return string(ret[0:size])
}


================================================
FILE: mounts_darwin.go
================================================
//go:build darwin
// +build darwin

package main

import (
	"golang.org/x/sys/unix"
)

func (m *Mount) Stat() unix.Statfs_t {
	return m.Metadata.(unix.Statfs_t)
}

func mounts() ([]Mount, []string, error) {
	var ret []Mount
	var warnings []string

	count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
	if err != nil {
		return nil, nil, err
	}
	fs := make([]unix.Statfs_t, count)
	if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
		return nil, nil, err
	}

	for _, stat := range fs {
		opts := "rw"
		if stat.Flags&unix.MNT_RDONLY != 0 {
			opts = "ro"
		}
		if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
			opts += ",sync"
		}
		if stat.Flags&unix.MNT_NOEXEC != 0 {
			opts += ",noexec"
		}
		if stat.Flags&unix.MNT_NOSUID != 0 {
			opts += ",nosuid"
		}
		if stat.Flags&unix.MNT_UNION != 0 {
			opts += ",union"
		}
		if stat.Flags&unix.MNT_ASYNC != 0 {
			opts += ",async"
		}
		if stat.Flags&unix.MNT_DONTBROWSE != 0 {
			opts += ",nobrowse"
		}
		if stat.Flags&unix.MNT_AUTOMOUNTED != 0 {
			opts += ",automounted"
		}
		if stat.Flags&unix.MNT_JOURNALED != 0 {
			opts += ",journaled"
		}
		if stat.Flags&unix.MNT_MULTILABEL != 0 {
			opts += ",multilabel"
		}
		if stat.Flags&unix.MNT_NOATIME != 0 {
			opts += ",noatime"
		}
		if stat.Flags&unix.MNT_NODEV != 0 {
			opts += ",nodev"
		}

		device := byteToString(stat.Mntfromname[:])
		mountPoint := byteToString(stat.Mntonname[:])
		fsType := byteToString(stat.Fstypename[:])

		if len(device) == 0 {
			continue
		}

		d := Mount{
			Device:     device,
			Mountpoint: mountPoint,
			Fstype:     fsType,
			Type:       fsType,
			Opts:       opts,
			Metadata:   stat,
			Total:      stat.Blocks * uint64(stat.Bsize),
			Free:       stat.Bavail * uint64(stat.Bsize),
			Used:       (stat.Blocks - stat.Bfree) * uint64(stat.Bsize),
			Inodes:     stat.Files,
			InodesFree: stat.Ffree,
			InodesUsed: stat.Files - stat.Ffree,
			Blocks:     stat.Blocks,
			BlockSize:  uint64(stat.Bsize),
		}
		d.DeviceType = deviceType(d)

		ret = append(ret, d)
	}

	return ret, warnings, nil
}


================================================
FILE: mounts_freebsd.go
================================================
//go:build freebsd
// +build freebsd

package main

import (
	"golang.org/x/sys/unix"
)

func (m *Mount) Stat() unix.Statfs_t {
	return m.Metadata.(unix.Statfs_t)
}

func mounts() ([]Mount, []string, error) {
	var ret []Mount
	var warnings []string

	count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
	if err != nil {
		return nil, nil, err
	}
	fs := make([]unix.Statfs_t, count)
	if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
		return nil, nil, err
	}

	for _, stat := range fs {
		opts := "rw"
		if stat.Flags&unix.MNT_RDONLY != 0 {
			opts = "ro"
		}
		if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
			opts += ",sync"
		}
		if stat.Flags&unix.MNT_NOEXEC != 0 {
			opts += ",noexec"
		}
		if stat.Flags&unix.MNT_NOSUID != 0 {
			opts += ",nosuid"
		}
		if stat.Flags&unix.MNT_UNION != 0 {
			opts += ",union"
		}
		if stat.Flags&unix.MNT_ASYNC != 0 {
			opts += ",async"
		}
		if stat.Flags&unix.MNT_SUIDDIR != 0 {
			opts += ",suiddir"
		}
		if stat.Flags&unix.MNT_SOFTDEP != 0 {
			opts += ",softdep"
		}
		if stat.Flags&unix.MNT_NOSYMFOLLOW != 0 {
			opts += ",nosymfollow"
		}
		if stat.Flags&unix.MNT_GJOURNAL != 0 {
			opts += ",gjournal"
		}
		if stat.Flags&unix.MNT_MULTILABEL != 0 {
			opts += ",multilabel"
		}
		if stat.Flags&unix.MNT_ACLS != 0 {
			opts += ",acls"
		}
		if stat.Flags&unix.MNT_NOATIME != 0 {
			opts += ",noatime"
		}
		if stat.Flags&unix.MNT_NOCLUSTERR != 0 {
			opts += ",noclusterr"
		}
		if stat.Flags&unix.MNT_NOCLUSTERW != 0 {
			opts += ",noclusterw"
		}
		if stat.Flags&unix.MNT_NFS4ACLS != 0 {
			opts += ",nfsv4acls"
		}

		device := byteToString(stat.Mntfromname[:])
		mountPoint := byteToString(stat.Mntonname[:])
		fsType := byteToString(stat.Fstypename[:])

		if len(device) == 0 {
			continue
		}

		d := Mount{
			Device:     device,
			Mountpoint: mountPoint,
			Fstype:     fsType,
			Type:       fsType,
			Opts:       opts,
			Metadata:   stat,
			Total:      (uint64(stat.Blocks) * uint64(stat.Bsize)),
			Free:       (uint64(stat.Bavail) * uint64(stat.Bsize)),
			Used:       (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(stat.Bsize),
			Inodes:     stat.Files,
			InodesFree: uint64(stat.Ffree),
			InodesUsed: stat.Files - uint64(stat.Ffree),
			Blocks:     uint64(stat.Blocks),
			BlockSize:  uint64(stat.Bsize),
		}
		d.DeviceType = deviceType(d)

		ret = append(ret, d)
	}

	return ret, warnings, nil
}


================================================
FILE: mounts_linux.go
================================================
//go:build linux
// +build linux

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"
)

const (
	// A line of self/mountinfo has the following structure:
	// 36  35  98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
	// (0) (1) (2)   (3)   (4)      (5)      (6)   (7) (8)    (9)           (10)
	//
	// (0) mount ID: unique identifier of the mount (may be reused after umount).
	//mountinfoMountID = 0
	// (1) parent ID: ID of parent (or of self for the top of the mount tree).
	//mountinfoParentID = 1
	// (2) major:minor: value of st_dev for files on filesystem.
	//mountinfoMajorMinor = 2
	// (3) root: root of the mount within the filesystem.
	//mountinfoRoot = 3
	// (4) mount point: mount point relative to the process's root.
	mountinfoMountPoint = 4
	// (5) mount options: per mount options.
	mountinfoMountOpts = 5
	// (6) optional fields: zero or more fields terminated by "-".
	mountinfoOptionalFields = 6
	// (7) separator between optional fields.
	//mountinfoSeparator = 7
	// (8) filesystem type: name of filesystem of the form.
	mountinfoFsType = 8
	// (9) mount source: filesystem specific information or "none".
	mountinfoMountSource = 9
	// (10) super options: per super block options.
	mountinfoSuperOptions = 10
)

// Stat returns the mountpoint's stat information.
func (m *Mount) Stat() unix.Statfs_t {
	return m.Metadata.(unix.Statfs_t)
}

func mounts() ([]Mount, []string, error) {
	var warnings []string

	filename := "/proc/self/mountinfo"
	lines, err := readLines(filename)
	if err != nil {
		// wrapcheck: add context to the error.
		return nil, nil, fmt.Errorf("reading mountinfo %q: %w", filename, err)
	}

	ret := make([]Mount, 0, len(lines))
	for _, line := range lines {
		nb, fields := parseMountInfoLine(line)
		if nb == 0 {
			continue
		}

		// if the number of fields does not match the structure of mountinfo,
		// emit a warning and ignore the line.
		if nb < 10 || nb > 11 {
			warnings = append(warnings, fmt.Sprintf("found invalid mountinfo line: %s", line))
			continue
		}

		// blockDeviceID := fields[mountinfoMountID]
		mountPoint := fields[mountinfoMountPoint]
		mountOpts := fields[mountinfoMountOpts]
		fstype := fields[mountinfoFsType]
		device := fields[mountinfoMountSource]

		var stat unix.Statfs_t
		err := unix.Statfs(mountPoint, &stat)
		if err != nil {
			if err != os.ErrPermission {
				warnings = append(warnings, fmt.Sprintf("%s: %s", mountPoint, err))
				continue
			}

			stat = unix.Statfs_t{}
		}

		d := Mount{
			Device:     device,
			Mountpoint: mountPoint,
			Fstype:     fstype,
			Type:       fsTypeMap[int64(stat.Type)], //nolint:unconvert
			Opts:       mountOpts,
			Metadata:   stat,
			Total:      (uint64(stat.Blocks) * uint64(stat.Bsize)),                      //nolint:unconvert
			Free:       (uint64(stat.Bavail) * uint64(stat.Bsize)),                      //nolint:unconvert
			Used:       (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(stat.Bsize), //nolint:unconvert
			Inodes:     stat.Files,
			InodesFree: stat.Ffree,
			InodesUsed: stat.Files - stat.Ffree,
			Blocks:     uint64(stat.Blocks), //nolint:unconvert
			BlockSize:  uint64(stat.Bsize),
		}
		d.DeviceType = deviceType(d)

		// Resolve /dev/mapper/* device names.
		if strings.HasPrefix(d.Device, "/dev/mapper/") {
			re := regexp.MustCompile(`^/dev/mapper/(.*)-(.*)`)
			match := re.FindAllStringSubmatch(d.Device, -1)
			if len(match) > 0 && len(match[0]) == 3 {
				d.Device = filepath.Join("/dev", match[0][1], match[0][2])
			}
		}

		ret = append(ret, d)
	}

	return ret, warnings, nil
}

// splitMountInfoFields splits a mountinfo line into its fields.
// It treats spaces and tabs as field separators and decodes certain octal escapes.
func splitMountInfoFields(line string) []string {
	var fields []string
	var buf strings.Builder

	for i := 0; i < len(line); i++ {
		c := line[i]

		// Treat both space and tab as separators
		if c == ' ' || c == '\t' {
			if buf.Len() > 0 {
				fields = append(fields, buf.String())
				buf.Reset()
			}
			continue
		}

		if c == '\\' && i+3 < len(line) {
			oct := line[i+1 : i+4]
			if v, err := strconv.ParseInt(oct, 8, 0); err == nil {
				switch byte(v) {
				case ' ', '\t', '\n':
					buf.WriteByte(byte(v))
					i += 3
					continue
				default:
					// keep unknown escapes as-is
					buf.WriteString("\\" + oct)
					i += 3
					continue
				}
			}
		}

		buf.WriteByte(c)
	}

	if buf.Len() > 0 {
		fields = append(fields, buf.String())
	}

	return fields
}

// parseMountInfoLine parses a line of /proc/self/mountinfo and returns the
// amount of parsed fields and their values.
func parseMountInfoLine(line string) (int, [11]string) {
	var fields [11]string

	if len(line) == 0 || line[0] == '#' {
		// ignore comments and empty lines
		return 0, fields
	}

	all := splitMountInfoFields(line)

	var i int
	sawSep := false
	sawSup := false

	for _, f := range all {
		if i >= len(fields) {
			break
		}

		if i == mountinfoOptionalFields {
			// (6)  optional fields: zero or more fields of the form "tag[:value]"; see below.
			// (7)  separator: the end of the optional fields is marked by a single hyphen.
			if f != "-" {
				// Join tokens with spaces for mountinfoOptionalFields.
				fields[i] = strings.TrimSpace(fields[i] + " " + f)
				continue
			}
			// Found separator.
			sawSep = true
			i++
			fields[i] = f
			i++
			continue
		}

		if i == mountinfoSuperOptions {
			// join tokens with spaces for WSL2 path=... they are splitted around spaces.
			fields[i] = strings.TrimSpace(fields[i] + " " + f)
			sawSup = true
			continue
		}

		// Default case: copy with unescape for certain fields
		switch i {
		case mountinfoMountPoint, mountinfoMountSource, mountinfoFsType:
			fields[i] = unescapeFstab(f)
		default:
			fields[i] = f
		}
		i++
	}

	// Handle malformed line (no "-" found).
	if !sawSep && len(all) > mountinfoOptionalFields {
		i = mountinfoOptionalFields
	}

	// When super options are present, the index is one less than 11.
	if sawSup {
		i++
	}

	// clear trailing empties.
	for j := i + 1; j < len(fields); j++ {
		fields[j] = ""
	}

	return i, fields
}


================================================
FILE: mounts_linux_test.go
================================================
//go:build linux
// +build linux

package main

import (
	"reflect"
	"testing"
)

func TestGetFields(t *testing.T) {
	var tt = []struct {
		input    string
		number   int
		expected [11]string
	}{
		// Empty lines
		{
			input:  "",
			number: 0,
		},
		{
			input:  " ",
			number: 0,
		},
		{
			input:  "   ",
			number: 0,
		},
		{
			input:  "	",
			number: 0,
		},

		// Comments
		{
			input:  "#",
			number: 0,
		},
		{
			input:  "# ",
			number: 0,
		},
		{
			input:  "#	",
			number: 0,
		},
		{
			input:  "# I'm a lazy dog",
			number: 0,
		},

		// Bad fields
		{
			input:    "1 2",
			number:   2,
			expected: [11]string{"1", "2"},
		},
		{
			input:    "1	2",
			number:   2,
			expected: [11]string{"1", "2"},
		},
		{
			input:    "1	2		3",
			number:   3,
			expected: [11]string{"1", "2", "3"},
		},
		{
			input:    "1	2		3   4",
			number:   4,
			expected: [11]string{"1", "2", "3", "4"},
		},

		// No optional separator or no options
		{
			input:    "1 2 3 4 5 6 7 NotASeparator 9 10 11",
			number:   6,
			expected: [11]string{"1", "2", "3", "4", "5", "6", "7 NotASeparator 9 10 11"},
		},
		{
			input:    "1 2 3 4 5 6 7 8 9 10 11",
			number:   6,
			expected: [11]string{"1", "2", "3", "4", "5", "6", "7 8 9 10 11"},
		},
		{
			input:    "1 2 3 4 5 6 - 9 10 11",
			number:   11,
			expected: [11]string{"1", "2", "3", "4", "5", "6", "", "-", "9", "10", "11"},
		},

		// Normal mount table line
		{
			input:    "22 27 0:21 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw",
			number:   11,
			expected: [11]string{"22", "27", "0:21", "/", "/proc", "rw,nosuid,nodev,noexec,relatime", "shared:5", "-", "proc", "proc", "rw"},
		},
		{
			input:    "31 23 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw,nsdelegate,memory_recursiveprot",
			number:   11,
			expected: [11]string{"31", "23", "0:27", "/", "/sys/fs/cgroup", "rw,nosuid,nodev,noexec,relatime", "shared:9", "-", "cgroup2", "cgroup2", "rw,nsdelegate,memory_recursiveprot"},
		},
		{
			input:    "40 27 0:33 / /tmp rw,nosuid,nodev shared:18 - tmpfs tmpfs",
			number:   10,
			expected: [11]string{"40", "27", "0:33", "/", "/tmp", "rw,nosuid,nodev", "shared:18", "-", "tmpfs", "tmpfs"},
		},
		{
			input:    "40 27 0:33 / /tmp rw,nosuid,nodev shared:18 shared:22 - tmpfs tmpfs",
			number:   10,
			expected: [11]string{"40", "27", "0:33", "/", "/tmp", "rw,nosuid,nodev", "shared:18 shared:22", "-", "tmpfs", "tmpfs"},
		},
		{
			input:    "50 27 0:33 / /tmp rw,nosuid,nodev - tmpfs tmpfs",
			number:   10,
			expected: [11]string{"50", "27", "0:33", "/", "/tmp", "rw,nosuid,nodev", "", "-", "tmpfs", "tmpfs"},
		},

		// Exceptional mount table lines
		{
			input:    "328 27 0:73 / /mnt/a rw,relatime shared:206 - tmpfs - rw,inode64",
			number:   11,
			expected: [11]string{"328", "27", "0:73", "/", "/mnt/a", "rw,relatime", "shared:206", "-", "tmpfs", "-", "rw,inode64"},
		},
		{
			input:    "330 27 0:73 / /mnt/a rw,relatime shared:206 - tmpfs 👾 rw,inode64",
			number:   11,
			expected: [11]string{"330", "27", "0:73", "/", "/mnt/a", "rw,relatime", "shared:206", "-", "tmpfs", "👾", "rw,inode64"},
		},
		{
			input:    "335 27 0:73 / /mnt/👾 rw,relatime shared:206 - tmpfs 👾 rw,inode64",
			number:   11,
			expected: [11]string{"335", "27", "0:73", "/", "/mnt/👾", "rw,relatime", "shared:206", "-", "tmpfs", "👾", "rw,inode64"},
		},
		{
			input:    "509 27 0:78 / /mnt/- rw,relatime shared:223 - tmpfs 👾 rw,inode64",
			number:   11,
			expected: [11]string{"509", "27", "0:78", "/", "/mnt/-", "rw,relatime", "shared:223", "-", "tmpfs", "👾", "rw,inode64"},
		},
		{
			input:    "362 27 0:76 / /mnt/a\\040b rw,relatime shared:215 - tmpfs 👾 rw,inode64",
			number:   11,
			expected: [11]string{"362", "27", "0:76", "/", "/mnt/a b", "rw,relatime", "shared:215", "-", "tmpfs", "👾", "rw,inode64"},
		},
		{
			input:    "1 2 3:3 / /mnt/\\011 rw shared:7 - tmpfs - rw,inode64",
			number:   11,
			expected: [11]string{"1", "2", "3:3", "/", "/mnt/\t", "rw", "shared:7", "-", "tmpfs", "-", "rw,inode64"},
		},
		{
			input:    "11 2 3:3 / /mnt/a\\012b rw shared:7 - tmpfs - rw,inode64",
			number:   11,
			expected: [11]string{"11", "2", "3:3", "/", "/mnt/a\nb", "rw", "shared:7", "-", "tmpfs", "-", "rw,inode64"},
		},
		{
			input:    "111 2 3:3 / /mnt/a\\134b rw shared:7 - tmpfs - rw,inode64",
			number:   11,
			expected: [11]string{"111", "2", "3:3", "/", "/mnt/a\\b", "rw", "shared:7", "-", "tmpfs", "-", "rw,inode64"},
		},
		{
			input:    "1111 2 3:3 / /mnt/a\\042b rw shared:7 - tmpfs - rw,inode64",
			number:   11,
			expected: [11]string{"1111", "2", "3:3", "/", "/mnt/a\"b", "rw", "shared:7", "-", "tmpfs", "-", "rw,inode64"},
		},
		// WSL2 9p mount table line.
		{
			input:    `380 383 0:33 / /usr/lib/wsl/drivers ro,nosuid,nodev,noatime - 9p drivers ro,dirsync,aname=drivers;fmask=222;dmask=222,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8`,
			number:   11,
			expected: [11]string{"380", "383", "0:33", "/", "/usr/lib/wsl/drivers", "ro,nosuid,nodev,noatime", "", "-", "9p", "drivers", "ro,dirsync,aname=drivers;fmask=222;dmask=222,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8"},
		},
		{
			input:    `488 383 0:128 / /mnt/c rw,noatime - 9p C:\134 rw,dirsync,aname=drvfs;path=C:\;uid=1000;gid=1000;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=5,wfd=5`,
			number:   11,
			expected: [11]string{"488", "383", "0:128", "/", "/mnt/c", "rw,noatime", "", "-", "9p", "C:\\", "rw,dirsync,aname=drvfs;path=C:\\;uid=1000;gid=1000;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=5,wfd=5"},
		},
		{
			input:  `516 78 0:136 / /Docker/host rw,noatime - 9p C:\134Program\040Files\134Docker\134Docker\134resources rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=3,wfd=3`,
			number: 11,
			expected: [11]string{"516", "78", "0:136", "/", "/Docker/host", "rw,noatime", "", "-", "9p", "C:\\Program Files\\Docker\\Docker\\resources", "rw,dirsync,aname=drvfs;path=C:\\Program Files\\Docker\\Docker\\resources;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=3,wfd=3"},
		},
	}

	for _, tc := range tt {
		nb, actual := parseMountInfoLine(tc.input)
		if nb != tc.number || !reflect.DeepEqual(actual, tc.expected) {
			t.Errorf("\nparseMountInfoLine(%q) == \n(%d) %q, \nexpected (%d) %q", tc.input, nb, actual, tc.number, tc.expected)
		}
	}
}


================================================
FILE: mounts_openbsd.go
================================================
//go:build openbsd
// +build openbsd

package main

import (
	"golang.org/x/sys/unix"
)

func (m *Mount) Stat() unix.Statfs_t {
	return m.Metadata.(unix.Statfs_t)
}

func mounts() ([]Mount, []string, error) {
	var ret []Mount
	var warnings []string

	count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
	if err != nil {
		return nil, nil, err
	}
	fs := make([]unix.Statfs_t, count)
	if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
		return nil, nil, err
	}

	for _, stat := range fs {
		opts := "rw"
		if stat.F_flags&unix.MNT_RDONLY != 0 {
			opts = "ro"
		}
		if stat.F_flags&unix.MNT_SYNCHRONOUS != 0 {
			opts += ",sync"
		}
		if stat.F_flags&unix.MNT_NOEXEC != 0 {
			opts += ",noexec"
		}
		if stat.F_flags&unix.MNT_NOSUID != 0 {
			opts += ",nosuid"
		}
		if stat.F_flags&unix.MNT_NODEV != 0 {
			opts += ",nodev"
		}
		if stat.F_flags&unix.MNT_ASYNC != 0 {
			opts += ",async"
		}
		if stat.F_flags&unix.MNT_SOFTDEP != 0 {
			opts += ",softdep"
		}
		if stat.F_flags&unix.MNT_NOATIME != 0 {
			opts += ",noatime"
		}
		if stat.F_flags&unix.MNT_WXALLOWED != 0 {
			opts += ",wxallowed"
		}

		device := byteToString(stat.F_mntfromname[:])
		mountPoint := byteToString(stat.F_mntonname[:])
		fsType := byteToString(stat.F_fstypename[:])

		if len(device) == 0 {
			continue
		}

		d := Mount{
			Device:     device,
			Mountpoint: mountPoint,
			Fstype:     fsType,
			Type:       fsType,
			Opts:       opts,
			Metadata:   stat,
			Total:      (uint64(stat.F_blocks) * uint64(stat.F_bsize)),
			Free:       (uint64(stat.F_bavail) * uint64(stat.F_bsize)),
			Used:       (uint64(stat.F_blocks) - uint64(stat.F_bfree)) * uint64(stat.F_bsize),
			Inodes:     stat.F_files,
			InodesFree: uint64(stat.F_ffree),
			InodesUsed: stat.F_files - uint64(stat.F_ffree),
			Blocks:     uint64(stat.F_blocks),
			BlockSize:  uint64(stat.F_bsize),
		}
		d.DeviceType = deviceType(d)

		ret = append(ret, d)
	}

	return ret, warnings, nil
}


================================================
FILE: mounts_windows.go
================================================
//go:build windows
// +build windows

package main

import (
	"fmt"
	"golang.org/x/sys/windows"
	"math"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"
)

// Local devices
const (
	guidBufLen       = windows.MAX_PATH + 1
	volumeNameBufLen = windows.MAX_PATH + 1
	rootPathBufLen   = windows.MAX_PATH + 1
	fileSystemBufLen = windows.MAX_PATH + 1
)

func getMountPoint(guidBuf []uint16) (mountPoint string, err error) {
	var rootPathLen uint32
	rootPathBuf := make([]uint16, rootPathBufLen)

	err = windows.GetVolumePathNamesForVolumeName(&guidBuf[0], &rootPathBuf[0], rootPathBufLen*2, &rootPathLen)
	if err != nil && err.(windows.Errno) == windows.ERROR_MORE_DATA {
		// Retry if buffer size is too small
		rootPathBuf = make([]uint16, (rootPathLen+1)/2)
		err = windows.GetVolumePathNamesForVolumeName(
			&guidBuf[0], &rootPathBuf[0], rootPathLen, &rootPathLen)
	}
	return windows.UTF16ToString(rootPathBuf), err
}

func getVolumeInfo(guidOrMountPointBuf []uint16) (volumeName string, fsType string, err error) {
	volumeNameBuf := make([]uint16, volumeNameBufLen)
	fsTypeBuf := make([]uint16, fileSystemBufLen)

	err = windows.GetVolumeInformation(&guidOrMountPointBuf[0], &volumeNameBuf[0], volumeNameBufLen*2,
		nil, nil, nil,
		&fsTypeBuf[0], fileSystemBufLen*2)

	return windows.UTF16ToString(volumeNameBuf), windows.UTF16ToString(fsTypeBuf), err
}

func getSpaceInfo(guidOrMountPointBuf []uint16) (totalBytes uint64, freeBytes uint64, err error) {
	err = windows.GetDiskFreeSpaceEx(&guidOrMountPointBuf[0], nil, &totalBytes, &freeBytes)
	return
}

func getClusterInfo(guidOrMountPointBuf []uint16) (totalClusters uint32, clusterSize uint32, err error) {
	var sectorsPerCluster uint32
	var bytesPerSector uint32
	err = GetDiskFreeSpace(&guidOrMountPointBuf[0], &sectorsPerCluster, &bytesPerSector, nil, &totalClusters)
	clusterSize = bytesPerSector * sectorsPerCluster
	return
}

func getMount(guidOrMountPointBuf []uint16, isGUID bool) (m Mount, skip bool, warnings []string) {
	var err error
	guidOrMountPoint := windows.UTF16ToString(guidOrMountPointBuf)

	mountPoint := guidOrMountPoint
	if isGUID {
		mountPoint, err = getMountPoint(guidOrMountPointBuf)
		if err != nil {
			warnings = append(warnings, fmt.Sprintf("%s: %s", guidOrMountPoint, err))
		}
		// Skip unmounted volumes
		if len(mountPoint) == 0 {
			skip = true
			return
		}
	}

	// Get volume name & filesystem type
	volumeName, fsType, err := getVolumeInfo(guidOrMountPointBuf)
	if err != nil {
		warnings = append(warnings, fmt.Sprintf("%s: %s", guidOrMountPoint, err))
	}

	// Get space info
	totalBytes, freeBytes, err := getSpaceInfo(guidOrMountPointBuf)
	if err != nil {
		warnings = append(warnings, fmt.Sprintf("%s: %s", guidOrMountPoint, err))
	}

	// Get cluster info
	totalClusters, clusterSize, err := getClusterInfo(guidOrMountPointBuf)
	if err != nil {
		warnings = append(warnings, fmt.Sprintf("%s: %s", guidOrMountPoint, err))
	}

	m = Mount{
		Device:     volumeName,
		Mountpoint: mountPoint,
		Fstype:     fsType,
		Type:       fsType,
		Opts:       "",
		Total:      totalBytes,
		Free:       freeBytes,
		Used:       totalBytes - freeBytes,
		Blocks:     uint64(totalClusters),
		BlockSize:  uint64(clusterSize),
	}
	m.DeviceType = deviceType(m)
	return
}

func getMountFromGUID(guidBuf []uint16) (m Mount, skip bool, warnings []string) {
	m, skip, warnings = getMount(guidBuf, true)

	// Use GUID as volume name if no label was set
	if len(m.Device) == 0 {
		m.Device = windows.UTF16ToString(guidBuf)
	}

	return
}

func getMountFromMountPoint(mountPointBuf []uint16) (m Mount, warnings []string) {
	m, _, warnings = getMount(mountPointBuf, false)

	// Use mount point as volume name if no label was set
	if len(m.Device) == 0 {
		m.Device = windows.UTF16ToString(mountPointBuf)
	}

	return m, warnings
}

func appendLocalMounts(mounts []Mount, warnings []string) ([]Mount, []string, error) {
	guidBuf := make([]uint16, guidBufLen)

	hFindVolume, err := windows.FindFirstVolume(&guidBuf[0], guidBufLen*2)
	if err != nil {
		return mounts, warnings, err
	}

VolumeLoop:
	for ; ; err = windows.FindNextVolume(hFindVolume, &guidBuf[0], guidBufLen*2) {
		if err != nil {
			switch err.(windows.Errno) {
			case windows.ERROR_NO_MORE_FILES:
				break VolumeLoop
			default:
				warnings = append(warnings, fmt.Sprintf("%s: %s", windows.UTF16ToString(guidBuf), err))
				continue VolumeLoop
			}
		}

		if m, skip, w := getMountFromGUID(guidBuf); !skip {
			mounts = append(mounts, m)
			warnings = append(warnings, w...)
		}
	}

	if err = windows.FindVolumeClose(hFindVolume); err != nil {
		warnings = append(warnings, fmt.Sprintf("%s", err))
	}
	return mounts, warnings, nil
}

// Network devices
func getMountFromNetResource(netResource NetResource) (m Mount, warnings []string) {
	mountPoint := windows.UTF16PtrToString(netResource.LocalName)
	if !strings.HasSuffix(mountPoint, string(filepath.Separator)) {
		mountPoint += string(filepath.Separator)
	}
	mountPointBuf := windows.StringToUTF16(mountPoint)

	m, _, warnings = getMount(mountPointBuf, false)

	// Use remote name as volume name if no label was set
	if len(m.Device) == 0 {
		m.Device = windows.UTF16PtrToString(netResource.RemoteName)
	}

	return
}

func appendNetworkMounts(mounts []Mount, warnings []string) ([]Mount, []string, error) {
	hEnumResource, err := WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK, RESOURCEUSAGE_CONNECTABLE, nil)
	if err != nil {
		return mounts, warnings, err
	}

EnumLoop:
	for {
		// Reference: https://docs.microsoft.com/en-us/windows/win32/wnet/enumerating-network-resources
		var nrBuf [16384]byte
		count := uint32(math.MaxUint32)
		size := uint32(len(nrBuf))
		if err := WNetEnumResource(hEnumResource, &count, &nrBuf[0], &size); err != nil {
			switch err.(windows.Errno) {
			case windows.ERROR_NO_MORE_ITEMS:
				break EnumLoop
			default:
				warnings = append(warnings, err.Error())
				break EnumLoop
			}
		}

		for i := uint32(0); i < count; i++ {
			nr := (*NetResource)(unsafe.Pointer(&nrBuf[uintptr(i)*NetResourceSize]))
			m, w := getMountFromNetResource(*nr)
			mounts = append(mounts, m)
			warnings = append(warnings, w...)
		}
	}

	if err = WNetCloseEnum(hEnumResource); err != nil {
		warnings = append(warnings, fmt.Sprintf("%s", err))
	}
	return mounts, warnings, nil
}

func mountPointAlreadyPresent(mounts []Mount, mountPoint string) bool {
	for _, m := range mounts {
		if m.Mountpoint == mountPoint {
			return true
		}
	}

	return false
}

func appendLogicalDrives(mounts []Mount, warnings []string) ([]Mount, []string) {
	driveBitmap, err := windows.GetLogicalDrives()
	if err != nil {
		warnings = append(warnings, fmt.Sprintf("GetLogicalDrives(): %s", err))
		return mounts, warnings
	}

	for drive := 'A'; drive <= 'Z'; drive, driveBitmap = drive+1, driveBitmap>>1 {
		if driveBitmap&0x1 == 0 {
			continue
		}

		mountPoint := fmt.Sprintf("%c:\\", drive)
		if mountPointAlreadyPresent(mounts, mountPoint) {
			continue
		}

		mountPointBuf := windows.StringToUTF16(mountPoint)
		m, w := getMountFromMountPoint(mountPointBuf)
		mounts = append(mounts, m)
		warnings = append(warnings, w...)
	}

	return mounts, warnings
}

func mounts() (ret []Mount, warnings []string, err error) {
	ret = make([]Mount, 0)

	// Local devices
	if ret, warnings, err = appendLocalMounts(ret, warnings); err != nil {
		return
	}

	// Network devices
	if ret, warnings, err = appendNetworkMounts(ret, warnings); err != nil {
		return
	}

	// Logical devices (from GetLogicalDrives bitflag)
	// Check any possible logical drives, in case of some special virtual devices, such as RAM disk
	ret, warnings = appendLogicalDrives(ret, warnings)

	return ret, warnings, nil
}

// Windows API
const (
	// Windows Networking const
	// Reference: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetopenenumw
	RESOURCE_CONNECTED  = 0x00000001
	RESOURCE_GLOBALNET  = 0x00000002
	RESOURCE_REMEMBERED = 0x00000003
	RESOURCE_RECENT     = 0x00000004
	RESOURCE_CONTEXT    = 0x00000005

	RESOURCETYPE_ANY      = 0x00000000
	RESOURCETYPE_DISK     = 0x00000001
	RESOURCETYPE_PRINT    = 0x00000002
	RESOURCETYPE_RESERVED = 0x00000008
	RESOURCETYPE_UNKNOWN  = 0xFFFFFFFF

	RESOURCEUSAGE_CONNECTABLE   = 0x00000001
	RESOURCEUSAGE_CONTAINER     = 0x00000002
	RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004
	RESOURCEUSAGE_SIBLING       = 0x00000008
	RESOURCEUSAGE_ATTACHED      = 0x00000010
	RESOURCEUSAGE_ALL           = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED
	RESOURCEUSAGE_RESERVED      = 0x80000000
)

var (
	// Windows syscall
	modmpr      = windows.NewLazySystemDLL("mpr.dll")
	modkernel32 = windows.NewLazySystemDLL("kernel32.dll")

	procWNetOpenEnumW     = modmpr.NewProc("WNetOpenEnumW")
	procWNetCloseEnum     = modmpr.NewProc("WNetCloseEnum")
	procWNetEnumResourceW = modmpr.NewProc("WNetEnumResourceW")
	procGetDiskFreeSpaceW = modkernel32.NewProc("GetDiskFreeSpaceW")

	NetResourceSize = unsafe.Sizeof(NetResource{})
)

// Reference: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/ns-winnetwk-netresourcew
type NetResource struct {
	Scope       uint32
	Type        uint32
	DisplayType uint32
	Usage       uint32
	LocalName   *uint16
	RemoteName  *uint16
	Comment     *uint16
	Provider    *uint16
}

// Reference: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetopenenumw
func WNetOpenEnum(scope uint32, resourceType uint32, usage uint32, resource *NetResource) (handle windows.Handle, err error) {
	r1, _, e1 := syscall.Syscall6(procWNetOpenEnumW.Addr(), 5, uintptr(scope), uintptr(resourceType), uintptr(usage), uintptr(unsafe.Pointer(resource)), uintptr(unsafe.Pointer(&handle)), 0)
	if r1 != windows.NO_ERROR {
		if e1 != 0 {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

// Reference: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetenumresourcew
func WNetEnumResource(enumResource windows.Handle, count *uint32, buffer *byte, bufferSize *uint32) (err error) {
	r1, _, e1 := syscall.Syscall6(procWNetEnumResourceW.Addr(), 4, uintptr(enumResource), uintptr(unsafe.Pointer(count)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(bufferSize)), 0, 0)
	if r1 != windows.NO_ERROR {
		if e1 != 0 {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

// Reference: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetcloseenum
func WNetCloseEnum(enumResource windows.Handle) (err error) {
	r1, _, e1 := syscall.Syscall(procWNetCloseEnum.Addr(), 1, uintptr(enumResource), 0, 0)
	if r1 != windows.NO_ERROR {
		if e1 != 0 {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

// Reference: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdiskfreespacew
func GetDiskFreeSpace(directoryName *uint16, sectorsPerCluster *uint32, bytesPerSector *uint32, numberOfFreeClusters *uint32, totalNumberOfClusters *uint32) (err error) {
	r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceW.Addr(), 5, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(sectorsPerCluster)), uintptr(unsafe.Pointer(bytesPerSector)), uintptr(unsafe.Pointer(numberOfFreeClusters)), uintptr(unsafe.Pointer(totalNumberOfClusters)), 0)
	if r1 == 0 {
		if e1 != 0 {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}


================================================
FILE: style.go
================================================
package main

import "github.com/mattn/go-runewidth"

func defaultStyleName() string {
	/*
		Due to a bug in github.com/mattn/go-runewidth v0.0.9, the width of unicode rune(such as '╭') could not be correctly
		calculated.	Degrade to ascii to prevent broken table structure. Remove this once the bug is fixed.
	*/
	if runewidth.RuneWidth('╭') > 1 {
		return "ascii"
	}

	return "unicode"
}


================================================
FILE: table.go
================================================
package main

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/jedib0t/go-pretty/v6/table"
	"github.com/jedib0t/go-pretty/v6/text"
	"github.com/mattn/go-runewidth"
	"github.com/muesli/termenv"
)

// TableOptions contains all options for the table.
type TableOptions struct {
	Columns   []int
	SortBy    int
	Style     table.Style
	StyleName string
}

// Column defines a column.
type Column struct {
	ID        string
	Name      string
	SortIndex int
	Width     int
}

// "Mounted on", "Size", "Used", "Avail", "Use%", "Inodes", "IUsed", "IAvail", "IUse%", "Type", "Filesystem"
// mountpoint, size, used, avail, usage, inodes, inodes_used, inodes_avail, inodes_usage, type, filesystem
var columns = []Column{
	{ID: "mountpoint", Name: "Mounted on", SortIndex: 1},
	{ID: "size", Name: "Size", SortIndex: 12, Width: 7},
	{ID: "used", Name: "Used", SortIndex: 13, Width: 7},
	{ID: "avail", Name: "Avail", SortIndex: 14, Width: 7},
	{ID: "usage", Name: "Use%", SortIndex: 15, Width: 6},
	{ID: "inodes", Name: "Inodes", SortIndex: 16, Width: 7},
	{ID: "inodes_used", Name: "IUsed", SortIndex: 17, Width: 7},
	{ID: "inodes_avail", Name: "IAvail", SortIndex: 18, Width: 7},
	{ID: "inodes_usage", Name: "IUse%", SortIndex: 19, Width: 6},
	{ID: "type", Name: "Type", SortIndex: 10},
	{ID: "filesystem", Name: "Filesystem", SortIndex: 11},
}

// initializeTable sets up the table writer with initial configurations.
func initializeTable(tab table.Writer, opts TableOptions) {
	tab.SetAllowedRowLength(int(*width))
	tab.SetOutputMirror(os.Stdout)
	tab.Style().Options.SeparateColumns = true
	tab.SetStyle(opts.Style)
}

// appendHeaders adds the header row to the table.
func appendHeaders(tab table.Writer) {
	headers := table.Row{}
	for _, v := range columns {
		headers = append(headers, v.Name)
	}
	tab.AppendHeader(headers)
}

// appendRows adds data rows to the table for each mount.
func appendRows(tab table.Writer, m []Mount) {
	for _, v := range m {
		var usage, inodeUsage float64
		if v.Total > 0 {
			usage = float64(v.Used) / float64(v.Total)
			if usage > 1.0 {
				usage = 1.0
			}
		}
		if v.Inodes > 0 {
			inodeUsage = float64(v.InodesUsed) / float64(v.Inodes)
			if inodeUsage > 1.0 {
				inodeUsage = 1.0
			}
		}

		tab.AppendRow([]interface{}{
			termenv.String(v.Mountpoint).Foreground(theme.colorBlue), // mounted on
			v.Total,      // size
			v.Used,       // used
			v.Free,       // avail
			usage,        // use%
			v.Inodes,     // inodes
			v.InodesUsed, // inodes used
			v.InodesFree, // inodes avail
			inodeUsage,   // inodes use%
			termenv.String(v.Fstype).Foreground(theme.colorGray), // type
			termenv.String(v.Device).Foreground(theme.colorGray), // filesystem
			v.Total,      // size sorting helper
			v.Used,       // used sorting helper
			v.Free,       // avail sorting helper
			usage,        // use% sorting helper
			v.Inodes,     // inodes sorting helper
			v.InodesUsed, // inodes used sorting helper
			v.InodesFree, // inodes avail sorting helper
			inodeUsage,   // inodes use% sorting helper
		})
	}
}

// computeMaxContentWidths calculates the maximum content width for each visible column.
func computeMaxContentWidths(m []Mount, opts TableOptions) map[int]int {
	visibleCols := append([]int{}, opts.Columns...)
	maxColContent := map[int]int{}
	// Seed with headers
	for _, ci := range visibleCols {
		maxColContent[ci] = runewidth.StringWidth(columns[ci-1].Name)
	}
	for _, v := range m {
		if inColumns(opts.Columns, 1) {
			if w := runewidth.StringWidth(v.Mountpoint); w > maxColContent[1] {
				maxColContent[1] = w
			}
		}
		if inColumns(opts.Columns, 2) {
			if w := runewidth.StringWidth(sizeToString(v.Total)); w > maxColContent[2] {
				maxColContent[2] = w
			}
		}
		if inColumns(opts.Columns, 3) {
			if w := runewidth.StringWidth(sizeToString(v.Used)); w > maxColContent[3] {
				maxColContent[3] = w
			}
		}
		if inColumns(opts.Columns, 4) {
			if w := runewidth.StringWidth(sizeToString(v.Free)); w > maxColContent[4] {
				maxColContent[4] = w
			}
		}
		if inColumns(opts.Columns, 5) {
			var usage float64
			if v.Total > 0 {
				usage = float64(v.Used) / float64(v.Total)
				if usage > 1.0 {
					usage = 1.0
				}
			}
			percentStr := fmt.Sprintf("%.1f%%", usage*100)
			if w := runewidth.StringWidth(percentStr); w > maxColContent[5] {
				maxColContent[5] = w
			}
		}
		if inColumns(opts.Columns, 6) {
			if w := runewidth.StringWidth(strconv.FormatUint(v.Inodes, 10)); w > maxColContent[6] {
				maxColContent[6] = w
			}
		}
		if inColumns(opts.Columns, 7) {
			if w := runewidth.StringWidth(strconv.FormatUint(v.InodesUsed, 10)); w > maxColContent[7] {
				maxColContent[7] = w
			}
		}
		if inColumns(opts.Columns, 8) {
			if w := runewidth.StringWidth(strconv.FormatUint(v.InodesFree, 10)); w > maxColContent[8] {
				maxColContent[8] = w
			}
		}
		if inColumns(opts.Columns, 9) {
			var usage float64
			if v.Inodes > 0 {
				usage = float64(v.InodesUsed) / float64(v.Inodes)
				if usage > 1.0 {
					usage = 1.0
				}
			}
			percentStr := fmt.Sprintf("%.1f%%", usage*100)
			if w := runewidth.StringWidth(percentStr); w > maxColContent[9] {
				maxColContent[9] = w
			}
		}
		if inColumns(opts.Columns, 10) {
			if w := runewidth.StringWidth(v.Fstype); w > maxColContent[10] {
				maxColContent[10] = w
			}
		}
		if inColumns(opts.Columns, 11) {
			if w := runewidth.StringWidth(v.Device); w > maxColContent[11] {
				maxColContent[11] = w
			}
		}
	}
	return maxColContent
}

// computeAssignedWidths computes the assigned widths for dynamic columns (1, 10, 11).
func computeAssignedWidths(maxColContent map[int]int, opts TableOptions) (map[int]int, int) {
	visibleCols := append([]int{}, opts.Columns...)
	nVis := len(visibleCols)

	// Non-content overhead
	sepWidth := 1
	paddingPerCol := 2
	overhead := (nVis+1)*sepWidth + nVis*paddingPerCol
	totalAllowed := int(*width)

	// Determine targets and their need
	targets := []int{}
	weights := map[int]float64{1: 0.4, 10: 0.2, 11: 0.4}
	weightSum := 0.0
	for _, t := range []int{1, 10, 11} {
		if inColumns(opts.Columns, t) {
			targets = append(targets, t)
			weightSum += weights[t]
		}
	}

	// Sum fixed widths of non-target visible columns
	fixedContentWidth := 0
	for _, ci := range visibleCols {
		if ci == 1 || ci == 10 || ci == 11 {
			continue
		}
		fixedContentWidth += maxColContent[ci]
	}

	availableContent := totalAllowed - overhead - fixedContentWidth
	if availableContent < 0 {
		availableContent = 0
	}

	// Cap target allocations by their max content need
	assigned := map[int]int{}
	used := 0
	if availableContent > 0 && len(targets) > 0 {
		for _, t := range targets {
			share := int(float64(availableContent) * (weights[t] / weightSum))
			if share > maxColContent[t] {
				share = maxColContent[t]
			}
			assigned[t] = share
			used += share
		}
		// remainder distribution
		remainder := availableContent - used
		for remainder > 0 {
			bestCol := 0
			bestNeed := 0
			for _, t := range targets {
				need := maxColContent[t] - assigned[t]
				if need > bestNeed {
					bestNeed = need
					bestCol = t
				}
			}
			if bestNeed <= 0 {
				break
			}
			take := remainder
			if take > bestNeed {
				take = bestNeed
			}
			assigned[bestCol] += take
			remainder -= take
		}
	}

	// Calculate final slack
	predictedTotal := overhead + fixedContentWidth
	for _, t := range targets {
		predictedTotal += assigned[t]
	}
	slack := totalAllowed - predictedTotal
	return assigned, slack
}

// setColumnConfigs configures the columns for the table.
func setColumnConfigs(tab table.Writer, maxColContent map[int]int, assigned map[int]int, opts TableOptions, barTransformerFunc func(interface{}) string) {
	cfgs := []table.ColumnConfig{
		{Number: 1, Hidden: !inColumns(opts.Columns, 1), WidthMax: assigned[1]},
		{Number: 2, Hidden: !inColumns(opts.Columns, 2), Transformer: sizeTransformer, Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[2]},
		{Number: 3, Hidden: !inColumns(opts.Columns, 3), Transformer: sizeTransformer, Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[3]},
		{Number: 4, Hidden: !inColumns(opts.Columns, 4), Transformer: spaceTransformer, Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[4]},
		{Number: 5, Hidden: !inColumns(opts.Columns, 5), Transformer: barTransformerFunc, AlignHeader: text.AlignCenter, WidthMax: maxColContent[5]},
		{Number: 6, Hidden: !inColumns(opts.Columns, 6), Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[6]},
		{Number: 7, Hidden: !inColumns(opts.Columns, 7), Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[7]},
		{Number: 8, Hidden: !inColumns(opts.Columns, 8), Align: text.AlignRight, AlignHeader: text.AlignRight, WidthMax: maxColContent[8]},
		{Number: 9, Hidden: !inColumns(opts.Columns, 9), Transformer: barTransformerFunc, AlignHeader: text.AlignCenter, WidthMax: maxColContent[9]},
		{Number: 10, Hidden: !inColumns(opts.Columns, 10), WidthMax: assigned[10]},
		{Number: 11, Hidden: !inColumns(opts.Columns, 11), WidthMax: assigned[11]},
		{Number: 12, Hidden: true}, // sortBy helper for size
		{Number: 13, Hidden: true}, // sortBy helper for used
		{Number: 14, Hidden: true}, // sortBy helper for avail
		{Number: 15, Hidden: true}, // sortBy helper for usage
		{Number: 16, Hidden: true}, // sortBy helper for inodes size
		{Number: 17, Hidden: true}, // sortBy helper for inodes used
		{Number: 18, Hidden: true}, // sortBy helper for inodes avail
		{Number: 19, Hidden: true}, // sortBy helper for inodes usage
	}
	tab.SetColumnConfigs(cfgs)
}

// printTable prints an individual table of mounts.
func printTable(title string, m []Mount, opts TableOptions) {
	tab := table.NewWriter()
	initializeTable(tab, opts)
	appendHeaders(tab)
	appendRows(tab, m)

	if tab.Length() == 0 {
		return
	}

	maxColContent := computeMaxContentWidths(m, opts)
	assigned, slack := computeAssignedWidths(maxColContent, opts)

	origPercentWidth5 := maxColContent[5]
	origPercentWidth9 := maxColContent[9]
	percentWidth := origPercentWidth5
	if origPercentWidth9 > percentWidth {
		percentWidth = origPercentWidth9
	}

	barWidth := 0
	numBars := 0
	if inColumns(opts.Columns, 5) {
		numBars++
	}
	if inColumns(opts.Columns, 9) {
		numBars++
	}
	if numBars > 0 && slack >= 6 {
		// Each bar consumes: barWidth + 1 (for space)
		// So for numBars, total consumption is: numBars * (barWidth + 1)
		maxBarWidth := min((slack/numBars)-1, 20)

		if maxBarWidth > 0 {
			barWidth = maxBarWidth
			if inColumns(opts.Columns, 5) {
				maxColContent[5] = barWidth + 1 + percentWidth
			}
			if inColumns(opts.Columns, 9) {
				maxColContent[9] = barWidth + 1 + percentWidth
			}
		}
	}

	// Define barTransformerFunc
	barTransformerFunc := func(val interface{}) string {
		usage := val.(float64)
		if barWidth <= 0 {
			s := fmt.Sprintf("%*s", percentWidth, fmt.Sprintf("%.1f%%", usage*100))
			return termenv.String(s).String()
		}

		bw := barWidth
		var filledChar, halfChar, emptyChar string
		if opts.StyleName == "unicode" {
			filledChar = "█"
			halfChar = "▌"
			emptyChar = " "
		} else {
			bw -= 2
			filledChar = "#"
			halfChar = "#"
			emptyChar = "."
		}

		filled := int(usage * float64(bw))
		partial := usage*float64(bw) - float64(filled)
		empty := bw - filled

		var filledStr, emptyStr string
		filledStr = strings.Repeat(filledChar, filled)

		// If we have a sufficiently large partial, render a half block.
		if partial >= 0.5 {
			filledStr += halfChar
			empty--
		}

		if empty < 0 {
			empty = 0
		}
		emptyStr = strings.Repeat(emptyChar, empty)

		var format string
		if opts.StyleName == "unicode" {
			format = "%s%s %*s"
		} else {
			format = "[%s%s] %*s"
		}

		// Apply colors
		redUsage, _ := strconv.ParseFloat(strings.Split(*usageThreshold, ",")[1], 64)
		yellowUsage, _ := strconv.ParseFloat(strings.Split(*usageThreshold, ",")[0], 64)

		var fgColor termenv.Color
		switch {
		case usage >= redUsage:
			fgColor = theme.colorRed
		case usage >= yellowUsage:
			fgColor = theme.colorYellow
		default:
			fgColor = theme.colorGreen
		}

		filledPart := termenv.String(filledStr).Foreground(fgColor)
		emptyPart := termenv.String(emptyStr)
		if opts.StyleName == "unicode" {
			// Add background to filled part to prevent black spaces in half blocks
			// Use a background color that complements the foreground
			var bgColor termenv.Color
			switch {
			case usage >= redUsage:
				bgColor = theme.colorBgRed
			case usage >= yellowUsage:
				bgColor = theme.colorBgYellow
			default:
				bgColor = theme.colorBgGreen
			}
			filledPart = filledPart.Background(bgColor).Foreground(fgColor)
			// Use a neutral background for empty areas
			emptyPart = emptyPart.Background(bgColor)
		}

		s := fmt.Sprintf(format, filledPart, emptyPart, percentWidth, fmt.Sprintf("%.1f%%", usage*100))
		return termenv.String(s).String()
	}

	setColumnConfigs(tab, maxColContent, assigned, opts, barTransformerFunc)

	suffix := "device"
	if tab.Length() > 1 {
		suffix = "devices"
	}
	tab.SetTitle("%d %s %s", tab.Length(), title, suffix)

	// tab.AppendFooter(table.Row{fmt.Sprintf("%d %s", tab.Length(), title)})
	sortMode := table.Asc
	if opts.SortBy >= 12 {
		sortMode = table.AscNumeric
	}

	tab.SortBy([]table.SortBy{{Number: opts.SortBy, Mode: sortMode}})
	tab.Render()
}

// sizeTransformer makes a size human-readable.
func sizeTransformer(val interface{}) string {
	return sizeToString(val.(uint64))
}

// spaceTransformer makes a size human-readable and applies a color coding.
func spaceTransformer(val interface{}) string {
	free := val.(uint64)

	s := termenv.String(sizeToString(free))
	redAvail, _ := stringToSize(strings.Split(*availThreshold, ",")[1])
	yellowAvail, _ := stringToSize(strings.Split(*availThreshold, ",")[0])
	switch {
	case free < redAvail:
		s = s.Foreground(theme.colorRed)
	case free < yellowAvail:
		s = s.Foreground(theme.colorYellow)
	default:
		s = s.Foreground(theme.colorGreen)
	}

	return s.String()
}

// inColumns return true if the column with index i is in the slice of visible
// columns cols.
func inColumns(cols []int, i int) bool {
	for _, v := range cols {
		if v == i {
			return true
		}
	}

	return false
}

// sizeToString prettifies sizes.
func sizeToString(size uint64) (str string) {
	b := float64(size)

	switch {
	case size >= 1<<60:
		str = fmt.Sprintf("%.1fE", b/(1<<60))
	case size >= 1<<50:
		str = fmt.Sprintf("%.1fP", b/(1<<50))
	case size >= 1<<40:
		str = fmt.Sprintf("%.1fT", b/(1<<40))
	case size >= 1<<30:
		str = fmt.Sprintf("%.1fG", b/(1<<30))
	case size >= 1<<20:
		str = fmt.Sprintf("%.1fM", b/(1<<20))
	case size >= 1<<10:
		str = fmt.Sprintf("%.1fK", b/(1<<10))
	default:
		str = fmt.Sprintf("%dB", size)
	}

	return
}

// stringToSize transforms an SI size into a number.
func stringToSize(s string) (size uint64, err error) {
	regex := regexp.MustCompile(`^(\d+)([KMGTPE]?)$`)
	matches := regex.FindStringSubmatch(s)
	if len(matches) == 0 {
		return 0, fmt.Errorf("'%s' is not valid, must have integer with optional SI prefix", s)
	}

	num, err := strconv.ParseUint(matches[1], 10, 64)
	if err != nil {
		return 0, err
	}
	if matches[2] != "" {
		prefix := matches[2]
		switch prefix {
		case "K":
			size = num << 10
		case "M":
			size = num << 20
		case "G":
			size = num << 30
		case "T":
			size = num << 40
		case "P":
			size = num << 50
		case "E":
			size = num << 60
		default:
			err = fmt.Errorf("prefix '%s' not allowed, valid prefixes are K, M, G, T, P, E", prefix)
			return
		}
	} else {
		size = num
	}
	return
}

// stringToColumn converts a column name to its index.
func stringToColumn(s string) (int, error) {
	s = strings.ToLower(s)

	for i, v := range columns {
		if v.ID == s {
			return i + 1, nil
		}
	}

	return 0, fmt.Errorf("unknown column: %s (valid: %s)", s, strings.Join(columnIDs(), ", "))
}

// stringToSortIndex converts a column name to its sort index.
func stringToSortIndex(s string) (int, error) {
	s = strings.ToLower(s)

	for _, v := range columns {
		if v.ID == s {
			return v.SortIndex, nil
		}
	}

	return 0, fmt.Errorf("unknown column: %s (valid: %s)", s, strings.Join(columnIDs(), ", "))
}

// columnsIDs returns a slice of all column IDs.
func columnIDs() []string {
	s := make([]string, len(columns))
	for i, v := range columns {
		s[i] = v.ID
	}

	return s
}


================================================
FILE: themes.go
================================================
package main

import (
	"fmt"

	"github.com/muesli/termenv"
)

// Theme defines a color theme used for printing tables.
type Theme struct {
	colorRed     termenv.Color
	colorYellow  termenv.Color
	colorGreen   termenv.Color
	colorBlue    termenv.Color
	colorGray    termenv.Color
	colorMagenta termenv.Color
	colorCyan    termenv.Color

	colorBgRed    termenv.Color
	colorBgYellow termenv.Color
	colorBgGreen  termenv.Color
}

func defaultThemeName() string {
	if !termenv.HasDarkBackground() {
		return "light"
	}
	return "dark"
}

func loadTheme(theme string) (Theme, error) {
	themes := make(map[string]Theme)

	themes["dark"] = Theme{
		colorRed:      env.Color("#E88388"),
		colorYellow:   env.Color("#DBAB79"),
		colorGreen:    env.Color("#A8CC8C"),
		colorBlue:     env.Color("#71BEF2"),
		colorGray:     env.Color("#B9BFCA"),
		colorMagenta:  env.Color("#D290E4"),
		colorCyan:     env.Color("#66C2CD"),
		colorBgRed:    env.Color("#2d1b1b"),
		colorBgYellow: env.Color("#2d2d1b"),
		colorBgGreen:  env.Color("#1b2d1b"),
	}

	themes["light"] = Theme{
		colorRed:      env.Color("#D70000"),
		colorYellow:   env.Color("#FFAF00"),
		colorGreen:    env.Color("#005F00"),
		colorBlue:     env.Color("#000087"),
		colorGray:     env.Color("#303030"),
		colorMagenta:  env.Color("#AF00FF"),
		colorCyan:     env.Color("#0087FF"),
		colorBgRed:    env.Color("#ffdede"),
		colorBgYellow: env.Color("#fff4d0"),
		colorBgGreen:  env.Color("#e6ffe6"),
	}

	themes["ansi"] = Theme{
		colorRed:      env.Color("9"),
		colorYellow:   env.Color("11"),
		colorGreen:    env.Color("10"),
		colorBlue:     env.Color("12"),
		colorGray:     env.Color("7"),
		colorMagenta:  env.Color("13"),
		colorCyan:     env.Color("8"),
		colorBgRed:    env.Color("1"),
		colorBgYellow: env.Color("3"),
		colorBgGreen:  env.Color("2"),
	}

	if _, ok := themes[theme]; !ok {
		return Theme{}, fmt.Errorf("unknown theme: %s", theme)
	}

	return themes[theme], nil
}
Download .txt
gitextract_nb2n7rrk/

├── .github/
│   ├── FUNDING.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── build.yml
│       ├── goreleaser.yml
│       ├── lint-soft.yml
│       ├── lint.yml
│       └── manpage.yml
├── .gitignore
├── .golangci-soft.yml
├── .golangci.yml
├── .goreleaser.yml
├── LICENSE
├── README.md
├── duf.1
├── filesystems.go
├── filesystems_darwin.go
├── filesystems_freebsd.go
├── filesystems_linux.go
├── filesystems_openbsd.go
├── filesystems_windows.go
├── go.mod
├── go.sum
├── groups.go
├── main.go
├── man.go
├── mounts.go
├── mounts_darwin.go
├── mounts_freebsd.go
├── mounts_linux.go
├── mounts_linux_test.go
├── mounts_openbsd.go
├── mounts_windows.go
├── style.go
├── table.go
└── themes.go
Download .txt
SYMBOL INDEX (226 symbols across 19 files)

FILE: filesystems.go
  function findMounts (line 9) | func findMounts(mounts []Mount, path string) ([]Mount, error) {
  function deviceType (line 53) | func deviceType(m Mount) string {

FILE: filesystems_darwin.go
  function isFuseFs (line 6) | func isFuseFs(m Mount) bool {
  function isNetworkFs (line 11) | func isNetworkFs(m Mount) bool {
  function isSpecialFs (line 16) | func isSpecialFs(m Mount) bool {
  function isHiddenFs (line 20) | func isHiddenFs(m Mount) bool {

FILE: filesystems_freebsd.go
  function isFuseFs (line 6) | func isFuseFs(m Mount) bool {
  function isNetworkFs (line 11) | func isNetworkFs(m Mount) bool {
  function isSpecialFs (line 23) | func isSpecialFs(m Mount) bool {
  function isHiddenFs (line 35) | func isHiddenFs(m Mount) bool {

FILE: filesystems_linux.go
  constant ADFS_SUPER_MAGIC (line 11) | ADFS_SUPER_MAGIC      = 0xadf5
  constant AFFS_SUPER_MAGIC (line 12) | AFFS_SUPER_MAGIC      = 0xADFF
  constant AUTOFS_SUPER_MAGIC (line 13) | AUTOFS_SUPER_MAGIC    = 0x0187
  constant BDEVFS_MAGIC (line 14) | BDEVFS_MAGIC          = 0x62646576
  constant BEFS_SUPER_MAGIC (line 15) | BEFS_SUPER_MAGIC      = 0x42465331
  constant BFS_MAGIC (line 16) | BFS_MAGIC             = 0x1BADFACE
  constant BINFMTFS_MAGIC (line 17) | BINFMTFS_MAGIC        = 0x42494e4d
  constant BPF_FS_MAGIC (line 18) | BPF_FS_MAGIC          = 0xcafe4a11
  constant BTRFS_SUPER_MAGIC (line 19) | BTRFS_SUPER_MAGIC     = 0x9123683E
  constant CGROUP_SUPER_MAGIC (line 20) | CGROUP_SUPER_MAGIC    = 0x27e0eb
  constant CGROUP2_SUPER_MAGIC (line 21) | CGROUP2_SUPER_MAGIC   = 0x63677270
  constant CIFS_MAGIC_NUMBER (line 22) | CIFS_MAGIC_NUMBER     = 0xFF534D42
  constant CODA_SUPER_MAGIC (line 23) | CODA_SUPER_MAGIC      = 0x73757245
  constant COH_SUPER_MAGIC (line 24) | COH_SUPER_MAGIC       = 0x012FF7B7
  constant CONFIGFS_MAGIC (line 25) | CONFIGFS_MAGIC        = 0x62656570
  constant CRAMFS_MAGIC (line 26) | CRAMFS_MAGIC          = 0x28cd3d45
  constant DEBUGFS_MAGIC (line 27) | DEBUGFS_MAGIC         = 0x64626720
  constant DEVFS_SUPER_MAGIC (line 28) | DEVFS_SUPER_MAGIC     = 0x1373
  constant DEVPTS_SUPER_MAGIC (line 29) | DEVPTS_SUPER_MAGIC    = 0x1cd1
  constant EFIVARFS_MAGIC (line 30) | EFIVARFS_MAGIC        = 0xde5e81e4
  constant EFS_SUPER_MAGIC (line 31) | EFS_SUPER_MAGIC       = 0x00414A53
  constant EXT_SUPER_MAGIC (line 32) | EXT_SUPER_MAGIC       = 0x137D
  constant EXT2_OLD_SUPER_MAGIC (line 33) | EXT2_OLD_SUPER_MAGIC  = 0xEF51
  constant EXT2_SUPER_MAGIC (line 34) | EXT2_SUPER_MAGIC      = 0xEF53
  constant EXT3_SUPER_MAGIC (line 35) | EXT3_SUPER_MAGIC      = 0xEF53
  constant EXT4_SUPER_MAGIC (line 36) | EXT4_SUPER_MAGIC      = 0xEF53
  constant FUSE_SUPER_MAGIC (line 37) | FUSE_SUPER_MAGIC      = 0x65735546
  constant FUTEXFS_SUPER_MAGIC (line 38) | FUTEXFS_SUPER_MAGIC   = 0xBAD1DEA
  constant HFS_SUPER_MAGIC (line 39) | HFS_SUPER_MAGIC       = 0x4244
  constant HFSPLUS_SUPER_MAGIC (line 40) | HFSPLUS_SUPER_MAGIC   = 0x482b
  constant HOSTFS_SUPER_MAGIC (line 41) | HOSTFS_SUPER_MAGIC    = 0x00c0ffee
  constant HPFS_SUPER_MAGIC (line 42) | HPFS_SUPER_MAGIC      = 0xF995E849
  constant HUGETLBFS_MAGIC (line 43) | HUGETLBFS_MAGIC       = 0x958458f6
  constant ISOFS_SUPER_MAGIC (line 44) | ISOFS_SUPER_MAGIC     = 0x9660
  constant JFFS2_SUPER_MAGIC (line 45) | JFFS2_SUPER_MAGIC     = 0x72b6
  constant JFS_SUPER_MAGIC (line 46) | JFS_SUPER_MAGIC       = 0x3153464a
  constant MINIX_SUPER_MAGIC (line 47) | MINIX_SUPER_MAGIC     = 0x137F
  constant MINIX_SUPER_MAGIC2 (line 48) | MINIX_SUPER_MAGIC2    = 0x138F
  constant MINIX2_SUPER_MAGIC (line 49) | MINIX2_SUPER_MAGIC    = 0x2468
  constant MINIX2_SUPER_MAGIC2 (line 50) | MINIX2_SUPER_MAGIC2   = 0x2478
  constant MINIX3_SUPER_MAGIC (line 51) | MINIX3_SUPER_MAGIC    = 0x4d5a
  constant MQUEUE_MAGIC (line 52) | MQUEUE_MAGIC          = 0x19800202
  constant MSDOS_SUPER_MAGIC (line 53) | MSDOS_SUPER_MAGIC     = 0x4d44
  constant NCP_SUPER_MAGIC (line 54) | NCP_SUPER_MAGIC       = 0x564c
  constant NFS_SUPER_MAGIC (line 55) | NFS_SUPER_MAGIC       = 0x6969
  constant NILFS_SUPER_MAGIC (line 56) | NILFS_SUPER_MAGIC     = 0x3434
  constant NTFS_SB_MAGIC (line 57) | NTFS_SB_MAGIC         = 0x5346544e
  constant OCFS2_SUPER_MAGIC (line 58) | OCFS2_SUPER_MAGIC     = 0x7461636f
  constant OPENPROM_SUPER_MAGIC (line 59) | OPENPROM_SUPER_MAGIC  = 0x9fa1
  constant PIPEFS_MAGIC (line 60) | PIPEFS_MAGIC          = 0x50495045
  constant PROC_SUPER_MAGIC (line 61) | PROC_SUPER_MAGIC      = 0x9fa0
  constant PSTOREFS_MAGIC (line 62) | PSTOREFS_MAGIC        = 0x6165676C
  constant QNX4_SUPER_MAGIC (line 63) | QNX4_SUPER_MAGIC      = 0x002f
  constant QNX6_SUPER_MAGIC (line 64) | QNX6_SUPER_MAGIC      = 0x68191122
  constant RAMFS_MAGIC (line 65) | RAMFS_MAGIC           = 0x858458f6
  constant REISERFS_SUPER_MAGIC (line 66) | REISERFS_SUPER_MAGIC  = 0x52654973
  constant ROMFS_MAGIC (line 67) | ROMFS_MAGIC           = 0x7275
  constant SELINUX_MAGIC (line 68) | SELINUX_MAGIC         = 0xf97cff8c
  constant SMACK_MAGIC (line 69) | SMACK_MAGIC           = 0x43415d53
  constant SMB_SUPER_MAGIC (line 70) | SMB_SUPER_MAGIC       = 0x517B
  constant SMB2_MAGIC_NUMBER (line 71) | SMB2_MAGIC_NUMBER     = 0xfe534d42
  constant SOCKFS_MAGIC (line 72) | SOCKFS_MAGIC          = 0x534F434B
  constant SQUASHFS_MAGIC (line 73) | SQUASHFS_MAGIC        = 0x73717368
  constant SYSFS_MAGIC (line 74) | SYSFS_MAGIC           = 0x62656572
  constant SYSV2_SUPER_MAGIC (line 75) | SYSV2_SUPER_MAGIC     = 0x012FF7B6
  constant SYSV4_SUPER_MAGIC (line 76) | SYSV4_SUPER_MAGIC     = 0x012FF7B5
  constant TMPFS_MAGIC (line 77) | TMPFS_MAGIC           = 0x01021994
  constant TRACEFS_MAGIC (line 78) | TRACEFS_MAGIC         = 0x74726163
  constant UDF_SUPER_MAGIC (line 79) | UDF_SUPER_MAGIC       = 0x15013346
  constant UFS_MAGIC (line 80) | UFS_MAGIC             = 0x00011954
  constant USBDEVICE_SUPER_MAGIC (line 81) | USBDEVICE_SUPER_MAGIC = 0x9fa2
  constant V9FS_MAGIC (line 82) | V9FS_MAGIC            = 0x01021997
  constant VXFS_SUPER_MAGIC (line 83) | VXFS_SUPER_MAGIC      = 0xa501FCF5
  constant XENFS_SUPER_MAGIC (line 84) | XENFS_SUPER_MAGIC     = 0xabba1974
  constant XENIX_SUPER_MAGIC (line 85) | XENIX_SUPER_MAGIC     = 0x012FF7B4
  constant XFS_SUPER_MAGIC (line 86) | XFS_SUPER_MAGIC       = 0x58465342
  constant _XIAFS_SUPER_MAGIC (line 87) | _XIAFS_SUPER_MAGIC    = 0x012FD16D
  constant AFS_SUPER_MAGIC (line 89) | AFS_SUPER_MAGIC             = 0x5346414F
  constant AUFS_SUPER_MAGIC (line 90) | AUFS_SUPER_MAGIC            = 0x61756673
  constant ANON_INODE_FS_SUPER_MAGIC (line 91) | ANON_INODE_FS_SUPER_MAGIC   = 0x09041934
  constant CEPH_SUPER_MAGIC (line 92) | CEPH_SUPER_MAGIC            = 0x00C36400
  constant ECRYPTFS_SUPER_MAGIC (line 93) | ECRYPTFS_SUPER_MAGIC        = 0xF15F
  constant FAT_SUPER_MAGIC (line 94) | FAT_SUPER_MAGIC             = 0x4006
  constant FHGFS_SUPER_MAGIC (line 95) | FHGFS_SUPER_MAGIC           = 0x19830326
  constant FUSEBLK_SUPER_MAGIC (line 96) | FUSEBLK_SUPER_MAGIC         = 0x65735546
  constant FUSECTL_SUPER_MAGIC (line 97) | FUSECTL_SUPER_MAGIC         = 0x65735543
  constant GFS_SUPER_MAGIC (line 98) | GFS_SUPER_MAGIC             = 0x1161970
  constant GPFS_SUPER_MAGIC (line 99) | GPFS_SUPER_MAGIC            = 0x47504653
  constant MTD_INODE_FS_SUPER_MAGIC (line 100) | MTD_INODE_FS_SUPER_MAGIC    = 0x11307854
  constant INOTIFYFS_SUPER_MAGIC (line 101) | INOTIFYFS_SUPER_MAGIC       = 0x2BAD1DEA
  constant ISOFS_R_WIN_SUPER_MAGIC (line 102) | ISOFS_R_WIN_SUPER_MAGIC     = 0x4004
  constant ISOFS_WIN_SUPER_MAGIC (line 103) | ISOFS_WIN_SUPER_MAGIC       = 0x4000
  constant JFFS_SUPER_MAGIC (line 104) | JFFS_SUPER_MAGIC            = 0x07C0
  constant KAFS_SUPER_MAGIC (line 105) | KAFS_SUPER_MAGIC            = 0x6B414653
  constant LUSTRE_SUPER_MAGIC (line 106) | LUSTRE_SUPER_MAGIC          = 0x0BD00BD0
  constant NFSD_SUPER_MAGIC (line 107) | NFSD_SUPER_MAGIC            = 0x6E667364
  constant PANFS_SUPER_MAGIC (line 108) | PANFS_SUPER_MAGIC           = 0xAAD7AAEA
  constant RPC_PIPEFS_SUPER_MAGIC (line 109) | RPC_PIPEFS_SUPER_MAGIC      = 0x67596969
  constant SECURITYFS_SUPER_MAGIC (line 110) | SECURITYFS_SUPER_MAGIC      = 0x73636673
  constant UFS_BYTESWAPPED_SUPER_MAGIC (line 111) | UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100
  constant VMHGFS_SUPER_MAGIC (line 112) | VMHGFS_SUPER_MAGIC          = 0xBACBACBC
  constant VZFS_SUPER_MAGIC (line 113) | VZFS_SUPER_MAGIC            = 0x565A4653
  constant ZFS_SUPER_MAGIC (line 114) | ZFS_SUPER_MAGIC             = 0x2FC12FC1
  function isFuseFs (line 266) | func isFuseFs(m Mount) bool {
  function isNetworkFs (line 271) | func isNetworkFs(m Mount) bool {
  function isSpecialFs (line 275) | func isSpecialFs(m Mount) bool {
  function isHiddenFs (line 283) | func isHiddenFs(m Mount) bool {

FILE: filesystems_openbsd.go
  function isFuseFs (line 6) | func isFuseFs(m Mount) bool {
  function isNetworkFs (line 11) | func isNetworkFs(m Mount) bool {
  function isSpecialFs (line 16) | func isSpecialFs(m Mount) bool {
  function isHiddenFs (line 20) | func isHiddenFs(m Mount) bool {

FILE: filesystems_windows.go
  constant WindowsSandboxMountPointRegistryPath (line 11) | WindowsSandboxMountPointRegistryPath = `Software\Microsoft\Windows\Curre...
  function loadRegisteredWindowsSandboxMountPoints (line 16) | func loadRegisteredWindowsSandboxMountPoints() (ret map[string]struct{}) {
  function isFuseFs (line 39) | func isFuseFs(m Mount) bool {
  function isNetworkFs (line 44) | func isNetworkFs(m Mount) bool {
  function isSpecialFs (line 49) | func isSpecialFs(m Mount) bool {
  function isHiddenFs (line 54) | func isHiddenFs(m Mount) bool {

FILE: groups.go
  constant localDevice (line 8) | localDevice   = "local"
  constant networkDevice (line 9) | networkDevice = "network"
  constant fuseDevice (line 10) | fuseDevice    = "fuse"
  constant specialDevice (line 11) | specialDevice = "special"
  constant loopsDevice (line 12) | loopsDevice   = "loops"
  constant bindsMount (line 13) | bindsMount    = "binds"
  type FilterOptions (line 17) | type FilterOptions struct
  function renderTables (line 29) | func renderTables(m []Mount, filters FilterOptions, opts TableOptions) {

FILE: main.go
  function renderJSON (line 59) | func renderJSON(m []Mount) error {
  function parseColumns (line 70) | func parseColumns(cols string) ([]int, error) {
  function parseStyle (line 92) | func parseStyle(styleOpt string) (table.Style, error) {
  function parseCommaSeparatedValues (line 104) | func parseCommaSeparatedValues(values string) map[string]struct{} {
  function validateGroups (line 119) | func validateGroups(m map[string]struct{}) error {
  function findInKey (line 138) | func findInKey(str string, km map[string]struct{}) bool {
  function printVersion (line 148) | func printVersion() {
  function main (line 195) | func main() {

FILE: man.go
  function init (line 16) | func init() {

FILE: mounts.go
  type Mount (line 10) | type Mount struct
  function readLines (line 28) | func readLines(filename string) ([]string, error) {
  function unescapeFstab (line 44) | func unescapeFstab(path string) string {
  function byteToString (line 53) | func byteToString(orig []byte) string {
  function intToString (line 77) | func intToString(orig []int8) string {

FILE: mounts_darwin.go
  method Stat (line 10) | func (m *Mount) Stat() unix.Statfs_t {
  function mounts (line 14) | func mounts() ([]Mount, []string, error) {

FILE: mounts_freebsd.go
  method Stat (line 10) | func (m *Mount) Stat() unix.Statfs_t {
  function mounts (line 14) | func mounts() ([]Mount, []string, error) {

FILE: mounts_linux.go
  constant mountinfoMountPoint (line 31) | mountinfoMountPoint = 4
  constant mountinfoMountOpts (line 33) | mountinfoMountOpts = 5
  constant mountinfoOptionalFields (line 35) | mountinfoOptionalFields = 6
  constant mountinfoFsType (line 39) | mountinfoFsType = 8
  constant mountinfoMountSource (line 41) | mountinfoMountSource = 9
  constant mountinfoSuperOptions (line 43) | mountinfoSuperOptions = 10
  method Stat (line 47) | func (m *Mount) Stat() unix.Statfs_t {
  function mounts (line 51) | func mounts() ([]Mount, []string, error) {
  function splitMountInfoFields (line 127) | func splitMountInfoFields(line string) []string {
  function parseMountInfoLine (line 172) | func parseMountInfoLine(line string) (int, [11]string) {

FILE: mounts_linux_test.go
  function TestGetFields (line 11) | func TestGetFields(t *testing.T) {

FILE: mounts_openbsd.go
  method Stat (line 10) | func (m *Mount) Stat() unix.Statfs_t {
  function mounts (line 14) | func mounts() ([]Mount, []string, error) {

FILE: mounts_windows.go
  constant guidBufLen (line 18) | guidBufLen       = windows.MAX_PATH + 1
  constant volumeNameBufLen (line 19) | volumeNameBufLen = windows.MAX_PATH + 1
  constant rootPathBufLen (line 20) | rootPathBufLen   = windows.MAX_PATH + 1
  constant fileSystemBufLen (line 21) | fileSystemBufLen = windows.MAX_PATH + 1
  function getMountPoint (line 24) | func getMountPoint(guidBuf []uint16) (mountPoint string, err error) {
  function getVolumeInfo (line 38) | func getVolumeInfo(guidOrMountPointBuf []uint16) (volumeName string, fsT...
  function getSpaceInfo (line 49) | func getSpaceInfo(guidOrMountPointBuf []uint16) (totalBytes uint64, free...
  function getClusterInfo (line 54) | func getClusterInfo(guidOrMountPointBuf []uint16) (totalClusters uint32,...
  function getMount (line 62) | func getMount(guidOrMountPointBuf []uint16, isGUID bool) (m Mount, skip ...
  function getMountFromGUID (line 113) | func getMountFromGUID(guidBuf []uint16) (m Mount, skip bool, warnings []...
  function getMountFromMountPoint (line 124) | func getMountFromMountPoint(mountPointBuf []uint16) (m Mount, warnings [...
  function appendLocalMounts (line 135) | func appendLocalMounts(mounts []Mount, warnings []string) ([]Mount, []st...
  function getMountFromNetResource (line 168) | func getMountFromNetResource(netResource NetResource) (m Mount, warnings...
  function appendNetworkMounts (line 185) | func appendNetworkMounts(mounts []Mount, warnings []string) ([]Mount, []...
  function mountPointAlreadyPresent (line 221) | func mountPointAlreadyPresent(mounts []Mount, mountPoint string) bool {
  function appendLogicalDrives (line 231) | func appendLogicalDrives(mounts []Mount, warnings []string) ([]Mount, []...
  function mounts (line 257) | func mounts() (ret []Mount, warnings []string, err error) {
  constant RESOURCE_CONNECTED (line 281) | RESOURCE_CONNECTED  = 0x00000001
  constant RESOURCE_GLOBALNET (line 282) | RESOURCE_GLOBALNET  = 0x00000002
  constant RESOURCE_REMEMBERED (line 283) | RESOURCE_REMEMBERED = 0x00000003
  constant RESOURCE_RECENT (line 284) | RESOURCE_RECENT     = 0x00000004
  constant RESOURCE_CONTEXT (line 285) | RESOURCE_CONTEXT    = 0x00000005
  constant RESOURCETYPE_ANY (line 287) | RESOURCETYPE_ANY      = 0x00000000
  constant RESOURCETYPE_DISK (line 288) | RESOURCETYPE_DISK     = 0x00000001
  constant RESOURCETYPE_PRINT (line 289) | RESOURCETYPE_PRINT    = 0x00000002
  constant RESOURCETYPE_RESERVED (line 290) | RESOURCETYPE_RESERVED = 0x00000008
  constant RESOURCETYPE_UNKNOWN (line 291) | RESOURCETYPE_UNKNOWN  = 0xFFFFFFFF
  constant RESOURCEUSAGE_CONNECTABLE (line 293) | RESOURCEUSAGE_CONNECTABLE   = 0x00000001
  constant RESOURCEUSAGE_CONTAINER (line 294) | RESOURCEUSAGE_CONTAINER     = 0x00000002
  constant RESOURCEUSAGE_NOLOCALDEVICE (line 295) | RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004
  constant RESOURCEUSAGE_SIBLING (line 296) | RESOURCEUSAGE_SIBLING       = 0x00000008
  constant RESOURCEUSAGE_ATTACHED (line 297) | RESOURCEUSAGE_ATTACHED      = 0x00000010
  constant RESOURCEUSAGE_ALL (line 298) | RESOURCEUSAGE_ALL           = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_...
  constant RESOURCEUSAGE_RESERVED (line 299) | RESOURCEUSAGE_RESERVED      = 0x80000000
  type NetResource (line 316) | type NetResource struct
  function WNetOpenEnum (line 328) | func WNetOpenEnum(scope uint32, resourceType uint32, usage uint32, resou...
  function WNetEnumResource (line 341) | func WNetEnumResource(enumResource windows.Handle, count *uint32, buffer...
  function WNetCloseEnum (line 354) | func WNetCloseEnum(enumResource windows.Handle) (err error) {
  function GetDiskFreeSpace (line 367) | func GetDiskFreeSpace(directoryName *uint16, sectorsPerCluster *uint32, ...

FILE: style.go
  function defaultStyleName (line 5) | func defaultStyleName() string {

FILE: table.go
  type TableOptions (line 17) | type TableOptions struct
  type Column (line 25) | type Column struct
  function initializeTable (line 49) | func initializeTable(tab table.Writer, opts TableOptions) {
  function appendHeaders (line 57) | func appendHeaders(tab table.Writer) {
  function appendRows (line 66) | func appendRows(tab table.Writer, m []Mount) {
  function computeMaxContentWidths (line 107) | func computeMaxContentWidths(m []Mount, opts TableOptions) map[int]int {
  function computeAssignedWidths (line 191) | func computeAssignedWidths(maxColContent map[int]int, opts TableOptions)...
  function setColumnConfigs (line 272) | func setColumnConfigs(tab table.Writer, maxColContent map[int]int, assig...
  function printTable (line 298) | func printTable(title string, m []Mount, opts TableOptions) {
  function sizeTransformer (line 444) | func sizeTransformer(val interface{}) string {
  function spaceTransformer (line 449) | func spaceTransformer(val interface{}) string {
  function inColumns (line 469) | func inColumns(cols []int, i int) bool {
  function sizeToString (line 480) | func sizeToString(size uint64) (str string) {
  function stringToSize (line 504) | func stringToSize(s string) (size uint64, err error) {
  function stringToColumn (line 541) | func stringToColumn(s string) (int, error) {
  function stringToSortIndex (line 554) | func stringToSortIndex(s string) (int, error) {
  function columnIDs (line 567) | func columnIDs() []string {

FILE: themes.go
  type Theme (line 10) | type Theme struct
  function defaultThemeName (line 24) | func defaultThemeName() string {
  function loadTheme (line 31) | func loadTheme(theme string) (Theme, error) {
Condensed preview — 35 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (115K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 15,
    "preview": "github: muesli\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 275,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    labels:\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 595,
    "preview": "name: build\non: [push, pull_request]\n\njobs:\n  build:\n    strategy:\n      matrix:\n        go-version: [~1.23, ^1]\n       "
  },
  {
    "path": ".github/workflows/goreleaser.yml",
    "chars": 448,
    "preview": "name: goreleaser\n\non:\n  pull_request:\n  push:\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-latest\n    steps:\n      - name: C"
  },
  {
    "path": ".github/workflows/lint-soft.yml",
    "chars": 745,
    "preview": "name: lint-soft\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\npermissions:\n  contents: read\n  # Optional: al"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 687,
    "preview": "name: lint\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\npermissions:\n  contents: read\n  # Optional: allow r"
  },
  {
    "path": ".github/workflows/manpage.yml",
    "chars": 829,
    "preview": "name: manpage\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  manpage:\n    runs-on: ubuntu-latest\n    steps:\n      - "
  },
  {
    "path": ".gitignore",
    "chars": 281,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": ".golangci-soft.yml",
    "chars": 530,
    "preview": "version: \"2\"\nrun:\n  tests: false\nlinters:\n  enable:\n    - exhaustive\n    - goconst\n    - godot\n    - godox\n    - gomoddi"
  },
  {
    "path": ".golangci.yml",
    "chars": 430,
    "preview": "version: \"2\"\nrun:\n  tests: false\nlinters:\n  enable:\n    - bodyclose\n    - gosec\n    - nilerr\n    - predeclared\n    - rev"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 1635,
    "preview": "version: 2\n\nenv:\n  - CGO_ENABLED=0\n\nbefore:\n  hooks:\n    - go mod tidy\n\nbuilds:\n  - binary: duf\n    flags:\n      - -trim"
  },
  {
    "path": "LICENSE",
    "chars": 2733,
    "preview": "MIT License\n\nCopyright (c) 2020 Christian Muehlhaeuser\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 4143,
    "preview": "# duf\n\n[![Latest Release](https://img.shields.io/github/release/muesli/duf.svg?style=for-the-badge)](https://github.com/"
  },
  {
    "path": "duf.1",
    "chars": 4169,
    "preview": ".TH DUF 1 \"2025-09-30\" \"duf\" \"Disk Usage/Free Utility\"\n.SH NAME\nduf - Disk Usage/Free Utility\n.SH SYNOPSIS\n\\fBduf\\fP [\\f"
  },
  {
    "path": "filesystems.go",
    "chars": 1292,
    "preview": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc findMounts(mounts []Mount, path string) ([]Mount, error"
  },
  {
    "path": "filesystems_darwin.go",
    "chars": 299,
    "preview": "//go:build darwin\n// +build darwin\n\npackage main\n\nfunc isFuseFs(m Mount) bool {\n\t//FIXME: implement\n\treturn false\n}\n\nfun"
  },
  {
    "path": "filesystems_freebsd.go",
    "chars": 517,
    "preview": "//go:build freebsd\n// +build freebsd\n\npackage main\n\nfunc isFuseFs(m Mount) bool {\n\t//FIXME: implement\n\treturn false\n}\n\nf"
  },
  {
    "path": "filesystems_linux.go",
    "chars": 12838,
    "preview": "//go:build linux\n// +build linux\n\npackage main\n\nimport \"strings\"\n\n//nolint:revive\nconst (\n\t// man statfs\n\tADFS_SUPER_MAG"
  },
  {
    "path": "filesystems_openbsd.go",
    "chars": 301,
    "preview": "//go:build openbsd\n// +build openbsd\n\npackage main\n\nfunc isFuseFs(m Mount) bool {\n\t//FIXME: implement\n\treturn false\n}\n\nf"
  },
  {
    "path": "filesystems_windows.go",
    "chars": 1075,
    "preview": "//go:build windows\n// +build windows\n\npackage main\n\nimport (\n\t\"golang.org/x/sys/windows/registry\"\n)\n\nconst (\n\tWindowsSan"
  },
  {
    "path": "go.mod",
    "chars": 700,
    "preview": "module github.com/muesli/duf\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/IGLOU-EU/go-wildcard v1.0.3\n\tgithub.com/jedib0t/go-pretty"
  },
  {
    "path": "go.sum",
    "chars": 3493,
    "preview": "github.com/IGLOU-EU/go-wildcard v1.0.3 h1:r8T46+8/9V1STciXJomTWRpPEv4nGJATDbJkdU0Nou0=\ngithub.com/IGLOU-EU/go-wildcard v"
  },
  {
    "path": "groups.go",
    "chars": 3206,
    "preview": "package main\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\tlocalDevice   = \"local\"\n\tnetworkDevice = \"network\"\n\tfuseDevice    = \"fuse\""
  },
  {
    "path": "main.go",
    "chars": 9113,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\twildcard \"github.c"
  },
  {
    "path": "man.go",
    "chars": 2674,
    "preview": "//go:build mango\n// +build mango\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/muesli/mango\"\n\tmpflag \"github.com/mu"
  },
  {
    "path": "mounts.go",
    "chars": 1802,
    "preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"os\"\n\t\"strconv\"\n)\n\n// Mount contains all metadata for a single filesystem mount.\ntype M"
  },
  {
    "path": "mounts_darwin.go",
    "chars": 2043,
    "preview": "//go:build darwin\n// +build darwin\n\npackage main\n\nimport (\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc (m *Mount) Stat() unix.Statf"
  },
  {
    "path": "mounts_freebsd.go",
    "chars": 2376,
    "preview": "//go:build freebsd\n// +build freebsd\n\npackage main\n\nimport (\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc (m *Mount) Stat() unix.Sta"
  },
  {
    "path": "mounts_linux.go",
    "chars": 6185,
    "preview": "//go:build linux\n// +build linux\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n"
  },
  {
    "path": "mounts_linux_test.go",
    "chars": 6444,
    "preview": "//go:build linux\n// +build linux\n\npackage main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGetFields(t *testing.T) {\n\tva"
  },
  {
    "path": "mounts_openbsd.go",
    "chars": 1943,
    "preview": "//go:build openbsd\n// +build openbsd\n\npackage main\n\nimport (\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc (m *Mount) Stat() unix.Sta"
  },
  {
    "path": "mounts_windows.go",
    "chars": 11323,
    "preview": "//go:build windows\n// +build windows\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"golang.org/x/sys/windows\"\n\t\"math\"\n\t\"path/filepath\""
  },
  {
    "path": "style.go",
    "chars": 390,
    "preview": "package main\n\nimport \"github.com/mattn/go-runewidth\"\n\nfunc defaultStyleName() string {\n\t/*\n\t\tDue to a bug in github.com/"
  },
  {
    "path": "table.go",
    "chars": 16374,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jedib0t/go-pretty/v6/table\"\n\t\"github.c"
  },
  {
    "path": "themes.go",
    "chars": 1940,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/muesli/termenv\"\n)\n\n// Theme defines a color theme used for printing tables.\n"
  }
]

About this extraction

This page contains the full source code of the muesli/duf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 35 files (101.4 KB), approximately 33.9k tokens, and a symbol index with 226 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!