@@ -18,8 +18,7 @@ use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleC
1818use plib:: PROJECT_NAME ;
1919#[ cfg( target_os = "macos" ) ]
2020use std:: ffi:: CStr ;
21- use std:: ffi:: CString ;
22- use std:: io;
21+ use std:: { cmp, ffi:: CString , io} ;
2322
2423#[ derive( Parser ) ]
2524#[ command( version, about = gettext( "df - report free storage space" ) ) ]
@@ -51,6 +50,133 @@ struct Args {
5150 files : Vec < String > ,
5251}
5352
53+ /// Display modes
54+ pub enum OutputMode {
55+ /// When both the -k and -P options are specified
56+ Posix ,
57+ /// When the -P option is specified without the -k option
58+ PosixLegacy ,
59+ /// The format of the default output from df is unspecified,
60+ /// but all space figures are reported in 512-byte units
61+ Unspecified ,
62+ }
63+
64+ impl OutputMode {
65+ pub fn new ( kilo : bool , portable : bool ) -> Self {
66+ match ( kilo, portable) {
67+ ( true , true ) => Self :: Posix ,
68+ ( false , true ) => Self :: PosixLegacy ,
69+ _ => Self :: Unspecified ,
70+ }
71+ }
72+
73+ pub fn get_block_size ( & self ) -> u64 {
74+ match self {
75+ OutputMode :: Posix => 1024 ,
76+ OutputMode :: PosixLegacy => 512 ,
77+ OutputMode :: Unspecified => 512 ,
78+ }
79+ }
80+ }
81+
82+ pub struct Field {
83+ caption : String ,
84+ width : usize ,
85+ }
86+
87+ impl Field {
88+ pub fn new ( caption : String , min_width : usize ) -> Self {
89+ let width = cmp:: max ( caption. len ( ) , min_width) ;
90+ Self { caption, width }
91+ }
92+
93+ pub fn print_header ( & self ) {
94+ print ! ( "{: <width$} " , self . caption, width = self . width) ;
95+ }
96+
97+ pub fn print_header_align_right ( & self ) {
98+ print ! ( "{: >width$} " , self . caption, width = self . width) ;
99+ }
100+
101+ pub fn print_string ( & self , value : & String ) {
102+ print ! ( "{: <width$} " , value, width = self . width) ;
103+ }
104+
105+ pub fn print_u64 ( & self , value : u64 ) {
106+ print ! ( "{: >width$} " , value, width = self . width) ;
107+ }
108+
109+ pub fn print_percentage ( & self , value : u32 ) {
110+ print ! ( "{: >width$}% " , value, width = self . width - 1 ) ;
111+ }
112+ }
113+
114+ pub struct Fields {
115+ pub mode : OutputMode ,
116+ /// file system
117+ pub source : Field ,
118+ /// FS size
119+ pub size : Field ,
120+ /// FS size used
121+ pub used : Field ,
122+ /// FS size available
123+ pub avail : Field ,
124+ /// percent used
125+ pub pcent : Field ,
126+ /// mount point
127+ pub target : Field ,
128+ // /// specified file name
129+ // file: Field,
130+ }
131+
132+ impl Fields {
133+ pub fn new ( mode : OutputMode ) -> Self {
134+ let size_caption = format ! ( "{}-{}" , mode. get_block_size( ) , gettext( "blocks" ) ) ;
135+ Self {
136+ mode,
137+ source : Field :: new ( gettext ( "Filesystem" ) , 14 ) ,
138+ size : Field :: new ( size_caption, 10 ) ,
139+ used : Field :: new ( gettext ( "Used" ) , 10 ) ,
140+ avail : Field :: new ( gettext ( "Available" ) , 10 ) ,
141+ pcent : Field :: new ( gettext ( "Capacity" ) , 5 ) ,
142+ target : Field :: new ( gettext ( "Mounted on" ) , 0 ) ,
143+ }
144+ }
145+
146+ pub fn print_header ( & self ) {
147+ self . source . print_header ( ) ;
148+ self . size . print_header_align_right ( ) ;
149+ self . used . print_header_align_right ( ) ;
150+ self . avail . print_header_align_right ( ) ;
151+ self . pcent . print_header_align_right ( ) ;
152+ self . target . print_header ( ) ;
153+ println ! ( ) ;
154+ }
155+
156+ fn print_row (
157+ & self ,
158+ fsname : & String ,
159+ total : u64 ,
160+ used : u64 ,
161+ avail : u64 ,
162+ percentage_used : u32 ,
163+ target : & String ,
164+ ) {
165+ // The remaining output with -P shall consist of one line of information
166+ // for each specified file system. These lines shall be formatted as follows:
167+ // "%s %d %d %d %d%% %s\n", <file system name>, <total space>,
168+ // <space used>, <space free>, <percentage used>,
169+ // <file system root>
170+ self . source . print_string ( fsname) ;
171+ self . size . print_u64 ( total) ;
172+ self . used . print_u64 ( used) ;
173+ self . avail . print_u64 ( avail) ;
174+ self . pcent . print_percentage ( percentage_used) ;
175+ self . target . print_string ( target) ;
176+ println ! ( ) ;
177+ }
178+ }
179+
54180#[ cfg( target_os = "macos" ) ]
55181fn to_cstr ( array : & [ libc:: c_char ] ) -> & CStr {
56182 unsafe {
@@ -79,6 +205,39 @@ struct Mount {
79205 cached_statfs : libc:: statfs ,
80206}
81207
208+ impl Mount {
209+ fn print ( & self , fields : & Fields ) {
210+ if !self . masked {
211+ return ;
212+ }
213+
214+ let sf = self . cached_statfs ;
215+
216+ let block_size = fields. mode . get_block_size ( ) ;
217+ let blksz = sf. f_bsize as u64 ;
218+
219+ let total = ( sf. f_blocks * blksz) / block_size;
220+ let avail = ( sf. f_bavail * blksz) / block_size;
221+ let free = ( sf. f_bfree * blksz) / block_size;
222+ let used = total - free;
223+
224+ // The percentage value shall be expressed as a positive integer,
225+ // with any fractional result causing it to be rounded to the next highest integer.
226+ let percentage_used = f64:: from ( used as u32 ) / f64:: from ( ( used + free) as u32 ) ;
227+ let percentage_used = percentage_used * 100.0 ;
228+ let percentage_used = percentage_used. ceil ( ) as u32 ;
229+
230+ fields. print_row (
231+ & self . devname ,
232+ total,
233+ used,
234+ avail,
235+ percentage_used,
236+ & self . dir ,
237+ ) ;
238+ }
239+ }
240+
82241struct MountList {
83242 mounts : Vec < Mount > ,
84243 has_masks : bool ,
@@ -192,60 +351,6 @@ fn mask_fs_by_file(info: &mut MountList, filename: &str) -> io::Result<()> {
192351 Ok ( ( ) )
193352}
194353
195- fn show_mount ( args : & Args , block_size : u64 , mount : & Mount ) {
196- let sf = & mount. cached_statfs ;
197-
198- let blksz = sf. f_bsize as u64 ;
199-
200- let total = ( sf. f_blocks * blksz) / block_size;
201- let avail = ( sf. f_bavail * blksz) / block_size;
202- let free = ( sf. f_bfree * blksz) / block_size;
203- let used = total - free;
204-
205- if total == 0 {
206- return ;
207- }
208-
209- let pct = ( ( total - avail) * 100 ) / total;
210-
211- if args. portable {
212- println ! (
213- "{:>20} {:>9} {:>9} {:>9} {:>7} {}" ,
214- mount. devname, total, used, avail, pct, mount. dir
215- ) ;
216- } else {
217- println ! (
218- "{:>20} {:>9} {:>9} {:>9} {:>3} {}" ,
219- mount. devname, total, used, avail, pct, mount. dir
220- ) ;
221- }
222- }
223-
224- fn show_info ( args : & Args , info : & MountList ) {
225- let block_size: u64 = match args. kilo {
226- true => 1024 ,
227- false => 512 ,
228- } ;
229-
230- if args. portable {
231- println ! (
232- "Filesystem {:>4}-blocks Used Available Capacity Mounted on" ,
233- block_size
234- ) ;
235- } else {
236- println ! (
237- "Filesystem {:>4}-blocks Used Available Use % Mounted on" ,
238- block_size
239- ) ;
240- }
241-
242- for mount in & info. mounts {
243- if mount. masked {
244- show_mount ( args, block_size, mount) ;
245- }
246- }
247- }
248-
249354fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
250355 // parse command line arguments
251356 let args = Args :: parse ( ) ;
@@ -265,7 +370,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
265370 }
266371
267372 info. ensure_masked ( ) ;
268- show_info ( & args, & info) ;
373+
374+ let mode = OutputMode :: new ( args. kilo , args. portable ) ;
375+ let fields = Fields :: new ( mode) ;
376+ fields. print_header ( ) ;
377+
378+ for mount in & info. mounts {
379+ mount. print ( & fields) ;
380+ }
269381
270382 Ok ( ( ) )
271383}
0 commit comments