1use std::os::unix::fs::MetadataExt;
20use std::sync::atomic::Ordering;
21
22use crate::ported::glob::{hasbraces, remnulargs, tokenize, xpandbraces};
23use crate::ported::lex::untokenize;
24use crate::ported::mem::ztrdup;
25use crate::ported::params::{
26 getstrvalue, getvalue, getvaluearr, locallevel, paramtab, setaparam, setarrvalue, sethparam,
27 setiparam, setsparam, setstrvalue,
28};
29use crate::ported::pattern::{haswilds, patcompile, pattry, Patprog};
30use crate::ported::string::tricat;
31use crate::ported::utils::{
32 adjustcolumns, inittyptab, niceztrlen, quotestring, set_noerrs, strpfx, ztrlen, zwarnnam,
33};
34use crate::ported::zle::comp_h::{
35 Cmatcher, Cpattern, CGF_NOSORT, CGF_UNIQALL, CGF_UNIQCON, CMF_LEFT, CMF_RIGHT, CPAT_ANY,
36 CPAT_CCLASS, CPAT_CHAR, CPAT_EQUIV, CPAT_NCLASS,
37};
38use crate::ported::zle::compcore::{begcmgroup, comppatmatch, endcmgroup, get_user_var, rembslash};
39use crate::ported::zle::complete::{
40 ignore_prefix, ignore_suffix, parse_cmatcher, restrict_range, COMPCURRENT, COMPPREFIX,
41 COMPQSTACK, COMPSUFFIX, COMPWORDS, INCOMPFUNC,
42};
43use crate::ported::zle::compmatch::{pattern_match, pattern_match1, pattern_match_equivalence};
44use crate::ported::zle::compresult::ztat;
45use crate::ported::zsh_h::{
46 isset, options, unset, value, Comma, Inbrace, Outbrace, GLOBDOTS, KSHARRAYS, MAX_OPS,
47 OPT_ISSET, PM_ARRAY, PM_TYPE, PP_LOWER, PP_RANGE, PP_UPPER, QT_BACKSLASH, QT_BACKSLASH_PATTERN,
48};
49use crate::ported::ztype_h::{iblank, idigit, imeta, inblank};
50
51#[allow(unused_imports)]
57use crate::ported::zle::{
58 deltochar::*, textobjects::*, zle_hist::*, zle_main::*, zle_misc::*, zle_move::*,
59 zle_params::*, zle_refresh::*, zle_tricky::*, zle_utils::*, zle_vi::*, zle_word::*,
60};
61#[allow(unused_imports)]
65#[allow(unused_imports)]
66pub type Cdset = Box<cdset>; #[derive(Debug, Default)]
81#[allow(non_camel_case_types)]
82pub struct cdset {
83 pub next: Option<Box<cdset>>, pub opts: Option<Vec<String>>, pub strs: Option<Box<cdstr>>, pub count: i32, pub desc: i32, }
90pub type Cdstr = Box<cdstr>; #[derive(Debug, Default, Clone)]
97#[allow(non_camel_case_types)]
98pub struct cdstr {
99 pub next: Option<Box<cdstr>>, pub str: Option<String>, pub desc: Option<String>, pub r#match: Option<String>, pub sortstr: Option<String>, pub len: i32, pub width: i32, pub other: Option<Box<cdstr>>, pub kind: i32, pub set: usize, pub run: Option<Box<cdstr>>, }
112pub type Cdrun = Box<cdrun>; #[derive(Debug, Default)]
119#[allow(non_camel_case_types)]
120pub struct cdrun {
121 pub next: Option<Box<cdrun>>, pub r#type: i32, pub strs: Option<Box<cdstr>>, pub count: i32, }
127
128#[derive(Debug, Default)]
132#[allow(non_camel_case_types)]
133pub struct cdstate {
134 pub showd: i32, pub sep: Option<String>, pub slen: i32, pub swidth: i32, pub maxmlen: i32, pub sets: Option<Box<cdset>>, pub pre: i32, pub premaxw: i32, pub suf: i32, pub maxg: i32, pub maxglen: i32, pub groups: i32, pub descs: i32, pub gprew: i32, pub runs: Option<Box<cdrun>>, }
151pub const CRT_SIMPLE: i32 = 0; pub const CRT_DESC: i32 = 1; pub const CRT_SPEC: i32 = 2; pub const CRT_DUMMY: i32 = 3; pub const CRT_EXPL: i32 = 4; pub static cd_parsed: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
167
168pub fn freecdsets(mut p: Option<Box<cdset>>) {
173 while let Some(mut set) = p {
175 p = set.next.take(); set.opts = None;
179 let mut s = set.strs.take();
181 while let Some(mut node) = s {
182 s = node.next.take();
183 node.sortstr = None; node.str = None; node.desc = None; node.r#match = None;
190 drop(node); }
192 if let Ok(mut st) = cd_state.lock() {
194 let mut r = st.runs.take();
195 while let Some(mut run) = r {
196 r = run.next.take();
197 drop(run); }
199 }
200 drop(set); }
202}
203
204pub fn cd_group(maxg: i32) {
210 let mut st = cd_state.lock().unwrap();
212 st.groups = 0; st.descs = 0;
214 st.maxglen = 0;
215 st.maxg = 0;
216
217 let st_ptr: *mut cdstate = &mut *st;
221 unsafe {
222 let mut set = (*st_ptr).sets.as_deref_mut();
223 while let Some(s) = set {
224 let mut sp = s.strs.as_deref_mut();
225 while let Some(sn) = sp {
226 sn.kind = 0;
227 sn.other = None;
228 sp = sn.next.as_deref_mut();
229 }
230 set = s.next.as_deref_mut();
231 }
232
233 let mut set1 = (*st_ptr).sets.as_deref_mut();
235 while let Some(s1) = set1 {
236 let s1_ptr: *mut cdset = s1;
237 let mut str1 = (*s1_ptr).strs.as_deref_mut();
238 while let Some(t1) = str1 {
239 if t1.desc.is_none() || t1.kind != 0 {
240 str1 = t1.next.as_deref_mut();
242 continue;
243 }
244 let mut num = 1i32; let mut width = t1.width + (*st_ptr).swidth; if width > (*st_ptr).maxglen {
247 (*st_ptr).maxglen = width;
248 }
249 let t1_desc = t1.desc.clone().unwrap_or_default();
252 let mut other_tail: *mut Option<Box<cdstr>> = &mut t1.other;
253 let mut hit_break = false;
254 let mut set2 = Some(&mut *s1_ptr);
255 let mut first_iter = true;
256 while let Some(s2) = set2 {
257 let s2_ptr: *mut cdset = s2;
258 let mut str2 = if first_iter {
259 t1.next.as_deref_mut()
260 } else {
261 (*s2_ptr).strs.as_deref_mut()
262 };
263 first_iter = false;
264 while let Some(t2) = str2 {
265 if t2.desc.as_deref() == Some(t1_desc.as_str()) {
266 width += CM_SPACE + t2.width; if width > (*st_ptr).maxmlen || num == maxg {
268 hit_break = true;
270 break;
271 }
272 if width > (*st_ptr).maxglen {
273 (*st_ptr).maxglen = width;
275 }
276 t1.kind = 1; t2.kind = 2;
278 num += 1;
279 let clone = Box::new(cdstr {
281 next: None,
282 str: t2.str.clone(),
283 desc: t2.desc.clone(),
284 r#match: t2.r#match.clone(),
285 sortstr: t2.sortstr.clone(),
286 len: t2.len,
287 width: t2.width,
288 other: None,
289 kind: t2.kind,
290 set: t2.set,
291 run: None,
292 });
293 *other_tail = Some(clone);
294 let nxt = &mut (*other_tail).as_mut().unwrap().other;
295 other_tail = nxt as *mut _;
296 }
297 str2 = t2.next.as_deref_mut();
298 }
299 if hit_break {
300 break;
301 }
302 set2 = (*s2_ptr).next.as_deref_mut();
303 }
304 if num > 1 {
305 (*st_ptr).groups += 1;
307 } else {
308 (*st_ptr).descs += 1; }
310 if num > (*st_ptr).maxg {
311 (*st_ptr).maxg = num;
313 }
314 str1 = t1.next.as_deref_mut();
315 }
316 set1 = s1.next.as_deref_mut();
317 }
318 }
319}
320
321pub fn cd_calc() {
327 let mut st = cd_state.lock().unwrap();
329 st.pre = 0; st.suf = 0;
331 let mut max_pre = 0i32;
332 let mut max_premaxw = st.premaxw;
333 let mut max_suf = 0i32;
334
335 let mut set = st.sets.as_deref_mut();
336 while let Some(s) = set {
337 s.count = 0; s.desc = 0;
339 let mut str_iter = s.strs.as_deref();
340 while let Some(st_node) = str_iter {
341 s.count += 1; let str_s = st_node.str.as_deref().unwrap_or("");
343 let l = str_s.len() as i32;
344 if l > max_pre {
345 max_pre = l;
346 } let nw = niceztrlen(str_s) as i32;
349 if nw > max_premaxw {
350 max_premaxw = nw;
351 }
352 if let Some(d) = st_node.desc.as_deref() {
353 s.desc += 1;
355 let dl = d.len() as i32;
356 if dl > max_suf {
357 max_suf = dl;
358 } }
360 str_iter = st_node.next.as_deref();
361 }
362 set = s.next.as_deref_mut();
363 }
364 st.pre = max_pre;
365 st.premaxw = max_premaxw;
366 st.suf = max_suf;
367}
368
369pub fn cd_groups_want_sorting() -> i32 {
373 let st = cd_state.lock().unwrap();
375 let mut set = st.sets.as_deref();
376 while let Some(s) = set {
377 if let Some(opts) = s.opts.as_deref() {
378 for o in opts {
379 if o.starts_with("-V") {
380 return 0;
381 } if o.starts_with("-J") {
383 return 1;
384 } }
386 }
387 set = s.next.as_deref();
388 }
389 1 }
391
392pub fn cd_sort(a: &cdstr, b: &cdstr) -> std::cmp::Ordering {
397 crate::ported::sort::zstrcmp(
399 a.sortstr.as_deref().unwrap_or(""),
400 b.sortstr.as_deref().unwrap_or(""),
401 0,
402 ) }
404
405pub fn cd_prep() -> i32 {
420 let mut new_runs: Vec<Box<cdrun>> = Vec::new();
425 let mut st = cd_state.lock().unwrap();
426 st.runs = None;
427
428 if st.groups != 0 {
429 let maxg = st.maxg.max(1) as usize;
439 let maxmlen = st.maxmlen;
440 let maxglen = st.maxglen;
441 let swidth = st.swidth;
442
443 let mut wids: Vec<i32> = vec![0; maxg];
445
446 let mut prep_lines: Vec<Box<cdstr>> = Vec::new();
449 let mut set = st.sets.as_deref();
450 while let Some(s) = set {
451 let mut str_iter = s.strs.as_deref();
452 while let Some(node) = str_iter {
453 if node.kind != 1 {
454 if node.kind == 0 && node.desc.is_some() {
455 if node.width > wids[0] {
457 wids[0] = node.width;
459 }
460 let mut clone = Box::new({
461 let n = node;
462 cdstr {
463 next: None,
464 str: n.str.clone(),
465 desc: n.desc.clone(),
466 r#match: n.r#match.clone(),
467 sortstr: n.sortstr.clone(),
468 len: n.len,
469 width: n.width,
470 other: None,
471 kind: n.kind,
472 set: n.set,
473 run: None,
474 }
475 });
476 clone.other = None; prep_lines.push(clone);
478 }
479 str_iter = node.next.as_deref();
480 continue;
481 }
482 let mut gs = Box::new({
485 let n = node;
486 cdstr {
487 next: None,
488 str: n.str.clone(),
489 desc: n.desc.clone(),
490 r#match: n.r#match.clone(),
491 sortstr: n.sortstr.clone(),
492 len: n.len,
493 width: n.width,
494 other: None,
495 kind: n.kind,
496 set: n.set,
497 run: None,
498 }
499 });
500 gs.kind = 2; gs.other = None;
502
503 let mut gp = node.other.as_deref();
506 while let Some(g_node) = gp {
507 let new_clone = Box::new({
508 let n = g_node;
509 cdstr {
510 next: None,
511 str: n.str.clone(),
512 desc: n.desc.clone(),
513 r#match: n.r#match.clone(),
514 sortstr: n.sortstr.clone(),
515 len: n.len,
516 width: n.width,
517 other: None,
518 kind: n.kind,
519 set: n.set,
520 run: None,
521 }
522 });
523 let mut chain: Vec<Box<cdstr>> = Vec::new();
527 chain.push(Box::new(cdstr {
529 next: None,
530 str: gs.str.clone(),
531 desc: gs.desc.clone(),
532 r#match: gs.r#match.clone(),
533 sortstr: gs.sortstr.clone(),
534 len: gs.len,
535 width: gs.width,
536 other: None,
537 kind: gs.kind,
538 set: gs.set,
539 run: None,
540 }));
541 let mut rest = gs.other.take();
542 while let Some(mut n) = rest {
543 rest = n.other.take();
544 chain.push(n);
545 }
546 let mut ins = chain.len();
548 for (i, c) in chain.iter().enumerate() {
549 if c.width <= new_clone.width {
550 ins = i;
551 break;
552 }
553 }
554 chain.insert(ins, new_clone);
555 let mut new_head = chain.remove(0);
557 let mut tail_ptr: *mut Option<Box<cdstr>> = &mut new_head.other;
558 for entry in chain {
559 unsafe {
560 *tail_ptr = Some(entry);
561 let nxt = &mut (*tail_ptr).as_mut().unwrap().other;
562 tail_ptr = nxt as *mut _;
563 }
564 }
565 gs = new_head;
566 gp = g_node.other.as_deref();
567 }
568
569 let mut col = 0usize;
571 let mut walker = Some(gs.as_ref());
572 while let Some(g) = walker {
573 if col < wids.len() && g.width > wids[col] {
574 wids[col] = g.width;
575 }
576 col += 1;
577 walker = g.other.as_deref();
578 }
579
580 prep_lines.push(gs);
581 str_iter = node.next.as_deref();
582 }
583 set = s.next.as_deref();
584 }
585
586 let mut gprew = 0i32;
588 for w in &wids {
589 gprew += w + CM_SPACE;
590 }
591 st.gprew = gprew;
592
593 if gprew > maxmlen && maxglen > 1 {
595 let _ = swidth;
596 return 1;
597 }
598
599 for line in prep_lines.iter_mut() {
601 let s = line.str.clone().unwrap_or_default();
602 line.sortstr = Some(crate::ported::utils::unmeta(&s));
603 }
604
605 let want_sort = {
609 drop(st);
610 let r = cd_groups_want_sorting();
611 st = cd_state.lock().unwrap();
612 r
613 };
614 if want_sort != 0 {
615 prep_lines.sort_by(|a, b| cd_sort(a, b)); }
618
619 let mut i = 0usize;
621 while i + 1 < prep_lines.len() {
622 let strp_desc = prep_lines[i].desc.clone().unwrap_or_default();
623 let next_desc = prep_lines[i + 1].desc.clone().unwrap_or_default();
624 if strp_desc == next_desc {
625 i += 1;
626 continue;
627 }
628 let mut found: Option<usize> = None;
630 for j in i + 2..prep_lines.len() {
631 if prep_lines[j].desc.clone().unwrap_or_default() == strp_desc {
632 found = Some(j);
633 break;
634 }
635 }
636 if let Some(j) = found {
637 let entry = prep_lines.remove(j);
638 prep_lines.insert(i + 1, entry);
639 }
640 i += 1;
641 }
642
643 let preplines = prep_lines.len();
644
645 if preplines > 0 {
648 let mut expl_head: Option<Box<cdstr>> = None;
649 let mut tail_ptr: *mut Option<Box<cdstr>> = &mut expl_head;
650 for line in &prep_lines {
651 let header = Box::new(cdstr {
652 next: None,
653 str: line.str.clone(),
654 desc: line.desc.clone(),
655 r#match: line.r#match.clone(),
656 sortstr: line.sortstr.clone(),
657 len: line.len,
658 width: line.width,
659 other: None,
660 kind: line.kind,
661 set: line.set,
662 run: None,
663 });
664 unsafe {
665 *tail_ptr = Some(header);
666 let nxt = &mut (*tail_ptr).as_mut().unwrap().run;
667 tail_ptr = nxt as *mut _;
668 }
669 }
670 let expl_run = Box::new(cdrun {
672 next: None,
673 r#type: CRT_EXPL,
674 strs: expl_head,
675 count: preplines as i32,
676 });
677 let mut grps: Vec<Option<Box<cdstr>>> = prep_lines.into_iter().map(Some).collect();
683
684 for line_opt in grps.iter_mut() {
685 if let Some(line) = line_opt.take() {
686 let mut owned = *line;
687 let next_col = owned.other.take();
688 owned.run = None;
689 let spec_run = Box::new(cdrun {
690 next: None,
691 r#type: CRT_SPEC,
692 strs: Some(Box::new(owned)),
693 count: 1,
694 });
695 new_runs.push(spec_run);
696 *line_opt = next_col;
697 }
698 }
699
700 for _col in 1..maxg {
702 let mut dummy_count = 0i32;
703 for line_opt in grps.iter_mut() {
704 if let Some(line) = line_opt.take() {
705 if dummy_count > 0 {
707 new_runs.push(Box::new(cdrun {
708 next: None,
709 r#type: CRT_DUMMY,
710 strs: None,
711 count: dummy_count,
712 }));
713 dummy_count = 0;
714 }
715 let mut owned = *line;
716 let next_col = owned.other.take();
717 owned.run = None;
718 new_runs.push(Box::new(cdrun {
719 next: None,
720 r#type: CRT_SPEC,
721 strs: Some(Box::new(owned)),
722 count: 1,
723 }));
724 *line_opt = next_col;
725 } else {
726 dummy_count += 1;
727 }
728 }
729 if dummy_count > 0 {
730 new_runs.push(Box::new(cdrun {
732 next: None,
733 r#type: CRT_DUMMY,
734 strs: None,
735 count: dummy_count,
736 }));
737 }
738 }
739
740 new_runs.push(expl_run);
742 }
743
744 let mut set = st.sets.as_deref();
747 while let Some(s) = set {
748 let mut head: Option<Box<cdstr>> = None;
749 let mut tail_ptr: *mut Option<Box<cdstr>> = &mut head;
750 let mut count = 0i32;
751 let mut str_iter = s.strs.as_deref();
752 while let Some(node) = str_iter {
753 if node.kind == 0 && node.desc.is_none() {
754 let clone = Box::new(cdstr {
755 next: None,
756 str: node.str.clone(),
757 desc: None,
758 r#match: node.r#match.clone(),
759 sortstr: node.sortstr.clone(),
760 len: node.len,
761 width: node.width,
762 other: None,
763 kind: 0,
764 set: node.set,
765 run: None,
766 });
767 unsafe {
768 *tail_ptr = Some(clone);
769 let nxt = &mut (*tail_ptr).as_mut().unwrap().run;
770 tail_ptr = nxt as *mut _;
771 }
772 count += 1;
773 }
774 str_iter = node.next.as_deref();
775 }
776 if count > 0 {
777 new_runs.push(Box::new(cdrun {
778 next: None,
779 r#type: CRT_SIMPLE,
780 strs: head,
781 count,
782 }));
783 }
784 set = s.next.as_deref();
785 }
786 } else if st.showd != 0 {
787 let mut set = st.sets.as_deref();
790 while let Some(s) = set {
791 if s.desc > 0 {
792 let mut head: Option<Box<cdstr>> = None;
794 let mut tail: *mut Option<Box<cdstr>> = &mut head;
795 let mut str_iter = s.strs.as_deref();
796 while let Some(st_node) = str_iter {
797 if st_node.desc.is_some() {
798 let clone = Box::new(cdstr {
799 next: None,
800 str: st_node.str.clone(),
801 desc: st_node.desc.clone(),
802 r#match: st_node.r#match.clone(),
803 sortstr: st_node.sortstr.clone(),
804 len: st_node.len,
805 width: st_node.width,
806 other: None,
807 kind: st_node.kind,
808 set: st_node.set,
809 run: None,
810 });
811 unsafe {
812 *tail = Some(clone);
813 let nxt = &mut (*tail).as_mut().unwrap().run;
814 tail = nxt as *mut _;
815 }
816 }
817 str_iter = st_node.next.as_deref();
818 }
819 new_runs.push(Box::new(cdrun {
820 next: None,
821 r#type: CRT_DESC,
822 strs: head,
823 count: s.desc,
824 }));
825 }
826 if s.desc != s.count {
827 let mut head: Option<Box<cdstr>> = None;
829 let mut tail: *mut Option<Box<cdstr>> = &mut head;
830 let mut str_iter = s.strs.as_deref();
831 while let Some(st_node) = str_iter {
832 if st_node.desc.is_none() {
833 let clone = Box::new(cdstr {
834 next: None,
835 str: st_node.str.clone(),
836 desc: st_node.desc.clone(),
837 r#match: st_node.r#match.clone(),
838 sortstr: st_node.sortstr.clone(),
839 len: st_node.len,
840 width: st_node.width,
841 other: None,
842 kind: st_node.kind,
843 set: st_node.set,
844 run: None,
845 });
846 unsafe {
847 *tail = Some(clone);
848 let nxt = &mut (*tail).as_mut().unwrap().run;
849 tail = nxt as *mut _;
850 }
851 }
852 str_iter = st_node.next.as_deref();
853 }
854 new_runs.push(Box::new(cdrun {
855 next: None,
856 r#type: CRT_SIMPLE,
857 strs: head,
858 count: s.count - s.desc,
859 }));
860 }
861 set = s.next.as_deref();
862 }
863 } else {
864 let mut set = st.sets.as_deref();
866 while let Some(s) = set {
867 if s.count != 0 {
868 let mut head: Option<Box<cdstr>> = None;
870 let mut tail: *mut Option<Box<cdstr>> = &mut head;
871 let mut str_iter = s.strs.as_deref();
872 while let Some(st_node) = str_iter {
873 let clone = Box::new(cdstr {
874 next: None,
875 str: st_node.str.clone(),
876 desc: st_node.desc.clone(),
877 r#match: st_node.r#match.clone(),
878 sortstr: st_node.sortstr.clone(),
879 len: st_node.len,
880 width: st_node.width,
881 other: None,
882 kind: st_node.kind,
883 set: st_node.set,
884 run: None,
885 });
886 unsafe {
887 *tail = Some(clone);
888 let nxt = &mut (*tail).as_mut().unwrap().run;
889 tail = nxt as *mut _;
890 }
891 str_iter = st_node.next.as_deref();
892 }
893 new_runs.push(Box::new(cdrun {
894 next: None,
895 r#type: CRT_SIMPLE,
896 strs: head,
897 count: s.count,
898 }));
899 }
900 set = s.next.as_deref();
901 }
902 }
903
904 let mut head: Option<Box<cdrun>> = None;
906 for run in new_runs.into_iter().rev() {
907 let mut run = run;
908 run.next = head;
909 head = Some(run);
910 }
911 st.runs = head;
912 0 }
914
915pub fn cd_arrcat(a: &[String], b: &[String]) -> Vec<String> {
934 let mut r: Vec<String> = Vec::with_capacity(a.len() + b.len()); for s in a {
940 r.push(ztrdup(s)); }
943 for s in b {
944 r.push(ztrdup(s)); }
947 r }
950
951pub fn cd_init(
962 nam: &str,
963 hide: &str,
964 mlen: &str,
965 sep: &str, opts: &[String],
967 args: &[String],
968 disp: i32,
969) -> i32 {
970 if cd_parsed.load(Ordering::Relaxed) != 0 {
972 let mut st = cd_state.lock().unwrap();
973 st.sep = None;
974 freecdsets(st.sets.take());
975 cd_parsed.store(0, Ordering::Relaxed);
976 }
977
978 {
980 let mut st = cd_state.lock().unwrap();
981 st.sep = Some(sep.to_string());
982 st.slen = sep.len() as i32;
983 st.swidth = niceztrlen(sep) as i32;
984 st.sets = None;
985 st.showd = disp;
986 st.maxg = 0;
987 st.groups = 0;
988 st.descs = 0;
989 st.maxmlen = mlen.parse::<i32>().unwrap_or(0);
990 st.premaxw = 0;
991 let cols = adjustcolumns() as i32;
992 let itmp = cols - st.swidth - 4; if st.maxmlen > itmp {
994 st.maxmlen = itmp;
995 }
996 if st.maxmlen < 4 {
997 st.maxmlen = 4;
998 }
999 }
1000
1001 let mut idx = 0usize;
1003 let grp = if args.first().map(|s| s.as_str()) == Some("-g") {
1004 idx = 1;
1005 true
1006 } else {
1007 false
1008 };
1009
1010 let mut sets_collected: Vec<Box<cdset>> = Vec::new();
1012 while idx < args.len() {
1013 let arg = &args[idx];
1014 let Some(mat_arr) = get_user_var(Some(arg.as_str())) else {
1015 zwarnnam(nam, &format!("invalid argument: {}", arg));
1017 let mut st = cd_state.lock().unwrap();
1018 st.sep = None;
1019 freecdsets(st.sets.take());
1020 return 1;
1021 };
1022 idx += 1;
1023
1024 let mut strs_vec: Vec<Box<cdstr>> = Vec::new();
1026 for entry in &mat_arr {
1027 let bytes = entry.as_bytes();
1028 let mut p = 0usize;
1029 while p < bytes.len() && bytes[p] != b':' {
1030 if bytes[p] == b'\\' && p + 1 < bytes.len() {
1032 p += 1;
1033 }
1034 p += 1;
1035 }
1036 let (match_part, desc_part) = if p < bytes.len() {
1037 let m = std::str::from_utf8(&bytes[..p]).unwrap_or("");
1038 let d = std::str::from_utf8(&bytes[p + 1..]).unwrap_or("");
1039 (rembslash(m), Some(rembslash(d)))
1040 } else {
1041 (rembslash(entry), None)
1042 };
1043 let str_s = match_part.clone();
1044 let mut new_str = Box::new(cdstr::default());
1045 new_str.str = Some(str_s.clone());
1046 new_str.r#match = Some(str_s.clone());
1047 new_str.desc = desc_part;
1048 new_str.len = str_s.len() as i32;
1049 new_str.width = niceztrlen(&str_s) as i32;
1050 new_str.kind = 0;
1051 strs_vec.push(new_str);
1052 }
1053
1054 if idx < args.len() && !args[idx].starts_with('-') {
1056 let Some(match_arr) = get_user_var(Some(args[idx].as_str())) else {
1057 zwarnnam(nam, &format!("invalid argument: {}", args[idx]));
1058 let mut st = cd_state.lock().unwrap();
1059 st.sep = None;
1060 freecdsets(st.sets.take());
1061 return 1;
1062 };
1063 for (i, m) in match_arr.iter().enumerate() {
1064 if i < strs_vec.len() {
1065 strs_vec[i].r#match = Some(m.clone());
1066 }
1067 }
1068 idx += 1;
1069 }
1070
1071 if !hide.is_empty() {
1073 let hb = hide.as_bytes();
1074 let double = hb.len() > 1;
1075 for s in strs_vec.iter_mut() {
1076 if let Some(cur) = s.str.clone() {
1077 let mut bytes = cur.into_bytes();
1078 if double && bytes.len() >= 2 && bytes[0] == b'-' && bytes[1] == b'-' {
1079 bytes.drain(0..2); } else if !bytes.is_empty() && (bytes[0] == b'-' || bytes[0] == b'+') {
1081 bytes.drain(0..1); }
1083 s.str = String::from_utf8(bytes).ok();
1084 }
1085 }
1086 }
1087
1088 let opt_start = idx;
1090 while idx < args.len()
1091 && !(args[idx].as_bytes().len() == 2
1092 && args[idx].as_bytes()[0] == b'-'
1093 && args[idx].as_bytes()[1] == b'-')
1094 {
1095 idx += 1;
1096 }
1097 let per_set: &[String] = &args[opt_start..idx];
1098 let combined = cd_arrcat(per_set, opts);
1099 if idx < args.len() {
1100 idx += 1;
1101 } let mut strs_head: Option<Box<cdstr>> = None;
1105 for s in strs_vec.into_iter().rev() {
1106 let mut s = s;
1107 s.next = strs_head;
1108 strs_head = Some(s);
1109 }
1110 let mut set = Box::new(cdset::default());
1111 set.opts = Some(combined);
1112 set.strs = strs_head;
1113 sets_collected.push(set);
1114 }
1115
1116 {
1118 let mut head: Option<Box<cdset>> = None;
1119 for s in sets_collected.into_iter().rev() {
1120 let mut s = s;
1121 s.next = head;
1122 head = Some(s);
1123 }
1124 cd_state.lock().unwrap().sets = head;
1125 }
1126
1127 if disp != 0 && grp {
1129 let cols = adjustcolumns() as i32;
1130 let mut mg = cols;
1131 loop {
1133 cd_group(mg);
1134 mg = {
1135 let st = cd_state.lock().unwrap();
1136 st.maxg - 1
1137 };
1138 cd_calc();
1139 if cd_prep() == 0 || mg <= 0 {
1140 break;
1141 }
1142 }
1143 } else {
1144 cd_calc();
1145 cd_prep();
1146 }
1147 cd_parsed.store(1, Ordering::Relaxed); 0
1149}
1150
1151pub fn cd_arrdup(a: &[String]) -> Vec<String> {
1154 a.to_vec()
1156}
1157
1158pub fn cd_get(params: &[String]) -> i32 {
1168 let run_opt = {
1172 let mut st = cd_state.lock().unwrap();
1173 st.runs.take().map(|mut r| {
1174 let next = r.next.take();
1175 st.runs = next;
1176 r
1177 })
1178 };
1179 let Some(run) = run_opt else {
1180 return 1;
1181 };
1182
1183 let mut mats: Vec<String> = Vec::new();
1184 let mut dpys: Vec<String> = Vec::new();
1185 let mut opts: Vec<String> = Vec::new();
1186 let mut csl: String = String::new();
1187
1188 let rtype = run.r#type;
1189
1190 let mut walk_run = |head: &Option<Box<cdstr>>, mut f: Box<dyn FnMut(&cdstr)>| {
1192 let mut cur = head.as_deref();
1193 while let Some(s) = cur {
1194 f(s);
1195 cur = s.run.as_deref();
1196 }
1197 };
1198
1199 if rtype == CRT_SIMPLE {
1200 let head_opts = run
1202 .strs
1203 .as_deref()
1204 .map(|s| {
1205 let st = cd_state.lock().unwrap();
1206 let mut set_iter = st.sets.as_deref();
1209 let mut found: Option<Vec<String>> = None;
1210 let mut idx_count = 0usize;
1211 while let Some(set) = set_iter {
1212 if idx_count == s.set {
1213 found = set.opts.clone();
1214 break;
1215 }
1216 idx_count += 1;
1217 set_iter = set.next.as_deref();
1218 }
1219 found.unwrap_or_default()
1220 })
1221 .unwrap_or_default();
1222 walk_run(
1223 &run.strs,
1224 Box::new(|s| {
1225 mats.push(s.r#match.clone().unwrap_or_default());
1227 dpys.push(
1228 s.str
1229 .clone()
1230 .or_else(|| s.r#match.clone())
1231 .unwrap_or_default(),
1232 );
1233 }),
1234 );
1235 let groups_flag = cd_state.lock().unwrap().groups;
1236 opts = if groups_flag != 0 {
1237 let mut filtered: Vec<String> = Vec::new();
1240 let mut skip_next = false;
1241 for o in head_opts.iter() {
1242 if skip_next {
1243 skip_next = false;
1244 continue;
1245 }
1246 if o.starts_with("-X") {
1247 if o.len() == 2 {
1249 skip_next = true;
1250 } continue;
1252 }
1253 filtered.push(o.clone());
1254 }
1255 filtered
1256 } else {
1257 head_opts
1258 };
1259 } else if rtype == CRT_DESC {
1260 let st_snapshot = {
1262 let st = cd_state.lock().unwrap();
1263 (
1264 st.pre,
1265 st.suf,
1266 st.premaxw,
1267 st.slen,
1268 st.swidth,
1269 st.sep.clone(),
1270 adjustcolumns() as i32,
1271 )
1272 };
1273 let (cd_pre, _cd_suf, cd_premaxw, _cd_slen, cd_swidth, cd_sep, cols) = st_snapshot;
1274 let sep_str = cd_sep.unwrap_or_default();
1275 walk_run(
1276 &run.strs,
1277 Box::new(|s| {
1278 let str_s = s.str.clone().unwrap_or_default();
1280 let desc_s = s.desc.clone().unwrap_or_default();
1281 let mut buf =
1282 String::with_capacity((cd_pre + cd_premaxw + cd_swidth + 16) as usize);
1283 buf.push_str(&str_s);
1285 let pad = (cd_premaxw - s.width + CM_SPACE).max(0) as usize;
1287 for _ in 0..pad {
1288 buf.push(' ');
1289 }
1290
1291 let mut remw = cols - cd_premaxw - cd_swidth - 3;
1293 while remw < 0 && cols > 0 {
1294 remw += cols;
1295 }
1296 if (sep_str.len() as i32) < remw {
1297 buf.push_str(&sep_str);
1299 remw -= sep_str.len() as i32;
1300 let dw = niceztrlen(&desc_s) as i32;
1301 if dw <= remw {
1302 buf.push_str(&desc_s);
1303 } else {
1304 let mut w_used = 0i32;
1307 for ch in desc_s.chars() {
1308 let cw = niceztrlen(&ch.to_string()) as i32;
1309 if w_used + cw > remw {
1310 break;
1311 }
1312 buf.push(ch);
1313 w_used += cw;
1314 }
1315 }
1316 }
1317 mats.push(s.r#match.clone().unwrap_or_default()); dpys.push(buf);
1319 }),
1320 );
1321 let head_opts = run
1323 .strs
1324 .as_deref()
1325 .map(|s| {
1326 let st = cd_state.lock().unwrap();
1327 let mut set_iter = st.sets.as_deref();
1328 let mut found: Option<Vec<String>> = None;
1329 let mut idx_count = 0usize;
1330 while let Some(set) = set_iter {
1331 if idx_count == s.set {
1332 found = set.opts.clone();
1333 break;
1334 }
1335 idx_count += 1;
1336 set_iter = set.next.as_deref();
1337 }
1338 found.unwrap_or_default()
1339 })
1340 .unwrap_or_default();
1341 opts = std::iter::once("-l".to_string()).chain(head_opts).collect();
1342 } else if rtype == CRT_SPEC {
1343 let s = run.strs.as_deref();
1345 if let Some(s) = s {
1346 mats.push(s.r#match.clone().unwrap_or_default());
1347 dpys.push(s.str.clone().unwrap_or_default());
1348 }
1349 let head_opts = s
1351 .map(|s| {
1352 let st = cd_state.lock().unwrap();
1353 let mut set_iter = st.sets.as_deref();
1354 let mut found: Option<Vec<String>> = None;
1355 let mut idx_count = 0usize;
1356 while let Some(set) = set_iter {
1357 if idx_count == s.set {
1358 found = set.opts.clone();
1359 break;
1360 }
1361 idx_count += 1;
1362 set_iter = set.next.as_deref();
1363 }
1364 found.unwrap_or_default()
1365 })
1366 .unwrap_or_default();
1367 let mut new_opts: Vec<String> = head_opts.clone();
1368 let mut found_jv = false;
1369 for i in 1..new_opts.len() {
1373 if new_opts[i].starts_with("-J") || new_opts[i].starts_with("-V") {
1374 let rest = new_opts[i][2..].to_string();
1375 new_opts[i] = format!("-2V{}", rest);
1376 found_jv = true;
1377 break;
1378 }
1379 }
1380 if !found_jv {
1381 new_opts.insert(0, "-2V-default-".to_string()); }
1383 opts = new_opts;
1384 csl = "packed".to_string();
1385 } else if rtype == CRT_DUMMY {
1386 let head_opts = run
1389 .strs
1390 .as_deref()
1391 .map(|s| {
1392 let st = cd_state.lock().unwrap();
1393 let mut set_iter = st.sets.as_deref();
1394 let mut found: Option<Vec<String>> = None;
1395 let mut idx_count = 0usize;
1396 while let Some(set) = set_iter {
1397 if idx_count == s.set {
1398 found = set.opts.clone();
1399 break;
1400 }
1401 idx_count += 1;
1402 set_iter = set.next.as_deref();
1403 }
1404 found.unwrap_or_default()
1405 })
1406 .unwrap_or_default();
1407 opts = std::iter::once(format!("-E{}", run.count))
1408 .chain(head_opts)
1409 .collect();
1410 csl = "packed".to_string();
1411 } else if rtype == CRT_EXPL {
1412 let st_snapshot = {
1414 let st = cd_state.lock().unwrap();
1415 (
1416 st.suf,
1417 st.slen,
1418 st.swidth,
1419 st.gprew,
1420 st.sep.clone(),
1421 adjustcolumns() as i32,
1422 )
1423 };
1424 let (_cd_suf, _cd_slen, cd_swidth, cd_gprew, cd_sep, cols) = st_snapshot;
1425 let sep_str = cd_sep.unwrap_or_default();
1426 let count = run.count;
1427
1428 walk_run(
1429 &run.strs,
1430 Box::new(|s| {
1431 let next_desc = s.run.as_deref().and_then(|n| n.desc.clone());
1434 if next_desc.is_some() && next_desc == s.desc {
1435 dpys.push(String::new());
1436 return;
1437 }
1438 let mut buf = String::new();
1439 buf.push_str(&sep_str);
1440 let mut remw = cols - cd_gprew - cd_swidth - CM_SPACE;
1441 let desc_s = s.desc.clone().unwrap_or_default();
1442 let dw = niceztrlen(&desc_s) as i32;
1443 if dw <= remw {
1444 buf.push_str(&desc_s);
1446 remw -= dw;
1447 } else {
1448 for ch in desc_s.chars() {
1449 let cw = niceztrlen(&ch.to_string()) as i32;
1450 if cw > remw {
1451 break;
1452 }
1453 buf.push(ch);
1454 remw -= cw;
1455 }
1456 }
1457 while remw > 0 {
1458 buf.push(' ');
1460 remw -= 1;
1461 }
1462 dpys.push(buf);
1463 }),
1464 );
1465 let head_opts = run
1467 .strs
1468 .as_deref()
1469 .map(|s| {
1470 let st = cd_state.lock().unwrap();
1471 let mut set_iter = st.sets.as_deref();
1472 let mut found: Option<Vec<String>> = None;
1473 let mut idx_count = 0usize;
1474 while let Some(set) = set_iter {
1475 if idx_count == s.set {
1476 found = set.opts.clone();
1477 break;
1478 }
1479 idx_count += 1;
1480 set_iter = set.next.as_deref();
1481 }
1482 found.unwrap_or_default()
1483 })
1484 .unwrap_or_default();
1485 opts = std::iter::once(format!("-E{}", count))
1486 .chain(head_opts)
1487 .collect();
1488 csl = "packed".to_string();
1489 }
1490
1491 if params.len() >= 4 {
1493 setsparam(¶ms[0], &csl);
1494 setaparam(¶ms[1], opts);
1495 setaparam(¶ms[2], mats);
1496 setaparam(¶ms[3], dpys);
1497 }
1498 0 }
1500
1501pub fn bin_compdescribe(
1510 nam: &str,
1511 args: &[String], _ops: &options,
1513 _func: i32,
1514) -> i32 {
1515 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
1516 zwarnnam(nam, "can only be called from completion function");
1518 return 1;
1519 }
1520 if args.is_empty() {
1521 return 1;
1522 }
1523 let a0 = args[0].as_bytes();
1524 if a0.len() != 2 || a0[0] != b'-' {
1526 zwarnnam(nam, &format!("invalid argument: {}", args[0]));
1527 return 1;
1528 }
1529 let n = args.len() as i32;
1530
1531 match a0[1] {
1532 b'i' => {
1533 if n < 3 {
1535 zwarnnam(nam, "not enough arguments");
1536 return 1;
1537 }
1538 cd_init(nam, &args[1], &args[2], "", &[], &args[3..], 0) }
1540 b'I' => {
1541 if n < 6 {
1543 zwarnnam(nam, "not enough arguments");
1544 return 1;
1545 }
1546 let opts_arr: Vec<String> = paramtab()
1548 .read()
1549 .ok()
1550 .and_then(|tab| tab.get(&args[4]).and_then(|pm| pm.u_arr.clone()))
1551 .unwrap_or_default();
1552 if opts_arr.is_empty()
1553 && paramtab()
1554 .read()
1555 .ok()
1556 .map_or(true, |tab| tab.get(&args[4]).is_none())
1557 {
1558 zwarnnam(nam, &format!("unknown parameter: {}", args[4]));
1559 return 1;
1560 }
1561 cd_init(
1562 nam,
1563 &args[1],
1564 &args[2],
1565 &args[3],
1566 &opts_arr, &args[5..],
1568 1,
1569 )
1570 }
1571 b'g' => {
1572 if cd_parsed.load(Ordering::Relaxed) == 0 {
1574 zwarnnam(nam, "no parsed state"); return 1;
1577 }
1578 if n != 5 {
1579 zwarnnam(
1580 nam,
1581 if n < 5 {
1582 "not enough arguments"
1583 } else {
1584 "too many arguments"
1585 },
1586 );
1587 return 1;
1588 }
1589 cd_get(&args[1..]) }
1591 _ => {
1592 zwarnnam(nam, &format!("invalid option: {}", args[0]));
1593 1
1594 }
1595 }
1596}
1597
1598pub type Cadef = Box<cadef>; #[derive(Debug, Default, Clone)]
1610#[allow(non_camel_case_types)]
1611pub struct cadef {
1612 pub next: Option<Box<cadef>>, pub snext: Option<Box<cadef>>, pub opts: Option<Box<caopt>>, pub nopts: i32, pub ndopts: i32, pub nodopts: i32, pub args: Option<Box<caarg>>, pub rest: Option<Box<caarg>>, pub defs: Option<Vec<String>>, pub ndefs: i32, pub lastt: i64, pub single: Option<Vec<Option<Box<caopt>>>>, pub r#match: Option<String>, pub argsactive: i32, pub set: Option<String>, pub flags: i32, pub nonarg: Option<String>, }
1631pub type Caopt = Box<caopt>; #[derive(Debug, Default, Clone)]
1637#[allow(non_camel_case_types)]
1638pub struct caopt {
1639 pub next: Option<Box<caopt>>, pub name: Option<String>, pub descr: Option<String>, pub xor: Option<Vec<String>>, pub r#type: i32, pub args: Option<Box<caarg>>, pub active: i32, pub num: i32, pub gsname: Option<String>, pub not: i32, }
1651pub type Caarg = Box<caarg>; #[derive(Debug, Default, Clone)]
1657#[allow(non_camel_case_types)]
1658pub struct caarg {
1659 pub next: Option<Box<caarg>>, pub descr: Option<String>, pub xor: Option<Vec<String>>, pub action: Option<String>, pub r#type: i32, pub end: Option<String>, pub opt: Option<String>, pub num: i32, pub min: i32, pub direct: i32, pub active: i32, pub gsname: Option<String>, }
1673
1674pub const CDF_SEP: i32 = 1; pub const CAO_NEXT: i32 = 1; pub const CAO_DIRECT: i32 = 2; pub const CAO_ODIRECT: i32 = 3; pub const CAO_EQUAL: i32 = 4; pub const CAO_OEQUAL: i32 = 5; pub const CAA_NORMAL: i32 = 1; pub const CAA_OPT: i32 = 2; pub const CAA_REST: i32 = 3; pub const CAA_RARGS: i32 = 4; pub const CAA_RREST: i32 = 5; pub const MAX_CACACHE: usize = 8; pub static cadef_cache: std::sync::Mutex<[Option<Box<cadef>>; MAX_CACACHE]> = std::sync::Mutex::new([const { None }; MAX_CACACHE]);
1720
1721pub fn arrcmp(a: Option<&[String]>, b: Option<&[String]>) -> i32 {
1726 match (a, b) {
1728 (None, None) => 1, (None, _) | (_, None) => 0, (Some(a), Some(b)) => {
1731 let len = a.len().min(b.len());
1734 for i in 0..len {
1735 if a[i] != b[i] {
1736 return 0;
1737 } }
1739 if a.len() == b.len() {
1741 1
1742 } else {
1743 0
1744 }
1745 }
1746 }
1747}
1748
1749pub fn freecaargs(mut a: Option<Box<caarg>>) {
1755 while let Some(mut node) = a {
1757 a = node.next.take(); node.descr = None; node.xor = None; node.action = None; node.end = None; node.opt = None; drop(node); }
1768}
1769
1770pub fn freecadef(mut d: Option<Box<cadef>>) {
1774 while let Some(mut node) = d {
1776 d = node.snext.take(); node.r#match = None;
1780 node.set = None;
1781 node.defs = None;
1782
1783 let mut p = node.opts.take();
1786 while let Some(mut popt) = p {
1787 p = popt.next.take();
1788 popt.name = None;
1789 popt.descr = None;
1790 popt.xor = None;
1791 freecaargs(popt.args.take()); drop(popt); }
1794 freecaargs(node.args.take()); freecaargs(node.rest.take()); node.nonarg = None; node.single = None; drop(node); }
1800}
1801
1802pub fn rembslashcolon(s: &str) -> String {
1822 let bytes = s.as_bytes(); let mut out = Vec::<u8>::with_capacity(bytes.len());
1825 let mut i = 0;
1826 while i < bytes.len() {
1827 let drop = bytes[i] == b'\\' && i + 1 < bytes.len() && bytes[i + 1] == b':';
1830 if !drop {
1831 out.push(bytes[i]); }
1833 i += 1; }
1835 String::from_utf8(out).unwrap_or_default() }
1838
1839pub fn bslashcolon(s: &str) -> String {
1859 let bytes = s.as_bytes(); let mut out = Vec::<u8>::with_capacity(2 * bytes.len() + 1);
1862 for &b in bytes {
1863 if b == b':' {
1865 out.push(b'\\'); }
1868 out.push(b); }
1870 String::from_utf8(out).unwrap_or_default() }
1873
1874pub fn single_index(pre: u8, opt: u8) -> i32 {
1891 if opt <= 0x20 || opt > 0x7e {
1893 return -1; }
1896 let off: i32 = if pre == b'-' { -0x21 } else { 94 - 0x21 };
1898 (opt as i32) + off
1899}
1900
1901pub fn parse_caarg(
1910 mult: i32,
1911 atype: i32,
1912 num: i32,
1913 opt: i32, oname: Option<&str>,
1915 bytes: &[u8],
1916 idx: &mut usize,
1917 set: Option<&str>,
1918) -> Box<caarg> {
1919 let mut ret = Box::new(caarg::default());
1920 ret.num = num; ret.min = num - opt; ret.r#type = atype; ret.opt = oname.map(|s| s.to_string()); ret.direct = 0; ret.gsname = set.map(|s| s.to_string()); let n = bytes.len();
1928
1929 let d_start = *idx;
1931 while *idx < n && bytes[*idx] != b':' {
1932 if bytes[*idx] == b'\\' && *idx + 1 < n {
1933 *idx += 1;
1934 }
1935 *idx += 1;
1936 }
1937 let has_sav = *idx < n;
1938 let descr_slice = &bytes[d_start..*idx];
1939 let descr_str = std::str::from_utf8(descr_slice).unwrap_or("");
1940 ret.descr = Some(rembslashcolon(descr_str)); if has_sav {
1943 if mult != 0 {
1945 *idx += 1;
1948 let a_start = *idx;
1949 while *idx < n && bytes[*idx] != b':' {
1950 if bytes[*idx] == b'\\' && *idx + 1 < n {
1951 *idx += 1;
1952 }
1953 *idx += 1;
1954 }
1955 let action_slice = &bytes[a_start..*idx];
1956 let action_str = std::str::from_utf8(action_slice).unwrap_or("");
1957 ret.action = Some(rembslashcolon(action_str)); } else {
1959 let action_slice = &bytes[*idx + 1..];
1962 let action_str = std::str::from_utf8(action_slice).unwrap_or("");
1963 ret.action = Some(rembslashcolon(action_str));
1964 *idx = n;
1965 }
1966 } else {
1967 ret.action = Some(String::new()); }
1970 ret
1974}
1975
1976pub fn alloc_cadef(
2009 args: Option<&[String]>,
2010 single: i32,
2011 matchstr: &str, nonarg: Option<&str>,
2013 flags: i32,
2014) -> Box<cadef> {
2015 Box::new(cadef {
2016 next: None, snext: None, opts: None, args: None, rest: None, nonarg: nonarg.map(|s| s.to_string()), defs: args.map(|a| a.to_vec()), ndefs: args.map_or(0, |a| a.len() as i32), nopts: 0, ndopts: 0, nodopts: 0, lastt: {
2028 use std::time::{SystemTime, UNIX_EPOCH};
2030 SystemTime::now()
2031 .duration_since(UNIX_EPOCH)
2032 .map(|d| d.as_secs() as i64)
2033 .unwrap_or(0)
2034 },
2035 set: None, single: if single != 0 {
2039 Some((0..188).map(|_| None).collect())
2040 } else {
2041 None
2042 },
2043 r#match: Some(matchstr.to_string()), argsactive: 0,
2045 flags, })
2047}
2048
2049pub fn set_cadef_opts(def: &mut cadef) {
2057 let mut xnum: i32 = 0;
2059 let mut argp = def.args.as_deref_mut(); while let Some(node) = argp {
2061 if node.direct == 0 {
2063 node.min = node.num - xnum; }
2066 if node.r#type == CAA_OPT {
2067 xnum += 1; }
2070 argp = node.next.as_deref_mut(); }
2072}
2073
2074pub fn parse_cadef(nam: &str, args: &[String]) -> Option<Box<cadef>> {
2080 if args.is_empty() {
2083 return None; }
2085
2086 let orig_args = args;
2087 let mut idx = 0usize;
2088 let mut single: i32 = 0;
2089 let mut flags: i32 = 0;
2090 let mut match_spec: String = "r:|[_-]=* r:|=*".to_string(); let mut nonarg: Option<String> = None;
2092
2093 let (adpre, adsuf): (Option<String>, Option<String>) = {
2096 let first = args[0].as_bytes();
2097 let mut split_at: Option<usize> = None;
2098 let mut i = 0usize;
2099 while i + 1 < first.len() {
2100 if first[i] == b'%' && first[i + 1] == b'd' {
2101 split_at = Some(i);
2102 break;
2103 }
2104 i += 1;
2105 }
2106 if let Some(at) = split_at {
2107 let pre = String::from_utf8_lossy(&first[..at]).into_owned();
2108 let suf = String::from_utf8_lossy(&first[at + 2..]).into_owned();
2109 (Some(pre), Some(suf))
2110 } else {
2111 (None, None)
2112 }
2113 };
2114
2115 idx += 1; while idx < args.len() {
2119 let p = &args[idx];
2120 let bytes = p.as_bytes();
2121 if bytes.len() < 2 || bytes[0] != b'-' {
2122 break;
2124 }
2125 let cluster = &bytes[1..];
2126 let mut ok = true;
2127 for (i, &c) in cluster.iter().enumerate() {
2128 match c {
2129 b's' => single = 1, b'S' => flags |= CDF_SEP, b'A' => {
2132 if i + 1 < cluster.len() {
2134 nonarg = Some(String::from_utf8_lossy(&cluster[i + 1..]).into_owned());
2136 } else if idx + 1 < args.len() {
2137 nonarg = Some(args[idx + 1].clone());
2139 idx += 1;
2140 } else {
2141 ok = false;
2142 }
2143 break;
2144 }
2145 b'M' => {
2146 if i + 1 < cluster.len() {
2148 match_spec = String::from_utf8_lossy(&cluster[i + 1..]).into_owned();
2150 } else if idx + 1 < args.len() {
2151 match_spec = args[idx + 1].clone();
2153 idx += 1;
2154 } else {
2155 ok = false;
2156 }
2157 break;
2158 }
2159 _ => {
2160 ok = false;
2161 break;
2162 }
2163 }
2164 }
2165 if !ok {
2166 break; }
2168 idx += 1; }
2170
2171 if idx < args.len() && args[idx] == ":" {
2172 idx += 1;
2174 }
2175 if idx >= args.len() {
2176 return None;
2178 }
2179
2180 let first_def = alloc_cadef(
2185 Some(orig_args),
2186 single,
2187 &match_spec,
2188 nonarg.as_deref(),
2189 flags,
2190 );
2191
2192 let mut sets: Vec<Box<cadef>> = vec![first_def];
2196 let mut opts_per_set: Vec<Vec<Box<caopt>>> = vec![Vec::new()];
2197 let mut args_per_set: Vec<Vec<Box<caarg>>> = vec![Vec::new()];
2198 let mut rest_per_set: Vec<Option<Box<caarg>>> = vec![None];
2199
2200 let sargs = idx; let mut anum: i32 = 1; let mut doset: Option<String> = None;
2203 let mut axor: Option<String> = None;
2204 let mut curset: Option<usize> = None; let mut pendset: Option<usize> = None;
2206 let mut foreignset = false;
2207
2208 'outer: loop {
2210 if idx >= args.len() {
2212 if pendset.is_none() {
2213 break 'outer;
2214 }
2215 {
2217 let cur = sets.last_mut().unwrap();
2218 let cur_args = args_per_set.last_mut().unwrap();
2219 let mut head: Option<Box<caarg>> = None;
2221 for arg_box in cur_args.drain(..).rev() {
2222 let mut a = arg_box;
2223 a.next = head;
2224 head = Some(a);
2225 }
2226 cur.args = head;
2227 set_cadef_opts(cur); let mut walk = cur.args.take();
2231 while let Some(mut node) = walk {
2232 walk = node.next.take();
2233 cur_args.push(node);
2234 }
2235 }
2236 idx = sargs; doset = None; sets.push(alloc_cadef(
2239 None,
2240 single,
2241 &match_spec, nonarg.as_deref(),
2243 flags,
2244 ));
2245 opts_per_set.push(Vec::new());
2246 args_per_set.push(Vec::new());
2247 rest_per_set.push(None);
2248 anum = 1; foreignset = false; curset = pendset; pendset = None; }
2253
2254 let arg = &args[idx];
2255 let arg_bytes = arg.as_bytes();
2256
2257 if arg_bytes == b"-" && idx + 1 < args.len() {
2259 if curset.is_some() && curset != Some(idx) {
2260 foreignset = true;
2262 if pendset.is_none() && Some(idx) > curset {
2263 pendset = Some(idx);
2265 }
2266 idx += 1; } else {
2268 foreignset = false;
2270 idx += 1;
2271 let p_str = &args[idx]; let pb = p_str.as_bytes();
2273 let l = pb.len().saturating_sub(1);
2274 let (set_name, ax) = if !pb.is_empty() && pb[0] == b'(' && pb[l] == b')' {
2276 let inner = String::from_utf8_lossy(&pb[1..l]).into_owned();
2277 (inner.clone(), Some(inner))
2278 } else {
2279 (p_str.clone(), None)
2280 };
2281 axor = ax;
2282 if set_name.is_empty() {
2283 zwarnnam(nam, "empty set name");
2285 return None;
2286 }
2287 let new_set = tricat(&set_name, "-", ""); doset = Some(new_set.clone());
2289 {
2290 let cur = sets.last_mut().unwrap();
2291 cur.set = Some(new_set);
2292 }
2293 curset = Some(idx); }
2295 idx += 1;
2296 continue; }
2298
2299 if arg_bytes == b"+" && idx + 1 < args.len() {
2301 foreignset = false; idx += 1;
2303 let p_str = &args[idx]; let pb = p_str.as_bytes();
2305 let l = pb.len().saturating_sub(1);
2306 let (group_name, ax) = if !pb.is_empty() && pb[0] == b'(' && pb[l] == b')' {
2307 let inner = String::from_utf8_lossy(&pb[1..l]).into_owned();
2308 (inner.clone(), Some(inner))
2309 } else {
2310 (p_str.clone(), None)
2311 };
2312 axor = ax;
2313 if group_name.is_empty() {
2314 zwarnnam(nam, "empty group name");
2316 return None;
2317 }
2318 doset = Some(tricat(&group_name, "-", "")); idx += 1;
2320 continue; }
2322
2323 if foreignset {
2325 idx += 1;
2326 continue;
2327 }
2328
2329 let bytes = arg_bytes;
2331 let mut p = 0usize;
2332 let mut xnum: i32 = 0; let mut not_flag = false;
2334 if p < bytes.len() && bytes[p] == b'!' {
2335 not_flag = true;
2337 p += 1;
2338 }
2339
2340 let mut xor: Option<Vec<String>> = None;
2341 if p < bytes.len() && bytes[p] == b'(' {
2342 let mut list: Vec<String> = Vec::new();
2344 let mut bad = false;
2346 'paren: loop {
2347 if p >= bytes.len() || bytes[p] == b')' {
2348 break;
2349 }
2350 p += 1; while p < bytes.len() && inblank(bytes[p]) {
2352 p += 1;
2353 } if p >= bytes.len() {
2355 bad = true;
2356 break 'paren;
2357 }
2358 if bytes[p] == b')' {
2359 break 'paren;
2360 }
2361 let q = p;
2362 p += 1;
2363 while p < bytes.len() && bytes[p] != b')' && !inblank(bytes[p]) {
2364 p += 1;
2365 }
2366 if p >= bytes.len() {
2367 bad = true;
2368 break 'paren;
2369 } let word = String::from_utf8_lossy(&bytes[q..p]).into_owned();
2371 list.push(word);
2372 xnum += 1; }
2374 if bad || p >= bytes.len() || bytes[p] != b')' {
2375 zwarnnam(nam, &format!("invalid argument: {}", arg));
2377 return None;
2378 }
2379 if doset.is_some() && axor.is_some() {
2380 xnum += 1;
2382 list.push(axor.clone().unwrap()); }
2384 xor = Some(list);
2385 p += 1; } else if doset.is_some() && axor.is_some() {
2387 xnum = 1;
2389 xor = Some(vec![axor.clone().unwrap()]);
2390 }
2391
2392 let is_opt = p < bytes.len()
2394 && (bytes[p] == b'-'
2395 || bytes[p] == b'+'
2396 || (bytes[p] == b'*'
2397 && p + 1 < bytes.len()
2398 && (bytes[p + 1] == b'-' || bytes[p + 1] == b'+')));
2399
2400 if is_opt {
2401 let mut again_iter = 0i32; let mut againp_start: Option<usize> = None;
2407 let mut p_state = p;
2408 let mut xor_state = xor;
2409 let mut xnum_state = xnum;
2410
2411 'rec: loop {
2412 let mut multi = false; if p_state < bytes.len() && bytes[p_state] == b'*' {
2414 multi = true;
2415 p_state += 1;
2416 }
2417
2418 let mut name_start: usize;
2419 let mut name_buf: Vec<u8>;
2420 let need_flip = p_state + 2 < bytes.len()
2421 && ((bytes[p_state] == b'-' && bytes[p_state + 1] == b'+')
2422 || (bytes[p_state] == b'+' && bytes[p_state + 1] == b'-'))
2423 && bytes[p_state + 2] != b':'
2424 && bytes[p_state + 2] != b'['
2425 && bytes[p_state + 2] != b'='
2426 && bytes[p_state + 2] != b'-'
2427 && bytes[p_state + 2] != b'+';
2428
2429 if need_flip {
2430 if again_iter == 0 {
2432 againp_start = Some(p_state);
2433 }
2434 name_start = p_state + 1;
2435 name_buf = bytes[name_start..].to_vec();
2436 if !name_buf.is_empty() {
2437 name_buf[0] = if again_iter != 0 { b'-' } else { b'+' };
2438 }
2439 again_iter += 1;
2440 p_state = name_start;
2441 } else {
2442 name_start = p_state;
2444 name_buf = bytes[name_start..].to_vec();
2445 if p_state + 1 < bytes.len()
2446 && bytes[p_state] == b'-'
2447 && bytes[p_state + 1] == b'-'
2448 {
2449 p_state += 1; }
2451 }
2452
2453 if p_state + 1 >= bytes.len() {
2454 zwarnnam(nam, &format!("invalid argument: {}", arg));
2456 return None;
2457 }
2458
2459 let mut np = p_state - name_start + 1;
2461 let nlen = name_buf.len();
2462 while np < nlen
2463 && name_buf[np] != b':'
2464 && name_buf[np] != b'['
2465 && !((name_buf[np] == b'-' || name_buf[np] == b'+')
2466 && np + 1 < nlen
2467 && (name_buf[np + 1] == b':' || name_buf[np + 1] == b'['))
2468 && !(name_buf[np] == b'='
2469 && np + 1 < nlen
2470 && (name_buf[np + 1] == b':'
2471 || name_buf[np + 1] == b'['
2472 || name_buf[np + 1] == b'-'))
2473 {
2474 if name_buf[np] == b'\\' && np + 1 < nlen {
2475 np += 1;
2476 }
2477 np += 1;
2478 }
2479
2480 let mut c_byte = if np < nlen { name_buf[np] } else { 0 };
2481 let opt_name_slice = &name_buf[..np];
2482 let opt_name = String::from_utf8_lossy(opt_name_slice).into_owned();
2483
2484 let mut otype = CAO_NEXT; if c_byte == b'-' {
2486 otype = CAO_DIRECT;
2488 np += 1;
2489 c_byte = if np < nlen { name_buf[np] } else { 0 };
2490 } else if c_byte == b'+' {
2491 otype = CAO_ODIRECT;
2493 np += 1;
2494 c_byte = if np < nlen { name_buf[np] } else { 0 };
2495 } else if c_byte == b'=' {
2496 otype = CAO_OEQUAL;
2498 np += 1;
2499 c_byte = if np < nlen { name_buf[np] } else { 0 };
2500 if c_byte == b'-' {
2501 otype = CAO_EQUAL; np += 1;
2503 c_byte = if np < nlen { name_buf[np] } else { 0 };
2504 }
2505 }
2506
2507 let mut descr_str: Option<String> = None;
2509 if c_byte == b'[' {
2510 np += 1;
2512 let d_start = np;
2513 while np < nlen && name_buf[np] != b']' {
2514 if name_buf[np] == b'\\' && np + 1 < nlen {
2515 np += 1;
2516 }
2517 np += 1;
2518 }
2519 if np >= nlen {
2520 zwarnnam(nam, &format!("invalid option definition: {}", arg));
2522 return None;
2523 }
2524 let d_slice = &name_buf[d_start..np];
2525 descr_str = Some(String::from_utf8_lossy(d_slice).into_owned());
2526 np += 1;
2527 c_byte = if np < nlen { name_buf[np] } else { 0 };
2528 }
2529
2530 if c_byte != 0 && c_byte != b':' {
2531 zwarnnam(nam, &format!("invalid option definition: {}", arg));
2533 return None;
2534 }
2535
2536 let clean_name = rembslashcolon(&opt_name);
2538 if !multi {
2539 let xv = xor_state.get_or_insert_with(Vec::new);
2540 if xv.len() <= xnum_state as usize {
2541 xv.resize(xnum_state as usize + 1, String::new());
2542 }
2543 xv[xnum_state as usize] = clean_name.clone();
2544 }
2545
2546 let mut oargs: Vec<Box<caarg>> = Vec::new();
2548 if c_byte == b':' {
2549 let mut oanum: i32 = 1; let mut onum: i32 = 0;
2551 while c_byte == b':' {
2552 let mut rest = 0;
2554 let mut end_str: Option<String> = None;
2555 np += 1; let atype: i32;
2557 c_byte = if np < nlen { name_buf[np] } else { 0 };
2558 if c_byte == b':' {
2559 atype = CAA_OPT;
2561 np += 1;
2562 } else if c_byte == b'*' {
2563 np += 1;
2565 if np < nlen && name_buf[np] != b':' {
2566 let end_start = np;
2568 while np < nlen && name_buf[np] != b':' {
2569 if name_buf[np] == b'\\' && np + 1 < nlen {
2570 np += 1;
2571 }
2572 np += 1;
2573 }
2574 let e_slice = &name_buf[end_start..np];
2575 end_str = Some(String::from_utf8_lossy(e_slice).into_owned());
2576 }
2577 if np >= nlen || name_buf[np] != b':' {
2578 zwarnnam(nam, &format!("invalid option definition: {}", arg));
2580 return None;
2581 }
2582 np += 1; if np < nlen && name_buf[np] == b':' {
2584 np += 1;
2586 if np < nlen && name_buf[np] == b':' {
2587 atype = CAA_RREST;
2589 np += 1;
2590 } else {
2591 atype = CAA_RARGS;
2592 }
2593 } else {
2594 atype = CAA_REST;
2595 }
2596 rest = 1;
2597 } else {
2598 atype = CAA_NORMAL;
2599 }
2600
2601 let mut oarg = parse_caarg(
2603 if rest != 0 { 0 } else { 1 },
2604 atype,
2605 oanum,
2606 onum,
2607 Some(&clean_name),
2608 &name_buf,
2609 &mut np,
2610 doset.as_deref(),
2611 );
2612 oanum += 1;
2613 if atype == CAA_OPT {
2614 onum += 1;
2615 } if let Some(end) = end_str {
2617 oarg.end = Some(end); }
2619 oargs.push(oarg);
2620
2621 if rest != 0 {
2622 break;
2623 } c_byte = if np < nlen { name_buf[np] } else { 0 }; }
2626 }
2627
2628 let mut opt_box = Box::new(caopt::default());
2630 opt_box.gsname = doset.clone(); opt_box.name = Some(clean_name.clone()); opt_box.descr = if let Some(d) = descr_str.clone() {
2633 Some(d)
2635 } else if adpre.is_some() && oargs.len() == 1 {
2636 let first_arg = &oargs[0];
2638 let d_field = first_arg.descr.as_deref().unwrap_or("");
2639 let has_visible = d_field.bytes().any(|b| !iblank(b));
2640 if has_visible {
2641 Some(tricat(
2643 adpre.as_deref().unwrap_or(""),
2644 d_field,
2645 adsuf.as_deref().unwrap_or(""),
2646 ))
2647 } else {
2648 None }
2650 } else {
2651 None
2652 };
2653 let xor_clone = if again_iter == 1 {
2654 xor_state.clone()
2656 } else {
2657 xor_state.take()
2658 };
2659 opt_box.xor = xor_clone;
2660 opt_box.r#type = otype; opt_box.not = if not_flag { 1 } else { 0 }; let mut head: Option<Box<caarg>> = None;
2665 for a in oargs.into_iter().rev() {
2666 let mut a = a;
2667 a.next = head;
2668 head = Some(a);
2669 }
2670 opt_box.args = head;
2671
2672 {
2673 let cur = sets.last_mut().unwrap();
2674 opt_box.num = cur.nopts;
2675 cur.nopts += 1; if otype == CAO_DIRECT || otype == CAO_EQUAL {
2677 cur.ndopts += 1;
2679 } else if otype == CAO_ODIRECT || otype == CAO_OEQUAL {
2680 cur.nodopts += 1;
2682 }
2683 if single != 0 {
2685 let nb = clean_name.as_bytes();
2686 if nb.len() == 2 && nb[1] != b'-' {
2687 let sidx = single_index(nb[0], nb[1]);
2688 if sidx >= 0 {
2689 if let Some(ref mut s) = cur.single {
2690 if (sidx as usize) < s.len() {
2691 s[sidx as usize] = Some(Box::new(caopt {
2692 next: None,
2693 name: opt_box.name.clone(),
2694 descr: opt_box.descr.clone(),
2695 xor: opt_box.xor.clone(),
2696 r#type: opt_box.r#type,
2697 args: None,
2698 active: 0,
2699 num: opt_box.num,
2700 gsname: opt_box.gsname.clone(),
2701 not: opt_box.not,
2702 }));
2703 }
2704 }
2705 }
2706 }
2707 }
2708 }
2709
2710 opts_per_set.last_mut().unwrap().push(opt_box);
2711
2712 if again_iter == 1 {
2713 if let Some(start) = againp_start {
2715 p_state = start;
2716 xnum_state = xnum; xor_state = xor_state.clone();
2718 continue 'rec;
2719 }
2720 }
2721 break 'rec;
2722 }
2723 } else if p < bytes.len() && bytes[p] == b'*' {
2724 if not_flag {
2726 idx += 1;
2728 continue;
2729 }
2730 p += 1; if p >= bytes.len() || bytes[p] != b':' {
2732 zwarnnam(nam, &format!("invalid rest argument definition: {}", arg));
2733 return None;
2734 }
2735 if rest_per_set.last().unwrap().is_some() {
2736 zwarnnam(nam, &format!("doubled rest argument definition: {}", arg));
2738 return None;
2739 }
2740 let mut atype = CAA_REST; p += 1; if p < bytes.len() && bytes[p] == b':' {
2743 p += 1;
2745 if p < bytes.len() && bytes[p] == b':' {
2746 atype = CAA_RREST;
2748 p += 1;
2749 } else {
2750 atype = CAA_RARGS;
2751 }
2752 }
2753 let mut rarg = parse_caarg(0, atype, -1, 0, None, bytes, &mut p, doset.as_deref()); rarg.xor = xor; *rest_per_set.last_mut().unwrap() = Some(rarg);
2756 } else {
2757 if not_flag {
2759 idx += 1;
2761 continue;
2762 }
2763 let mut direct = 0; if p < bytes.len() && idigit(bytes[p]) {
2765 direct = 1;
2767 let mut num: i32 = 0;
2768 while p < bytes.len() && idigit(bytes[p]) {
2769 num = num * 10 + (bytes[p] - b'0') as i32;
2770 p += 1;
2771 }
2772 anum = num + 1; } else {
2774 anum += 1; }
2776 if p >= bytes.len() || bytes[p] != b':' {
2777 zwarnnam(nam, &format!("invalid argument: {}", arg));
2779 return None;
2780 }
2781 let mut atype = CAA_NORMAL;
2782 p += 1; if p < bytes.len() && bytes[p] == b':' {
2784 atype = CAA_OPT;
2786 p += 1;
2787 }
2788 let mut narg =
2789 parse_caarg(0, atype, anum - 1, 0, None, bytes, &mut p, doset.as_deref()); narg.xor = xor; narg.direct = direct; let target = anum - 1;
2795 let cur_args = args_per_set.last_mut().unwrap();
2796 let mut insert_at = cur_args.len();
2797 for (i, existing) in cur_args.iter().enumerate() {
2798 if existing.num >= target {
2799 insert_at = i;
2800 break;
2801 }
2802 }
2803 if insert_at < cur_args.len() && cur_args[insert_at].num == target {
2804 zwarnnam(nam, &format!("doubled argument definition: {}", arg));
2805 return None;
2806 }
2807 cur_args.insert(insert_at, narg);
2808 }
2809
2810 idx += 1;
2811 }
2812
2813 {
2815 let last_idx = sets.len() - 1;
2816 let cur = &mut sets[last_idx];
2817 let cur_args = &mut args_per_set[last_idx];
2818 let mut head: Option<Box<caarg>> = None;
2819 for a in cur_args.drain(..).rev() {
2820 let mut a = a;
2821 a.next = head;
2822 head = Some(a);
2823 }
2824 cur.args = head;
2825 set_cadef_opts(cur);
2826 }
2827
2828 let n_sets = sets.len();
2830 for i in 0..n_sets {
2831 let mut head: Option<Box<caopt>> = None;
2833 for o in opts_per_set[i].drain(..).rev() {
2834 let mut o = o;
2835 o.next = head;
2836 head = Some(o);
2837 }
2838 sets[i].opts = head;
2839 if !args_per_set[i].is_empty() {
2843 let mut head: Option<Box<caarg>> = None;
2844 for a in args_per_set[i].drain(..).rev() {
2845 let mut a = a;
2846 a.next = head;
2847 head = Some(a);
2848 }
2849 sets[i].args = head;
2850 }
2851 sets[i].rest = rest_per_set[i].take();
2852 }
2853
2854 while sets.len() > 1 {
2856 let tail = sets.pop().unwrap();
2857 let prev = sets.last_mut().unwrap();
2858 let mut cursor: &mut Option<Box<cadef>> = &mut prev.snext;
2860 while cursor.is_some() {
2861 cursor = &mut cursor.as_mut().unwrap().snext;
2862 }
2863 *cursor = Some(tail);
2864 }
2865
2866 Some(sets.pop().unwrap())
2867}
2868
2869pub fn get_cadef(nam: &str, args: &[String]) -> i32 {
2885 let na = args.len() as i32;
2887 let now = {
2888 use std::time::{SystemTime, UNIX_EPOCH};
2890 SystemTime::now()
2891 .duration_since(UNIX_EPOCH)
2892 .map(|d| d.as_secs() as i64)
2893 .unwrap_or(0)
2894 };
2895
2896 if let Ok(mut cache) = cadef_cache.lock() {
2897 let mut min_idx: Option<usize> = None;
2901 let mut min_lastt: i64 = i64::MAX;
2902 let mut hit_idx: Option<usize> = None;
2903 for (i, slot) in cache.iter().enumerate() {
2904 match slot {
2905 Some(entry) => {
2906 if entry.ndefs == na
2908 && entry.defs.as_deref().map_or(false, |d| {
2909 d.len() == args.len() && d.iter().zip(args.iter()).all(|(a, b)| a == b)
2910 })
2911 {
2912 hit_idx = Some(i);
2913 break; }
2915 if entry.lastt < min_lastt {
2917 min_lastt = entry.lastt;
2918 min_idx = Some(i);
2919 }
2920 }
2921 None => {
2922 min_idx = Some(i);
2924 break;
2925 }
2926 }
2927 }
2928 if let Some(i) = hit_idx {
2929 if let Some(entry) = cache[i].as_mut() {
2930 entry.lastt = now; }
2932 return 1; }
2934 if let Some(new) = parse_cadef(nam, args) {
2936 let idx = min_idx.unwrap_or(0);
2937 cache[idx] = Some(new);
2938 }
2939 }
2940 0 }
2942
2943pub fn ca_get_opt(
2955 d: &cadef,
2956 line: &str,
2957 full: i32, end: &mut usize,
2959) -> Option<Box<caopt>> {
2960 let line_bytes = line.as_bytes();
2961
2962 let mut cur = d.opts.as_deref();
2964 while let Some(p) = cur {
2965 if p.active != 0 {
2967 if let Some(name) = p.name.as_deref() {
2969 if name == line {
2970 *end = line_bytes.len(); return Some(Box::new(caopt {
2972 next: None,
2974 name: p.name.clone(),
2975 descr: p.descr.clone(),
2976 xor: p.xor.clone(),
2977 r#type: p.r#type,
2978 args: None,
2979 active: p.active,
2980 num: p.num,
2981 gsname: p.gsname.clone(),
2982 not: p.not,
2983 }));
2984 }
2985 }
2986 }
2987 cur = p.next.as_deref();
2988 }
2989
2990 if full == 0 {
2991 let mut cur = d.opts.as_deref();
2994 while let Some(p) = cur {
2995 if p.active != 0 {
2996 if let Some(name) = p.name.as_deref() {
2998 let is_match = if p.args.is_none() || p.r#type == CAO_NEXT {
3000 name == line
3001 } else {
3002 strpfx(name, line)
3003 };
3004 if is_match {
3005 let l = name.len();
3006 if (p.r#type == CAO_OEQUAL || p.r#type == CAO_EQUAL)
3009 && l < line_bytes.len()
3010 && line_bytes[l] != b'='
3011 {
3012 cur = p.next.as_deref();
3013 continue; }
3015 let mut at = l;
3017 if (p.r#type == CAO_OEQUAL || p.r#type == CAO_EQUAL)
3018 && l < line_bytes.len()
3019 && line_bytes[l] == b'='
3020 {
3021 at += 1; }
3023 *end = at; return Some(Box::new(caopt {
3025 next: None,
3027 name: p.name.clone(),
3028 descr: p.descr.clone(),
3029 xor: p.xor.clone(),
3030 r#type: p.r#type,
3031 args: None,
3032 active: p.active,
3033 num: p.num,
3034 gsname: p.gsname.clone(),
3035 not: p.not,
3036 }));
3037 }
3038 }
3039 }
3040 cur = p.next.as_deref();
3041 }
3042 }
3043 None }
3045
3046pub fn ca_get_sopt(
3054 d: &cadef,
3055 line: &str, end: &mut usize,
3057 lp: &mut Option<Vec<Box<caopt>>>,
3058) -> Option<Box<caopt>> {
3059 let line_bytes = line.as_bytes();
3060 if line_bytes.is_empty() {
3061 *lp = None;
3062 return None;
3063 }
3064 let pre = line_bytes[0]; let mut idx: usize = 1;
3066 *lp = None; let single = match d.single.as_ref() {
3069 Some(s) => s,
3071 None => return None,
3072 };
3073
3074 let mut p_cur: Option<&caopt> = None; let mut pp_cur: Option<&caopt> = None;
3076 let mut list_acc: Option<Vec<Box<caopt>>> = None;
3077
3078 while idx < line_bytes.len() {
3079 let ch = line_bytes[idx];
3081 let sidx = single_index(pre, ch); let lookup: Option<&caopt> = if sidx >= 0 && (sidx as usize) < single.len() {
3085 single[sidx as usize].as_deref()
3086 } else {
3087 None
3088 };
3089 if lookup.is_some() {
3090 p_cur = lookup;
3091 }
3092 let active_with_args = lookup.filter(|p| p.active != 0 && p.args.is_some());
3093
3094 if let Some(p) = active_with_args {
3095 if p.r#type == CAO_NEXT {
3097 let list = list_acc.get_or_insert_with(Vec::new);
3099 list.push(Box::new(caopt {
3100 next: None,
3102 name: p.name.clone(),
3103 descr: p.descr.clone(),
3104 xor: p.xor.clone(),
3105 r#type: p.r#type,
3106 args: None,
3107 active: p.active,
3108 num: p.num,
3109 gsname: p.gsname.clone(),
3110 not: p.not,
3111 }));
3112 } else {
3113 idx += 1; if (p.r#type == CAO_OEQUAL || p.r#type == CAO_EQUAL) && idx < line_bytes.len() && line_bytes[idx] == b'='
3117 {
3118 idx += 1; }
3120 *end = idx; pp_cur = Some(p); break; }
3124 } else if p_cur.is_none() || p_cur.map_or(true, |p| p.active == 0) {
3125 return None; }
3128
3129 pp_cur = p_cur.filter(|p| {
3131 p.name
3132 .as_deref()
3133 .and_then(|n| n.as_bytes().first().copied())
3134 .map_or(false, |b| b == pre)
3135 });
3136 p_cur = None;
3137 idx += 1; }
3139
3140 if pp_cur.is_some() {
3142 *end = idx;
3143 }
3144
3145 *lp = list_acc;
3146
3147 pp_cur.map(|p| {
3148 Box::new(caopt {
3149 next: None,
3151 name: p.name.clone(),
3152 descr: p.descr.clone(),
3153 xor: p.xor.clone(),
3154 r#type: p.r#type,
3155 args: None,
3156 active: p.active,
3157 num: p.num,
3158 gsname: p.gsname.clone(),
3159 not: p.not,
3160 })
3161 })
3162}
3163
3164pub fn ca_foreign_opt(curset: &cadef, all: &cadef, option: &str) -> i32 {
3170 let curset_ptr = curset as *const cadef;
3172 let mut d_opt = Some(all);
3173 while let Some(d) = d_opt {
3174 if std::ptr::addr_eq(d as *const cadef, curset_ptr) {
3176 d_opt = d.snext.as_deref();
3178 continue;
3179 }
3180 let mut p = d.opts.as_deref();
3181 while let Some(opt) = p {
3182 if opt.name.as_deref() == Some(option) {
3184 return 1; }
3187 p = opt.next.as_deref();
3188 }
3189 d_opt = d.snext.as_deref();
3190 }
3191 0 }
3193
3194pub fn ca_get_arg(d: &cadef, mut n: i32) -> Option<Box<caarg>> {
3200 if d.argsactive == 0 {
3202 return None; }
3205
3206 let mut a = d.args.as_deref();
3209 while let Some(node) = a {
3210 let in_range = node.active != 0 && n >= node.min && n <= node.num;
3212 if in_range {
3213 break;
3214 } if node.active == 0 {
3216 n += 1; }
3219 a = node.next.as_deref(); }
3221
3222 if let Some(node) = a {
3223 if node.active != 0 && node.min <= n && node.num >= n {
3225 return Some(Box::new(caarg {
3226 next: None,
3228 descr: node.descr.clone(),
3229 xor: node.xor.clone(),
3230 action: node.action.clone(),
3231 r#type: node.r#type,
3232 end: node.end.clone(),
3233 opt: node.opt.clone(),
3234 num: node.num,
3235 min: node.min,
3236 direct: node.direct,
3237 active: node.active,
3238 gsname: node.gsname.clone(),
3239 }));
3240 }
3241 }
3242
3243 if let Some(r) = d.rest.as_deref() {
3245 if r.active != 0 {
3246 return Some(Box::new(caarg {
3247 next: None,
3248 descr: r.descr.clone(),
3249 xor: r.xor.clone(),
3250 action: r.action.clone(),
3251 r#type: r.r#type,
3252 end: r.end.clone(),
3253 opt: r.opt.clone(),
3254 num: r.num,
3255 min: r.min,
3256 direct: r.direct,
3257 active: r.active,
3258 gsname: r.gsname.clone(),
3259 }));
3260 }
3261 }
3262 None }
3264
3265pub fn ca_inactive(d: &mut cadef, xor: &[String], cur: i32, opts: i32) {
3274 if (xor.is_empty() && opts == 0) || cur > COMPCURRENT.load(Ordering::Relaxed)
3278 {
3279 return;
3280 }
3281
3282 let single = opts == 0 && cur == COMPCURRENT.load(Ordering::Relaxed);
3286
3287 let iter_xor: Vec<String> = if opts != 0 {
3289 vec!["-".to_string()]
3290 } else {
3291 xor.to_vec()
3292 };
3293
3294 for x_orig in iter_xor.iter() {
3295 let mut x = x_orig.as_str();
3296 let mut excludeall = 0; let mut grp: Option<&str> = None;
3298 let mut grplen: usize = 0;
3299
3300 let xb = x.as_bytes();
3302 let mut sep_byte = if xb.is_empty() { 0u8 } else { xb[0] };
3303 let mut sep_pos = 0usize;
3304 loop {
3305 if sep_pos >= xb.len() {
3306 break;
3307 }
3308 sep_byte = xb[sep_pos];
3309 if sep_byte == b'+'
3310 || sep_byte == b'-'
3311 || sep_byte == b':'
3312 || sep_byte == b'*'
3313 || idigit(sep_byte)
3314 {
3315 break;
3316 }
3317 let after = &xb[sep_pos..];
3319 let dash_off = after.iter().position(|&b| b == b'-');
3320 match dash_off {
3321 None => {
3322 excludeall = 1; sep_pos = xb.len();
3324 break;
3325 }
3326 Some(d) => {
3327 let next = sep_pos + d + 1;
3328 if next >= xb.len() {
3329 excludeall = 1;
3331 sep_pos = xb.len();
3332 break;
3333 }
3334 sep_pos = next;
3335 }
3336 }
3337 }
3338 if sep_pos > 0 && sep_pos < xb.len() {
3339 grp = Some(&x[..sep_pos]);
3341 grplen = sep_pos;
3342 x = &x[sep_pos..];
3343 } else if sep_pos > 0 && excludeall != 0 && sep_pos == xb.len() {
3344 grp = Some(x);
3346 grplen = sep_pos;
3347 x = "";
3348 }
3349 let xb = x.as_bytes();
3350
3351 if excludeall != 0 || (xb.len() == 1 && xb[0] == b':') {
3353 if let Some(g) = grp {
3354 let mut cur_arg = d.args.as_deref_mut();
3356 while let Some(a) = cur_arg {
3357 let matches = a.gsname.as_deref().map_or(false, |gn| {
3358 let gnb = gn.as_bytes();
3359 gnb.len() == grplen + (excludeall as usize) && gn.starts_with(g)
3360 });
3361 if matches {
3362 a.active = 0; }
3364 cur_arg = a.next.as_deref_mut();
3365 }
3366 if let Some(r) = d.rest.as_deref_mut() {
3367 let matches = r.gsname.as_deref().map_or(false, |gn| {
3368 let gnb = gn.as_bytes();
3369 gnb.len() == grplen + (excludeall as usize) && gn.starts_with(g)
3370 });
3371 if matches {
3372 r.active = 0; }
3374 }
3375 } else {
3376 d.argsactive = 0; }
3378 }
3379
3380 if excludeall != 0 || (xb.len() == 1 && xb[0] == b'-') {
3382 let mut cur_opt = d.opts.as_deref_mut();
3383 while let Some(p) = cur_opt {
3384 let grp_ok = grp.map_or(true, |g| {
3385 p.gsname.as_deref().map_or(false, |gn| {
3386 gn.len() == grplen + (excludeall as usize) && gn.starts_with(g)
3387 })
3388 });
3389 let single_skip = single
3390 && p.name.as_deref().map_or(false, |n| {
3391 let nb = n.as_bytes();
3392 nb.len() >= 3 && nb[0] != 0
3393 });
3394 if grp_ok && !single_skip {
3395 p.active = 0; }
3397 cur_opt = p.next.as_deref_mut();
3398 }
3399 }
3400
3401 if excludeall != 0 || (xb.len() == 1 && xb[0] == b'*') {
3403 if let Some(r) = d.rest.as_deref_mut() {
3404 let grp_ok = grp.map_or(true, |g| {
3405 r.gsname.as_deref().map_or(false, |gn| {
3406 gn.len() == grplen + (excludeall as usize) && gn.starts_with(g)
3407 })
3408 });
3409 if grp_ok {
3410 r.active = 0; }
3412 }
3413 }
3414
3415 if excludeall == 0 {
3416 if !xb.is_empty() && idigit(xb[0]) {
3418 let n: i32 = x
3420 .bytes()
3421 .take_while(|b| idigit(*b))
3422 .fold(0i32, |acc, b| acc * 10 + (b - b'0') as i32);
3423 let mut cur_arg = d.args.as_deref_mut();
3424 let mut hit: Option<&mut caarg> = None;
3425 while let Some(a) = cur_arg {
3426 if a.num >= n {
3427 hit = Some(a);
3428 break;
3429 }
3430 cur_arg = a.next.as_deref_mut();
3431 }
3432 if let Some(a) = hit {
3433 if a.num == n {
3434 let grp_ok = grp.map_or(true, |g| {
3435 a.gsname.as_deref().map_or(false, |gn| gn.starts_with(g))
3436 });
3437 if grp_ok {
3438 a.active = 0; }
3440 }
3441 }
3442 } else {
3443 let mut end_unused = 0usize;
3445 if let Some(matched) = ca_get_opt(d, x, 1, &mut end_unused) {
3446 let grp_ok = grp.map_or(true, |g| {
3447 matched
3448 .gsname
3449 .as_deref()
3450 .map_or(false, |gn| gn.starts_with(g))
3451 });
3452 let single_skip = single
3453 && matched.name.as_deref().map_or(false, |n| {
3454 let nb = n.as_bytes();
3455 nb.len() >= 3 && nb[0] != 0
3456 });
3457 if grp_ok && !single_skip {
3458 let target_name = matched.name.clone();
3460 let mut cur_opt = d.opts.as_deref_mut();
3461 while let Some(p) = cur_opt {
3462 if p.name == target_name {
3463 p.active = 0; break;
3465 }
3466 cur_opt = p.next.as_deref_mut();
3467 }
3468 }
3469 }
3470 }
3471 if opts != 0 {
3472 break; }
3474 }
3475 let _ = sep_byte;
3476 }
3477}
3478
3479pub type Castate = Box<castate>; #[derive(Debug, Default, Clone)]
3492#[allow(non_camel_case_types)]
3493pub struct castate {
3494 pub snext: Option<Box<castate>>, pub d: Option<Box<cadef>>, pub nopts: i32, pub def: Option<Box<caarg>>, pub ddef: Option<Box<caarg>>, pub curopt: Option<Box<caopt>>, pub dopt: Option<Box<caopt>>, pub opt: i32, pub arg: i32, pub argbeg: i32, pub optbeg: i32, pub nargbeg: i32, pub restbeg: i32, pub curpos: i32, pub argend: i32, pub inopt: i32, pub inarg: i32, pub nth: i32, pub singles: i32, pub oopt: i32, pub actopts: i32, pub args: Option<Vec<String>>, pub oargs: Option<Vec<Option<Vec<String>>>>, }
3519
3520pub static ca_parsed: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
3523
3524pub static ca_doff: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
3529
3530pub fn freecastate(s: &mut castate) {
3533 s.args = None; s.oargs = None; }
3537
3538pub fn ca_opt_arg(opt_name: &str, line: &str, equal_kind: bool) -> String {
3541 let o_bytes = opt_name.as_bytes();
3548 let l_bytes = line.as_bytes();
3549 let mut oi = 0usize;
3550 let mut li = 0usize;
3551 loop {
3552 if oi >= o_bytes.len() || li >= l_bytes.len() {
3554 break;
3555 }
3556 let mut oc = o_bytes[oi];
3557 if oc == b'\\' {
3558 oi += 1;
3560 if oi >= o_bytes.len() {
3561 break;
3562 }
3563 oc = o_bytes[oi];
3564 }
3565 let mut lc = l_bytes[li];
3566 if matches!(lc, b'\\' | b'\'' | b'"') {
3567 li += 1;
3569 if li >= l_bytes.len() {
3570 break;
3571 }
3572 lc = l_bytes[li];
3573 }
3574 if oc != lc {
3575 break;
3577 }
3578 oi += 1;
3579 li += 1;
3580 }
3581 let rest = &l_bytes[li..];
3582 let mut s = String::from_utf8_lossy(rest).into_owned();
3583 if equal_kind && s.starts_with('\\') {
3584 s.remove(0);
3586 }
3587 if equal_kind {
3588 s = s.strip_prefix('=').map(|t| t.to_string()).unwrap_or(s); }
3590 s
3591}
3592
3593pub fn ca_parse_line(d: &mut cadef, all: &cadef, multi: i32, first: i32) -> i32 {
3611 let compcur = COMPCURRENT.load(Ordering::Relaxed);
3614
3615 if first != 0 && ca_alloced.load(Ordering::Relaxed) != 0 {
3617 if let Ok(mut ls) = ca_laststate.lock() {
3618 freecastate(&mut ls);
3619 ls.snext = None;
3620 }
3621 }
3622
3623 let mut p = d.opts.as_deref_mut();
3625 while let Some(o) = p {
3626 o.active = 1;
3627 p = o.next.as_deref_mut();
3628 }
3629 d.argsactive = 1;
3630 if let Some(r) = d.rest.as_deref_mut() {
3631 r.active = 1;
3632 }
3633 let mut a = d.args.as_deref_mut();
3634 while let Some(ar) = a {
3635 ar.active = 1;
3636 a = ar.next.as_deref_mut();
3637 }
3638
3639 let compwords: Vec<String> = COMPWORDS
3641 .get()
3642 .and_then(|m| m.lock().ok().map(|w| w.clone()))
3643 .unwrap_or_default();
3644 let argend_init = (compwords.len() as i32) - 1;
3645
3646 let mut state = castate {
3651 snext: None,
3652 d: None,
3653 nopts: d.nopts,
3654 def: None,
3655 ddef: None,
3656 curopt: None,
3657 dopt: None,
3658 opt: 1,
3659 arg: 1,
3660 argbeg: 1,
3661 optbeg: 1,
3662 nargbeg: 1,
3663 restbeg: 1,
3664 actopts: 1,
3665 nth: 1,
3666 inarg: 1,
3667 inopt: 0,
3668 singles: 0,
3669 oopt: 0,
3670 argend: argend_init,
3671 curpos: compcur,
3672 args: Some(Vec::new()),
3673 oargs: Some((0..d.nopts as usize).map(|_| None).collect()),
3674 };
3675 ca_alloced.store(1, Ordering::Relaxed);
3676
3677 if let Ok(mut ls) = ca_laststate.lock() {
3680 *ls = clone_castate(&state, d);
3681 }
3682
3683 if compwords.len() < 2 {
3684 if let Ok(mut ls) = ca_laststate.lock() {
3686 ls.opt = 0;
3687 ls.arg = 0;
3688 }
3689 } else {
3691 let napat = d
3693 .nonarg
3694 .as_deref()
3695 .and_then(|s| patcompile(&{ let mut __pat_tok = (s).to_string(); crate::ported::glob::tokenize(&mut __pat_tok); __pat_tok }, 0, None::<&mut String>));
3696 let mut endpat: Option<Patprog> = None;
3697
3698 let mut cur = 2i32;
3700 let mut argxor: Option<Vec<String>> = None;
3701 let mut sopts: Vec<Box<caopt>> = Vec::new();
3702 let mut wasopt_idx: Option<usize> = None;
3703 let mut doff: i32 = 0;
3704 let mut adef: Option<Box<caarg>> = None;
3705 let mut ddef: Option<Box<caarg>> = None;
3706 let mut dopt: Option<Box<caopt>> = None;
3707 state.curopt = None;
3708 state.def = None;
3709
3710 loop {
3711 let line_idx = (cur - 1) as usize;
3712 if line_idx >= compwords.len() {
3713 break;
3714 }
3715 let oline = compwords[line_idx].clone();
3716 let mut line = oline.clone();
3717 ddef = None;
3718 adef = None;
3719 dopt = None;
3720 state.singles = 0;
3721 let mut arglast = 0;
3722
3723 remnulargs(&mut line);
3724 line = untokenize(&line);
3725
3726 if let Some(xor) = argxor.take() {
3728 ca_inactive(d, &xor, cur - 1, 0);
3729 }
3730
3731 if (d.flags & CDF_SEP) != 0 && cur != compcur && state.actopts != 0 && line == "--" {
3733 ca_inactive(d, &[], cur, 1);
3734 state.actopts = 0;
3735 cur += 1;
3736 continue;
3737 }
3738
3739 if state.def.is_some() {
3741 state.arg = 0;
3742 if let Some(co) = state.curopt.as_deref() {
3743 let cn = co.num as usize;
3744 if let Some(oargs) = state.oargs.as_mut() {
3745 if cn < oargs.len() {
3746 oargs[cn].get_or_insert_with(Vec::new).push(oline.clone());
3747 }
3748 }
3749 }
3750 let def_type = state.def.as_deref().map_or(0, |d| d.r#type);
3751 let def_is_opt = def_type == CAA_OPT;
3752 state.opt = if def_is_opt { 1 } else { 0 };
3753 if def_is_opt {
3754 if state.def.as_deref().map_or(false, |d| d.opt.is_some()) {
3755 state.oopt += 1;
3756 }
3757 }
3758
3759 if def_type == CAA_REST || def_type == CAA_RARGS || def_type == CAA_RREST {
3760 let matched_end = state
3762 .def
3763 .as_deref()
3764 .and_then(|d| d.end.as_deref())
3765 .map_or(false, |_| {
3766 endpat.as_ref().map_or(false, |ep| pattry(ep, &line))
3767 });
3768 if matched_end {
3769 state.def = None;
3770 state.curopt = None;
3771 state.opt = 1;
3772 state.arg = 1;
3773 state.argend = cur - 1;
3774 if let Ok(mut ls) = ca_laststate.lock() {
3775 ls.argend = cur - 1;
3776 }
3777 }
3779 } else {
3780 let next = state.def.as_deref().and_then(|d| d.next.clone());
3782 if next.is_some() {
3783 state.def = next;
3784 state.argbeg = cur;
3785 state.argend = argend_init;
3786 } else if let Some(s) = sopts.first().cloned() {
3787 sopts.remove(0);
3789 state.curopt = Some(s);
3790 state.def = state.curopt.as_deref().and_then(|c| c.args.clone());
3791 state.opt = 0;
3792 state.argbeg = cur;
3793 state.optbeg = cur;
3794 state.inopt = cur;
3795 state.argend = argend_init;
3796 doff = 0;
3797 state.singles = 1;
3798 if let Some(co) = state.curopt.as_deref() {
3799 let cn = co.num as usize;
3800 if let Some(oargs) = state.oargs.as_mut() {
3801 if cn < oargs.len() && oargs[cn].is_none() {
3802 oargs[cn] = Some(Vec::new());
3803 }
3804 }
3805 }
3806 } else {
3808 state.curopt = None;
3809 state.opt = 1;
3810 }
3811 }
3812 } else {
3813 state.opt = 1;
3814 state.arg = 1;
3815 state.curopt = None;
3816 }
3817 if state.opt != 0 {
3818 let lb = line.as_bytes();
3819 state.opt = if lb.is_empty() {
3820 0
3821 } else if lb.len() == 1 {
3822 1
3823 } else {
3824 2
3825 };
3826 }
3827
3828 let mut pe_off: i32 = 0;
3829 wasopt_idx = None;
3830
3831 let opt_match = if state.opt == 2 {
3833 let lb = line.as_bytes();
3834 if !lb.is_empty() && (lb[0] == b'-' || lb[0] == b'+') {
3835 let mut end = 0usize;
3836 if let Some(found) = ca_get_opt(d, &line, 0, &mut end) {
3837 pe_off = end as i32;
3838 let pe_ok = match found.r#type {
3840 t if t == CAO_OEQUAL => {
3841 (line_idx + 1 < compwords.len())
3842 || (pe_off > 0 && lb.get(pe_off as usize - 1) == Some(&b'='))
3843 }
3844 t if t == CAO_EQUAL => {
3845 pe_off > 0
3846 && (lb.get(pe_off as usize - 1) == Some(&b'=')
3847 || pe_off as usize >= lb.len())
3848 }
3849 _ => true,
3850 };
3851 if pe_ok {
3852 Some(found)
3853 } else {
3854 None
3855 }
3856 } else {
3857 None
3858 }
3859 } else {
3860 None
3861 }
3862 } else {
3863 None
3864 };
3865
3866 if let Some(co) = opt_match {
3867 let co_name = co.name.clone().unwrap_or_default();
3869 let co_type = co.r#type;
3870 let co_num = co.num;
3871 let co_xor = co.xor.clone().unwrap_or_default();
3872 let co_args = co.args.clone();
3873 state.curopt = Some(co);
3874 let pe_at_eq =
3875 pe_off > 0 && line.as_bytes().get(pe_off as usize - 1) == Some(&b'=');
3876 let pe_tail_present = (pe_off as usize) < line.as_bytes().len();
3877
3878 let take_args = co_type != CAO_EQUAL || pe_at_eq;
3879 state.def = if take_args { co_args.clone() } else { None };
3880 if state.def.is_some() {
3881 ddef = state.def.clone();
3882 dopt = state.curopt.clone();
3883 }
3884
3885 doff = pe_off;
3886 state.optbeg = cur;
3887 state.argbeg = cur;
3888 state.inopt = cur;
3889 state.argend = argend_init;
3890 let single_ok = d.single.is_some()
3891 && !pe_tail_present
3892 && co_name.as_bytes().len() >= 2
3893 && co_name.as_bytes()[1] != b'-'
3894 && co_name.as_bytes().get(2).is_none();
3895 state.singles = if single_ok { 1 } else { 0 };
3896
3897 let cn = co_num as usize;
3898 if let Some(oargs) = state.oargs.as_mut() {
3899 if cn < oargs.len() && oargs[cn].is_none() {
3900 oargs[cn] = Some(Vec::new());
3901 }
3902 }
3903 ca_inactive(d, &co_xor, cur, 0); let collect_arg = state.def.is_some()
3906 && (co_type == CAO_DIRECT
3907 || co_type == CAO_EQUAL
3908 || (co_type == CAO_ODIRECT && pe_tail_present)
3909 || (co_type == CAO_OEQUAL && (pe_tail_present || pe_at_eq)));
3910 if collect_arg {
3911 let dtype = state.def.as_deref().map_or(0, |d| d.r#type);
3912 if dtype != CAA_REST && dtype != CAA_RARGS && dtype != CAA_RREST {
3913 let next = state.def.as_deref().and_then(|d| d.next.clone());
3914 state.def = next;
3915 }
3916 let arg_str = ca_opt_arg(&co_name, &oline, false);
3917 if let Some(oargs) = state.oargs.as_mut() {
3918 if cn < oargs.len() {
3919 oargs[cn].get_or_insert_with(Vec::new).push(arg_str);
3920 }
3921 }
3922 }
3923 if state.def.is_some() {
3924 state.opt = 0;
3925 } else {
3926 if d.single.is_none()
3927 || (co_name.as_bytes().len() >= 3 && co_name.as_bytes()[1] != 0)
3928 {
3929 wasopt_idx = Some(co_num as usize); }
3931 state.curopt = None;
3932 }
3933 } else if state.opt == 2
3934 && d.single.is_some()
3935 && line
3936 .as_bytes()
3937 .first()
3938 .copied()
3939 .map_or(false, |b| b == b'-' || b == b'+')
3940 {
3941 let mut end = 0usize;
3943 let mut tmp_sopts: Option<Vec<Box<caopt>>> = None;
3944 let s_match = ca_get_sopt(d, &line, &mut end, &mut tmp_sopts);
3945 if let Some(queued) = tmp_sopts {
3946 sopts.extend(queued);
3947 }
3948 let active_sopt = s_match.or_else(|| {
3949 if cur != compcur && !sopts.is_empty() {
3950 Some(sopts.remove(0))
3951 } else {
3952 None
3953 }
3954 });
3955 if let Some(co) = active_sopt {
3956 let co_name = co.name.clone().unwrap_or_default();
3957 let co_type = co.r#type;
3958 let co_num = co.num;
3959 let co_xor = co.xor.clone().unwrap_or_default();
3960 let co_args = co.args.clone();
3961 state.curopt = Some(co);
3962
3963 let cn = co_num as usize;
3964 if let Some(oargs) = state.oargs.as_mut() {
3965 if cn < oargs.len() && oargs[cn].is_none() {
3966 oargs[cn] = Some(Vec::new());
3967 }
3968 }
3969 state.def = co_args.clone();
3970 if co_type == CAO_NEXT && cur == compcur {
3971 ddef = None;
3972 } else {
3973 ddef = state.def.clone();
3974 }
3975 dopt = state.curopt.clone();
3976 doff = end as i32;
3977 state.optbeg = cur;
3978 state.argbeg = cur;
3979 state.inopt = cur;
3980 state.argend = argend_init;
3981 state.singles = if end >= line.as_bytes().len() { 1 } else { 0 };
3982
3983 let lb = line.as_bytes();
3984 let pre = lb.first().copied().unwrap_or(0);
3985 let mut p_idx = 1usize;
3986 while p_idx < end.min(lb.len()) {
3987 let sidx = single_index(pre, lb[p_idx]);
3988 if sidx >= 0 && (sidx as usize) < d.single.as_ref().map_or(0, |s| s.len()) {
3989 let tmp_xor = d
3990 .single
3991 .as_ref()
3992 .and_then(|s| s.get(sidx as usize))
3993 .and_then(|so| so.as_ref())
3994 .and_then(|so| so.xor.clone());
3995 let tmp_num = d
3996 .single
3997 .as_ref()
3998 .and_then(|s| s.get(sidx as usize))
3999 .and_then(|so| so.as_ref())
4000 .map_or(-1, |so| so.num);
4001 let tn = tmp_num as usize;
4002 if tmp_num >= 0 {
4003 if let Some(oargs) = state.oargs.as_mut() {
4004 if tn < oargs.len() && oargs[tn].is_none() {
4005 oargs[tn] = Some(Vec::new());
4006 }
4007 }
4008 }
4009 if let Some(xor) = tmp_xor {
4010 ca_inactive(d, &xor, cur, 0);
4011 }
4012 }
4013 p_idx += 1;
4014 }
4015
4016 let pe_tail_present = end < line.as_bytes().len();
4017 let pe_at_eq = end > 0 && line.as_bytes().get(end - 1) == Some(&b'=');
4018 let collect_arg = state.def.is_some()
4019 && (co_type == CAO_DIRECT
4020 || co_type == CAO_EQUAL
4021 || (co_type == CAO_ODIRECT && pe_tail_present)
4022 || (co_type == CAO_OEQUAL && (pe_tail_present || pe_at_eq)));
4023 if collect_arg {
4024 let dtype = state.def.as_deref().map_or(0, |d| d.r#type);
4025 if dtype != CAA_REST && dtype != CAA_RARGS && dtype != CAA_RREST {
4026 let next = state.def.as_deref().and_then(|d| d.next.clone());
4027 state.def = next;
4028 }
4029 let arg_str = ca_opt_arg(&co_name, &line, false);
4030 if let Some(oargs) = state.oargs.as_mut() {
4031 if cn < oargs.len() {
4032 oargs[cn].get_or_insert_with(Vec::new).push(arg_str);
4033 }
4034 }
4035 }
4036 if state.def.is_some() {
4037 state.opt = 0;
4038 } else {
4039 state.curopt = None;
4040 }
4041 }
4042 } else if multi != 0
4043 && line
4044 .as_bytes()
4045 .first()
4046 .copied()
4047 .map_or(false, |b| b == b'-' || b == b'+')
4048 && cur != compcur
4049 && ca_foreign_opt(d, all, &line) != 0
4050 {
4051 return 1; } else if state.arg != 0 && cur <= compcur {
4053 if let Some(np) = napat.as_ref() {
4056 if cur < compcur && state.actopts != 0 {
4057 if pattry(np, &line) {
4058 cur += 1;
4059 continue;
4060 }
4061 ca_inactive(d, &[], cur + 1, 1);
4062 state.actopts = 0;
4063 }
4064 }
4065
4066 arglast = 1;
4067 if state.inopt != 0 {
4068 state.inopt = 0;
4070 state.nargbeg = cur - 1;
4071 state.argend = argend_init;
4072 }
4073 let lb = line.as_bytes();
4075 let non_flag = !lb.is_empty() && lb[0] != b'-' && lb[0] != b'+';
4076 if d.args.is_none() && d.rest.is_none() && non_flag {
4077 if multi == 0 && cur > compcur {
4078 break;
4079 }
4080 return 1;
4081 }
4082
4083 adef = ca_get_arg(d, state.nth);
4084 state.def = adef.clone();
4085 let dtype = state.def.as_deref().map_or(0, |d| d.r#type);
4086 if state.def.is_some() && (dtype == CAA_RREST || dtype == CAA_RARGS) {
4087 if ca_laststate
4088 .lock()
4089 .map(|ls| ls.def.is_some())
4090 .unwrap_or(false)
4091 {
4092 break;
4093 }
4094 state.opt = if cur == state.nargbeg + 1
4095 && (multi == 0 || line.is_empty() || lb[0] == b'-' || lb[0] == b'+')
4096 {
4097 1
4098 } else {
4099 0
4100 };
4101 state.optbeg = state.nargbeg;
4102 state.argbeg = cur - 1;
4103 state.argend = argend_init;
4104 while line_idx < compwords.len() {
4106 state
4107 .args
4108 .get_or_insert_with(Vec::new)
4109 .push(compwords[line_idx].clone());
4110 cur += 1;
4111 if (cur - 1) as usize >= compwords.len() {
4112 break;
4113 }
4114 }
4115 if let Ok(mut ls) = ca_laststate.lock() {
4116 *ls = clone_castate(&state, d);
4117 ls.ddef = None;
4118 ls.dopt = None;
4119 }
4120 break;
4121 }
4122 state.args.get_or_insert_with(Vec::new).push(line.clone());
4123 if let Some(a) = adef.as_deref() {
4124 state.oopt = a.num - state.nth;
4125 }
4126
4127 if state.def.is_some() && cur != compcur {
4128 argxor = state.def.as_deref().and_then(|d| d.xor.clone());
4130 }
4131 let dtype2 = state.def.as_deref().map_or(0, |d| d.r#type);
4132 if state.def.is_some()
4133 && dtype2 != CAA_NORMAL
4134 && dtype2 != CAA_OPT
4135 && state.inarg != 0
4136 {
4137 state.restbeg = cur;
4138 state.inarg = 0;
4139 } else if state.def.is_none() || dtype2 == CAA_NORMAL || dtype2 == CAA_OPT {
4140 state.inarg = 1;
4141 }
4142 state.nth += 1;
4143 state.def = None;
4144 }
4145
4146 if state.def.is_some() && state.curopt.is_some() {
4148 let dt = state.def.as_deref().map_or(0, |d| d.r#type);
4149 if dt == CAA_RREST || dt == CAA_RARGS {
4150 let end_pat_str = state.def.as_deref().and_then(|d| d.end.clone());
4151 if let Some(eps) = end_pat_str {
4152 endpat = patcompile(&{ let mut __pat_tok = (&eps).to_string(); crate::ported::glob::tokenize(&mut __pat_tok); __pat_tok }, 0, None::<&mut String>);
4153 } else {
4154 if cur < compcur {
4156 if let Ok(mut ls) = ca_laststate.lock() {
4157 *ls = clone_castate(&state, d);
4158 }
4159 }
4160 let cn = state.curopt.as_deref().map(|c| c.num as usize);
4161 if let Some(cn) = cn {
4162 if let Some(oargs) = state.oargs.as_mut() {
4163 if cn < oargs.len() {
4164 let bucket = oargs[cn].get_or_insert_with(Vec::new);
4165 let mut k = line_idx;
4166 while k < compwords.len() {
4167 bucket.push(compwords[k].clone());
4168 k += 1;
4169 }
4170 }
4171 }
4172 }
4173 if let Ok(mut ls) = ca_laststate.lock() {
4174 ls.ddef = None;
4175 ls.dopt = None;
4176 }
4177 break;
4178 }
4179 }
4180 } else if state.def.is_some() {
4181 let eps = state.def.as_deref().and_then(|d| d.end.clone());
4182 if let Some(eps) = eps {
4183 endpat = patcompile(&{ let mut __pat_tok = (&eps).to_string(); crate::ported::glob::tokenize(&mut __pat_tok); __pat_tok }, 0, None::<&mut String>);
4184 }
4185 }
4186
4187 if cur + 1 == compcur {
4189 if let Ok(mut ls) = ca_laststate.lock() {
4190 *ls = clone_castate(&state, d);
4191 ls.ddef = None;
4192 ls.dopt = None;
4193 }
4194 } else if cur == compcur {
4195 let mut ls = ca_laststate.lock().unwrap();
4196 if ls.def.is_none() {
4197 if let Some(ddef_v) = ddef.clone() {
4198 ls.def = Some(ddef_v);
4199 ls.singles = state.singles;
4200 if state
4201 .curopt
4202 .as_deref()
4203 .map_or(false, |c| c.r#type == CAO_NEXT)
4204 {
4205 ls.ddef = ddef.clone();
4206 ls.dopt = dopt.clone();
4207 ls.def = None;
4208 ls.opt = 1;
4209 if let Some(co) = state.curopt.as_deref() {
4211 let target_name = co.name.clone();
4212 let mut p = d.opts.as_deref_mut();
4213 while let Some(op) = p {
4214 if op.name == target_name {
4215 op.active = 1;
4216 break;
4217 }
4218 p = op.next.as_deref_mut();
4219 }
4220 }
4221 } else {
4222 ca_doff.store(doff, Ordering::Relaxed);
4223 ls.opt = 0;
4224 }
4225 } else {
4226 ls.def = adef.clone();
4227 ls.opt = if arglast == 0
4228 || multi == 0
4229 || line.is_empty()
4230 || line.as_bytes()[0] == b'-'
4231 || line.as_bytes()[0] == b'+'
4232 {
4233 1
4234 } else {
4235 0
4236 };
4237 ls.ddef = None;
4238 ls.dopt = None;
4239 ls.optbeg = state.nargbeg;
4240 ls.argbeg = state.restbeg;
4241 ls.argend = state.argend;
4242 ls.singles = state.singles;
4243 ls.oopt = state.oopt;
4244 if let Some(wi) = wasopt_idx {
4245 let mut p = d.opts.as_deref_mut();
4246 while let Some(op) = p {
4247 if op.num as usize == wi {
4248 op.active = 1;
4249 break;
4250 }
4251 p = op.next.as_deref_mut();
4252 }
4253 }
4254 }
4255 }
4256 }
4257 cur += 1;
4258 }
4259 let _ = (endpat, ddef, dopt, adef);
4260 }
4261
4262 let mut actopts = 0i32;
4264 let mut p = d.opts.as_deref();
4265 while let Some(o) = p {
4266 if o.active != 0 {
4267 actopts += 1;
4268 }
4269 p = o.next.as_deref();
4270 }
4271 if let Ok(mut ls) = ca_laststate.lock() {
4272 ls.actopts = actopts;
4273 ls.d = Some(Box::new(clone_cadef_shallow(d)));
4275 }
4276 0
4277}
4278
4279pub fn ca_nullist(l: &[String]) -> Vec<u8> {
4281 if l.is_empty() {
4287 return Vec::new(); }
4289 let mut out = Vec::new();
4290 for (i, item) in l.iter().enumerate() {
4291 if i > 0 {
4292 out.push(0);
4293 }
4294 out.extend_from_slice(item.as_bytes());
4295 }
4296 out
4297}
4298
4299pub fn ca_colonlist(l: &[String]) -> String {
4301 if l.is_empty() {
4305 return String::new(); }
4307 let mut out = String::new();
4308 for (i, item) in l.iter().enumerate() {
4309 if i > 0 {
4311 out.push(':'); }
4313 for ch in item.chars() {
4314 if ch == ':' || ch == '\\' {
4315 out.push('\\');
4317 }
4318 out.push(ch);
4319 }
4320 }
4321 out
4322}
4323
4324pub fn ca_set_data(
4335 descr: &mut Vec<String>, act: &mut Vec<String>,
4337 subc: &mut Vec<String>,
4338 opt: Option<&str>,
4339 start_arg: Option<Box<caarg>>,
4340 optdef: Option<&caopt>,
4341 single: i32,
4342) {
4343 let mut arg: Option<Box<caarg>> = start_arg;
4344 let mut opt = opt.map(|s| s.to_string());
4345 let mut restr = 0;
4346 let mut miss = 0;
4347 let mut oopt = 1i32;
4348 let mut lopt;
4349
4350 'rec: loop {
4351 let addopt = if opt.is_some() {
4353 0
4354 } else {
4355 ca_laststate.lock().map(|s| s.oopt).unwrap_or(0)
4356 };
4357
4358 while let Some(a) = arg.as_ref() {
4360 let cont = {
4361 let nth = ca_laststate.lock().map(|s| s.nth).unwrap_or(0);
4362 opt.is_some() || a.num < 0 || (a.min <= nth + addopt && a.num >= nth)
4363 };
4364 if !cont {
4365 break;
4366 }
4367
4368 lopt = a.r#type == CAA_OPT; if opt.is_none() && !lopt && oopt > 0 {
4370 oopt = 0;
4372 }
4373
4374 let mut dup = false;
4376 let descr_str = a.descr.clone().unwrap_or_default();
4377 let act_str = a.action.clone().unwrap_or_default();
4378 for (d, ac) in descr.iter().zip(act.iter()) {
4379 if d == &descr_str && ac == &act_str {
4380 dup = true;
4381 break;
4382 }
4383 }
4384
4385 if single != 0 && a.opt.is_none() {
4387 return;
4388 }
4389
4390 if !dup {
4391 descr.push(descr_str.clone()); act.push(act_str.clone());
4394
4395 if restr == 0 {
4396 let nrestr = if a.r#type == CAA_RARGS {
4398 let (optbeg, argend) = ca_laststate
4400 .lock()
4401 .map(|s| (s.optbeg, s.argend))
4402 .unwrap_or((0, 0));
4403 restrict_range(optbeg, argend);
4404 1
4405 } else if a.r#type == CAA_RREST {
4406 let (argbeg, argend) = ca_laststate
4408 .lock()
4409 .map(|s| (s.argbeg, s.argend))
4410 .unwrap_or((0, 0));
4411 restrict_range(argbeg, argend);
4412 1
4413 } else {
4414 0
4415 };
4416 restr = nrestr;
4417 }
4418
4419 let buf = if let Some(o) = a.opt.as_deref() {
4421 let gs = a.gsname.as_deref().unwrap_or("");
4423 if a.num > 0 && a.r#type < CAA_REST {
4424 format!("{}option{}-{}", gs, o, a.num)
4426 } else {
4427 format!("{}option{}-rest", gs, o)
4429 }
4430 } else if a.num > 0 {
4431 if let Some(gs) = a.gsname.as_deref() {
4433 format!("{}argument-{}", gs, a.num)
4434 } else {
4435 format!("argument-{}", a.num)
4436 }
4437 } else {
4438 if let Some(gs) = a.gsname.as_deref() {
4440 format!("{}argument-rest", gs)
4441 } else {
4442 "argument-rest".to_string()
4443 }
4444 };
4445 subc.push(buf); }
4447
4448 if a.r#type == CAA_NORMAL && opt.is_some() {
4452 if let Some(od) = optdef {
4453 if od.r#type == CAO_NEXT || od.r#type == CAO_ODIRECT || od.r#type == CAO_OEQUAL
4454 {
4455 return;
4456 }
4457 }
4458 }
4459
4460 if single != 0 {
4461 break;
4462 } if opt.is_none() {
4466 let next_is_none_and_miss = a.num >= 0 && a.next.is_none() && miss != 0;
4468 if next_is_none_and_miss {
4469 let rest = ca_laststate
4471 .lock()
4472 .ok()
4473 .and_then(|s| s.d.as_ref().and_then(|d| d.rest.clone()));
4474 arg = rest.filter(|r| r.active != 0); } else {
4476 let onum = a.num; let nth = ca_laststate.lock().map(|s| s.nth).unwrap_or(0);
4478 let rest_flag = onum != a.min && onum == nth; let next = a.next.clone();
4480 if let Some(n) = next {
4481 if n.num != onum + 1 {
4483 miss = 1;
4484 } arg = Some(n);
4486 } else if rest_flag || (oopt > 0 && opt.is_none()) {
4487 let rest = ca_laststate
4489 .lock()
4490 .ok()
4491 .and_then(|s| s.d.as_ref().and_then(|d| d.rest.clone()));
4492 arg = rest.filter(|r| r.active != 0);
4493 oopt = -1;
4494 } else {
4495 arg = None;
4496 }
4497 }
4498 } else {
4499 if !lopt {
4501 break;
4502 } arg = a.next.clone(); }
4505 }
4506
4507 let laststate_oopt = ca_laststate.lock().map(|s| s.oopt).unwrap_or(0);
4509 let cur_lopt = arg.as_ref().map_or(false, |a| a.r#type == CAA_OPT);
4510 if single == 0 && opt.is_some() && (cur_lopt || laststate_oopt != 0) {
4511 opt = None;
4512 let nth = ca_laststate.lock().map(|s| s.nth).unwrap_or(0);
4513 arg = ca_laststate
4515 .lock()
4516 .ok()
4517 .and_then(|s| s.d.as_ref().and_then(|d| ca_get_arg(d, nth)));
4518 continue 'rec;
4519 }
4520 if opt.is_none() && oopt > 0 {
4522 oopt = -1;
4523 let rest = ca_laststate
4524 .lock()
4525 .ok()
4526 .and_then(|s| s.d.as_ref().and_then(|d| d.rest.clone()));
4527 arg = rest.filter(|r| r.active != 0);
4528 continue 'rec;
4529 }
4530 break 'rec;
4531 }
4532}
4533
4534pub fn bin_comparguments(
4541 nam: &str,
4542 args: &[String], _ops: &options,
4544 _func: i32,
4545) -> i32 {
4546 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
4547 zwarnnam(nam, "can only be called from completion function");
4549 return 1;
4550 }
4551 if args.is_empty() {
4552 return 1;
4553 }
4554 let a0 = args[0].as_bytes();
4555 if a0.len() != 2 || a0[0] != b'-' {
4557 zwarnnam(nam, &format!("invalid argument: {}", args[0]));
4558 return 1;
4559 }
4560 let sub = a0[1];
4561
4562 if sub != b'i' && sub != b'I' && ca_parsed.load(Ordering::Relaxed) == 0 {
4564 zwarnnam(nam, "no parsed state");
4565 return 1;
4566 }
4567
4568 let (min, max): (i32, i32) = match sub {
4570 b'i' => (2, -1),
4571 b'D' => (3, 3),
4572 b'O' => (4, 4),
4573 b'L' => (3, 4),
4574 b's' => (1, 1),
4575 b'M' => (1, 1),
4576 b'a' => (0, 0),
4577 b'W' => (3, 3),
4578 b'n' => (1, 1),
4579 _ => {
4580 zwarnnam(nam, &format!("invalid option: {}", args[0]));
4581 return 1;
4582 }
4583 };
4584 let n = (args.len() as i32) - 1;
4585 if n < min {
4586 zwarnnam(nam, "not enough arguments");
4587 return 1;
4588 }
4589 if max >= 0 && n > max {
4590 zwarnnam(nam, "too many arguments");
4591 return 1;
4592 }
4593
4594 match sub {
4595 b'i' => {
4596 let compcur = COMPCURRENT.load(Ordering::Relaxed);
4599 let compwords_nonempty = COMPWORDS
4600 .get()
4601 .and_then(|m| m.lock().ok().map(|w| !w.is_empty() && !w[0].is_empty()))
4602 .unwrap_or(false);
4603 if compcur <= 1 || !compwords_nonempty {
4604 return 1; }
4606 let spec = &args[1..];
4610 let _ = get_cadef(nam, spec); let cached: Option<Box<cadef>> = {
4613 let cache = cadef_cache.lock().ok();
4614 cache.and_then(|c| {
4615 c.iter().find_map(|slot| {
4616 slot.as_ref()
4617 .filter(|e| {
4618 e.ndefs == spec.len() as i32
4619 && e.defs.as_deref().map_or(false, |d| {
4620 d.len() == spec.len()
4621 && d.iter().zip(spec.iter()).all(|(a, b)| a == b)
4622 })
4623 })
4624 .cloned()
4625 })
4626 })
4627 };
4628 let Some(mut def_head) = cached else {
4629 return 1;
4630 };
4631 ca_parsed.store(0, Ordering::Relaxed); ca_doff.store(0, Ordering::Relaxed);
4633 let all_clone = Box::new(clone_cadef_shallow(&def_head));
4634
4635 let mut first = 1;
4641 let mut def_opt: Option<Box<cadef>> = Some(def_head);
4642 let mut multi = 0;
4643 if let Some(ref d) = def_opt {
4646 if d.snext.is_some() {
4647 multi = 1;
4648 }
4649 }
4650 let mut states: Vec<castate> = Vec::new(); let mut ret = 0i32;
4652
4653 while let Some(mut current) = def_opt {
4654 let next = current.snext.take();
4655 let parse_ret = ca_parse_line(&mut current, &all_clone, multi, first);
4656 let use_state = parse_ret == 0; let has_next = next.is_some();
4658 if use_state && has_next {
4659 if let Ok(ls) = ca_laststate.lock() {
4662 states.push(clone_castate_full(&ls));
4663 }
4664 } else if !use_state && !has_next {
4665 if let Some(saved) = states.pop() {
4668 if let Ok(mut ls) = ca_laststate.lock() {
4669 freecastate(&mut ls);
4670 *ls = saved;
4671 }
4672 } else {
4673 ret = 1; }
4675 }
4676 first = 0; def_opt = next;
4678 }
4679 ca_parsed.store(1, Ordering::Relaxed); if !states.is_empty() {
4683 if let Ok(mut ls) = ca_laststate.lock() {
4684 let mut head: Option<Box<castate>> = None;
4686 for s in states.into_iter().rev() {
4687 let mut s = s;
4688 s.snext = head;
4689 head = Some(Box::new(s));
4690 }
4691 ls.snext = head;
4692 }
4693 }
4694 ret }
4696
4697 b'D' => {
4698 let mut descr: Vec<String> = Vec::new();
4700 let mut act: Vec<String> = Vec::new();
4701 let mut subc: Vec<String> = Vec::new();
4702 let mut ret = 1i32;
4703 ignore_prefix(ca_doff.load(Ordering::Relaxed));
4704
4705 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4707 while let Some(s) = state_clone {
4708 let arg = s.def.clone();
4709 if let Some(a) = arg {
4710 ret = 0;
4711 let opt_str = a.opt.clone();
4712 let optdef = s.curopt.clone();
4713 ca_set_data(
4714 &mut descr,
4715 &mut act,
4716 &mut subc,
4717 opt_str.as_deref(),
4718 Some(a),
4719 optdef.as_deref(),
4720 if ca_doff.load(Ordering::Relaxed) > 0 {
4721 1
4722 } else {
4723 0
4724 },
4725 );
4726 }
4727 state_clone = s.snext.map(|b| *b);
4728 }
4729 if ret == 0 {
4730 setaparam(&args[1], descr);
4732 setaparam(&args[2], act);
4733 setaparam(&args[3], subc);
4734 }
4735 ret
4736 }
4737
4738 b'M' => {
4739 let m = ca_laststate
4741 .lock()
4742 .ok()
4743 .and_then(|s| s.d.as_ref().and_then(|d| d.r#match.clone()))
4744 .unwrap_or_default();
4745 setsparam(&args[1], &m);
4746 0
4747 }
4748
4749 b'a' => {
4750 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4752 while let Some(s) = state_clone {
4753 if s.d
4754 .as_ref()
4755 .map_or(false, |d| d.args.is_some() || d.rest.is_some())
4756 {
4757 return 0;
4758 }
4759 state_clone = s.snext.map(|b| *b);
4760 }
4761 1 }
4763
4764 b'n' => {
4765 let optbeg = ca_laststate.lock().map(|s| s.optbeg).unwrap_or(0);
4767 let kshoffset = if isset(KSHARRAYS) { 0 } else { 1 };
4768 setiparam(&args[1], (optbeg + kshoffset) as i64);
4769 0
4770 }
4771
4772 b'O' => {
4773 let mut next_l: Vec<String> = Vec::new();
4777 let mut direct_l: Vec<String> = Vec::new();
4778 let mut odirect_l: Vec<String> = Vec::new();
4779 let mut equal_l: Vec<String> = Vec::new();
4780 let mut ret = 1i32;
4781
4782 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4783 while let Some(s) = state_clone {
4784 let actopts_ok = s.actopts != 0
4786 && (s.opt != 0
4787 || (ca_doff.load(Ordering::Relaxed) != 0 && s.def.is_some())
4788 || (s.def.is_some()
4789 && s.def.as_deref().map_or(false, |d| {
4790 d.opt.is_some()
4791 && (d.r#type == CAA_OPT || (d.r#type >= CAA_RARGS && d.num < 0))
4792 })));
4793 let pos_ok = s.def.is_none()
4794 || s.def.as_deref().map_or(true, |d| d.r#type < CAA_RARGS)
4795 || (s.def.as_deref().map_or(false, |d| d.r#type == CAA_RARGS)
4796 && s.curpos == s.argbeg + 1)
4797 || COMPCURRENT.load(Ordering::Relaxed) == 1;
4798 if actopts_ok && pos_ok {
4799 ret = 0;
4800 if let Some(d) = s.d.as_ref() {
4801 let mut p = d.opts.as_deref();
4802 while let Some(opt) = p {
4803 if opt.active != 0 && opt.not == 0 {
4804 let bucket: &mut Vec<String> = match opt.r#type {
4805 t if t == CAO_NEXT => &mut next_l,
4806 t if t == CAO_DIRECT => &mut direct_l,
4807 t if t == CAO_ODIRECT => &mut odirect_l,
4808 _ => &mut equal_l,
4809 };
4810 let name_esc = bslashcolon(opt.name.as_deref().unwrap_or(""));
4811 let str_val = if let Some(desc) = opt.descr.as_deref() {
4812 format!("{}:{}", name_esc, desc)
4813 } else {
4814 name_esc
4815 };
4816 if !bucket.iter().any(|s| s == &str_val) {
4817 bucket.push(str_val);
4818 }
4819 }
4820 p = opt.next.as_deref();
4821 }
4822 }
4823 }
4824 state_clone = s.snext.map(|b| *b);
4825 }
4826
4827 if ret == 0 {
4828 setaparam(&args[1], next_l);
4829 setaparam(&args[2], direct_l);
4830 setaparam(&args[3], odirect_l);
4831 setaparam(&args[4], equal_l);
4832 0
4833 } else {
4834 let singles = ca_laststate.lock().map(|s| s.singles).unwrap_or(0);
4835 if singles != 0 {
4836 2
4837 } else {
4838 1
4839 } }
4841 }
4842
4843 b'L' => {
4844 let mut descr: Vec<String> = Vec::new();
4847 let mut act: Vec<String> = Vec::new();
4848 let mut subc: Vec<String> = Vec::new();
4849 let mut ret = 1i32;
4850 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4851 while let Some(s) = state_clone {
4852 if let Some(d) = s.d.as_ref() {
4853 let mut end = 0usize;
4854 if let Some(opt) = ca_get_opt(d, &args[1], 1, &mut end) {
4855 if opt.args.is_some() {
4856 ret = 0;
4857 let opt_name = opt.name.clone();
4858 let opt_args = opt.args.clone();
4859 ca_set_data(
4860 &mut descr,
4861 &mut act,
4862 &mut subc,
4863 opt_name.as_deref(),
4864 opt_args,
4865 Some(&opt),
4866 1,
4867 );
4868 }
4869 }
4870 }
4871 state_clone = s.snext.map(|b| *b);
4872 }
4873 if ret == 0 {
4874 setaparam(&args[2], descr);
4875 setaparam(&args[3], act);
4876 setaparam(&args[4], subc);
4877 }
4878 ret
4879 }
4880
4881 b's' => {
4882 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4884 while let Some(s) = state_clone {
4885 let single_active = s.d.as_ref().map_or(false, |d| d.single.is_some())
4886 && s.singles != 0
4887 && s.actopts != 0;
4888 if single_active {
4889 let kind = if let (Some(_), Some(dopt)) = (&s.ddef, &s.dopt) {
4890 match dopt.r#type {
4891 t if t == CAO_DIRECT => "direct",
4892 t if t == CAO_OEQUAL || t == CAO_EQUAL => "equal",
4893 _ => "next",
4894 }
4895 } else {
4896 ""
4897 };
4898 setsparam(&args[1], kind);
4899 return 0;
4900 }
4901 state_clone = s.snext.map(|b| *b);
4902 }
4903 1
4904 }
4905
4906 b'W' => {
4907 let mut all_args: Vec<String> = Vec::new();
4910 let opt_args_use_nul = !args[3].starts_with('0');
4911 let mut state_clone = ca_laststate.lock().map(|s| clone_castate_full(&s)).ok();
4912 let mut snapshot = state_clone.clone();
4914 while let Some(s) = snapshot {
4915 if let Some(a) = s.args.as_ref() {
4916 all_args.extend(a.iter().cloned());
4917 }
4918 snapshot = s.snext.map(|b| *b);
4919 }
4920 setaparam(&args[1], all_args);
4921
4922 let mut hash_vec: Vec<String> = Vec::new();
4924 while let Some(s) = state_clone {
4925 if let Some(d) = s.d.as_ref() {
4926 let mut o = d.opts.as_deref();
4927 let mut a_idx = 0usize;
4928 let oargs_ref = s.oargs.as_deref();
4929 while let Some(op) = o {
4930 if let Some(oa) = oargs_ref
4931 .and_then(|v| v.get(a_idx))
4932 .and_then(|x| x.as_ref())
4933 {
4934 let key = match (op.gsname.as_deref(), op.name.as_deref()) {
4935 (Some(gs), Some(n)) => format!("{}{}", gs, n),
4936 (None, Some(n)) => n.to_string(),
4937 _ => String::new(),
4938 };
4939 hash_vec.push(key);
4940 let joined = if opt_args_use_nul {
4941 String::from_utf8_lossy(&ca_nullist(oa)).into_owned()
4942 } else {
4943 ca_colonlist(oa)
4944 };
4945 hash_vec.push(joined);
4946 }
4947 a_idx += 1;
4948 o = op.next.as_deref();
4949 }
4950 }
4951 state_clone = s.snext.map(|b| *b);
4952 }
4953 sethparam(&args[2], hash_vec);
4954 0
4955 }
4956
4957 _ => 0,
4958 }
4959}
4960
4961pub type Cvdef = Box<cvdef>; #[derive(Debug, Default, Clone)]
4973#[allow(non_camel_case_types)]
4974pub struct cvdef {
4975 pub descr: Option<String>, pub hassep: i32, pub sep: i32, pub argsep: i32, pub next: Option<Box<cvdef>>, pub vals: Option<Box<cvval>>, pub defs: Option<Vec<String>>, pub ndefs: i32, pub lastt: i64, pub words: i32, }
4987pub type Cvval = Box<cvval>; #[derive(Debug, Default, Clone)]
4993#[allow(non_camel_case_types)]
4994pub struct cvval {
4995 pub next: Option<Box<cvval>>, pub name: Option<String>, pub descr: Option<String>, pub xor: Option<Vec<String>>, pub r#type: i32, pub arg: Option<Box<caarg>>, pub active: i32, }
5004
5005pub const CVV_NOARG: i32 = 0; pub const CVV_ARG: i32 = 1; pub const CVV_OPT: i32 = 2; pub const MAX_CVCACHE: usize = 8; pub static cvdef_cache: std::sync::Mutex<[Option<Box<cvdef>>; MAX_CVCACHE]> = std::sync::Mutex::new([const { None }; MAX_CVCACHE]);
5025
5026pub fn freecvdef(d: Option<Box<cvdef>>) {
5030 let Some(mut node) = d else {
5032 return;
5033 }; node.descr = None; node.defs = None; let mut p = node.vals.take();
5037 while let Some(mut v) = p {
5038 p = v.next.take(); v.name = None; v.descr = None; v.xor = None; freecaargs(v.arg.take()); drop(v); }
5046 drop(node); }
5048
5049#[cfg(test)]
5053mod cao_caa_tests {
5054 use super::*;
5055
5056 #[test]
5057 fn cao_values_match_c_source() {
5058 let _g = crate::test_util::global_state_lock();
5059 let _g = zle_test_setup();
5060 assert_eq!(CAO_NEXT, 1);
5062 assert_eq!(CAO_DIRECT, 2);
5063 assert_eq!(CAO_ODIRECT, 3);
5064 assert_eq!(CAO_EQUAL, 4);
5065 assert_eq!(CAO_OEQUAL, 5);
5066 }
5067
5068 #[test]
5069 fn caa_values_match_c_source() {
5070 let _g = crate::test_util::global_state_lock();
5071 let _g = zle_test_setup();
5072 assert_eq!(CAA_NORMAL, 1);
5074 assert_eq!(CAA_OPT, 2);
5075 assert_eq!(CAA_REST, 3);
5076 assert_eq!(CAA_RARGS, 4);
5077 assert_eq!(CAA_RREST, 5);
5078 }
5079
5080 #[test]
5081 fn crt_values_match_c_source() {
5082 let _g = crate::test_util::global_state_lock();
5083 let _g = zle_test_setup();
5084 assert_eq!(CRT_SIMPLE, 0);
5086 assert_eq!(CRT_DESC, 1);
5087 assert_eq!(CRT_SPEC, 2);
5088 assert_eq!(CRT_DUMMY, 3);
5089 assert_eq!(CRT_EXPL, 4);
5090 }
5091
5092 #[test]
5093 fn cvv_values_match_c_source() {
5094 let _g = crate::test_util::global_state_lock();
5095 let _g = zle_test_setup();
5096 assert_eq!(CVV_NOARG, 0);
5098 assert_eq!(CVV_ARG, 1);
5099 assert_eq!(CVV_OPT, 2);
5100 }
5101
5102 #[test]
5103 fn cache_sizes_are_8() {
5104 let _g = crate::test_util::global_state_lock();
5105 let _g = zle_test_setup();
5106 assert_eq!(MAX_CACACHE, 8);
5108 assert_eq!(MAX_CVCACHE, 8);
5109 }
5110
5111 #[test]
5112 fn max_tags_is_256() {
5113 let _g = crate::test_util::global_state_lock();
5114 let _g = zle_test_setup();
5115 assert_eq!(MAX_TAGS, 256);
5116 }
5117
5118 #[test]
5119 fn path_max2_is_8192() {
5120 let _g = crate::test_util::global_state_lock();
5121 let _g = zle_test_setup();
5122 assert_eq!(PATH_MAX2, 8192);
5123 }
5124}
5125
5126pub fn parse_cvdef(nam: &str, args: &[String]) -> Option<Box<cvdef>> {
5131 let orig_args = args;
5134 let mut idx = 0usize;
5135
5136 let mut sep: i32 = 0; let mut asep: i32 = b'=' as i32; let mut hassep: i32 = 0; let mut words: i32 = 0; while idx + 1 < args.len()
5143 && args[idx].len() == 2
5144 && args[idx].starts_with('-')
5145 && (args[idx].as_bytes()[1] == b's'
5146 || args[idx].as_bytes()[1] == b'S'
5147 || args[idx].as_bytes()[1] == b'w')
5148 {
5149 let flag = args[idx].as_bytes()[1];
5150 if flag == b's' {
5151 hassep = 1;
5153 sep = args[idx + 1].as_bytes().first().copied().unwrap_or(0) as i32;
5154 idx += 2;
5155 } else if flag == b'S' {
5156 asep = args[idx + 1].as_bytes().first().copied().unwrap_or(0) as i32;
5158 idx += 2;
5159 } else {
5160 words = 1;
5162 idx += 1;
5163 }
5164 }
5165
5166 if idx + 1 >= args.len() {
5167 zwarnnam(nam, "not enough arguments");
5169 return None;
5170 }
5171 let descr = args[idx].clone(); idx += 1;
5173
5174 let mut ret = Box::new(cvdef {
5175 descr: Some(descr), hassep, sep, argsep: asep, next: None, vals: None, defs: Some(orig_args.to_vec()), ndefs: orig_args.len() as i32, lastt: {
5184 use std::time::{SystemTime, UNIX_EPOCH};
5186 SystemTime::now()
5187 .duration_since(UNIX_EPOCH)
5188 .map(|d| d.as_secs() as i64)
5189 .unwrap_or(0)
5190 },
5191 words, });
5193
5194 let mut vals_collected: Vec<Box<cvval>> = Vec::new();
5196
5197 while idx < args.len() {
5198 let spec = &args[idx];
5199 let bytes = spec.as_bytes();
5200 let mut p: usize = 0;
5201 let mut xnum: i32 = 0; let mut bs = 0; let mut xor: Option<Vec<String>> = None;
5204
5205 if p < bytes.len() && bytes[p] == b'(' {
5207 let mut list: Vec<String> = Vec::new();
5209 let mut bad = false;
5210 'paren: loop {
5211 if p >= bytes.len() || bytes[p] == b')' {
5212 break;
5213 }
5214 p += 1; while p < bytes.len() && inblank(bytes[p]) {
5216 p += 1;
5217 }
5218 if p >= bytes.len() {
5219 bad = true;
5220 break 'paren;
5221 }
5222 if bytes[p] == b')' {
5223 break 'paren;
5224 }
5225 let q = p;
5226 p += 1;
5227 while p < bytes.len() && bytes[p] != b')' && !inblank(bytes[p]) {
5228 p += 1;
5229 }
5230 if p >= bytes.len() {
5231 bad = true;
5232 break 'paren;
5233 }
5234 let word = String::from_utf8_lossy(&bytes[q..p]).into_owned();
5235 list.push(word);
5236 xnum += 1;
5237 }
5238 if bad || p >= bytes.len() || bytes[p] != b')' {
5239 zwarnnam(nam, &format!("invalid argument: {}", spec));
5241 return None;
5242 }
5243 xor = Some(list);
5244 p += 1; }
5246
5247 let multi = p < bytes.len() && bytes[p] == b'*';
5249 if multi {
5250 p += 1;
5251 }
5252
5253 let name_start = p;
5255 while p < bytes.len() && bytes[p] != b':' && bytes[p] != b'[' {
5256 if bytes[p] == b'\\' && p + 1 < bytes.len() {
5258 p += 1;
5259 bs = 1; }
5261 p += 1;
5262 }
5263
5264 if hassep != 0 && sep == 0 && name_start + (bs as usize) + 1 < p {
5266 zwarnnam(nam, "no multi-letter values with empty separator allowed");
5268 return None;
5269 }
5270
5271 let name_bytes = &bytes[name_start..p];
5272 let name = String::from_utf8_lossy(name_bytes).into_owned();
5273
5274 let mut value_descr: Option<String> = None;
5276 let mut c_byte = if p < bytes.len() { bytes[p] } else { 0 };
5277 if c_byte == b'[' {
5278 p += 1;
5280 let d_start = p;
5281 while p < bytes.len() && bytes[p] != b']' {
5282 if bytes[p] == b'\\' && p + 1 < bytes.len() {
5284 p += 1;
5285 }
5286 p += 1;
5287 }
5288 if p >= bytes.len() {
5289 zwarnnam(nam, &format!("invalid value definition: {}", spec));
5291 return None;
5292 }
5293 value_descr = Some(String::from_utf8_lossy(&bytes[d_start..p]).into_owned());
5294 p += 1; c_byte = if p < bytes.len() { bytes[p] } else { 0 };
5296 }
5297
5298 if c_byte != 0 && c_byte != b':' {
5299 zwarnnam(nam, &format!("invalid value definition: {}", spec));
5301 return None;
5302 }
5303
5304 let mut vtype = CVV_NOARG;
5306 let mut arg: Option<Box<caarg>> = None;
5307 if c_byte == b':' {
5308 if hassep != 0 && sep == 0 {
5310 zwarnnam(nam, "no value with argument with empty separator allowed");
5312 return None;
5313 }
5314 p += 1; if p < bytes.len() && bytes[p] == b':' {
5316 p += 1;
5318 vtype = CVV_OPT; } else {
5320 vtype = CVV_ARG; }
5322 arg = Some(parse_caarg(0, 0, 0, 0, Some(&name), bytes, &mut p, None));
5323 }
5325
5326 if !multi {
5328 let xv = xor.get_or_insert_with(Vec::new);
5330 if xv.len() <= xnum as usize {
5331 xv.resize(xnum as usize + 1, String::new());
5332 }
5333 xv[xnum as usize] = name.clone(); }
5335
5336 let v = Box::new(cvval {
5337 next: None,
5339 name: Some(name), descr: value_descr, xor, r#type: vtype, arg, active: 0,
5345 });
5346 vals_collected.push(v);
5347
5348 idx += 1;
5349 }
5350
5351 let mut head: Option<Box<cvval>> = None;
5353 for v in vals_collected.into_iter().rev() {
5354 let mut v = v;
5355 v.next = head;
5356 head = Some(v);
5357 }
5358 ret.vals = head;
5359
5360 Some(ret)
5361}
5362
5363pub fn get_cvdef(nam: &str, args: &[String]) -> i32 {
5369 let na = args.len() as i32;
5371 let now = {
5372 use std::time::{SystemTime, UNIX_EPOCH};
5374 SystemTime::now()
5375 .duration_since(UNIX_EPOCH)
5376 .map(|d| d.as_secs() as i64)
5377 .unwrap_or(0)
5378 };
5379
5380 if let Ok(mut cache) = cvdef_cache.lock() {
5381 let mut min_idx: Option<usize> = None;
5382 let mut min_lastt: i64 = i64::MAX;
5383 let mut hit_idx: Option<usize> = None;
5384 for (i, slot) in cache.iter().enumerate() {
5385 match slot {
5387 Some(entry) => {
5388 if entry.ndefs == na && entry.defs.as_deref()
5390 .map_or(false, |d| d.len() == args.len()
5391 && d.iter().zip(args.iter()).all(|(a, b)| a == b))
5392 {
5393 hit_idx = Some(i);
5394 break;
5395 }
5396 if entry.lastt < min_lastt {
5397 min_lastt = entry.lastt;
5399 min_idx = Some(i);
5400 }
5401 }
5402 None => {
5403 min_idx = Some(i);
5405 break;
5406 }
5407 }
5408 }
5409 if let Some(i) = hit_idx {
5410 if let Some(entry) = cache[i].as_mut() {
5412 entry.lastt = now; }
5414 return 1; }
5416 if let Some(new) = parse_cvdef(nam, args) {
5418 let idx = min_idx.unwrap_or(0);
5419 cache[idx] = Some(new); }
5421 }
5422 0 }
5424
5425pub fn cv_get_val(d: &cvdef, name: &str) -> Option<Box<cvval>> {
5429 let mut p = d.vals.as_deref();
5431 while let Some(v) = p {
5432 if v.name.as_deref() == Some(name) {
5434 return Some(Box::new(cvval {
5436 next: None,
5437 name: v.name.clone(),
5438 descr: v.descr.clone(),
5439 xor: v.xor.clone(),
5440 r#type: v.r#type,
5441 arg: v.arg.clone(),
5442 active: v.active,
5443 }));
5444 }
5445 p = v.next.as_deref();
5446 }
5447 None }
5449
5450pub fn cv_quote_get_val(d: &cvdef, name: &str) -> Option<Box<cvval>> {
5455 let mut s = name.to_string();
5458 set_noerrs(2);
5462 let parsed = crate::ported::lex::parse_subst_string(&s).ok();
5463 set_noerrs(0);
5464 if let Some(p) = parsed {
5465 s = p;
5466 }
5467 remnulargs(&mut s);
5469 let s = untokenize(&s);
5471 cv_get_val(d, &s)
5473}
5474
5475pub fn cv_inactive(d: &mut cvdef, xor: &[String]) {
5479 for name in xor {
5481 let mut p = d.vals.as_deref_mut();
5483 while let Some(v) = p {
5484 if v.name.as_deref() == Some(name.as_str()) {
5485 v.active = 0; }
5487 p = v.next.as_deref_mut();
5488 }
5489 }
5490}
5491
5492#[derive(Debug, Default)]
5499#[allow(non_camel_case_types)]
5500pub struct cvstate {
5501 pub d: Option<Box<cvdef>>, pub def: Option<Box<caarg>>, pub val: Option<Box<cvval>>, pub vals: Option<Vec<String>>, }
5507
5508pub static cv_parsed: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
5511
5512pub fn cv_next(
5518 d: &cvdef,
5519 sp: &mut Option<String>, ap: &mut Option<String>,
5521) -> Option<Box<cvval>> {
5522 let s_in = sp.take().unwrap_or_default();
5523 if s_in.is_empty() {
5524 *sp = None;
5526 *ap = None;
5527 return None;
5528 }
5529 let bytes = s_in.as_bytes();
5530
5531 if (d.hassep != 0 && d.sep == 0) || d.argsep == 0 {
5533 let ec_byte: u8 = if d.hassep != 0 && d.sep != 0 {
5535 d.sep as u8
5536 } else {
5537 d.argsep as u8
5538 };
5539 let mut s_idx: usize = 0;
5540 let mut r: Option<Box<cvval>> = None;
5541 loop {
5543 s_idx += 1;
5544 if s_idx > bytes.len() {
5545 break;
5546 }
5547 let candidate = std::str::from_utf8(&bytes[..s_idx]).unwrap_or("");
5548 r = cv_quote_get_val(d, candidate);
5549 if r.is_some() {
5550 break;
5551 }
5552 if s_idx >= bytes.len() || bytes[s_idx] == ec_byte {
5553 break;
5554 }
5555 }
5556 let os = s_idx;
5557 if d.hassep != 0 && d.sep != 0 {
5559 let sep_byte = d.sep as u8;
5560 if let Some(off) = bytes[s_idx.min(bytes.len())..]
5561 .iter()
5562 .position(|&b| b == sep_byte)
5563 {
5564 let after = s_idx + off + 1;
5565 *sp = Some(String::from_utf8_lossy(&bytes[after..]).into_owned());
5566 } else {
5567 *sp = None;
5568 }
5569 } else {
5570 *sp = if s_idx < bytes.len() {
5571 Some(String::from_utf8_lossy(&bytes[s_idx..]).into_owned())
5572 } else {
5573 None
5574 };
5575 }
5576 let argsep_b = d.argsep as u8;
5578 if d.argsep != 0 && os < bytes.len() && bytes[os] == argsep_b {
5579 *ap = Some(String::from_utf8_lossy(&bytes[os + 1..]).into_owned());
5580 *sp = None;
5581 } else if r.as_deref().map_or(false, |v| v.r#type != CVV_NOARG) {
5582 *ap = if os < bytes.len() {
5583 Some(String::from_utf8_lossy(&bytes[os..]).into_owned())
5584 } else {
5585 None
5586 };
5587 } else {
5588 *ap = None;
5589 }
5590 return r;
5591 }
5592
5593 if d.hassep != 0 {
5595 let sep_b = d.sep as u8;
5597 let argsep_b = d.argsep as u8;
5598 let ns = bytes.iter().position(|&b| b == sep_b);
5599 let mut as_off: Option<usize> = None;
5600 let mut skip = false;
5601 if d.argsep != 0 {
5602 if let Some(a_pos) = bytes.iter().position(|&b| b == argsep_b) {
5603 if ns.map_or(true, |n| a_pos <= n) {
5604 as_off = Some(a_pos);
5606 *ap = Some(String::from_utf8_lossy(&bytes[a_pos + 1..]).into_owned());
5607 skip = true;
5608 }
5609 }
5610 }
5611 let sap = as_off.or(ns);
5612 let head = match sap {
5613 Some(p) => std::str::from_utf8(&bytes[..p]).unwrap_or(""),
5614 None => std::str::from_utf8(&bytes).unwrap_or(""),
5615 };
5616 let r = cv_quote_get_val(d, head);
5617 let ns_eff = if (r.as_deref().map_or(true, |v| v.r#type == CVV_NOARG)) && skip {
5619 as_off
5620 } else if skip {
5621 ns.filter(|&n| as_off.map_or(true, |a| n > a))
5623 } else {
5624 ns
5625 };
5626 let next_off = match ns_eff {
5627 None => None,
5628 Some(n)
5629 if Some(n) == as_off && r.as_deref().map_or(false, |v| v.r#type != CVV_NOARG) =>
5630 {
5631 None
5632 }
5633 Some(n) => Some(n + 1),
5634 };
5635 *sp = next_off.map(|o| String::from_utf8_lossy(&bytes[o..]).into_owned());
5636 if !skip {
5637 *ap = None;
5638 }
5639 return r;
5640 }
5641
5642 *sp = None; let argsep_b = d.argsep as u8;
5645 let as_pos = bytes.iter().position(|&b| b == argsep_b);
5646 let head = match as_pos {
5647 Some(p) => {
5648 *ap = Some(String::from_utf8_lossy(&bytes[p + 1..]).into_owned());
5649 std::str::from_utf8(&bytes[..p]).unwrap_or("")
5650 }
5651 None => {
5652 *ap = None;
5653 &s_in
5654 }
5655 };
5656 cv_quote_get_val(d, head) }
5658
5659pub fn cv_parse_word(d: &mut cvdef) {
5664 if cv_alloced.load(Ordering::Relaxed) != 0 {
5668 if let Ok(mut ls) = cv_laststate.lock() {
5669 ls.vals = None;
5670 }
5671 }
5672 let mut v = d.vals.as_deref_mut();
5674 while let Some(vv) = v {
5675 vv.active = 1;
5676 v = vv.next.as_deref_mut();
5677 }
5678
5679 let mut state_vals: Vec<String> = Vec::new();
5680 let mut state_def: Option<Box<caarg>> = None;
5681 let mut state_val: Option<Box<cvval>> = None;
5682 cv_alloced.store(1, Ordering::Relaxed);
5683
5684 let compcur = COMPCURRENT.load(Ordering::Relaxed);
5685 let compwords: Vec<String> = COMPWORDS
5686 .get()
5687 .and_then(|m| m.lock().ok().map(|w| w.clone()))
5688 .unwrap_or_default();
5689 let compprefix: String = COMPPREFIX
5690 .get()
5691 .and_then(|m| m.lock().ok().map(|s| s.clone()))
5692 .unwrap_or_default();
5693 let compsuffix: String = COMPSUFFIX
5694 .get()
5695 .and_then(|m| m.lock().ok().map(|s| s.clone()))
5696 .unwrap_or_default();
5697 let mut pign = compprefix.clone(); let mut nosfx = false;
5699
5700 if d.words != 0 && !compwords.is_empty() && !compwords[0].is_empty() {
5702 for i in 1..compwords.len() {
5703 if (i as i32) == compcur - 1 {
5704 continue;
5705 }
5706 let mut str_opt: Option<String> = Some(compwords[i].clone());
5707 while str_opt.as_deref().map_or(false, |s| !s.is_empty()) {
5708 let mut ap: Option<String> = None;
5709 let val = cv_next(d, &mut str_opt, &mut ap);
5710 if let Some(v) = val {
5711 state_vals.push(v.name.clone().unwrap_or_default());
5712 state_vals.push(ap.unwrap_or_default());
5713 if (i as i32) + 1 < compcur {
5714 let xor = v.xor.clone().unwrap_or_default();
5715 cv_inactive(d, &xor);
5716 }
5717 } else {
5718 break;
5719 }
5720 }
5721 }
5722 }
5723
5724 let mut str_opt: Option<String> = Some(compprefix.clone());
5726 let mut last_arg: Option<String> = None;
5727 while str_opt.as_deref().map_or(false, |s| !s.is_empty()) {
5728 let mut ap: Option<String> = None;
5729 let val = cv_next(d, &mut str_opt, &mut ap);
5730 if let Some(v) = val {
5731 state_vals.push(v.name.clone().unwrap_or_default());
5732 match ap.as_deref() {
5733 Some(arg_v) => {
5734 if str_opt.is_some() {
5735 state_vals.push(arg_v.to_string());
5736 } else {
5737 let joined = format!("{}{}", arg_v, compsuffix);
5738 state_vals.push(joined);
5739 nosfx = true;
5740 }
5741 last_arg = ap.clone();
5742 }
5743 None => state_vals.push(String::new()),
5744 }
5745 let xor = v.xor.clone().unwrap_or_default();
5746 cv_inactive(d, &xor);
5747 if let Some(s) = str_opt.as_deref() {
5748 pign = s.to_string();
5749 } else {
5750 let target_name = v.name.clone();
5752 let mut p = d.vals.as_deref_mut();
5753 while let Some(vv) = p {
5754 if vv.name == target_name {
5755 vv.active = 1;
5756 break;
5757 }
5758 p = vv.next.as_deref_mut();
5759 }
5760 }
5761 state_val = Some(v);
5762 } else {
5763 break;
5764 }
5765 }
5766 if state_val.is_some() && last_arg.is_some() && str_opt.is_none() {
5767 state_def = state_val.as_ref().and_then(|v| v.arg.clone());
5769 }
5770
5771 if !nosfx && d.hassep != 0 {
5773 let pign_len = pign.len();
5774 let cp_len = compprefix.len();
5775 ignore_prefix(cp_len as i32 - pign_len as i32); let mut ign = 0usize;
5778 let mut more: Option<String> = None;
5779 if d.sep == 0
5780 && (state_val.is_none() || state_val.as_deref().map_or(true, |v| v.r#type == CVV_NOARG))
5781 {
5782 ign = compsuffix.len();
5783 more = Some(compsuffix.clone());
5784 } else if d.sep != 0 {
5785 let sep_b = d.sep as u8;
5786 let ns_pos = compsuffix.as_bytes().iter().position(|&b| b == sep_b);
5787 let as_pos = if d.argsep != 0 {
5788 compsuffix
5789 .as_bytes()
5790 .iter()
5791 .position(|&b| b == d.argsep as u8)
5792 } else {
5793 None
5794 };
5795 if let Some(a) = as_pos {
5796 if ns_pos.map_or(true, |n| a <= n) {
5797 ign = compsuffix.len() - a;
5798 } else {
5799 ign = ns_pos.map_or(0, |n| compsuffix.len() - n);
5800 }
5801 } else {
5802 ign = ns_pos.map_or(0, |n| compsuffix.len() - n);
5803 }
5804 more = ns_pos.map(|n| compsuffix[n + 1..].to_string());
5805 } else if d.argsep != 0 {
5806 let as_pos = compsuffix
5807 .as_bytes()
5808 .iter()
5809 .position(|&b| b == d.argsep as u8);
5810 if let Some(a) = as_pos {
5811 ign = compsuffix.len() - a;
5812 }
5813 }
5814
5815 if ign > 0 {
5816 ignore_suffix(ign as i32); }
5818
5819 let mut more_opt = more;
5820 while more_opt.as_deref().map_or(false, |s| !s.is_empty()) {
5821 let mut ap: Option<String> = None;
5823 let val = cv_next(d, &mut more_opt, &mut ap);
5824 if let Some(v) = val {
5825 state_vals.push(v.name.clone().unwrap_or_default());
5826 match ap.as_deref() {
5827 Some(arg_v) => {
5828 if more_opt.is_some() {
5829 state_vals.push(arg_v.to_string());
5830 } else {
5831 state_vals.push(format!("{}{}", arg_v, compsuffix));
5832 }
5833 }
5834 None => state_vals.push(String::new()),
5835 }
5836 let xor = v.xor.clone().unwrap_or_default();
5837 cv_inactive(d, &xor);
5838 } else {
5839 break;
5840 }
5841 }
5842 } else if last_arg.is_some() {
5843 let cp_len = compprefix.len();
5844 let arg_off = compprefix
5845 .find(last_arg.as_deref().unwrap_or(""))
5846 .map(|i| i as i32)
5847 .unwrap_or(cp_len as i32);
5848 ignore_prefix(arg_off); } else {
5850 let cp_len = compprefix.len();
5851 ignore_prefix(cp_len as i32 - pign.len() as i32); }
5853
5854 if let Ok(mut ls) = cv_laststate.lock() {
5856 *ls = cvstate {
5857 d: Some(Box::new(d.clone())),
5858 def: state_def,
5859 val: state_val,
5860 vals: if state_vals.is_empty() {
5861 None
5862 } else {
5863 Some(state_vals)
5864 },
5865 };
5866 }
5867}
5868
5869pub fn bin_compvalues(
5876 nam: &str,
5877 args: &[String], _ops: &options,
5879 _func: i32,
5880) -> i32 {
5881 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
5882 zwarnnam(nam, "can only be called from completion function");
5884 return 1;
5885 }
5886 if args.is_empty() {
5887 return 1;
5888 }
5889 let a0 = args[0].as_bytes();
5890 if a0.len() != 2 || a0[0] != b'-' {
5891 zwarnnam(nam, &format!("invalid argument: {}", args[0]));
5893 return 1;
5894 }
5895 let sub = a0[1];
5896
5897 if sub != b'i' && cv_parsed.load(Ordering::Relaxed) == 0 {
5898 zwarnnam(nam, "no parsed state");
5900 return 1;
5901 }
5902
5903 let (min, max): (i32, i32) = match sub {
5904 b'i' => (2, -1),
5906 b'D' => (2, 2),
5907 b'C' => (1, 1),
5908 b'V' => (3, 3),
5909 b's' => (1, 1),
5910 b'S' => (1, 1),
5911 b'd' => (1, 1),
5912 b'L' => (3, 4),
5913 b'v' => (1, 1),
5914 _ => {
5915 zwarnnam(nam, &format!("invalid option: {}", args[0]));
5916 return 1;
5917 }
5918 };
5919 let n = (args.len() as i32) - 1;
5920 if n < min {
5921 zwarnnam(nam, "not enough arguments");
5922 return 1;
5923 }
5924 if max >= 0 && n > max {
5925 zwarnnam(nam, "too many arguments");
5926 return 1;
5927 }
5928
5929 match sub {
5930 b'i' => {
5931 let spec = &args[1..];
5933 let _ = get_cvdef(nam, spec);
5934 let mut cached: Option<Box<cvdef>> = {
5935 let cache = cvdef_cache.lock().ok();
5936 cache.and_then(|c| {
5937 c.iter().find_map(|slot| {
5938 slot.as_ref()
5939 .filter(|e| {
5940 e.ndefs == spec.len() as i32
5941 && e.defs.as_deref().map_or(false, |d| {
5942 d.len() == spec.len()
5943 && d.iter().zip(spec.iter()).all(|(a, b)| a == b)
5944 })
5945 })
5946 .cloned()
5947 })
5948 })
5949 };
5950 let Some(ref mut def) = cached else {
5951 return 1;
5952 };
5953 cv_parsed.store(0, Ordering::Relaxed); cv_parse_word(def); cv_parsed.store(1, Ordering::Relaxed); 0
5957 }
5958
5959 b'D' => {
5960 let arg = cv_laststate.lock().ok().and_then(|s| s.def.clone());
5962 if let Some(a) = arg {
5963 setsparam(&args[1], a.descr.as_deref().unwrap_or("")); setsparam(&args[2], a.action.as_deref().unwrap_or("")); 0
5966 } else {
5967 1 }
5969 }
5970
5971 b'C' => {
5972 let arg = cv_laststate.lock().ok().and_then(|s| s.def.clone());
5974 if let Some(a) = arg {
5975 setsparam(&args[1], a.opt.as_deref().unwrap_or("")); 0
5977 } else {
5978 1 }
5980 }
5981
5982 b'V' => {
5983 let mut noarg: Vec<String> = Vec::new();
5985 let mut arg_l: Vec<String> = Vec::new();
5986 let mut opt_l: Vec<String> = Vec::new();
5987 if let Ok(ls) = cv_laststate.lock() {
5988 if let Some(d) = ls.d.as_ref() {
5989 let mut p = d.vals.as_deref();
5990 while let Some(v) = p {
5991 if v.active != 0 {
5992 let bucket: &mut Vec<String> = match v.r#type {
5994 t if t == CVV_NOARG => &mut noarg,
5995 t if t == CVV_ARG => &mut arg_l,
5996 _ => &mut opt_l,
5997 };
5998 let name = v.name.as_deref().unwrap_or("");
5999 let str_val = if let Some(d) = v.descr.as_deref() {
6000 format!("{}:{}", name, d)
6001 } else {
6002 name.to_string()
6003 };
6004 bucket.push(str_val); }
6006 p = v.next.as_deref();
6007 }
6008 }
6009 }
6010 setaparam(&args[1], noarg);
6011 setaparam(&args[2], arg_l);
6012 setaparam(&args[3], opt_l);
6013 0 }
6015
6016 b's' => {
6017 let (hassep, sep) = cv_laststate
6019 .lock()
6020 .ok()
6021 .and_then(|ls| ls.d.as_ref().map(|d| (d.hassep, d.sep)))
6022 .unwrap_or((0, 0));
6023 if hassep != 0 {
6024 let tmp = (sep as u8 as char).to_string();
6025 setsparam(&args[1], &tmp);
6026 0 } else {
6028 1 }
6030 }
6031
6032 b'S' => {
6033 let argsep = cv_laststate
6035 .lock()
6036 .ok()
6037 .and_then(|ls| ls.d.as_ref().map(|d| d.argsep))
6038 .unwrap_or(0);
6039 let tmp = (argsep as u8 as char).to_string();
6040 setsparam(&args[1], &tmp);
6041 0 }
6043
6044 b'd' => {
6045 let descr = cv_laststate
6047 .lock()
6048 .ok()
6049 .and_then(|ls| ls.d.as_ref().and_then(|d| d.descr.clone()))
6050 .unwrap_or_default();
6051 setsparam(&args[1], &descr);
6052 0
6053 }
6054
6055 b'L' => {
6056 let val = cv_laststate
6058 .lock()
6059 .ok()
6060 .and_then(|ls| ls.d.as_ref().and_then(|d| cv_get_val(d, &args[1])));
6061 if let Some(v) = val {
6062 if let Some(a) = v.arg.as_deref() {
6063 setsparam(&args[2], a.descr.as_deref().unwrap_or(""));
6065 setsparam(&args[3], a.action.as_deref().unwrap_or(""));
6066 if args.len() > 4 {
6067 setsparam(&args[4], v.name.as_deref().unwrap_or(""));
6069 }
6070 return 0;
6071 }
6072 }
6073 1 }
6075
6076 b'v' => {
6077 let vals = cv_laststate.lock().ok().and_then(|ls| ls.vals.clone());
6079 if let Some(v) = vals {
6080 sethparam(&args[1], v);
6081 0
6082 } else {
6083 1 }
6085 }
6086
6087 _ => 1, }
6089}
6090
6091pub fn comp_quote(str: &str, prefix: i32) -> String {
6093 let (s_eff, x) = if prefix != 0 && str.starts_with('=') {
6096 ("x".to_string() + &str[1..], true) } else {
6099 (str.to_string(), false)
6100 };
6101 let qhead = COMPQSTACK
6104 .get()
6105 .and_then(|m| m.lock().ok().and_then(|str| str.bytes().next()))
6106 .unwrap_or(0);
6107 let mut ret = quotename(&s_eff, qhead as i32);
6108 if x {
6110 if !ret.is_empty() {
6111 ret.replace_range(0..1, "=");
6112 }
6113 }
6114 ret
6115}
6116
6117pub fn bin_compquote(
6152 nam: &str,
6153 args: &[String], ops: &options,
6155 _func: i32,
6156) -> i32 {
6157 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
6158 zwarnnam(nam, "can only be called from completion function");
6160 return 1;
6161 }
6162 let qstack_empty = COMPQSTACK
6164 .get()
6165 .map(|m| m.lock().map(|s| s.is_empty()).unwrap_or(true))
6166 .unwrap_or(true);
6167 if qstack_empty {
6168 return 0;
6169 }
6170 let p_flag = OPT_ISSET(ops, b'p'); for name in args {
6174 let mut vbuf = value {
6176 pm: None,
6177 arr: Vec::new(),
6178 scanflags: 0,
6179 valflags: 0,
6180 start: 0,
6181 end: 0,
6182 };
6183 let mut nameref: &str = name.as_str();
6184 let v = getvalue(Some(&mut vbuf), &mut nameref, 0); if v.is_none() {
6186 zwarnnam(nam, &format!("unknown parameter: {}", name));
6188 continue;
6189 }
6190 let v = v.unwrap();
6191 let flags = v.pm.as_ref().map(|pm| pm.node.flags).unwrap_or(0);
6192 let pm_type = PM_TYPE(flags as u32);
6193 if pm_type == 0 || (flags as u32 & crate::ported::zsh_h::PM_NAMEREF) != 0 {
6195 let s = getstrvalue(Some(v));
6196 let q = comp_quote(&s, p_flag as i32);
6197 let mut nameref_re: &str = name.as_str();
6198 setstrvalue(getvalue(Some(&mut vbuf), &mut nameref_re, 0), &q);
6199 } else if pm_type == PM_ARRAY {
6200 let arr = getvaluearr(Some(v));
6202 let new_arr: Vec<String> = arr
6203 .into_iter()
6204 .map(|elem| comp_quote(&elem, p_flag as i32))
6205 .collect();
6206 let mut vbuf2 = value {
6209 pm: None,
6210 arr: Vec::new(),
6211 scanflags: 0,
6212 valflags: 0,
6213 start: 0,
6214 end: 0,
6215 };
6216 let mut nameref2: &str = name.as_str();
6217 if let Some(v2) = getvalue(Some(&mut vbuf2), &mut nameref2, 0) {
6218 setarrvalue(v2, new_arr);
6219 }
6220 } else {
6221 zwarnnam(nam, &format!("invalid parameter type: {}", name));
6223 }
6224 }
6225 0 }
6227
6228pub type Ctags = Box<ctags>; #[derive(Debug, Default)]
6239#[allow(non_camel_case_types)]
6240pub struct ctags {
6241 pub all: Option<Vec<String>>, pub context: Option<String>, pub init: i32, pub sets: Option<Box<ctset>>, }
6247pub type Ctset = Box<ctset>; #[derive(Debug, Default)]
6252#[allow(non_camel_case_types)]
6253pub struct ctset {
6254 pub next: Option<Box<ctset>>, pub tags: Option<Vec<String>>, pub tag: Option<String>, pub ptr: i32, }
6260
6261pub const MAX_TAGS: usize = 256; pub static comptags: std::sync::Mutex<[Option<Box<ctags>>; MAX_TAGS]> = std::sync::Mutex::new([const { None }; MAX_TAGS]);
6270
6271pub static lasttaglevel: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
6275
6276pub fn freectset(mut s: Option<Box<ctset>>) {
6279 while let Some(mut node) = s {
6281 s = node.next.take(); node.tags = None; node.tag = None; drop(node); }
6287}
6288
6289pub fn freectags(t: Option<Box<ctags>>) {
6292 let Some(mut node) = t else {
6294 return;
6295 }; node.all = None; node.context = None; freectset(node.sets.take()); drop(node); }
6301
6302pub fn settags(level: i32, tags: &[String]) {
6308 let idx = level as usize;
6310 if idx >= MAX_TAGS {
6311 return;
6312 } if let Ok(mut tab) = comptags.lock() {
6315 if tab[idx].is_some() {
6316 freectags(tab[idx].take()); }
6319 let context = tags.first().cloned(); let all: Vec<String> = tags.iter().skip(1).cloned().collect(); tab[idx] = Some(Box::new(ctags {
6322 all: Some(all), context, init: 1, sets: None, }));
6328 }
6329}
6330
6331pub fn arrcontains(a: &[String], s: &str, colon: bool) -> i32 {
6333 for entry in a {
6337 if colon {
6338 let p = s.split(':').next().unwrap_or(s);
6339 let q = entry.split(':').next().unwrap_or(entry);
6340 if p == q {
6341 return 1; }
6343 } else if entry == s {
6344 return 1; }
6346 }
6347 0 }
6349
6350pub fn bin_comptags(
6357 nam: &str,
6358 args: &[String], _ops: &options,
6360 _func: i32,
6361) -> i32 {
6362 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
6363 zwarnnam(nam, "can only be called from completion function");
6365 return 1;
6366 }
6367 if args.is_empty() {
6368 return 1;
6369 }
6370 let a0 = args[0].as_bytes();
6371 if a0.len() < 2 || a0[0] != b'-' || (a0.len() > 2 && (a0[2] != b'-' || a0.len() > 3)) {
6373 zwarnnam(nam, &format!("invalid argument: {}", args[0]));
6374 return 1;
6375 }
6376
6377 let level: i32 = locallevel.load(Ordering::Relaxed) - if a0.len() > 2 { 1 } else { 0 };
6379 if level < 0 || (level as usize) >= MAX_TAGS {
6380 zwarnnam(nam, "nesting level too deep");
6382 return 1;
6383 }
6384 let lvl_idx = level as usize;
6385
6386 let sub = a0[1];
6387
6388 if sub != b'i' && sub != b'I' {
6390 let registered = {
6391 let tab = comptags.lock().unwrap();
6392 tab[lvl_idx].is_some()
6393 };
6394 if !registered {
6395 zwarnnam(nam, "no tags registered");
6396 return 1;
6397 }
6398 }
6399
6400 let (min, max): (i32, i32) = match sub {
6402 b'i' => (2, -1),
6403 b'C' => (1, 1),
6404 b'T' => (0, 0),
6405 b'N' => (0, 0),
6406 b'R' => (1, 1),
6407 b'S' => (1, 1),
6408 b'A' => (2, 3),
6409 _ => {
6410 zwarnnam(nam, &format!("invalid option: {}", args[0]));
6411 return 1;
6412 }
6413 };
6414 let n = (args.len() as i32) - 1;
6415 if n < min {
6416 zwarnnam(nam, "not enough arguments");
6417 return 1;
6418 }
6419 if max >= 0 && n > max {
6420 zwarnnam(nam, "too many arguments");
6421 return 1;
6422 }
6423
6424 match sub {
6425 b'i' => {
6426 settags(level, &args[1..]);
6428 lasttaglevel.store(level, Ordering::Relaxed); 0
6430 }
6431 b'C' => {
6432 let ctx = {
6434 let tab = comptags.lock().unwrap();
6435 tab[lvl_idx]
6436 .as_ref()
6437 .and_then(|t| t.context.clone())
6438 .unwrap_or_default()
6439 };
6440 setsparam(&args[1], &ctx); 0
6442 }
6443 b'T' => {
6444 let empty = {
6446 let tab = comptags.lock().unwrap();
6447 tab[lvl_idx].as_ref().map_or(true, |t| t.sets.is_none())
6448 };
6449 if empty {
6450 1
6451 } else {
6452 0
6453 } }
6455 b'N' => {
6456 let mut tab = comptags.lock().unwrap();
6458 if let Some(t) = tab[lvl_idx].as_mut() {
6459 if t.init != 0 {
6460 t.init = 0;
6462 } else if let Some(mut s) = t.sets.take() {
6463 t.sets = s.next.take(); freectset(Some(s)); }
6467 if t.sets.is_some() {
6468 0
6469 } else {
6470 1
6471 } } else {
6473 1
6474 }
6475 }
6476 b'R' => {
6477 let tab = comptags.lock().unwrap();
6479 let hit = tab[lvl_idx]
6480 .as_ref()
6481 .and_then(|t| t.sets.as_ref())
6482 .map(|s| {
6483 s.tags
6484 .as_deref()
6485 .map_or(false, |tgs| arrcontains(tgs, &args[1], true) != 0)
6486 })
6487 .unwrap_or(false);
6488 if hit {
6489 0
6490 } else {
6491 1
6492 } }
6494 b'A' => {
6495 let mut tab = comptags.lock().unwrap();
6497 let Some(t) = tab[lvl_idx].as_mut() else {
6498 return 1;
6499 };
6500 let Some(s) = t.sets.as_mut() else {
6501 return 1;
6502 };
6503 if s.tag.as_deref() != Some(args[1].as_str()) {
6505 s.tag = Some(args[1].clone()); s.ptr = 0; }
6508 let tags_vec = s.tags.clone().unwrap_or_default();
6509 let mut found: Option<(usize, String, String)> = None;
6511 for (i, q) in tags_vec.iter().enumerate().skip(s.ptr as usize) {
6512 if strpfx(&args[1], q) {
6513 let l = args[1].len();
6515 let qb = q.as_bytes();
6516 if qb.len() == l {
6517 found = Some((i, q.clone(), q.clone()));
6519 break;
6520 } else if qb.len() > l && qb[l] == b':' {
6521 let v = String::from_utf8_lossy(&qb[l + 1..]).into_owned();
6523 found = Some((i, q.clone(), v));
6524 break;
6525 }
6526 }
6527 }
6528 let (q_idx, q_full, v) = match found {
6529 None => {
6530 s.tag = None;
6532 return 1;
6533 }
6534 Some(t) => t,
6535 };
6536 s.ptr = (q_idx + 1) as i32; let value = if v.starts_with('-') {
6539 crate::ported::string::dyncat(&args[1], &v)
6540 } else {
6541 v.clone()
6542 };
6543 setsparam(&args[2], &value);
6544 if args.len() > 3 {
6546 let pre_colon: String = q_full.splitn(2, ':').next().unwrap_or("").to_string();
6547 setsparam(&args[3], &pre_colon);
6548 }
6549 0 }
6551 b'S' => {
6552 let tab = comptags.lock().unwrap();
6554 if let Some(tags) = tab[lvl_idx]
6555 .as_ref()
6556 .and_then(|t| t.sets.as_ref())
6557 .and_then(|s| s.tags.clone())
6558 {
6559 setaparam(&args[1], tags); 0
6561 } else {
6562 1 }
6564 }
6565 _ => 0,
6566 }
6567}
6568
6569pub fn bin_comptry(
6583 nam: &str,
6584 args: &[String], _ops: &options,
6586 _func: i32,
6587) -> i32 {
6588 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
6589 zwarnnam(nam, "can only be called from completion function");
6591 return 1;
6592 }
6593
6594 let lvl = lasttaglevel.load(Ordering::Relaxed);
6595 if lvl <= 0 {
6596 zwarnnam(nam, "no tags registered");
6598 return 1;
6599 }
6600 let lvl_idx = lvl as usize;
6601 let registered = comptags
6602 .lock()
6603 .ok()
6604 .map(|t| lvl_idx < t.len() && t[lvl_idx].is_some())
6605 .unwrap_or(false);
6606 if !registered {
6607 zwarnnam(nam, "no tags registered");
6608 return 1;
6609 }
6610
6611 if args.is_empty() {
6612 return 0;
6613 } let append_set = |tags: Vec<String>| {
6617 if let Ok(mut tab) = comptags.lock() {
6618 if let Some(t) = tab[lvl_idx].as_mut() {
6619 let new_set = Box::new(ctset {
6620 next: None,
6621 tags: Some(tags),
6622 tag: None,
6623 ptr: 0,
6624 });
6625 if let Some(head) = t.sets.as_mut() {
6627 let mut cur = head.as_mut();
6628 while cur.next.is_some() {
6629 cur = cur.next.as_mut().unwrap();
6630 }
6631 cur.next = Some(new_set);
6632 } else {
6633 t.sets = Some(new_set);
6634 }
6635 }
6636 }
6637 };
6638
6639 if args[0] == "-m" {
6640 for arg in &args[1..] {
6643 let mut s = arg.as_bytes().to_vec();
6645 let mut list: Vec<String> = Vec::new();
6646 let mut num = 0i32;
6647 let mut i = 0usize;
6648
6649 while i < s.len() {
6650 while i < s.len() && iblank(s[i]) {
6652 i += 1;
6653 }
6654 if i >= s.len() {
6655 break;
6656 }
6657 let p_start = i;
6660 let mut p_pos = i;
6661 let mut colon_at: Option<usize> = None;
6662 while i < s.len() && !inblank(s[i]) {
6663 if colon_at.is_none() && s[i] == b':' {
6664 colon_at = Some(p_pos);
6665 }
6666 if s[i] == b'\\' && i + 1 < s.len() {
6667 i += 1;
6668 }
6669 s[p_pos] = s[i];
6670 p_pos += 1;
6671 i += 1;
6672 }
6673 if i < s.len() {
6675 i += 1;
6676 }
6677
6678 let token_full = String::from_utf8_lossy(&s[p_start..p_pos]).into_owned();
6679 if token_full.is_empty() {
6680 continue;
6681 }
6682
6683 let (q, c_opt): (String, Option<String>) = match colon_at {
6685 Some(c_idx) => {
6686 let head = String::from_utf8_lossy(&s[p_start..c_idx]).into_owned();
6687 let tail = String::from_utf8_lossy(&s[c_idx + 1..p_pos]).into_owned();
6688 (head, Some(tail))
6689 }
6690 None => (token_full.clone(), None),
6691 };
6692 if q.is_empty() {
6693 continue;
6694 }
6695
6696 let mut qq: String = q
6699 .chars()
6700 .map(|ch| match ch {
6701 '\\' => ch, '{' => Inbrace,
6703 '}' => Outbrace,
6704 ',' => Comma,
6705 other => other,
6706 })
6707 .collect();
6708 tokenize(&mut qq);
6711
6712 let has_meta = hasbraces(&qq, false) || haswilds(&qq);
6714 let all_arr: Vec<String> = comptags
6715 .lock()
6716 .ok()
6717 .and_then(|t| t[lvl_idx].as_ref().and_then(|c| c.all.clone()))
6718 .unwrap_or_default();
6719 let sets_clone: Vec<Vec<String>> = comptags
6720 .lock()
6721 .ok()
6722 .map(|t| {
6723 let mut out = Vec::new();
6724 if let Some(c) = t[lvl_idx].as_ref() {
6725 let mut p = c.sets.as_deref();
6726 while let Some(set) = p {
6727 if let Some(ts) = set.tags.as_ref() {
6728 out.push(ts.clone());
6729 }
6730 p = set.next.as_deref();
6731 }
6732 }
6733 out
6734 })
6735 .unwrap_or_default();
6736
6737 if has_meta {
6738 let mut blist: Vec<String> = vec![qq.clone()];
6740 let mut bi = 0usize;
6741 while bi < blist.len() {
6742 if hasbraces(&blist[bi], false) {
6743 let expanded = xpandbraces(&blist[bi], false);
6744 blist.remove(bi);
6745 for e in expanded {
6746 blist.insert(bi, e);
6747 bi += 1;
6748 }
6749 } else {
6750 bi += 1;
6751 }
6752 }
6753 for bb in &blist {
6754 if let Some(prog) = patcompile(bb, 0, None::<&mut String>) {
6756 for a in &all_arr {
6757 let already = list.iter().any(|item| {
6760 let item_head = item.split(':').next().unwrap_or(item);
6761 item_head == a.as_str()
6762 });
6763 if already {
6764 continue;
6765 }
6766 if pattry(&prog, a) {
6767 let entry = match &c_opt {
6769 Some(c) => format!("{}:{}", a, c),
6770 None => a.clone(),
6771 };
6772 list.push(entry);
6773 num += 1;
6774 }
6775 }
6776 }
6777 }
6778 } else if arrcontains(&all_arr, &q, false) != 0 {
6779 let in_set = sets_clone.iter().any(|s| arrcontains(s, &q, false) != 0);
6782 if !in_set {
6783 list.push(q.clone());
6784 num += 1;
6785 }
6786 }
6787 }
6788
6789 if num > 0 {
6790 append_set(list);
6792 }
6793 }
6794 } else {
6795 let mut idx = 0usize;
6797 let sep = args[idx] == "-s"; if sep {
6799 idx += 1;
6800 }
6801 let all_arr: Vec<String> = comptags
6802 .lock()
6803 .ok()
6804 .and_then(|t| t[lvl_idx].as_ref().and_then(|c| c.all.clone()))
6805 .unwrap_or_default();
6806 let sets_clone: Vec<Vec<String>> = comptags
6807 .lock()
6808 .ok()
6809 .map(|t| {
6810 let mut out = Vec::new();
6811 if let Some(c) = t[lvl_idx].as_ref() {
6812 let mut p = c.sets.as_deref();
6813 while let Some(set) = p {
6814 if let Some(ts) = set.tags.as_ref() {
6815 out.push(ts.clone());
6816 }
6817 p = set.next.as_deref();
6818 }
6819 }
6820 out
6821 })
6822 .unwrap_or_default();
6823
6824 let filtered: Vec<String> = args[idx..]
6826 .iter()
6827 .filter(|p| {
6828 arrcontains(&all_arr, p, true) != 0
6829 && !sets_clone.iter().any(|s| arrcontains(s, p, false) != 0)
6830 })
6831 .cloned()
6832 .collect();
6833
6834 if filtered.is_empty() {
6835 return 0;
6836 }
6837
6838 if sep {
6840 for t in &filtered {
6841 append_set(vec![t.clone()]);
6842 }
6843 } else {
6844 append_set(filtered);
6845 }
6846 }
6847 0 }
6849
6850pub const PATH_MAX2: usize = 8192; pub fn cfp_test_exact(
6863 names: &[String],
6864 accept: &[String], skipped: &str,
6866) -> Option<Vec<String>> {
6867 let compprefix = COMPPREFIX
6868 .get()
6869 .and_then(|m| m.lock().ok().map(|s| s.clone()))
6870 .unwrap_or_default();
6871 let compsuffix = COMPSUFFIX
6872 .get()
6873 .and_then(|m| m.lock().ok().map(|s| s.clone()))
6874 .unwrap_or_default();
6875
6876 if compprefix.is_empty() && compsuffix.is_empty() {
6878 return None;
6879 }
6880
6881 let accept_off = accept.is_empty()
6883 || (accept.len() == 1 && matches!(accept[0].as_str(), "false" | "no" | "off" | "0"));
6884 if accept_off {
6885 return None;
6887 }
6888
6889 let mut alist: Option<Vec<Patprog>> = None;
6891 let is_boolean_true =
6892 accept.len() == 1 && matches!(accept[0].as_str(), "true" | "yes" | "on" | "1");
6893 if !is_boolean_true {
6894 let mut list: Vec<Patprog> = Vec::new();
6895 let mut all_star = false;
6896 for p in accept {
6897 if p == "*" {
6898 all_star = true;
6900 break;
6901 }
6902 let mut p_copy = p.clone();
6903 tokenize(&mut p_copy);
6904 if let Some(prog) = patcompile(&p_copy, 0, None::<&mut String>) {
6905 list.push(prog);
6906 }
6907 }
6908 if !all_star {
6909 alist = Some(list);
6910 }
6911 }
6912
6913 let sl = skipped.len() + compprefix.len() + compsuffix.len();
6915 if sl > PATH_MAX2 {
6916 return None;
6918 }
6919 let suf = format!(
6920 "{}{}",
6921 skipped,
6922 rembslash(&format!("{}{}", compprefix, compsuffix))
6923 );
6924
6925 let mut ret: Vec<String> = Vec::new();
6926 for p in names {
6927 let l = p.len();
6929 if l + sl >= PATH_MAX2 {
6930 continue;
6931 } let buf = format!("{}{}", p, suf);
6933 if ztat(&buf, false).is_none() {
6934 continue;
6935 } if let Some(ref ps) = alist {
6938 let any_match = ps.iter().any(|prog| pattry(prog, &buf));
6939 if !any_match {
6940 continue;
6941 }
6942 }
6943 ret.push(buf); }
6945
6946 if ret.is_empty() {
6947 None
6948 } else {
6949 Some(ret)
6950 } }
6952
6953pub fn cfp_matcher_range(
6964 ms: &[Option<Box<Cmatcher>>], add: &str,
6966) -> String {
6967 fn patmatchrange_local(s: Option<&[u8]>, c: u32) -> Option<(u32, i32)> {
6975 let bytes = s?;
6976 let pp_range_marker = (0x80u8).wrapping_add(PP_RANGE as u8);
6977 let pp_lower_marker = (0x80u8).wrapping_add(PP_LOWER as u8);
6978 let pp_upper_marker = (0x80u8).wrapping_add(PP_UPPER as u8);
6979
6980 let mut idx: u32 = 0;
6981 let mut i = 0usize;
6982 while i < bytes.len() {
6983 let b = bytes[i];
6984 if b == pp_range_marker {
6985 if i + 2 >= bytes.len() {
6986 break;
6987 }
6988 let r1 = bytes[i + 1] as u32;
6989 let r2 = bytes[i + 2] as u32;
6990 if c >= r1 && c <= r2 {
6991 return Some((idx, 0));
6992 }
6993 idx += 1;
6994 i += 3;
6995 } else if b >= 0x80 {
6996 let is_lower = b == pp_lower_marker;
6997 let is_upper = b == pp_upper_marker;
6998 let matched = if is_lower {
6999 c < 256 && (c as u8).is_ascii_lowercase()
7000 } else if is_upper {
7001 c < 256 && (c as u8).is_ascii_uppercase()
7002 } else {
7003 false
7004 };
7005 if matched {
7006 return Some((idx, (b as i32) - 0x80));
7007 }
7008 idx += 1;
7009 i += 1;
7010 } else {
7011 if c == b as u32 {
7012 return Some((idx, 0));
7013 }
7014 idx += 1;
7015 i += 1;
7016 }
7017 }
7018 None
7019 }
7020
7021 let mut out = String::with_capacity(add.len() * 2);
7022 let add_chars: Vec<(usize, char)> = add.char_indices().collect();
7023
7024 for (i, (_byte_idx, ch)) in add_chars.iter().enumerate() {
7025 let addc = *ch as u32;
7026 let m_opt = ms.get(i).and_then(|x| x.as_deref());
7027
7028 match m_opt {
7029 None => {
7030 out.push(*ch);
7032 }
7033 Some(m) if (m.flags & CMF_RIGHT) != 0 => {
7034 out.push('*');
7036 out.push(*ch);
7037 }
7038 Some(m) => {
7039 let word: Option<&Cpattern> = m.word.as_deref();
7040 let line: Option<&Cpattern> = m.line.as_deref();
7041 if let (Some(l), Some(w)) = (line, word) {
7042 if l.tp == CPAT_EQUIV && w.tp == CPAT_EQUIV {
7043 out.push('[');
7045 out.push(*ch);
7046 if let Some((ind, mtp)) = patmatchrange_local(l.str.as_deref(), addc) {
7047 let eq = pattern_match_equivalence(w, ind + 1, mtp, addc);
7048 if eq != u32::MAX {
7049 if let Some(c) = char::from_u32(eq) {
7050 let _ = if eq <= 0xff { imeta(eq as u8) } else { false };
7054 out.push(c);
7055 }
7056 }
7057 }
7058 out.push(']');
7059 continue;
7060 }
7061 }
7062 fn decode_range_bytes(bytes: &[u8]) -> String {
7067 let pp_range_marker = (0x80u8).wrapping_add(PP_RANGE as u8);
7068 let mut out = String::new();
7069 let mut i = 0usize;
7070 while i < bytes.len() {
7071 let b = bytes[i];
7072 if b == pp_range_marker && i + 2 < bytes.len() {
7073 out.push(bytes[i + 1] as char);
7074 out.push('-');
7075 out.push(bytes[i + 2] as char);
7076 i += 3;
7077 } else if b >= 0x80 {
7078 let cls = (b as usize) - 0x80;
7079 const POSIX_CLASSES: &[&str] = &[
7083 "alpha", "alnum", "blank", "cntrl", "digit", "graph", "lower",
7084 "print", "punct", "space", "upper", "xdigit",
7085 ];
7086 if cls > 0 && cls - 1 < POSIX_CLASSES.len() {
7087 out.push_str(&format!("[:{}:]", POSIX_CLASSES[cls - 1]));
7088 }
7089 i += 1;
7090 } else {
7091 out.push(b as char);
7092 i += 1;
7093 }
7094 }
7095 out
7096 }
7097
7098 if let Some(w) = word {
7099 match w.tp {
7100 x if x == CPAT_NCLASS => {
7101 out.push('[');
7103 out.push('^');
7104 if let Some(bytes) = w.str.as_deref() {
7105 out.push_str(&decode_range_bytes(bytes));
7106 }
7107 out.push(']');
7108 }
7109 x if x == CPAT_CCLASS || x == CPAT_EQUIV || x == CPAT_CHAR => {
7110 out.push('[');
7112 let mut mt = 0i32;
7113 let addadd = pattern_match1(w, addc, &mut mt) == 0;
7114 if addadd && *ch == ']' {
7116 out.push(*ch);
7117 }
7118 if w.tp == CPAT_CHAR {
7119 if let Some(c) = char::from_u32(w.chr) {
7121 out.push(c);
7122 }
7123 } else {
7124 if let Some(bytes) = w.str.as_deref() {
7126 out.push_str(&decode_range_bytes(bytes));
7127 }
7128 }
7129 if addadd && *ch != ']' {
7130 out.push(*ch);
7132 }
7133 out.push(']');
7134 }
7135 x if x == CPAT_ANY => {
7136 out.push('?');
7138 }
7139 _ => {
7140 out.push(*ch);
7142 }
7143 }
7144 } else {
7145 out.push(*ch);
7146 }
7147 }
7148 }
7149 }
7150 out
7151}
7152
7153pub fn cfp_matcher_pats(matcher: &str, add: &str) -> String {
7165 let m_chain = parse_cmatcher("", matcher);
7169 let Some(mut m_chain) = m_chain else {
7170 return add.to_string(); };
7172
7173 let zl = ztrlen(add); let mut ms: Vec<Option<Box<Cmatcher>>> = (0..zl).map(|_| None).collect();
7176 let mut add_owned = add.to_string();
7177
7178 let mut m_opt: Option<&Cmatcher> = Some(&*m_chain);
7179 while let Some(m) = m_opt {
7180 let mut stopp: Option<&Cpattern> = None;
7181 let mut stopl: i32 = 0;
7182
7183 if (m.flags & (CMF_LEFT | CMF_RIGHT)) == 0 {
7184 if m.llen == 1 && m.wlen == 1 {
7186 let chars: Vec<(usize, char)> = add_owned.char_indices().collect();
7190 for (i, (byte_idx, _ch)) in chars.iter().enumerate() {
7191 if i >= ms.len() {
7192 break;
7193 }
7194 let slice = &add_owned[*byte_idx..];
7195 if pattern_match(m.line.as_deref(), slice, None, "") != 0 {
7196 if ms[i].is_some() {
7198 add_owned.truncate(*byte_idx); break;
7200 } else {
7201 ms[i] = Some(Box::new(m.clone())); }
7203 }
7204 }
7205 } else {
7206 stopp = m.line.as_deref(); stopl = m.llen;
7208 }
7209 } else if (m.flags & CMF_RIGHT) != 0 {
7210 if m.wlen < 0 && m.llen == 0 && m.ralen == 1 {
7212 let chars: Vec<(usize, char)> = add_owned.char_indices().collect();
7214 for (i, (byte_idx, _ch)) in chars.iter().enumerate() {
7215 if i >= ms.len() {
7216 break;
7217 }
7218 let slice = &add_owned[*byte_idx..];
7219 if pattern_match(m.right.as_deref(), slice, None, "") != 0 {
7220 let leading_dot = *byte_idx == 0 && slice.starts_with('.');
7222 if ms[i].is_some() || leading_dot {
7223 add_owned.truncate(*byte_idx); break;
7225 } else {
7226 ms[i] = Some(Box::new(m.clone()));
7227 }
7228 }
7229 }
7230 } else if m.llen != 0 {
7231 stopp = m.line.as_deref();
7233 stopl = m.llen;
7234 } else {
7235 stopp = m.right.as_deref(); stopl = m.ralen;
7237 }
7238 } else {
7239 if m.lalen == 0 {
7241 return String::new(); }
7244 stopp = m.left.as_deref();
7245 stopl = m.lalen;
7246 }
7247
7248 if let Some(sp) = stopp {
7250 let chars: Vec<(usize, char)> = add_owned.char_indices().collect();
7251 let mut bytes_remaining = add_owned.len() as i32;
7252 for (_i, (byte_idx, _ch)) in chars.iter().enumerate() {
7253 if bytes_remaining < stopl {
7254 break;
7255 }
7256 let slice = &add_owned[*byte_idx..];
7257 if pattern_match(Some(sp), slice, None, "") != 0 {
7258 add_owned.truncate(*byte_idx); break;
7260 }
7261 bytes_remaining -= 1;
7262 }
7263 }
7264
7265 m_opt = m.next.as_deref();
7266 }
7267
7268 if !add_owned.is_empty() {
7270 cfp_matcher_range(&ms, &add_owned)
7271 } else {
7272 add_owned }
7274}
7275
7276pub fn cfp_opt_pats(pats: &[String], matcher: &str) -> Vec<String> {
7285 let compprefix = COMPPREFIX
7288 .get()
7289 .and_then(|m| m.lock().ok().map(|s| s.clone()))
7290 .unwrap_or_default();
7291 if compprefix.is_empty() {
7292 return pats.to_vec();
7294 }
7295 let compsuffix = COMPSUFFIX
7296 .get()
7297 .and_then(|m| m.lock().ok().map(|s| s.clone()))
7298 .unwrap_or_default();
7299
7300 let cpm_set = comppatmatch
7302 .get()
7303 .and_then(|m| m.lock().ok().map(|g| g.is_some()))
7304 .unwrap_or(false);
7305 if cpm_set {
7306 let merged = format!("{}{}", compprefix, compsuffix);
7307 let mut t = rembslash(&merged);
7308 tokenize(&mut t);
7309 remnulargs(&mut t);
7310 if haswilds(&t) {
7311 return pats.to_vec(); }
7313 }
7314
7315 const SPECIALS: &[u8] = b"*?<>()[]|#^~=";
7318 let cp_bytes = compprefix.as_bytes();
7319 let mut add: Vec<u8> = Vec::with_capacity(cp_bytes.len() * 2);
7320 let mut i = 0usize;
7321 while i < cp_bytes.len() {
7322 let c = cp_bytes[i];
7323 let keep = if c == b'\\' && i + 1 < cp_bytes.len() {
7324 let next = cp_bytes[i + 1];
7326 !SPECIALS.contains(&next)
7327 } else {
7328 true
7329 };
7330 if keep {
7331 let unescaped_at_start = i == 0 || cp_bytes[i - 1] != b'\\';
7332 if unescaped_at_start && SPECIALS.contains(&c) {
7333 add.push(b'\\');
7335 }
7336 add.push(c);
7337 }
7338 i += 1;
7339 }
7340 let mut add_s: String = String::from_utf8_lossy(&add).into_owned();
7341
7342 for p_orig in pats {
7344 if add_s.is_empty() {
7345 break;
7346 }
7347 let mut q_bytes: Vec<u8> = p_orig.as_bytes().to_vec();
7348 if q_bytes.is_empty() {
7349 continue;
7350 }
7351 if let Some(b')') = q_bytes.last().copied() {
7353 let mut t = q_bytes.len() - 1;
7354 let mut found = None;
7355 while t > 0 {
7356 t -= 1;
7357 let c = q_bytes[t];
7358 if c == b')' || c == b'|' || c == b'~' || c == b'(' {
7359 found = Some((t, c));
7360 break;
7361 }
7362 }
7363 if let Some((idx, c)) = found {
7364 if c == b'(' {
7365 q_bytes.truncate(idx);
7366 }
7367 }
7368 }
7369
7370 let mut qi = 0usize;
7371 while qi < q_bytes.len() && !add_s.is_empty() {
7372 let c = q_bytes[qi];
7373 if c == b'\\' && qi + 1 < q_bytes.len() {
7374 qi += 1;
7376 let target = q_bytes[qi];
7377 if let Some(pos) = add_s.find(target as char) {
7379 add_s.truncate(pos);
7380 }
7381 } else if c == b'<' {
7382 let cut_at = add_s.bytes().position(|b| idigit(b));
7385 if let Some(pos) = cut_at {
7386 add_s.truncate(pos);
7387 }
7388 } else if c == b'[' {
7389 let mut xi = qi + 1;
7392 let not = xi < q_bytes.len() && (q_bytes[xi] == b'!' || q_bytes[xi] == b'^');
7393 if not {
7394 xi += 1;
7395 }
7396 let _ = not;
7397 while xi < q_bytes.len() && q_bytes[xi] != b']' {
7398 if xi + 2 < q_bytes.len() && q_bytes[xi + 1] == b'-' {
7399 let c1 = q_bytes[xi];
7400 let c2 = q_bytes[xi + 2];
7401 let cut_at = add_s.bytes().position(|b| b >= c1 && b <= c2);
7402 if let Some(pos) = cut_at {
7403 add_s.truncate(pos);
7404 }
7405 xi += 3;
7406 } else {
7407 let cut_at = add_s.find(q_bytes[xi] as char);
7408 if let Some(pos) = cut_at {
7409 add_s.truncate(pos);
7410 }
7411 xi += 1;
7412 }
7413 }
7414 qi = xi;
7415 } else if c != b'?'
7416 && c != b'*'
7417 && c != b'('
7418 && c != b')'
7419 && c != b'|'
7420 && c != b'~'
7421 && c != b'#'
7422 {
7424 let cut_at = add_s.find(c as char);
7425 if let Some(pos) = cut_at {
7426 add_s.truncate(pos);
7427 }
7428 }
7429 qi += 1;
7430 }
7431 }
7432
7433 let mut out: Vec<String> = pats.to_vec();
7435 if !add_s.is_empty() {
7436 let final_add = if !matcher.is_empty() {
7437 let m = cfp_matcher_pats(matcher, &add_s);
7438 if m.is_empty() {
7439 return out;
7440 } m
7442 } else {
7443 add_s
7444 };
7445 for p in out.iter_mut() {
7446 if p.starts_with('*') {
7447 *p = format!("{}{}", final_add, p);
7448 }
7449 }
7450 }
7451 out
7452}
7453
7454pub fn cfp_bld_pats(
7462 _dirs: i32,
7463 names: &[String],
7464 skipped: &str, pats: &[String],
7466) -> Vec<String> {
7467 let compprefix = COMPPREFIX
7468 .get()
7469 .and_then(|m| m.lock().ok().map(|s| s.clone()))
7470 .unwrap_or_default();
7471 let dot = unset(GLOBDOTS) && compprefix.starts_with('.');
7473
7474 let mut ret: Vec<String> = Vec::new();
7475 for o in names {
7476 for p in pats {
7478 ret.push(format!("{}{}{}", o, skipped, p));
7481 if dot && !p.starts_with('.') {
7484 ret.push(format!("{}{}.{}", o, skipped, p));
7485 }
7486 }
7487 }
7488 ret }
7490
7491pub fn cfp_add_sdirs(
7494 final_list: &mut Vec<String>,
7495 orig: &[String], _skipped: &str,
7497 sdirs: &str,
7498 fake: &[String],
7499) {
7500 let mut add = 0;
7504 if !sdirs.is_empty() {
7505 match sdirs {
7507 "yes" | "true" | "on" | "1" => add = 2, ".." => add = 1, _ => {}
7510 }
7511 }
7512 if add > 0 {
7513 for f in fake {
7514 final_list.push(f.clone());
7515 }
7516 for o in orig {
7517 if !final_list.contains(o) {
7518 final_list.push(o.clone());
7519 }
7520 }
7521 }
7522}
7523
7524pub fn cf_pats(
7547 dirs: i32,
7548 noopt: i32,
7549 names: &[String], accept: &[String],
7551 skipped: &str,
7552 matcher: &str,
7553 sdirs: &str,
7554 fake: &[String],
7555 pats: &[String],
7556) -> Vec<String> {
7557 if let Some(exact) = cfp_test_exact(names, accept, skipped) {
7559 let mut out = exact;
7560 cfp_add_sdirs(&mut out, names, skipped, sdirs, fake); return out;
7562 }
7563
7564 let dir_pats = vec!["*(-/)".to_string()];
7566 let active_pats: Vec<String> = if dirs != 0 {
7567 dir_pats
7568 } else if noopt == 0 {
7569 cfp_opt_pats(pats, matcher)
7571 } else {
7572 pats.to_vec()
7573 };
7574
7575 let mut out = cfp_bld_pats(dirs, names, skipped, &active_pats);
7577 cfp_add_sdirs(&mut out, names, skipped, sdirs, fake);
7578 out
7579}
7580
7581pub fn cf_ignore(names: &[String], ign: &mut Vec<String>, style: &str, path: &str) {
7592 let pl = path.len();
7595 let tpar = style.contains("parent"); let pwd = crate::ported::params::getsparam("PWD").unwrap_or_default();
7597 let est = if !pwd.is_empty() {
7598 ztat(&pwd, true)
7599 } else {
7600 None
7601 };
7602 let tpwd = style.contains("pwd") && est.is_some(); if !tpar && !tpwd {
7605 return;
7606 } for n in names {
7609 let nst = match ztat(n, true) {
7611 Some(m) if m.is_dir() => m,
7613 _ => continue,
7614 };
7615 if tpwd {
7616 if let Some(ref est) = est {
7617 if nst.dev() == est.dev() && nst.ino() == est.ino() {
7618 ign.push(quotestring(n, QT_BACKSLASH)); continue;
7621 }
7622 }
7623 }
7624 if tpar && pl > 0 && n.starts_with(path) {
7625 let mut c = n.clone();
7627 let mut found = false;
7628 while let Some(idx) = c.rfind('/') {
7630 if idx <= pl {
7631 break;
7632 }
7633 c.truncate(idx);
7634 if let Some(st) = ztat(&c, false) {
7635 if st.dev() == nst.dev() && st.ino() == nst.ino() {
7637 found = true;
7638 break;
7639 }
7640 }
7641 }
7642 let last_match = if !found {
7644 if let Some(idx) = c.rfind('/') {
7645 if idx > pl {
7646 c.truncate(idx);
7647 ztat(&c, true)
7648 .map_or(false, |st| st.dev() == nst.dev() && st.ino() == nst.ino())
7649 } else {
7650 false
7651 }
7652 } else {
7653 false
7654 }
7655 } else {
7656 false
7657 };
7658 if found || last_match {
7659 ign.push(quotestring(n, QT_BACKSLASH)); }
7661 }
7662 }
7663}
7664
7665pub fn cf_remove_other(names: &[String], pre: &str, amb: &mut i32) -> Option<Vec<String>> {
7673 if let Some(slash) = pre.find('/') {
7674 let pre2 = format!("{}/", &pre[..slash]);
7677
7678 let any_match = names.iter().any(|n| strpfx(&pre2, n));
7680
7681 if any_match {
7682 let ret: Vec<String> = names.iter().filter(|n| strpfx(&pre2, n)).cloned().collect();
7685 *amb = 0;
7686 return Some(ret); } else {
7688 let mut it = names.iter();
7691 let Some(first) = it.next() else {
7692 *amb = 0; return None;
7694 };
7695 let p_head = match first.find('/') {
7697 Some(i) => format!("{}/", &first[..i]),
7698 None => format!("{}/", first),
7699 };
7700 for n in it {
7701 if !strpfx(&p_head, n) {
7703 *amb = 1; return None;
7705 }
7706 }
7707 }
7709 } else {
7710 let mut it = names.iter();
7713 let Some(first) = it.next() else {
7714 *amb = 0;
7715 return None;
7716 };
7717 for n in it {
7718 if first != n {
7720 *amb = 1;
7722 return None;
7723 }
7724 }
7725 }
7726 None }
7728
7729pub fn bin_compfiles(
7736 nam: &str,
7737 args: &[String], _ops: &options,
7739 _func: i32,
7740) -> i32 {
7741 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
7742 zwarnnam(nam, "can only be called from completion function");
7744 return 1;
7745 }
7746 if args.is_empty() || !args[0].starts_with('-') {
7747 let bad = args.first().map(|s| s.as_str()).unwrap_or("");
7749 zwarnnam(nam, &format!("missing option: {}", bad));
7750 return 1;
7751 }
7752 let a0 = args[0].as_bytes();
7753 if a0.len() < 2 {
7754 zwarnnam(nam, &format!("missing option: {}", args[0]));
7755 return 1;
7756 }
7757 let sub = a0[1];
7758
7759 let get_arr = |name: &str| -> Option<Vec<String>> {
7761 paramtab()
7762 .read()
7763 .ok()
7764 .and_then(|tab| tab.get(name).and_then(|pm| pm.u_arr.clone()))
7765 };
7766
7767 match sub {
7768 b'p' | b'P' => {
7769 let noopt = a0.len() > 2;
7772 if noopt && (a0.len() != 4 || a0[2] != b'-' || a0[3] != b'-') {
7773 zwarnnam(nam, &format!("invalid option: {}", args[0]));
7774 return 1;
7775 }
7776 let required = if sub == b'p' { 8 } else { 7 };
7777 if args.len() <= required {
7778 zwarnnam(nam, "too few arguments");
7780 return 1;
7781 }
7782 let Some(src) = get_arr(&args[1]) else {
7784 zwarnnam(nam, &format!("unknown parameter: {}", args[1]));
7785 return 0;
7786 };
7787 let l: Vec<String> = src
7789 .iter()
7790 .map(|s| quotestring(s, QT_BACKSLASH_PATTERN))
7791 .collect();
7792 let result = cf_pats(
7794 if sub == b'P' { 1 } else { 0 },
7795 if noopt { 1 } else { 0 },
7796 &l,
7797 &get_arr(&args[2]).unwrap_or_default(),
7798 &args[3],
7799 &args[4],
7800 &args[5],
7801 &get_arr(&args[6]).unwrap_or_default(),
7802 &args[7..],
7803 );
7804 setaparam(&args[1], result);
7805 0
7806 }
7807 b'i' => {
7808 if a0.len() > 2 {
7810 zwarnnam(nam, &format!("invalid option: {}", args[0]));
7812 return 1;
7813 }
7814 if args.len() < 5 {
7815 zwarnnam(nam, "too few arguments");
7817 return 1;
7818 }
7819 if args.len() > 5 {
7820 zwarnnam(nam, "too many arguments");
7822 return 1;
7823 }
7824 let mut l: Vec<String> = get_arr(&args[2]).unwrap_or_default();
7825 let Some(tmp) = get_arr(&args[1]) else {
7826 zwarnnam(nam, &format!("unknown parameter: {}", args[1]));
7828 return 0;
7829 };
7830 cf_ignore(&tmp, &mut l, &args[3], &args[4]); setaparam(&args[2], l); 0
7833 }
7834 b'r' => {
7835 if args.len() < 3 {
7837 zwarnnam(nam, "too few arguments");
7839 return 1;
7840 }
7841 if args.len() > 3 {
7842 zwarnnam(nam, "too many arguments");
7844 return 1;
7845 }
7846 let Some(tmp) = get_arr(&args[1]) else {
7847 zwarnnam(nam, &format!("unknown parameter: {}", args[1]));
7849 return 0;
7850 };
7851 let mut ret = 0i32;
7852 if let Some(l) = cf_remove_other(&tmp, &args[2], &mut ret) {
7854 setaparam(&args[1], l);
7855 }
7856 ret }
7858 _ => {
7859 zwarnnam(nam, &format!("invalid option: {}", args[0]));
7860 1
7861 }
7862 }
7863}
7864
7865pub fn bin_compgroups(
7875 nam: &str,
7876 args: &[String], _ops: &options,
7878 _func: i32,
7879) -> i32 {
7880 if INCOMPFUNC.load(Ordering::Relaxed) != 1 {
7881 zwarnnam(nam, "can only be called from completion function");
7883 return 1;
7884 }
7885 for n in args {
7887 endcmgroup(None); begcmgroup(Some(n), CGF_NOSORT | CGF_UNIQCON); endcmgroup(None);
7891 begcmgroup(Some(n), CGF_UNIQALL); endcmgroup(None);
7893 begcmgroup(Some(n), CGF_NOSORT | CGF_UNIQCON); endcmgroup(None);
7895 begcmgroup(Some(n), CGF_UNIQALL); endcmgroup(None);
7897 begcmgroup(Some(n), CGF_NOSORT); endcmgroup(None);
7899 begcmgroup(Some(n), 0); }
7901 0 }
7903
7904pub fn setup_() -> i32 {
7908 if let Ok(mut cache) = cadef_cache.lock() {
7911 for slot in cache.iter_mut() {
7912 freecadef(slot.take());
7913 }
7914 }
7915 if let Ok(mut cache) = cvdef_cache.lock() {
7917 for slot in cache.iter_mut() {
7918 freecvdef(slot.take());
7919 }
7920 }
7921 if let Ok(mut tab) = comptags.lock() {
7923 for slot in tab.iter_mut() {
7924 freectags(slot.take());
7925 }
7926 }
7927 lasttaglevel.store(0, Ordering::Relaxed);
7929 0 }
7931
7932pub fn features_() -> i32 {
7935 0
7939}
7940
7941pub fn enables_() -> i32 {
7944 0
7948}
7949
7950pub fn boot_() -> i32 {
7953 0
7956}
7957
7958pub fn cleanup_() -> i32 {
7961 0
7965}
7966
7967pub fn finish_() -> i32 {
7971 if let Ok(mut cache) = cadef_cache.lock() {
7974 for slot in cache.iter_mut() {
7975 freecadef(slot.take());
7976 }
7977 }
7978 if let Ok(mut cache) = cvdef_cache.lock() {
7980 for slot in cache.iter_mut() {
7981 freecvdef(slot.take());
7982 }
7983 }
7984 if let Ok(mut tab) = comptags.lock() {
7986 for slot in tab.iter_mut() {
7987 freectags(slot.take());
7988 }
7989 }
7990 0 }
7992
7993pub static cd_state: std::sync::Mutex<cdstate> = std::sync::Mutex::new(cdstate {
7997 showd: 0,
7998 sep: None,
7999 slen: 0,
8000 swidth: 0,
8001 maxmlen: 0,
8002 sets: None,
8003 pre: 0,
8004 premaxw: 0,
8005 suf: 0,
8006 maxg: 0,
8007 maxglen: 0,
8008 groups: 0,
8009 descs: 0,
8010 gprew: 0,
8011 runs: None,
8012 });
8013
8014pub const CM_SPACE: i32 = 2; pub static ca_laststate: std::sync::Mutex<castate> = std::sync::Mutex::new(castate {
8024 snext: None,
8025 d: None,
8026 nopts: 0,
8027 def: None,
8028 ddef: None,
8029 curopt: None,
8030 dopt: None,
8031 opt: 0,
8032 arg: 0,
8033 argbeg: 0,
8034 optbeg: 0,
8035 nargbeg: 0,
8036 restbeg: 0,
8037 curpos: 0,
8038 argend: 0,
8039 inopt: 0,
8040 inarg: 0,
8041 nth: 0,
8042 singles: 0,
8043 oopt: 0,
8044 actopts: 0,
8045 args: None,
8046 oargs: None,
8047 });
8048
8049pub static ca_alloced: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
8052
8053pub static cv_laststate: std::sync::Mutex<cvstate> = std::sync::Mutex::new(cvstate {
8057 d: None,
8058 def: None,
8059 val: None,
8060 vals: None,
8061 });
8062
8063pub static cv_alloced: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
8066
8067#[allow(dead_code)]
8070fn clone_castate_full(s: &castate) -> castate {
8071 castate {
8072 snext: s.snext.clone(),
8073 d: s.d.clone(),
8074 nopts: s.nopts,
8075 def: s.def.clone(),
8076 ddef: s.ddef.clone(),
8077 curopt: s.curopt.clone(),
8078 dopt: s.dopt.clone(),
8079 opt: s.opt,
8080 arg: s.arg,
8081 argbeg: s.argbeg,
8082 optbeg: s.optbeg,
8083 nargbeg: s.nargbeg,
8084 restbeg: s.restbeg,
8085 curpos: s.curpos,
8086 argend: s.argend,
8087 inopt: s.inopt,
8088 inarg: s.inarg,
8089 nth: s.nth,
8090 singles: s.singles,
8091 oopt: s.oopt,
8092 actopts: s.actopts,
8093 args: s.args.clone(),
8094 oargs: s.oargs.clone(),
8095 }
8096}
8097
8098#[allow(dead_code)]
8104fn clone_castate(s: &castate, d: &cadef) -> castate {
8105 castate {
8106 snext: None,
8107 d: Some(Box::new(clone_cadef_shallow(d))),
8108 nopts: s.nopts,
8109 def: s.def.clone(),
8110 ddef: s.ddef.clone(),
8111 curopt: s.curopt.clone(),
8112 dopt: s.dopt.clone(),
8113 opt: s.opt,
8114 arg: s.arg,
8115 argbeg: s.argbeg,
8116 optbeg: s.optbeg,
8117 nargbeg: s.nargbeg,
8118 restbeg: s.restbeg,
8119 curpos: s.curpos,
8120 argend: s.argend,
8121 inopt: s.inopt,
8122 inarg: s.inarg,
8123 nth: s.nth,
8124 singles: s.singles,
8125 oopt: s.oopt,
8126 actopts: s.actopts,
8127 args: s.args.clone(),
8128 oargs: s.oargs.clone(),
8129 }
8130}
8131
8132#[allow(dead_code)]
8133fn clone_cadef_shallow(d: &cadef) -> cadef {
8134 cadef {
8135 next: None,
8136 snext: None,
8137 opts: d.opts.clone(),
8138 nopts: d.nopts,
8139 ndopts: d.ndopts,
8140 nodopts: d.nodopts,
8141 args: d.args.clone(),
8142 rest: d.rest.clone(),
8143 defs: d.defs.clone(),
8144 ndefs: d.ndefs,
8145 lastt: d.lastt,
8146 single: d.single.clone(),
8147 r#match: d.r#match.clone(),
8148 argsactive: d.argsactive,
8149 set: d.set.clone(),
8150 flags: d.flags,
8151 nonarg: d.nonarg.clone(),
8152 }
8153}
8154
8155#[cfg(test)]
8156mod tests {
8157 use super::*;
8158
8159 #[test]
8171 fn test_rembslashcolon() {
8172 let _g = crate::test_util::global_state_lock();
8173 let _g = zle_test_setup();
8174 assert_eq!(rembslashcolon("a\\:b\\:c"), "a:b:c");
8176 }
8177
8178 #[test]
8179 fn test_rembslashcolon_lone_backslash_kept() {
8180 let _g = crate::test_util::global_state_lock();
8181 let _g = zle_test_setup();
8182 assert_eq!(rembslashcolon("a\\nb"), "a\\nb");
8184 }
8185
8186 #[test]
8187 fn test_rembslashcolon_trailing_backslash() {
8188 let _g = crate::test_util::global_state_lock();
8189 let _g = zle_test_setup();
8190 assert_eq!(rembslashcolon("a\\"), "a\\");
8192 }
8193
8194 #[test]
8195 fn test_rembslashcolon_unescaped_colon_passes_through() {
8196 let _g = crate::test_util::global_state_lock();
8197 let _g = zle_test_setup();
8198 assert_eq!(rembslashcolon("a:b"), "a:b");
8200 }
8201
8202 #[test]
8203 fn test_bslashcolon() {
8204 let _g = crate::test_util::global_state_lock();
8205 let _g = zle_test_setup();
8206 assert_eq!(bslashcolon("a:b:c"), "a\\:b\\:c");
8208 }
8209
8210 #[test]
8211 fn test_bslashcolon_no_colons() {
8212 let _g = crate::test_util::global_state_lock();
8213 let _g = zle_test_setup();
8214 assert_eq!(bslashcolon("hello"), "hello");
8216 }
8217
8218 #[test]
8219 fn test_bslashcolon_already_escaped_doubled() {
8220 let _g = crate::test_util::global_state_lock();
8221 let _g = zle_test_setup();
8222 assert_eq!(bslashcolon("a\\:b"), "a\\\\:b");
8226 }
8227
8228 #[test]
8229 fn test_single_index_dash_prefix() {
8230 let _g = crate::test_util::global_state_lock();
8231 let _g = zle_test_setup();
8232 assert_eq!(single_index(b'-', b'a'), 64);
8235 assert_eq!(single_index(b'-', b'A'), 32);
8237 assert_eq!(single_index(b'-', b'!'), 0);
8239 assert_eq!(single_index(b'-', b'~'), 93);
8241 }
8242
8243 #[test]
8244 fn test_single_index_plus_prefix() {
8245 let _g = crate::test_util::global_state_lock();
8246 let _g = zle_test_setup();
8247 assert_eq!(single_index(b'+', b'a'), 158);
8250 assert_eq!(single_index(b'+', b'!'), 94);
8252 assert_eq!(single_index(b'+', b'~'), 187);
8254 }
8255
8256 #[test]
8257 fn test_single_index_out_of_range() {
8258 let _g = crate::test_util::global_state_lock();
8259 let _g = zle_test_setup();
8260 assert_eq!(single_index(b'-', 0x20), -1); assert_eq!(single_index(b'-', 0x00), -1); assert_eq!(single_index(b'-', 0x7f), -1); assert_eq!(single_index(b'+', 0xff), -1); }
8266
8267 #[test]
8272 fn caarg_default_zero_initialized() {
8273 let _g = crate::test_util::global_state_lock();
8274 let _g = zle_test_setup();
8275 let a = caarg::default();
8277 assert!(a.next.is_none());
8278 assert!(a.descr.is_none());
8279 assert!(a.action.is_none());
8280 assert_eq!(a.r#type, 0);
8281 assert_eq!(a.num, 0);
8282 assert_eq!(a.active, 0);
8283 }
8284
8285 #[test]
8286 fn caopt_default_zero_initialized() {
8287 let _g = crate::test_util::global_state_lock();
8288 let _g = zle_test_setup();
8289 let o = caopt::default();
8291 assert!(o.next.is_none());
8292 assert!(o.name.is_none());
8293 assert!(o.args.is_none());
8294 assert_eq!(o.r#type, 0);
8295 assert_eq!(o.num, 0);
8296 assert_eq!(o.not, 0);
8297 }
8298
8299 #[test]
8300 fn cadef_default_zero_initialized() {
8301 let _g = crate::test_util::global_state_lock();
8302 let _g = zle_test_setup();
8303 let d = cadef::default();
8305 assert!(d.next.is_none());
8306 assert!(d.opts.is_none());
8307 assert!(d.args.is_none());
8308 assert_eq!(d.nopts, 0);
8309 assert_eq!(d.flags, 0);
8310 }
8311
8312 #[test]
8313 fn freecaargs_walks_chain() {
8314 let _g = crate::test_util::global_state_lock();
8315 let _g = zle_test_setup();
8316 let mut head = caarg {
8320 descr: Some("a".into()),
8321 ..Default::default()
8322 };
8323 let mid = caarg {
8324 descr: Some("b".into()),
8325 ..Default::default()
8326 };
8327 let tail = caarg {
8328 descr: Some("c".into()),
8329 ..Default::default()
8330 };
8331 let mut mid_box = Box::new(mid);
8332 mid_box.next = Some(Box::new(tail));
8333 head.next = Some(mid_box);
8334 freecaargs(Some(Box::new(head)));
8335 }
8337
8338 #[test]
8339 fn cao_caa_constants_match_c() {
8340 let _g = crate::test_util::global_state_lock();
8341 let _g = zle_test_setup();
8342 assert_eq!(CAO_NEXT, 1);
8344 assert_eq!(CAO_DIRECT, 2);
8345 assert_eq!(CAO_ODIRECT, 3);
8346 assert_eq!(CAO_EQUAL, 4);
8347 assert_eq!(CAO_OEQUAL, 5);
8348 assert_eq!(CAA_NORMAL, 1);
8349 assert_eq!(CAA_OPT, 2);
8350 assert_eq!(CAA_REST, 3);
8351 assert_eq!(CAA_RARGS, 4);
8352 assert_eq!(CAA_RREST, 5);
8353 }
8354
8355 #[test]
8356 fn cdf_max_cacache_constants_match_c() {
8357 let _g = crate::test_util::global_state_lock();
8358 let _g = zle_test_setup();
8359 assert_eq!(CDF_SEP, 1);
8361 assert_eq!(MAX_CACACHE, 8);
8362 }
8363
8364 #[test]
8365 fn crt_constants_match_c() {
8366 let _g = crate::test_util::global_state_lock();
8367 let _g = zle_test_setup();
8368 assert_eq!(CRT_SIMPLE, 0);
8370 assert_eq!(CRT_DESC, 1);
8371 assert_eq!(CRT_SPEC, 2);
8372 assert_eq!(CRT_DUMMY, 3);
8373 assert_eq!(CRT_EXPL, 4);
8374 }
8375
8376 #[test]
8377 fn cdstr_default_zero_initialized() {
8378 let _g = crate::test_util::global_state_lock();
8379 let _g = zle_test_setup();
8380 let s = cdstr::default();
8382 assert!(s.next.is_none());
8383 assert!(s.str.is_none());
8384 assert!(s.desc.is_none());
8385 assert!(s.r#match.is_none());
8386 assert_eq!(s.len, 0);
8387 assert_eq!(s.width, 0);
8388 assert_eq!(s.kind, 0);
8389 }
8390
8391 #[test]
8392 fn cdrun_default_zero_initialized() {
8393 let _g = crate::test_util::global_state_lock();
8394 let _g = zle_test_setup();
8395 let r = cdrun::default();
8397 assert!(r.next.is_none());
8398 assert!(r.strs.is_none());
8399 assert_eq!(r.r#type, 0);
8400 assert_eq!(r.count, 0);
8401 }
8402
8403 #[test]
8404 fn cdset_default_zero_initialized() {
8405 let _g = crate::test_util::global_state_lock();
8406 let _g = zle_test_setup();
8407 let s = cdset::default();
8409 assert!(s.next.is_none());
8410 assert!(s.opts.is_none());
8411 assert!(s.strs.is_none());
8412 assert_eq!(s.count, 0);
8413 assert_eq!(s.desc, 0);
8414 }
8415
8416 #[test]
8417 fn cdstate_default_zero_initialized() {
8418 let _g = crate::test_util::global_state_lock();
8419 let _g = zle_test_setup();
8420 let st = cdstate::default();
8422 assert_eq!(st.showd, 0);
8423 assert!(st.sep.is_none());
8424 assert!(st.sets.is_none());
8425 assert!(st.runs.is_none());
8426 }
8427
8428 #[test]
8429 fn freecdsets_walks_chain() {
8430 let _g = crate::test_util::global_state_lock();
8431 let _g = zle_test_setup();
8432 let head_str = cdstr {
8435 str: Some("foo".into()),
8436 desc: Some("first".into()),
8437 ..Default::default()
8438 };
8439 let tail_str = cdstr {
8440 str: Some("bar".into()),
8441 ..Default::default()
8442 };
8443 let mut head_str_b = Box::new(head_str);
8444 head_str_b.next = Some(Box::new(tail_str));
8445 let set = cdset {
8446 strs: Some(head_str_b),
8447 count: 2,
8448 ..Default::default()
8449 };
8450 freecdsets(Some(Box::new(set)));
8451 }
8453
8454 #[test]
8455 fn castate_default_zero_initialized() {
8456 let _g = crate::test_util::global_state_lock();
8457 let _g = zle_test_setup();
8458 let s = castate::default();
8460 assert!(s.snext.is_none());
8461 assert!(s.d.is_none());
8462 assert!(s.def.is_none());
8463 assert!(s.args.is_none());
8464 assert_eq!(s.nopts, 0);
8465 assert_eq!(s.curpos, 0);
8466 }
8467
8468 #[test]
8469 fn cvdef_default_zero_initialized() {
8470 let _g = crate::test_util::global_state_lock();
8471 let _g = zle_test_setup();
8472 let d = cvdef::default();
8474 assert!(d.descr.is_none());
8475 assert!(d.vals.is_none());
8476 assert_eq!(d.hassep, 0);
8477 assert_eq!(d.sep, 0);
8478 assert_eq!(d.argsep, 0);
8479 }
8480
8481 #[test]
8482 fn cvval_default_zero_initialized() {
8483 let _g = crate::test_util::global_state_lock();
8484 let _g = zle_test_setup();
8485 let v = cvval::default();
8487 assert!(v.next.is_none());
8488 assert!(v.name.is_none());
8489 assert!(v.arg.is_none());
8490 assert_eq!(v.r#type, 0);
8491 assert_eq!(v.active, 0);
8492 }
8493
8494 #[test]
8495 fn cvstate_default_zero_initialized() {
8496 let _g = crate::test_util::global_state_lock();
8497 let _g = zle_test_setup();
8498 let s = cvstate::default();
8500 assert!(s.d.is_none());
8501 assert!(s.def.is_none());
8502 assert!(s.val.is_none());
8503 assert!(s.vals.is_none());
8504 }
8505
8506 #[test]
8507 fn ctags_default_zero_initialized() {
8508 let _g = crate::test_util::global_state_lock();
8509 let _g = zle_test_setup();
8510 let t = ctags::default();
8512 assert!(t.all.is_none());
8513 assert!(t.context.is_none());
8514 assert!(t.sets.is_none());
8515 assert_eq!(t.init, 0);
8516 }
8517
8518 #[test]
8519 fn ctset_default_zero_initialized() {
8520 let _g = crate::test_util::global_state_lock();
8521 let _g = zle_test_setup();
8522 let s = ctset::default();
8524 assert!(s.next.is_none());
8525 assert!(s.tags.is_none());
8526 assert!(s.tag.is_none());
8527 assert_eq!(s.ptr, 0);
8528 }
8529
8530 #[test]
8531 fn cvv_constants_match_c() {
8532 let _g = crate::test_util::global_state_lock();
8533 let _g = zle_test_setup();
8534 assert_eq!(CVV_NOARG, 0);
8536 assert_eq!(CVV_ARG, 1);
8537 assert_eq!(CVV_OPT, 2);
8538 }
8539
8540 #[test]
8541 fn max_tags_cvcache_match_c() {
8542 let _g = crate::test_util::global_state_lock();
8543 let _g = zle_test_setup();
8544 assert_eq!(MAX_TAGS, 256);
8546 assert_eq!(MAX_CVCACHE, 8);
8547 }
8548
8549 #[test]
8550 fn freectset_walks_chain() {
8551 let _g = crate::test_util::global_state_lock();
8552 let _g = zle_test_setup();
8553 let mut head = ctset {
8556 tag: Some("foo".into()),
8557 ..Default::default()
8558 };
8559 let tail = ctset {
8560 tag: Some("bar".into()),
8561 ..Default::default()
8562 };
8563 head.next = Some(Box::new(tail));
8564 freectset(Some(Box::new(head)));
8565 }
8566
8567 #[test]
8568 fn freectags_drops_one_node() {
8569 let _g = crate::test_util::global_state_lock();
8570 let _g = zle_test_setup();
8571 let t = ctags {
8573 all: Some(vec!["a".into(), "b".into()]),
8574 context: Some("ctx".into()),
8575 ..Default::default()
8576 };
8577 freectags(Some(Box::new(t)));
8578 }
8579
8580 #[test]
8581 fn freecvdef_walks_vals_chain() {
8582 let _g = crate::test_util::global_state_lock();
8583 let _g = zle_test_setup();
8584 let v_tail = cvval {
8586 name: Some("opt2".into()),
8587 ..Default::default()
8588 };
8589 let mut v_head = cvval {
8590 name: Some("opt1".into()),
8591 ..Default::default()
8592 };
8593 v_head.next = Some(Box::new(v_tail));
8594 let d = cvdef {
8595 descr: Some("test".into()),
8596 vals: Some(Box::new(v_head)),
8597 ..Default::default()
8598 };
8599 freecvdef(Some(Box::new(d)));
8600 }
8601
8602 #[test]
8607 fn parse_cadef_simple_opt_and_rest() {
8608 let _g = crate::test_util::global_state_lock();
8609 let _g = zle_test_setup();
8610 inittyptab();
8611 let args = vec![
8612 String::from(""), String::from("-foo[only foo]"),
8614 String::from("*:file:_files"),
8615 ];
8616 let def = parse_cadef("_arguments", &args).expect("cadef built");
8617 let opt = def.opts.as_deref().expect("opt linked");
8618 assert_eq!(opt.name.as_deref(), Some("-foo"));
8619 assert_eq!(opt.descr.as_deref(), Some("only foo"));
8620 assert_eq!(opt.r#type, CAO_NEXT);
8621 let xor = opt.xor.as_ref().expect("xor list");
8623 assert!(
8624 xor.iter().any(|s| s == "-foo"),
8625 "xor must include -foo: {:?}",
8626 xor
8627 );
8628
8629 let rest = def.rest.as_deref().expect("rest linked");
8630 assert_eq!(rest.r#type, CAA_REST);
8631 assert_eq!(rest.descr.as_deref(), Some("file"));
8632 assert_eq!(rest.action.as_deref(), Some("_files"));
8633 }
8634
8635 #[test]
8639 fn parse_cadef_numbered_positional_arg() {
8640 let _g = crate::test_util::global_state_lock();
8641 let _g = zle_test_setup();
8642 inittyptab();
8643 let args = vec![String::from(""), String::from("1:cmd:_commands")];
8644 let def = parse_cadef("_arguments", &args).expect("cadef built");
8645 let pos = def.args.as_deref().expect("positional arg linked");
8646 assert_eq!(pos.num, 1);
8647 assert_eq!(pos.r#type, CAA_NORMAL);
8648 assert_eq!(pos.descr.as_deref(), Some("cmd"));
8649 assert_eq!(pos.action.as_deref(), Some("_commands"));
8650 assert_eq!(pos.direct, 1, "explicit numbering sets direct=1");
8651 }
8652
8653 #[test]
8656 fn parse_cadef_doubled_arg_errors() {
8657 let _g = crate::test_util::global_state_lock();
8658 let _g = zle_test_setup();
8659 inittyptab();
8660 let args = vec![
8661 String::from(""),
8662 String::from("1:a:_a"),
8663 String::from("1:b:_b"),
8664 ];
8665 let def = parse_cadef("_arguments", &args);
8666 assert!(def.is_none(), "duplicate arg num=1 must reject");
8667 }
8668
8669 #[test]
8673 fn parse_cadef_xor_list_populated() {
8674 let _g = crate::test_util::global_state_lock();
8675 let _g = zle_test_setup();
8676 inittyptab();
8677 let args = vec![String::from(""), String::from("(opt-x opt-y)-foo[descr]")];
8678 let def = parse_cadef("_arguments", &args).expect("cadef built");
8679 let opt = def.opts.as_deref().expect("opt linked");
8680 let xor = opt.xor.as_ref().expect("xor list");
8681 assert_eq!(xor.len(), 3, "xor: {:?}", xor);
8682 assert_eq!(xor[0], "opt-x");
8683 assert_eq!(xor[1], "opt-y");
8684 assert_eq!(xor[2], "-foo");
8685 }
8686
8687 #[test]
8690 fn settags_populates_slot() {
8691 let _g = crate::test_util::global_state_lock();
8692 let _g = zle_test_setup();
8693 if let Ok(mut tab) = comptags.lock() {
8695 tab[0] = None;
8696 }
8697 settags(
8698 0,
8699 &["ctx".to_string(), "tag-a".to_string(), "tag-b".to_string()],
8700 );
8701 let tab = comptags.lock().unwrap();
8702 let slot = tab[0].as_deref().expect("comptags[0] populated");
8703 assert_eq!(slot.context.as_deref(), Some("ctx"));
8704 assert_eq!(slot.init, 1);
8705 let all = slot.all.as_ref().expect("all populated");
8706 assert_eq!(all.len(), 2);
8707 assert_eq!(all[0], "tag-a");
8708 assert_eq!(all[1], "tag-b");
8709 assert!(slot.sets.is_none());
8710 }
8711
8712 #[test]
8715 fn ca_get_opt_exact_match() {
8716 let _g = crate::test_util::global_state_lock();
8717 let _g = zle_test_setup();
8718 inittyptab();
8719 let args = vec![String::from(""), String::from("-foo[d]")];
8720 let mut def = *parse_cadef("_arguments", &args).expect("cadef built");
8721 let mut cur = def.opts.as_deref_mut();
8723 while let Some(o) = cur {
8724 o.active = 1;
8725 cur = o.next.as_deref_mut();
8726 }
8727 let mut end: usize = 0;
8728 let hit = ca_get_opt(&def, "-foo", 1, &mut end).expect("hit");
8729 assert_eq!(hit.name.as_deref(), Some("-foo"));
8730 assert_eq!(end, 4);
8731 }
8732
8733 #[test]
8737 fn ca_get_arg_argsactive_zero_returns_none() {
8738 let _g = crate::test_util::global_state_lock();
8739 let _g = zle_test_setup();
8740 inittyptab();
8741 let args = vec![String::from(""), String::from("1:c:_c")];
8742 let def = *parse_cadef("_arguments", &args).expect("cadef built");
8743 assert!(ca_get_arg(&def, 1).is_none());
8745 }
8746
8747 #[test]
8750 fn ca_get_arg_in_range_active() {
8751 let _g = crate::test_util::global_state_lock();
8752 let _g = zle_test_setup();
8753 inittyptab();
8754 let args = vec![String::from(""), String::from("1:c:_c")];
8755 let mut def = *parse_cadef("_arguments", &args).expect("cadef built");
8756 def.argsactive = 1;
8757 if let Some(a) = def.args.as_deref_mut() {
8758 a.active = 1;
8759 }
8760 let hit = ca_get_arg(&def, 1).expect("hit");
8761 assert_eq!(hit.num, 1);
8762 assert_eq!(hit.descr.as_deref(), Some("c"));
8763 }
8764
8765 #[test]
8768 fn parse_cvdef_sep_and_two_vals() {
8769 let _g = crate::test_util::global_state_lock();
8770 let _g = zle_test_setup();
8771 inittyptab();
8772 let args = vec![
8773 String::from("-s"),
8774 String::from(","),
8775 String::from("descr"),
8776 String::from("opt1[a]:val1:"),
8777 String::from("opt2[b]"),
8778 ];
8779 let def = parse_cvdef("_values", &args).expect("cvdef built");
8780 assert_eq!(def.hassep, 1);
8781 assert_eq!(def.sep, b',' as i32);
8782 assert_eq!(def.descr.as_deref(), Some("descr"));
8783 let v1 = def.vals.as_deref().expect("val1");
8784 assert_eq!(v1.name.as_deref(), Some("opt1"));
8785 assert_eq!(v1.descr.as_deref(), Some("a"));
8786 assert_eq!(v1.r#type, CVV_ARG);
8787 let v2 = v1.next.as_deref().expect("val2");
8788 assert_eq!(v2.name.as_deref(), Some("opt2"));
8789 assert_eq!(v2.descr.as_deref(), Some("b"));
8790 assert_eq!(v2.r#type, CVV_NOARG);
8791 }
8792
8793 #[test]
8797 fn ca_foreign_opt_finds_in_other_set() {
8798 let _g = crate::test_util::global_state_lock();
8799 let _g = zle_test_setup();
8800 inittyptab();
8801 let other = cadef {
8802 opts: Some(Box::new(caopt {
8803 name: Some("-bar".into()),
8804 active: 1,
8805 ..Default::default()
8806 })),
8807 ..Default::default()
8808 };
8809 let all = cadef {
8810 opts: Some(Box::new(caopt {
8811 name: Some("-foo".into()),
8812 active: 1,
8813 ..Default::default()
8814 })),
8815 snext: Some(Box::new(other)),
8816 ..Default::default()
8817 };
8818 assert_eq!(ca_foreign_opt(&all, &all, "-bar"), 1);
8820 assert_eq!(ca_foreign_opt(&all, &all, "-foo"), 0);
8822 assert_eq!(ca_foreign_opt(&all, &all, "-missing"), 0);
8824 }
8825
8826 #[test]
8829 fn ca_inactive_guard_noop_keeps_active() {
8830 let _g = crate::test_util::global_state_lock();
8831 let _g = zle_test_setup();
8832 inittyptab();
8833 let mut d = cadef {
8834 opts: Some(Box::new(caopt {
8835 name: Some("-foo".into()),
8836 active: 1,
8837 ..Default::default()
8838 })),
8839 argsactive: 1,
8840 ..Default::default()
8841 };
8842 ca_inactive(&mut d, &[], 0, 0);
8843 assert_eq!(d.opts.as_deref().unwrap().active, 1);
8844 assert_eq!(d.argsactive, 1);
8845 }
8846
8847 #[test]
8849 fn ca_inactive_opts_flag_deactivates_options() {
8850 let _g = crate::test_util::global_state_lock();
8851 let _g = zle_test_setup();
8852 inittyptab();
8853 let saved_compcur = COMPCURRENT.load(Ordering::Relaxed);
8854 let mut d = cadef {
8855 opts: Some(Box::new(caopt {
8856 name: Some("-foo".into()),
8857 active: 1,
8858 num: 0,
8859 next: Some(Box::new(caopt {
8860 name: Some("-bar".into()),
8861 active: 1,
8862 num: 1,
8863 ..Default::default()
8864 })),
8865 ..Default::default()
8866 })),
8867 argsactive: 1,
8868 ..Default::default()
8869 };
8870 COMPCURRENT.store(2, Ordering::Relaxed);
8872 ca_inactive(&mut d, &[], 1, 1);
8873 COMPCURRENT.store(saved_compcur, Ordering::Relaxed);
8876 let mut p = d.opts.as_deref();
8877 while let Some(o) = p {
8878 assert_eq!(o.active, 0, "{:?} should be deactivated", o.name);
8879 p = o.next.as_deref();
8880 }
8881 }
8882
8883 #[test]
8886 fn bin_comptags_init_and_context() {
8887 let _g = crate::test_util::global_state_lock();
8888 let _g = zle_test_setup();
8889 inittyptab();
8890 let saved_incompfunc = INCOMPFUNC.load(Ordering::Relaxed);
8891 let saved_locallevel = locallevel.load(Ordering::Relaxed);
8892 if let Ok(mut tab) = comptags.lock() {
8894 tab[0] = None;
8895 }
8896 locallevel.store(0, Ordering::Relaxed);
8897 INCOMPFUNC.store(1, Ordering::Relaxed);
8898
8899 let ops = options {
8900 ind: [0u8; MAX_OPS],
8901 args: Vec::new(),
8902 argscount: 0,
8903 argsalloc: 0,
8904 };
8905 let r = bin_comptags(
8907 "comptags",
8908 &["-i".into(), "my-ctx".into(), "tag-a".into(), "tag-b".into()],
8909 &ops,
8910 0,
8911 );
8912 assert_eq!(r, 0);
8913 let r = bin_comptags("comptags", &["-T".into()], &ops, 0);
8915 assert_eq!(r, 1);
8916 let ctx = comptags.lock().unwrap()[0]
8918 .as_ref()
8919 .and_then(|t| t.context.clone());
8920 INCOMPFUNC.store(saved_incompfunc, Ordering::Relaxed);
8922 locallevel.store(saved_locallevel, Ordering::Relaxed);
8923 assert_eq!(ctx.as_deref(), Some("my-ctx"));
8924 }
8925
8926 #[test]
8928 fn cv_get_val_hits_and_misses() {
8929 let _g = crate::test_util::global_state_lock();
8930 let _g = zle_test_setup();
8931 let d = cvdef {
8932 vals: Some(Box::new(cvval {
8933 name: Some("foo".into()),
8934 r#type: CVV_NOARG,
8935 next: Some(Box::new(cvval {
8936 name: Some("bar".into()),
8937 r#type: CVV_ARG,
8938 ..Default::default()
8939 })),
8940 ..Default::default()
8941 })),
8942 ..Default::default()
8943 };
8944 let hit = cv_get_val(&d, "bar").expect("hit");
8945 assert_eq!(hit.name.as_deref(), Some("bar"));
8946 assert_eq!(hit.r#type, CVV_ARG);
8947 assert!(cv_get_val(&d, "missing").is_none());
8948 }
8949
8950 #[test]
8954 fn setup_clears_all_caches() {
8955 let _g = crate::test_util::global_state_lock();
8956 let _g = zle_test_setup();
8957 if let Ok(mut cache) = cadef_cache.lock() {
8959 cache[0] = Some(Box::new(cadef::default()));
8960 }
8961 if let Ok(mut cache) = cvdef_cache.lock() {
8962 cache[0] = Some(Box::new(cvdef::default()));
8963 }
8964 if let Ok(mut tab) = comptags.lock() {
8965 tab[0] = Some(Box::new(ctags::default()));
8966 }
8967 lasttaglevel.store(42, Ordering::Relaxed);
8968
8969 let r = setup_();
8970 assert_eq!(r, 0);
8971
8972 assert!(
8973 cadef_cache.lock().unwrap()[0].is_none(),
8974 "cadef_cache[0] should be cleared"
8975 );
8976 assert!(
8977 cvdef_cache.lock().unwrap()[0].is_none(),
8978 "cvdef_cache[0] should be cleared"
8979 );
8980 assert!(
8981 comptags.lock().unwrap()[0].is_none(),
8982 "comptags[0] should be cleared"
8983 );
8984 assert_eq!(
8985 lasttaglevel.load(Ordering::Relaxed),
8986 0,
8987 "lasttaglevel should reset to 0"
8988 );
8989 }
8990
8991 #[test]
8995 fn finish_frees_all_caches() {
8996 let _g = crate::test_util::global_state_lock();
8997 let _g = zle_test_setup();
8998 if let Ok(mut cache) = cadef_cache.lock() {
8999 cache[1] = Some(Box::new(cadef::default()));
9000 }
9001 if let Ok(mut cache) = cvdef_cache.lock() {
9002 cache[1] = Some(Box::new(cvdef::default()));
9003 }
9004 if let Ok(mut tab) = comptags.lock() {
9005 tab[1] = Some(Box::new(ctags::default()));
9006 }
9007 let r = finish_();
9008 assert_eq!(r, 0);
9009 assert!(cadef_cache.lock().unwrap()[1].is_none());
9010 assert!(cvdef_cache.lock().unwrap()[1].is_none());
9011 assert!(comptags.lock().unwrap()[1].is_none());
9012 }
9013
9014 #[test]
9026 fn cfp_matcher_pats_left_anchor_zero_lalen_returns_empty_via_invalid_spec() {
9027 let _g = crate::test_util::global_state_lock();
9028 let _g = zle_test_setup();
9029 inittyptab();
9030 let r = cfp_matcher_pats("not-a-real-matcher-spec", "xyz");
9036 assert_eq!(r, "xyz");
9037 }
9038
9039 #[test]
9042 fn bin_comptry_no_taglevel_errors() {
9043 let _g = crate::test_util::global_state_lock();
9044 let _g = zle_test_setup();
9045 let saved_incompfunc = INCOMPFUNC.load(Ordering::Relaxed);
9046 let saved_lvl = lasttaglevel.load(Ordering::Relaxed);
9047 INCOMPFUNC.store(1, Ordering::Relaxed);
9048 lasttaglevel.store(0, Ordering::Relaxed);
9049 let ops = options {
9050 ind: [0u8; MAX_OPS],
9051 args: Vec::new(),
9052 argscount: 0,
9053 argsalloc: 0,
9054 };
9055 let r = bin_comptry("comptry", &["tag1".into()], &ops, 0);
9056 INCOMPFUNC.store(saved_incompfunc, Ordering::Relaxed);
9057 lasttaglevel.store(saved_lvl, Ordering::Relaxed);
9058 assert_eq!(r, 1);
9059 }
9060
9061 #[test]
9065 fn bin_comptry_plain_adds_set() {
9066 let _g = crate::test_util::global_state_lock();
9067 let _g = zle_test_setup();
9068 let saved_incompfunc = INCOMPFUNC.load(Ordering::Relaxed);
9069 let saved_lvl = lasttaglevel.load(Ordering::Relaxed);
9070 if let Ok(mut tab) = comptags.lock() {
9072 tab[1] = Some(Box::new(ctags {
9073 all: Some(vec!["files".into(), "directories".into()]),
9074 context: Some("ctx".into()),
9075 init: 0,
9076 sets: None,
9077 }));
9078 }
9079 INCOMPFUNC.store(1, Ordering::Relaxed);
9080 lasttaglevel.store(1, Ordering::Relaxed);
9081 let ops = options {
9082 ind: [0u8; MAX_OPS],
9083 args: Vec::new(),
9084 argscount: 0,
9085 argsalloc: 0,
9086 };
9087 let r = bin_comptry("comptry", &["files".into(), "unknown".into()], &ops, 0);
9088 let sets_first_tags = comptags.lock().unwrap()[1]
9091 .as_ref()
9092 .and_then(|t| t.sets.as_ref().and_then(|s| s.tags.clone()));
9093 if let Ok(mut tab) = comptags.lock() {
9095 tab[1] = None;
9096 }
9097 INCOMPFUNC.store(saved_incompfunc, Ordering::Relaxed);
9098 lasttaglevel.store(saved_lvl, Ordering::Relaxed);
9099 assert_eq!(r, 0);
9100 let tags = sets_first_tags.expect("a set was appended");
9101 assert_eq!(
9102 tags,
9103 vec!["files".to_string()],
9104 "only registered tags survive the filter"
9105 );
9106 }
9107
9108 #[test]
9111 fn cfp_matcher_pats_empty_matcher_passthrough() {
9112 let _g = crate::test_util::global_state_lock();
9113 let _g = zle_test_setup();
9114 inittyptab();
9115 let r = cfp_matcher_pats("", "abc");
9116 assert_eq!(r, "abc");
9117 }
9118
9119 #[test]
9122 fn cfp_matcher_range_no_matchers_verbatim() {
9123 let _g = crate::test_util::global_state_lock();
9124 let _g = zle_test_setup();
9125 let ms: Vec<Option<Box<Cmatcher>>> = vec![None, None, None];
9126 let r = cfp_matcher_range(&ms, "abc");
9127 assert_eq!(r, "abc");
9128 }
9129
9130 #[test]
9135 fn cd_prep_groups_emits_expl_and_spec_runs() {
9136 let _g = crate::test_util::global_state_lock();
9137 let _g = zle_test_setup();
9138 {
9140 let mut st = cd_state.lock().unwrap();
9141 st.showd = 0;
9142 st.groups = 2;
9143 st.descs = 2;
9144 st.maxg = 1;
9145 st.maxglen = 5;
9146 st.maxmlen = 100;
9147 st.sets = Some(Box::new(cdset {
9148 count: 2,
9149 desc: 2,
9150 strs: Some(Box::new(cdstr {
9151 str: Some("alpha".into()),
9152 r#match: Some("alpha".into()),
9153 desc: Some("first".into()),
9154 width: 5,
9155 len: 5,
9156 kind: 0,
9157 next: Some(Box::new(cdstr {
9158 str: Some("beta".into()),
9159 r#match: Some("beta".into()),
9160 desc: Some("second".into()),
9161 width: 4,
9162 len: 4,
9163 kind: 0,
9164 ..Default::default()
9165 })),
9166 ..Default::default()
9167 })),
9168 ..Default::default()
9169 }));
9170 st.runs = None;
9171 }
9172 let r = cd_prep();
9173 assert_eq!(r, 0);
9174 let st = cd_state.lock().unwrap();
9175 let r1 = st.runs.as_deref().expect("first run");
9176 assert_eq!(r1.r#type, CRT_SPEC, "first run is CRT_SPEC");
9177 let r2 = r1.next.as_deref().expect("second run");
9178 assert_eq!(r2.r#type, CRT_SPEC, "second run is CRT_SPEC");
9179 let r3 = r2.next.as_deref().expect("third run");
9180 assert_eq!(r3.r#type, CRT_EXPL, "third run is CRT_EXPL");
9181 assert_eq!(r3.count, 2, "CRT_EXPL covers both prep_lines");
9182 }
9183
9184 #[test]
9187 fn cfp_bld_pats_concatenates_skipped_and_pat() {
9188 let _g = crate::test_util::global_state_lock();
9189 let _g = zle_test_setup();
9190 let out = cfp_bld_pats(0, &["dir".to_string()], "", &["*.c".to_string()]);
9191 assert_eq!(out, vec!["dir*.c".to_string()]);
9192 }
9193
9194 #[test]
9197 fn cfp_bld_pats_globdots_variant() {
9198 let _g = crate::test_util::global_state_lock();
9199 let _g = zle_test_setup();
9200 let m = COMPPREFIX.get_or_init(|| std::sync::Mutex::new(String::new()));
9202 *m.lock().unwrap() = ".foo".to_string();
9203 let out = cfp_bld_pats(0, &["d".to_string()], "", &["*.x".to_string()]);
9205 *m.lock().unwrap() = String::new();
9207 assert!(out.contains(&"d*.x".to_string()));
9208 assert!(
9209 out.contains(&"d.*.x".to_string()),
9210 "dot-variant must be emitted: {:?}",
9211 out
9212 );
9213 }
9214
9215 #[test]
9217 fn cfp_opt_pats_passthrough_empty_compprefix() {
9218 let _g = crate::test_util::global_state_lock();
9219 let _g = zle_test_setup();
9220 let m = COMPPREFIX.get_or_init(|| std::sync::Mutex::new(String::new()));
9221 *m.lock().unwrap() = String::new();
9222 let pats = vec!["*".to_string(), "*.c".to_string()];
9223 let out = cfp_opt_pats(&pats, "");
9224 assert_eq!(out, pats);
9225 }
9226
9227 #[test]
9230 fn cfp_test_exact_no_anchor_returns_none() {
9231 let _g = crate::test_util::global_state_lock();
9232 let _g = zle_test_setup();
9233 let pm = COMPPREFIX.get_or_init(|| std::sync::Mutex::new(String::new()));
9234 *pm.lock().unwrap() = String::new();
9235 let sm = COMPSUFFIX.get_or_init(|| std::sync::Mutex::new(String::new()));
9236 *sm.lock().unwrap() = String::new();
9237 let r = cfp_test_exact(&["/tmp".to_string()], &["true".to_string()], "");
9238 assert!(r.is_none());
9239 }
9240
9241 #[test]
9244 fn bin_compgroups_empty_args_succeeds() {
9245 let _g = crate::test_util::global_state_lock();
9246 let _g = zle_test_setup();
9247 let saved = INCOMPFUNC.load(Ordering::Relaxed);
9248 INCOMPFUNC.store(1, Ordering::Relaxed);
9249 let ops = options {
9250 ind: [0u8; MAX_OPS],
9251 args: Vec::new(),
9252 argscount: 0,
9253 argsalloc: 0,
9254 };
9255 let r = bin_compgroups("compgroups", &[], &ops, 0);
9256 INCOMPFUNC.store(saved, Ordering::Relaxed);
9257 assert_eq!(r, 0);
9258 }
9259
9260 #[test]
9263 fn cd_groups_want_sorting_respects_opts() {
9264 let _g = crate::test_util::global_state_lock();
9265 let _g = zle_test_setup();
9266 {
9268 let mut st = cd_state.lock().unwrap();
9269 st.sets = None;
9270 }
9271 assert_eq!(cd_groups_want_sorting(), 1);
9272 {
9274 let mut st = cd_state.lock().unwrap();
9275 st.sets = Some(Box::new(cdset {
9276 opts: Some(vec!["-V".into(), "grpname".into()]),
9277 ..Default::default()
9278 }));
9279 }
9280 assert_eq!(cd_groups_want_sorting(), 0);
9281 cd_state.lock().unwrap().sets = None;
9283 }
9284
9285 #[test]
9287 fn cd_sort_orders_by_sortstr() {
9288 let _g = crate::test_util::global_state_lock();
9289 let _g = zle_test_setup();
9290 let a = cdstr {
9291 sortstr: Some("apple".into()),
9292 ..Default::default()
9293 };
9294 let b = cdstr {
9295 sortstr: Some("banana".into()),
9296 ..Default::default()
9297 };
9298 assert_eq!(cd_sort(&a, &b), std::cmp::Ordering::Less);
9299 assert_eq!(cd_sort(&b, &a), std::cmp::Ordering::Greater);
9300 assert_eq!(cd_sort(&a, &a), std::cmp::Ordering::Equal);
9301 }
9302
9303 #[test]
9306 fn cd_prep_default_builds_simple_run_per_set() {
9307 let _g = crate::test_util::global_state_lock();
9308 let _g = zle_test_setup();
9309 {
9311 let mut st = cd_state.lock().unwrap();
9312 st.showd = 0;
9313 st.groups = 0;
9314 st.sets = Some(Box::new(cdset {
9315 count: 1,
9316 strs: Some(Box::new(cdstr {
9317 str: Some("a".into()),
9318 r#match: Some("a".into()),
9319 ..Default::default()
9320 })),
9321 next: Some(Box::new(cdset {
9322 count: 1,
9323 strs: Some(Box::new(cdstr {
9324 str: Some("b".into()),
9325 r#match: Some("b".into()),
9326 ..Default::default()
9327 })),
9328 ..Default::default()
9329 })),
9330 ..Default::default()
9331 }));
9332 st.runs = None;
9333 }
9334 let r = cd_prep();
9335 assert_eq!(r, 0);
9336 let st = cd_state.lock().unwrap();
9338 let r1 = st.runs.as_deref().expect("first run");
9339 assert_eq!(r1.r#type, CRT_SIMPLE);
9340 assert_eq!(r1.count, 1);
9341 let r2 = r1.next.as_deref().expect("second run");
9342 assert_eq!(r2.r#type, CRT_SIMPLE);
9343 assert_eq!(r2.count, 1);
9344 assert!(r2.next.is_none(), "no third run");
9345 }
9346
9347 #[test]
9349 fn bin_compdescribe_rejects_bad_option() {
9350 let _g = crate::test_util::global_state_lock();
9351 let _g = zle_test_setup();
9352 let saved_incompfunc = INCOMPFUNC.load(Ordering::Relaxed);
9353 INCOMPFUNC.store(1, Ordering::Relaxed);
9354 let ops = options {
9355 ind: [0u8; MAX_OPS],
9356 args: Vec::new(),
9357 argscount: 0,
9358 argsalloc: 0,
9359 };
9360 let r = bin_compdescribe("compdescribe", &["-x".into()], &ops, 0);
9363 INCOMPFUNC.store(saved_incompfunc, Ordering::Relaxed);
9364 assert_eq!(r, 1);
9365 }
9366
9367 #[test]
9370 fn cf_remove_other_filters_by_dir_head() {
9371 let _g = crate::test_util::global_state_lock();
9372 let _g = zle_test_setup();
9373 let names = vec![
9374 "dir/a".to_string(),
9375 "dir/b".to_string(),
9376 "other/c".to_string(),
9377 ];
9378 let mut amb = 99;
9379 let ret = cf_remove_other(&names, "dir/foo", &mut amb);
9380 assert_eq!(amb, 0);
9381 let v = ret.expect("matching names returned");
9382 assert_eq!(v, vec!["dir/a".to_string(), "dir/b".to_string()]);
9383 }
9384
9385 #[test]
9388 fn cf_remove_other_no_slash_diverge_sets_amb() {
9389 let _g = crate::test_util::global_state_lock();
9390 let _g = zle_test_setup();
9391 let names = vec!["a".to_string(), "b".to_string()];
9392 let mut amb = 0;
9393 let ret = cf_remove_other(&names, "x", &mut amb);
9394 assert!(ret.is_none());
9395 assert_eq!(amb, 1);
9396 }
9397
9398 #[test]
9401 fn cf_ignore_no_style_is_noop() {
9402 let _g = crate::test_util::global_state_lock();
9403 let _g = zle_test_setup();
9404 let mut ign: Vec<String> = vec!["x".into()];
9405 cf_ignore(&["/tmp".into()], &mut ign, "", "/tmp/foo");
9406 assert_eq!(
9407 ign,
9408 vec!["x".to_string()],
9409 "no style match must leave ign untouched"
9410 );
9411 }
9412
9413 #[test]
9415 fn bin_compquote_returns_zero_when_qstack_empty() {
9416 let _g = crate::test_util::global_state_lock();
9417 let _g = zle_test_setup();
9418 let saved_incompfunc = INCOMPFUNC.load(Ordering::Relaxed);
9419 INCOMPFUNC.store(1, Ordering::Relaxed);
9420 if let Some(m) = COMPQSTACK.get() {
9422 if let Ok(mut s) = m.lock() {
9423 s.clear();
9424 }
9425 }
9426 let ops = options {
9427 ind: [0u8; MAX_OPS],
9428 args: Vec::new(),
9429 argscount: 0,
9430 argsalloc: 0,
9431 };
9432 let r = bin_compquote("compquote", &["foo".into()], &ops, 0);
9433 INCOMPFUNC.store(saved_incompfunc, Ordering::Relaxed);
9434 assert_eq!(r, 0);
9435 }
9436
9437 #[test]
9441 fn cv_quote_get_val_unquotes_then_lookup() {
9442 let _g = crate::test_util::global_state_lock();
9443 let _g = zle_test_setup();
9444 inittyptab();
9445 let d = cvdef {
9446 vals: Some(Box::new(cvval {
9447 name: Some("foo".into()),
9448 r#type: CVV_NOARG,
9449 ..Default::default()
9450 })),
9451 ..Default::default()
9452 };
9453 assert!(cv_quote_get_val(&d, "foo").is_some());
9455 assert!(cv_quote_get_val(&d, "bar").is_none());
9457 }
9458
9459 #[test]
9461 fn cv_inactive_clears_named_vals() {
9462 let _g = crate::test_util::global_state_lock();
9463 let _g = zle_test_setup();
9464 let mut d = cvdef {
9465 vals: Some(Box::new(cvval {
9466 name: Some("a".into()),
9467 active: 1,
9468 next: Some(Box::new(cvval {
9469 name: Some("b".into()),
9470 active: 1,
9471 next: Some(Box::new(cvval {
9472 name: Some("c".into()),
9473 active: 1,
9474 ..Default::default()
9475 })),
9476 ..Default::default()
9477 })),
9478 ..Default::default()
9479 })),
9480 ..Default::default()
9481 };
9482 cv_inactive(&mut d, &["a".into(), "c".into()]);
9483 let mut p = d.vals.as_deref();
9484 let mut by_name = std::collections::HashMap::new();
9485 while let Some(v) = p {
9486 by_name.insert(v.name.clone().unwrap_or_default(), v.active);
9487 p = v.next.as_deref();
9488 }
9489 assert_eq!(by_name["a"], 0);
9490 assert_eq!(by_name["b"], 1, "untouched val stays active");
9491 assert_eq!(by_name["c"], 0);
9492 }
9493
9494 #[test]
9498 fn computil_corpus_cd_arrcat_concatenates() {
9499 let a = vec!["a".to_string(), "b".to_string()];
9500 let b = vec!["c".to_string(), "d".to_string()];
9501 let r = cd_arrcat(&a, &b);
9502 assert_eq!(r, vec!["a", "b", "c", "d"]);
9503 }
9504
9505 #[test]
9507 fn computil_corpus_cd_arrcat_empty_left() {
9508 let a: Vec<String> = Vec::new();
9509 let b = vec!["x".to_string(), "y".to_string()];
9510 let r = cd_arrcat(&a, &b);
9511 assert_eq!(r, vec!["x", "y"]);
9512 }
9513
9514 #[test]
9516 fn computil_corpus_cd_arrcat_empty_right() {
9517 let a = vec!["p".to_string(), "q".to_string()];
9518 let b: Vec<String> = Vec::new();
9519 let r = cd_arrcat(&a, &b);
9520 assert_eq!(r, vec!["p", "q"]);
9521 }
9522
9523 #[test]
9525 fn computil_corpus_cd_arrcat_both_empty() {
9526 let a: Vec<String> = Vec::new();
9527 let b: Vec<String> = Vec::new();
9528 let r = cd_arrcat(&a, &b);
9529 assert!(r.is_empty());
9530 }
9531
9532 #[test]
9534 fn computil_corpus_cd_arrdup_round_trips() {
9535 let a = vec!["one".to_string(), "two".to_string(), "three".to_string()];
9536 let r = cd_arrdup(&a);
9537 assert_eq!(r, a);
9538 }
9539
9540 #[test]
9542 fn computil_corpus_cd_arrdup_empty() {
9543 let a: Vec<String> = Vec::new();
9544 let r = cd_arrdup(&a);
9545 assert!(r.is_empty());
9546 }
9547
9548 #[test]
9551 fn computil_corpus_cd_arrdup_independent_copy() {
9552 let a = vec!["alpha".to_string()];
9553 let mut r = cd_arrdup(&a);
9554 r[0].push('!');
9555 assert_eq!(a[0], "alpha", "original unchanged");
9556 assert_eq!(r[0], "alpha!", "copy mutated independently");
9557 }
9558
9559 #[test]
9569 fn freecdsets_none_idempotent() {
9570 for _ in 0..5 {
9571 freecdsets(None);
9572 }
9573 }
9574
9575 #[test]
9577 fn freecaargs_none_idempotent() {
9578 for _ in 0..5 {
9579 freecaargs(None);
9580 }
9581 }
9582
9583 #[test]
9585 fn freecadef_none_idempotent() {
9586 for _ in 0..5 {
9587 freecadef(None);
9588 }
9589 }
9590
9591 #[test]
9594 fn arrcmp_both_none_returns_one_inverted_boolean() {
9595 assert_eq!(
9596 arrcmp(None, None),
9597 1,
9598 "C inverted-boolean: 1=equal, 0=unequal (per c:980)"
9599 );
9600 }
9601
9602 #[test]
9604 fn arrcmp_none_vs_some_returns_zero_unequal() {
9605 let a = vec!["x".to_string()];
9606 assert_eq!(arrcmp(None, Some(&a)), 0, "None vs Some → 0 (unequal)");
9607 assert_eq!(arrcmp(Some(&a), None), 0, "Some vs None → 0 (unequal)");
9608 }
9609
9610 #[test]
9612 fn arrcmp_identical_arrays_returns_one_equal() {
9613 let a = vec!["x".to_string(), "y".to_string()];
9614 assert_eq!(
9615 arrcmp(Some(&a), Some(&a)),
9616 1,
9617 "identical arrays → 1 (equal) per c:989"
9618 );
9619 }
9620
9621 #[test]
9623 fn arrcmp_is_pure() {
9624 let a = vec!["x".to_string()];
9625 let b = vec!["y".to_string()];
9626 let first = arrcmp(Some(&a), Some(&b));
9627 for _ in 0..5 {
9628 assert_eq!(arrcmp(Some(&a), Some(&b)), first, "arrcmp must be pure");
9629 }
9630 }
9631
9632 #[test]
9634 fn rembslashcolon_empty_returns_empty() {
9635 assert_eq!(rembslashcolon(""), "");
9636 }
9637
9638 #[test]
9640 fn rembslashcolon_is_pure() {
9641 for s in ["", "abc", r"a\:b", r"\:\:"] {
9642 let first = rembslashcolon(s);
9643 for _ in 0..3 {
9644 assert_eq!(
9645 rembslashcolon(s),
9646 first,
9647 "rembslashcolon({:?}) must be pure",
9648 s
9649 );
9650 }
9651 }
9652 }
9653
9654 #[test]
9656 fn bslashcolon_empty_returns_empty() {
9657 assert_eq!(bslashcolon(""), "");
9658 }
9659
9660 #[test]
9662 fn single_index_returns_i32_type() {
9663 let _: i32 = single_index(0, 0);
9664 }
9665
9666 #[test]
9668 fn ca_opt_arg_empty_inputs_no_panic() {
9669 let _: String = ca_opt_arg("", "", false);
9670 }
9671
9672 #[test]
9674 fn bslashcolon_is_pure() {
9675 for s in ["", "a:b", "no colon", "a:b:c:d"] {
9676 let first = bslashcolon(s);
9677 for _ in 0..3 {
9678 assert_eq!(bslashcolon(s), first, "bslashcolon({:?}) must be pure", s);
9679 }
9680 }
9681 }
9682
9683 #[test]
9692 fn cd_arrcat_both_empty_returns_empty() {
9693 assert!(cd_arrcat(&[], &[]).is_empty(), "empty + empty → empty");
9694 }
9695
9696 #[test]
9698 fn cd_arrcat_empty_a_returns_clone_of_b() {
9699 let b = vec!["x".to_string(), "y".to_string()];
9700 assert_eq!(cd_arrcat(&[], &b), b, "empty + B → B");
9701 }
9702
9703 #[test]
9705 fn cd_arrcat_empty_b_returns_clone_of_a() {
9706 let a = vec!["m".to_string(), "n".to_string()];
9707 assert_eq!(cd_arrcat(&a, &[]), a, "A + empty → A");
9708 }
9709
9710 #[test]
9712 fn cd_arrcat_preserves_a_then_b_order() {
9713 let a = vec!["1".to_string(), "2".to_string()];
9714 let b = vec!["3".to_string(), "4".to_string()];
9715 let r = cd_arrcat(&a, &b);
9716 assert_eq!(r, vec!["1", "2", "3", "4"]);
9717 }
9718
9719 #[test]
9721 fn cd_arrcat_returns_vec_string_type() {
9722 let _: Vec<String> = cd_arrcat(&[], &[]);
9723 }
9724
9725 #[test]
9727 fn cd_arrdup_returns_independent_clone() {
9728 let a = vec!["x".to_string(), "y".to_string()];
9729 let dup = cd_arrdup(&a);
9730 assert_eq!(dup, a, "dup equal to input");
9731 let mut mut_dup = dup;
9732 mut_dup.push("added".to_string());
9733 assert_eq!(a.len(), 2, "original unchanged after mutating dup");
9734 }
9735
9736 #[test]
9738 fn cd_arrdup_empty_returns_empty() {
9739 assert!(cd_arrdup(&[]).is_empty());
9740 }
9741
9742 #[test]
9744 fn cd_get_returns_i32_type() {
9745 let _g = crate::test_util::global_state_lock();
9746 let _: i32 = cd_get(&[]);
9747 }
9748
9749 #[test]
9751 fn bin_compdescribe_returns_i32_type() {
9752 let _g = crate::test_util::global_state_lock();
9753 let ops = crate::ported::zsh_h::options {
9754 ind: [0u8; crate::ported::zsh_h::MAX_OPS],
9755 args: Vec::new(),
9756 argscount: 0,
9757 argsalloc: 0,
9758 };
9759 let _: i32 = bin_compdescribe("compdescribe", &[], &ops, 0);
9760 }
9761
9762 #[test]
9764 fn freecaargs_none_no_panic() {
9765 freecaargs(None);
9766 }
9767
9768 #[test]
9770 fn freecadef_none_no_panic() {
9771 freecadef(None);
9772 }
9773
9774 #[test]
9776 fn rembslashcolon_returns_string_type() {
9777 let _: String = rembslashcolon("");
9778 }
9779
9780 #[test]
9782 fn bslashcolon_returns_string_type() {
9783 let _: String = bslashcolon("");
9784 }
9785
9786 #[test]
9789 fn bslashcolon_rembslashcolon_roundtrip_safe() {
9790 for s in ["a:b", "x:y:z", "no_colon"] {
9791 let escaped = bslashcolon(s);
9792 let unescaped = rembslashcolon(&escaped);
9793 assert_eq!(
9794 unescaped, s,
9795 "bslashcolon→rembslashcolon must round-trip for {:?}",
9796 s
9797 );
9798 }
9799 }
9800
9801 #[test]
9803 fn get_cadef_returns_i32_type() {
9804 let _g = crate::test_util::global_state_lock();
9805 let _: i32 = get_cadef("", &[]);
9806 }
9807}