relibc/header/getopt/
mod.rs1use crate::{
6 byte_literal::ByteLiteral,
7 header::{
8 stdio, string,
9 unistd::{optarg, opterr, optind, optopt},
10 },
11 platform::types::{c_char, c_int, size_t},
12};
13use core::ptr;
14
15static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
17
18pub const no_argument: c_int = 0;
20pub const required_argument: c_int = 1;
22pub const optional_argument: c_int = 2;
24
25#[repr(C)]
27pub struct option {
28 name: *const c_char,
29 has_arg: c_int,
30 flag: *mut c_int,
31 val: c_int,
32}
33
34#[unsafe(no_mangle)]
38pub unsafe extern "C" fn getopt_long(
39 argc: c_int,
40 argv: *const *mut c_char,
41 optstring: *const c_char,
42 longopts: *const option,
43 longindex: *mut c_int,
44) -> c_int {
45 unsafe {
47 optarg = ptr::null_mut();
48 }
49
50 unsafe {
52 if optind == 0 {
53 optind = 1;
54 CURRENT_OPT = ptr::null_mut();
55 }
56 }
57
58 if unsafe { CURRENT_OPT.is_null() || *CURRENT_OPT == 0 } {
59 if unsafe { optind >= argc } {
60 -1
61 } else {
62 let current_arg = unsafe { *argv.offset(optind as isize) };
63 if unsafe {
64 current_arg.is_null()
65 || *current_arg != ByteLiteral::cast_cchar(b'-')
66 || *current_arg.offset(1) == 0
67 } {
68 -1
69 } else if unsafe { string::strcmp(current_arg, c"--".as_ptr()) == 0 } {
70 unsafe {
71 optind += 1;
72 }
73 -1
74 } else {
75 let current_arg = unsafe { current_arg.offset(1) };
77
78 if unsafe { *current_arg == ByteLiteral::cast_cchar(b'-') } && !longopts.is_null() {
79 let current_arg = unsafe { current_arg.offset(1) };
80 for i in 0.. {
82 let opt = unsafe { &*longopts.offset(i) };
83 if opt.name.is_null() {
84 break;
85 }
86
87 let mut end = 0;
88 while {
89 let c = unsafe { *current_arg.offset(end) };
90 c != 0 && c != ByteLiteral::cast_cchar(b'=')
91 } {
92 end += 1;
93 }
94
95 if unsafe { string::strncmp(current_arg, opt.name, end as size_t) == 0 } {
96 unsafe {
97 optind += 1;
98 if !longindex.is_null() {
99 *longindex = i as c_int;
100 }
101 }
102
103 if opt.has_arg == optional_argument {
104 unsafe {
105 if *current_arg.offset(end) == ByteLiteral::cast_cchar(b'=') {
106 optarg = current_arg.offset(end + 1);
107 }
108 }
109 } else if opt.has_arg == required_argument {
110 unsafe {
111 if *current_arg.offset(end) == ByteLiteral::cast_cchar(b'=') {
112 optarg = current_arg.offset(end + 1);
113 } else if optind < argc {
114 optarg = *argv.offset(optind as isize);
115 optind += 1;
116 } else if *optstring == ByteLiteral::cast_cchar(b':') {
117 return c_int::from(b':');
118 } else {
119 stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
120 stdio::fputs(
121 c": option '--".as_ptr().cast(),
122 &raw mut *stdio::stderr,
123 );
124 stdio::fputs(current_arg, &raw mut *stdio::stderr);
125 stdio::fputs(
126 c"' requires an argument\n".as_ptr().cast(),
127 &raw mut *stdio::stderr,
128 );
129 return c_int::from(b'?');
130 }
131 }
132 }
133
134 if opt.flag.is_null() {
135 return opt.val;
136 } else {
137 unsafe { *opt.flag = opt.val };
138 return 0;
139 }
140 }
141 }
142 }
143
144 unsafe { parse_arg(argc, argv, current_arg, optstring) }
145 }
146 }
147 } else {
148 unsafe { parse_arg(argc, argv, CURRENT_OPT, optstring) }
149 }
150}
151
152unsafe fn parse_arg(
153 argc: c_int,
154 argv: *const *mut c_char,
155 current_arg: *mut c_char,
156 optstring: *const c_char,
157) -> c_int {
158 let update_current_opt = || unsafe {
159 CURRENT_OPT = current_arg.offset(1);
160 if *CURRENT_OPT == 0 {
161 optind += 1;
162 }
163 };
164
165 let print_error = |desc: &[u8]| unsafe {
166 stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
168 stdio::fputs(desc.as_ptr().cast(), &raw mut *stdio::stderr);
169 stdio::fputc((*current_arg).into(), &raw mut *stdio::stderr);
170 stdio::fputc(b'\n'.into(), &raw mut *stdio::stderr);
171 };
172
173 match unsafe { find_option(*current_arg, optstring) } {
174 Some(GetoptOption::Flag) => {
175 update_current_opt();
176
177 unsafe { c_int::from(*current_arg) }
178 }
179 Some(GetoptOption::OptArg) => unsafe {
180 CURRENT_OPT = c"".as_ptr().cast_mut();
181 if *current_arg.offset(1) == 0 {
182 optind += 2;
183 if optind > argc {
184 CURRENT_OPT = ptr::null_mut();
185
186 optopt = c_int::from(*current_arg);
187 let errch = if *optstring == ByteLiteral::cast_cchar(b':') {
188 b':'
189 } else {
190 if opterr != 0 {
191 print_error(b": option requries an argument -- \0");
192 }
193
194 b'?'
195 };
196 c_int::from(errch)
197 } else {
198 optarg = *argv.offset(optind as isize - 1);
199
200 c_int::from(*current_arg)
201 }
202 } else {
203 optarg = current_arg.offset(1);
204 optind += 1;
205
206 c_int::from(*current_arg)
207 }
208 },
209 None => {
210 if unsafe { opterr != 0 } {
212 print_error(b": illegal option -- \0");
213 }
214
215 update_current_opt();
216
217 unsafe {
218 optopt = c_int::from(*current_arg);
219 }
220 c_int::from(b'?')
221 }
222 }
223}
224
225enum GetoptOption {
226 Flag,
227 OptArg,
228}
229
230unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
231 let mut i = 0;
232
233 while unsafe { *optstring.offset(i) != 0 } {
234 if unsafe { *optstring.offset(i) == ch } {
235 let result = if unsafe { *optstring.offset(i + 1) == ByteLiteral::cast_cchar(b':') } {
236 GetoptOption::OptArg
237 } else {
238 GetoptOption::Flag
239 };
240 return Some(result);
241 }
242 i += 1;
243 }
244
245 None
246}