mod.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //! getopt implementation for relibc
  2. use core::ptr;
  3. use header::{stdio, string};
  4. use header::unistd::{optarg, optind, opterr, optopt};
  5. use platform::types::*;
  6. static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
  7. pub const no_argument: c_int = 0;
  8. pub const required_argument: c_int = 1;
  9. pub const optional_argument: c_int = 2;
  10. #[repr(C)]
  11. pub struct option {
  12. name: *const c_char,
  13. has_arg: c_int,
  14. flag: *mut c_int,
  15. val: c_int
  16. }
  17. #[no_mangle]
  18. #[linkage = "weak"] // often redefined in GNU programs
  19. pub unsafe extern "C" fn getopt_long(
  20. argc: c_int,
  21. argv: *const *mut c_char,
  22. optstring: *const c_char,
  23. longopts: *const option,
  24. longindex: *mut c_int
  25. ) -> c_int {
  26. // if optarg is not set, we still don't want the previous value leaking
  27. optarg = ptr::null_mut();
  28. // handle reinitialization request
  29. if optind == 0 {
  30. optind = 1;
  31. CURRENT_OPT = ptr::null_mut();
  32. }
  33. if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
  34. if optind >= argc {
  35. -1
  36. } else {
  37. let current_arg = *argv.offset(optind as isize);
  38. if current_arg.is_null()
  39. || *current_arg != b'-' as c_char
  40. || *current_arg.offset(1) == 0
  41. {
  42. -1
  43. } else if string::strcmp(current_arg, b"--\0".as_ptr() as _) == 0 {
  44. optind += 1;
  45. -1
  46. } else {
  47. // remove the '-'
  48. let current_arg = current_arg.offset(1);
  49. if *current_arg == b'-' as c_char && !longopts.is_null() {
  50. let current_arg = current_arg.offset(1);
  51. // is a long option
  52. for i in 0.. {
  53. let opt = &*longopts.offset(i);
  54. if opt.name.is_null() {
  55. break;
  56. }
  57. let mut end = 0;
  58. while { let c = *current_arg.offset(end); c != 0 && c != b'=' as c_char } {
  59. end += 1;
  60. }
  61. if string::strncmp(current_arg, opt.name, end as size_t) == 0 {
  62. optind += 1;
  63. *longindex = i as c_int;
  64. if opt.has_arg == optional_argument {
  65. if *current_arg.offset(end) == b'=' as c_char {
  66. optarg = current_arg.offset(end + 1);
  67. }
  68. } else if opt.has_arg == required_argument {
  69. if *current_arg.offset(end) == b'=' as c_char {
  70. optarg = current_arg.offset(end + 1);
  71. } else if optind < argc {
  72. optarg = *argv.offset(optind as isize);
  73. optind += 1;
  74. } else {
  75. if *optstring == b':' as c_char {
  76. return b':' as c_int;
  77. } else {
  78. stdio::fputs(*argv as _, &mut *stdio::stderr);
  79. stdio::fputs(": option '--\0".as_ptr() as _, &mut *stdio::stderr);
  80. stdio::fputs(current_arg, &mut *stdio::stderr);
  81. stdio::fputs("' requires an argument\n\0".as_ptr() as _, &mut *stdio::stderr);
  82. return b'?' as c_int;
  83. }
  84. }
  85. }
  86. if opt.flag.is_null() {
  87. return opt.val;
  88. } else {
  89. *opt.flag = opt.val;
  90. return 0;
  91. }
  92. }
  93. }
  94. }
  95. parse_arg(argc, argv, current_arg, optstring)
  96. }
  97. }
  98. } else {
  99. parse_arg(argc, argv, CURRENT_OPT, optstring)
  100. }
  101. }
  102. unsafe fn parse_arg(
  103. argc: c_int,
  104. argv: *const *mut c_char,
  105. current_arg: *mut c_char,
  106. optstring: *const c_char
  107. ) -> c_int {
  108. let update_current_opt = || {
  109. CURRENT_OPT = current_arg.offset(1);
  110. if *CURRENT_OPT == 0 {
  111. optind += 1;
  112. }
  113. };
  114. let print_error = |desc: &[u8]| {
  115. // NOTE: we don't use fprintf to get around the usage of va_list
  116. stdio::fputs(*argv as _, &mut *stdio::stderr);
  117. stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
  118. stdio::fputc(*current_arg as _, &mut *stdio::stderr);
  119. stdio::fputc(b'\n' as _, &mut *stdio::stderr);
  120. };
  121. match find_option(*current_arg, optstring) {
  122. Some(GetoptOption::Flag) => {
  123. update_current_opt();
  124. *current_arg as c_int
  125. }
  126. Some(GetoptOption::OptArg) => {
  127. CURRENT_OPT = b"\0".as_ptr() as _;
  128. if *current_arg.offset(1) == 0 {
  129. optind += 2;
  130. if optind > argc {
  131. CURRENT_OPT = ptr::null_mut();
  132. optopt = *current_arg as c_int;
  133. let errch = if *optstring == b':' as c_char {
  134. b':'
  135. } else {
  136. if opterr != 0 {
  137. print_error(b": option requries an argument -- \0");
  138. }
  139. b'?'
  140. };
  141. errch as c_int
  142. } else {
  143. optarg = *argv.offset(optind as isize - 1);
  144. *current_arg as c_int
  145. }
  146. } else {
  147. optarg = current_arg.offset(1);
  148. optind += 1;
  149. *current_arg as c_int
  150. }
  151. }
  152. None => {
  153. // couldn't find the given option in optstring
  154. if opterr != 0 {
  155. print_error(b": illegal option -- \0");
  156. }
  157. update_current_opt();
  158. optopt = *current_arg as c_int;
  159. b'?' as c_int
  160. }
  161. }
  162. }
  163. enum GetoptOption {
  164. Flag,
  165. OptArg,
  166. }
  167. unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
  168. let mut i = 0;
  169. while *optstring.offset(i) != 0 {
  170. if *optstring.offset(i) == ch {
  171. let result = if *optstring.offset(i + 1) == b':' as c_char {
  172. GetoptOption::OptArg
  173. } else {
  174. GetoptOption::Flag
  175. };
  176. return Some(result);
  177. }
  178. i += 1;
  179. }
  180. None
  181. }