1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use super::{Kind, Result, Unimplemented};
bitflags!{
    /// Conversion flags
    pub struct Conv: u32 {

        /// Translate from EBCDIC to ASCII
        const ASCII = 0x0001 ;
        /// Translate from ASCII to EBCDIC
        const EBCDIC= 0x0002;
        /// <NOT IMPLEMENTED> Translate from ASCII to IBM
        const IBM = 0x0004;
        /// <NOT IMPLEMENTED> Translate from EBCDIC to old ASCII
        const OLDASCII = 0x0008 ;
        /// <NOT IMPLEMENTED> Translate from ASCII to old EBCDIC
        const OLDEBCDIC = 0x0010 ;
        /// <NOT IMPLEMENTED> Translate from ASCII to old IBM
        const OLDIBM = 0x0020 ;
        /// "block" mode: Treats the input as a sequence of newline or end-of-file terminated variable length records independent of input and output block bound-
        /// aries.  Any trailing newline character is discarded.  Each input record is converted to a fixed length output record where the length is
        /// specified by the cbs operand.  Input records shorter than the conversion record size are padded with spaces.  Input records longer than
        /// the conversion record size are truncated.  The number of truncated input records, if any, are reported to the standard error output at
        /// the completion of the copy.
        const BLOCK = 0x0040;
        /// Treats the input as a sequence of fixed length records independent of input and output block boundaries.  The length of the input records
        /// is specified by the cbs operand.  Any trailing space characters are discarded and a newline character is appended.
        const UNBLOCK = 0x0080 ;
        /// Transform uppercase [A-Z] characters into lowercase [a-z] characters.
        const LCASE = 0x0100;
        /// Transform lowercase [a-z] characters into uppercase [A-Z] characters
        const UCASE = 0x0200;
        /// Pad every input block to the input buffer size.  Spaces are used for pad bytes if a
        /// block oriented conversion value is specified, otherwise NUL bytes are used.
        const SYNC = 0x0400;
        /// <NOT IMPLEMENTED> Pad the final output block to the full output block size.  If the input file is not a multiple of the output block size after conversion,
        /// this conversion forces the final output block to be the same size as preceding blocks for use on devices that require regularly sized
        /// blocks to be written.  This option is incompatible with use of the bs=n block size specification.
        /// sparse   If one or more output blocks would consist solely of NUL bytes, try to seek the output file by the required space instead of filling them
        /// with NULs, resulting in a sparse file.
        const OSYNC = 0x0800;
        /// <NOT IMPLEMENTED> Do not stop processing on an input error.  When an input error occurs, a diagnostic message followed by the current input and output
        /// block counts will be written to the standard error output in the same format as the standard completion message.  If the sync conversion
        /// is also specified, any missing input data will be replaced with NUL bytes (or with spaces if a block oriented conversion value was speci-
        /// fied) and processed as a normal input buffer.  If the sync conversion is not specified, the input block is omitted from the output.  On
        /// input files which are not tapes or pipes, the file offset will be positioned past the block in which the error occurred using lseek(2).
        const NOERROR = 0x1000;
        /// Do not truncate the output file.  This will preserve any blocks in the output file not explicitly written by dd.  The notrunc value is
        /// not supported for tapes.
        const NOTRUNC = 0x2000;
        /// If one or more output blocks would consist solely of NUL bytes, try to seek the out-
        /// put file by the required space instead of filling them with NULs, resulting in a
        /// sparse file.
        const SPARSE = 0x4000;
        /// Swap every pair of input bytes.  If an input buffer has an odd number of bytes, the last byte will be ignored during swapping.
        const SWAB = 0x8000;
    }
}

impl Conv {
    pub fn new(s: &str) -> Result<Self> {
        let mut flags = Conv::default();
        for flag in s.split(',').map(str::trim).filter(|s| s.len() > 0) {
            Self::check_if_implemented(flag)?;
            flags |= Self::from_str(flag)?;
        }
        Ok(flags)
    }

    fn from_str(flag: &str) -> Result<Self> {
        Ok(match flag {
            "ascii" => Self::ASCII,
            "ebcdic" => Self::EBCDIC,
            "oldascii" => Self::OLDASCII,
            "ibm" => Self::IBM,
            "oldebcdic" => Self::OLDEBCDIC,
            "oldibm" => Self::OLDIBM,
            "noerror" => Self::NOERROR,
            "osync" => Self::OSYNC,
            "block" => Self::BLOCK,

            "lcase" => Self::LCASE,
            "notrunc" => Self::NOTRUNC,
            "sparse" => Self::SPARSE,
            "swab" => Self::SWAB,
            "sync" => Self::SYNC,
            "ucase" => Self::UCASE,
            "unblock" => Self::UNBLOCK,
            arg => return Self::unknown(arg.to_owned()),
        })
    }
}

impl Unimplemented for Conv {
    const KIND: Kind = Kind::CFlag;
    const UNIMPLEMENTED: &'static [&'static str] = &["oldascii", "ibm", "oldebcdic", "noerror", "osync", "oldibm"];
}
impl Default for Conv {
    fn default() -> Conv { Conv::empty() }
}