1use std::io::Write;
2
3use crate::ast::Expr;
4use crate::error::{Error, Result, SourceLocation};
5use crate::value::Value;
6
7use super::Interpreter;
8
9impl<'a> Interpreter<'a> {
10 pub fn call_function<W: Write>(
12 &mut self,
13 name: &str,
14 args: &[Expr],
15 location: SourceLocation,
16 output: &mut W,
17 ) -> Result<Value> {
18 match name {
20 "sub" | "gsub" => return self.call_regex_sub(name, args, location),
21 "match" => return self.call_match(args, location),
22 "split" => return self.call_split(args, location),
23 "patsplit" => return self.call_patsplit(args, location),
24 "asort" | "asorti" => return self.call_asort(name == "asorti", args, location),
25 "close" => return self.call_close(args, location),
26 "fflush" => return self.call_fflush(args, location, output),
27 "system" => return self.call_system(args, output),
28 "length" => {
29 if let Some(Expr::Var(var_name, _)) = args.first() {
33 if let Some(arr) = self.arrays.get(self.resolve_array_name(var_name)) {
36 return Ok(Value::Number(arr.len() as f64));
37 }
38 }
39 }
40 _ => {}
41 }
42
43 let arg_values: Result<Vec<Value>> = args.iter().map(|e| self.eval_expr(e)).collect();
45 let arg_values = arg_values?;
46
47 if let Some(result) = self.call_builtin(name, &arg_values)? {
49 return Ok(result);
50 }
51
52 if let Some(func) = self.functions.get(name).cloned() {
54 let array_refs: Vec<Option<String>> = args
56 .iter()
57 .map(|e| match e {
58 Expr::Var(name, _) if self.arrays.contains_key(name) => Some(name.clone()),
59 _ => None,
60 })
61 .collect();
62 return self.call_user_function(func, arg_values, array_refs, output);
63 }
64
65 Err(Error::runtime_at(
66 format!("undefined function: {}", name),
67 location.line,
68 location.column,
69 ))
70 }
71
72 fn extract_pattern(&mut self, expr: &Expr) -> Result<String> {
74 match expr {
75 Expr::Regex(pattern, _) => Ok(pattern.clone()),
76 other => Ok(self.eval_expr(other)?.to_string_val()),
77 }
78 }
79
80 fn call_regex_sub(
82 &mut self,
83 name: &str,
84 args: &[Expr],
85 _location: SourceLocation,
86 ) -> Result<Value> {
87 let global = name == "gsub";
88
89 let pattern = args
90 .first()
91 .map(|e| self.extract_pattern(e))
92 .transpose()?
93 .unwrap_or_default();
94
95 let replacement = args
96 .get(1)
97 .map(|e| self.eval_expr(e))
98 .transpose()?
99 .map(|v| v.to_string_val())
100 .unwrap_or_default();
101
102 let (target_value, target_expr) = if let Some(target_arg) = args.get(2) {
104 (
105 self.eval_expr(target_arg)?.to_string_val(),
106 Some(target_arg),
107 )
108 } else {
109 (self.record.clone(), None)
110 };
111
112 let re = self.get_regex(&pattern)?.clone();
113
114 let (new_str, count) = regex_sub_helper(&re, &replacement, &target_value, global);
115
116 if let Some(target_arg) = target_expr {
118 self.assign_to_lvalue(target_arg, Value::from_string(new_str))?;
119 } else {
120 self.set_record(&new_str);
121 }
122
123 Ok(Value::Number(count as f64))
124 }
125
126 fn call_match(&mut self, args: &[Expr], _location: SourceLocation) -> Result<Value> {
128 let s = args
129 .first()
130 .map(|e| self.eval_expr(e))
131 .transpose()?
132 .map(|v| v.to_string_val())
133 .unwrap_or_default();
134
135 let pattern = args
136 .get(1)
137 .map(|e| self.extract_pattern(e))
138 .transpose()?
139 .unwrap_or_default();
140
141 let re = self.get_regex(&pattern)?.clone();
142
143 if let Some(m) = re.find(&s) {
144 self.rstart = m.start() + 1;
145 self.rlength = m.len() as i32;
146 Ok(Value::Number(self.rstart as f64))
147 } else {
148 self.rstart = 0;
149 self.rlength = -1;
150 Ok(Value::Number(0.0))
151 }
152 }
153
154 fn call_split(&mut self, args: &[Expr], location: SourceLocation) -> Result<Value> {
156 let s = args
157 .first()
158 .map(|e| self.eval_expr(e))
159 .transpose()?
160 .map(|v| v.to_string_val())
161 .unwrap_or_default();
162
163 let array_name = match args.get(1) {
165 Some(Expr::Var(name, _)) => name.clone(),
166 Some(Expr::ArrayAccess { array, .. }) => array.clone(),
167 Some(_) => {
168 return Err(Error::runtime_at(
169 "split: second argument must be an array",
170 location.line,
171 location.column,
172 ));
173 }
174 None => {
175 return Err(Error::runtime_at(
176 "split: missing array argument",
177 location.line,
178 location.column,
179 ));
180 }
181 };
182
183 let sep = if let Some(sep_expr) = args.get(2) {
185 self.extract_pattern(sep_expr)?
186 } else {
187 self.fs.clone()
188 };
189
190 self.arrays.remove(&array_name);
192
193 let parts: Vec<&str> = if sep.is_empty() {
195 s.char_indices()
197 .map(|(start, c)| &s[start..start + c.len_utf8()])
198 .collect()
199 } else if sep == " " {
200 s.split_whitespace().collect()
201 } else if sep.len() == 1 {
202 s.split(&sep).collect()
203 } else {
204 let re = self.get_regex(&sep)?.clone();
206 re.split(&s).collect()
207 };
208
209 for (i, part) in parts.iter().enumerate() {
210 let key = (i + 1).to_string();
211 self.set_array_element(&array_name, &key, Value::from_string(part.to_string()));
212 }
213
214 Ok(Value::Number(parts.len() as f64))
215 }
216
217 fn call_asort(
220 &mut self,
221 sort_indices: bool,
222 args: &[Expr],
223 location: SourceLocation,
224 ) -> Result<Value> {
225 let source_name = match args.first() {
227 Some(Expr::Var(name, _)) => name.clone(),
228 _ => {
229 return Err(Error::runtime_at(
230 if sort_indices {
231 "asorti: first argument must be an array"
232 } else {
233 "asort: first argument must be an array"
234 },
235 location.line,
236 location.column,
237 ));
238 }
239 };
240
241 let dest_name = match args.get(1) {
243 Some(Expr::Var(name, _)) => Some(name.clone()),
244 None => None,
245 _ => {
246 return Err(Error::runtime_at(
247 if sort_indices {
248 "asorti: second argument must be an array"
249 } else {
250 "asort: second argument must be an array"
251 },
252 location.line,
253 location.column,
254 ));
255 }
256 };
257
258 let items: Vec<String> = if let Some(arr) = self.arrays.get(&source_name) {
260 if sort_indices {
261 arr.keys().cloned().collect()
262 } else {
263 arr.values().map(|v| v.to_string_val()).collect()
264 }
265 } else {
266 Vec::new()
267 };
268
269 let mut sorted = items;
270 sorted.sort();
271
272 let count = sorted.len();
273
274 let target = dest_name.unwrap_or_else(|| source_name.clone());
276 self.arrays.remove(&target);
277
278 for (i, item) in sorted.iter().enumerate() {
279 let key = (i + 1).to_string();
280 self.set_array_element(&target, &key, Value::from_string(item.clone()));
281 }
282
283 Ok(Value::Number(count as f64))
284 }
285
286 fn call_patsplit(&mut self, args: &[Expr], location: SourceLocation) -> Result<Value> {
288 let s = args
290 .first()
291 .map(|e| self.eval_expr(e))
292 .transpose()?
293 .map(|v| v.to_string_val())
294 .unwrap_or_default();
295
296 let array_name = match args.get(1) {
298 Some(Expr::Var(name, _)) => name.clone(),
299 _ => {
300 return Err(Error::runtime_at(
301 "patsplit: second argument must be an array",
302 location.line,
303 location.column,
304 ));
305 }
306 };
307
308 let fieldpat = if let Some(pat_expr) = args.get(2) {
310 self.extract_pattern(pat_expr)?
311 } else {
312 return Err(Error::runtime_at(
313 "patsplit: missing fieldpat argument",
314 location.line,
315 location.column,
316 ));
317 };
318
319 let seps_name = match args.get(3) {
321 Some(Expr::Var(name, _)) => Some(name.clone()),
322 None => None,
323 _ => None,
324 };
325
326 self.arrays.remove(&array_name);
328 if let Some(ref name) = seps_name {
329 self.arrays.remove(name);
330 }
331
332 let re = self.get_regex(&fieldpat)?;
334 let matches: Vec<regex::Match> = re.find_iter(&s).collect();
335
336 for (i, mat) in matches.iter().enumerate() {
338 let key = (i + 1).to_string();
339 self.set_array_element(
340 &array_name,
341 &key,
342 Value::from_string(mat.as_str().to_string()),
343 );
344 }
345
346 if let Some(ref name) = seps_name {
348 let mut last_end = 0;
349 for (i, mat) in matches.iter().enumerate() {
350 let sep = &s[last_end..mat.start()];
351 let key = i.to_string();
352 self.set_array_element(name, &key, Value::from_string(sep.to_string()));
353 last_end = mat.end();
354 }
355 let key = matches.len().to_string();
357 self.set_array_element(name, &key, Value::from_string(s[last_end..].to_string()));
358 }
359
360 Ok(Value::Number(matches.len() as f64))
361 }
362
363 fn call_close(&mut self, args: &[Expr], location: SourceLocation) -> Result<Value> {
365 let filename = args
366 .first()
367 .map(|e| self.eval_expr(e))
368 .transpose()?
369 .map(|v| v.to_string_val())
370 .unwrap_or_default();
371
372 let found = self.output_files.remove(&filename).is_some()
374 || self.input_files.remove(&filename).is_some()
375 || self.pipes.remove(&filename).is_some();
376
377 let _ = location;
378 Ok(Value::Number(if found { 0.0 } else { -1.0 }))
379 }
380
381 fn call_fflush<W: Write>(
383 &mut self,
384 args: &[Expr],
385 _location: SourceLocation,
386 output: &mut W,
387 ) -> Result<Value> {
388 if args.is_empty() {
389 output.flush().map_err(Error::Io)?;
391 for file in self.output_files.values_mut() {
392 let _ = file.flush();
393 }
394 Ok(Value::Number(0.0))
395 } else {
396 let filename = self.eval_expr(&args[0])?.to_string_val();
397 if filename.is_empty() {
398 output.flush().map_err(Error::Io)?;
399 Ok(Value::Number(0.0))
400 } else if let Some(file) = self.output_files.get_mut(&filename) {
401 file.flush().map_err(Error::Io)?;
402 Ok(Value::Number(0.0))
403 } else {
404 Ok(Value::Number(-1.0))
405 }
406 }
407 }
408
409 fn call_system<W: Write>(&mut self, args: &[Expr], output: &mut W) -> Result<Value> {
412 let cmd = match args.first() {
413 Some(expr) => self.eval_expr(expr)?.to_string_val(),
414 None => String::new(),
415 };
416
417 output.flush().map_err(Error::Io)?;
418 for file in self.output_files.values_mut() {
419 let _ = file.flush();
420 }
421
422 let status = std::process::Command::new("sh")
423 .arg("-c")
424 .arg(&cmd)
425 .status()
426 .map(|s| s.code().unwrap_or(-1))
427 .unwrap_or(-1);
428 Ok(Value::Number(status as f64))
429 }
430
431 fn call_builtin(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>> {
432 match name {
433 "length" => {
435 let s = args
436 .first()
437 .map(|v| v.to_string_val())
438 .unwrap_or_else(|| self.record.clone());
439 Ok(Some(Value::Number(s.chars().count() as f64)))
441 }
442
443 "substr" => {
444 let s = args.first().map(|v| v.to_string_val()).unwrap_or_default();
445 let start = args.get(1).map(|v| v.to_number() as usize).unwrap_or(1);
446 let len = args.get(2).map(|v| v.to_number() as usize);
447
448 let start = start.max(1).saturating_sub(1);
450 let result = if let Some(len) = len {
451 s.chars().skip(start).take(len).collect()
452 } else {
453 s.chars().skip(start).collect()
454 };
455 Ok(Some(Value::from_string(result)))
456 }
457
458 "index" => {
459 let s = args.first().map(|v| v.to_string_val()).unwrap_or_default();
460 let target = args.get(1).map(|v| v.to_string_val()).unwrap_or_default();
461 let pos = s
463 .find(&target)
464 .map(|byte_idx| {
465 s[..byte_idx].chars().count() + 1
467 })
468 .unwrap_or(0);
469 Ok(Some(Value::Number(pos as f64)))
470 }
471
472 "sprintf" => {
473 let format = args.first().map(|v| v.to_string_val()).unwrap_or_default();
474 let rest = if args.len() > 1 { &args[1..] } else { &[] };
475 let result = self.format_printf(&format, rest);
476 Ok(Some(Value::from_string(result)))
477 }
478
479 "tolower" => {
480 let s = args.first().map(|v| v.to_string_val()).unwrap_or_default();
481 Ok(Some(Value::from_string(s.to_lowercase())))
482 }
483
484 "toupper" => {
485 let s = args.first().map(|v| v.to_string_val()).unwrap_or_default();
486 Ok(Some(Value::from_string(s.to_uppercase())))
487 }
488
489 "sin" => {
491 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
492 Ok(Some(Value::Number(n.sin())))
493 }
494
495 "cos" => {
496 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
497 Ok(Some(Value::Number(n.cos())))
498 }
499
500 "atan2" => {
501 let y = args.first().map(|v| v.to_number()).unwrap_or(0.0);
502 let x = args.get(1).map(|v| v.to_number()).unwrap_or(0.0);
503 Ok(Some(Value::Number(y.atan2(x))))
504 }
505
506 "exp" => {
507 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
508 Ok(Some(Value::Number(n.exp())))
509 }
510
511 "log" => {
512 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
513 Ok(Some(Value::Number(n.ln())))
514 }
515
516 "sqrt" => {
517 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
518 Ok(Some(Value::Number(n.sqrt())))
519 }
520
521 "int" => {
522 let n = args.first().map(|v| v.to_number()).unwrap_or(0.0);
523 Ok(Some(Value::Number(n.trunc())))
524 }
525
526 "rand" => {
527 let random = self.next_random();
529 Ok(Some(Value::Number(random)))
530 }
531
532 "srand" => {
533 let old_seed = self.rand_seed;
534 if let Some(seed) = args.first() {
535 self.rand_seed = seed.to_number() as u64;
536 } else {
537 use std::time::{SystemTime, UNIX_EPOCH};
538 self.rand_seed = SystemTime::now()
539 .duration_since(UNIX_EPOCH)
540 .map(|d| d.as_secs())
541 .unwrap_or(0);
542 }
543 self.rand_state = self.rand_seed;
544 Ok(Some(Value::Number(old_seed as f64)))
545 }
546
547 "systime" => {
552 use std::time::{SystemTime, UNIX_EPOCH};
554 let secs = SystemTime::now()
555 .duration_since(UNIX_EPOCH)
556 .map(|d| d.as_secs())
557 .unwrap_or(0);
558 Ok(Some(Value::Number(secs as f64)))
559 }
560
561 "mktime" => {
562 let datespec = args.first().map(|v| v.to_string_val()).unwrap_or_default();
564 let parts: Vec<i64> = datespec
565 .split_whitespace()
566 .filter_map(|s| s.parse().ok())
567 .collect();
568
569 if parts.len() >= 6 {
570 let year = parts[0];
573 let month = parts[1];
574 let day = parts[2];
575 let hour = parts[3];
576 let min = parts[4];
577 let sec = parts[5];
578
579 let epoch = simple_mktime(year, month, day, hour, min, sec);
581 Ok(Some(Value::Number(epoch as f64)))
582 } else {
583 Ok(Some(Value::Number(-1.0)))
584 }
585 }
586
587 "strftime" => {
588 let format = args
590 .first()
591 .map(|v| v.to_string_val())
592 .unwrap_or_else(|| "%a %b %e %H:%M:%S %Z %Y".to_string());
593 use std::time::{SystemTime, UNIX_EPOCH};
594 let timestamp = args
595 .get(1)
596 .map(|v| v.to_number() as u64)
597 .unwrap_or_else(|| {
598 SystemTime::now()
599 .duration_since(UNIX_EPOCH)
600 .map(|d| d.as_secs())
601 .unwrap_or(0)
602 });
603
604 let result = format_strftime(&format, timestamp);
605 Ok(Some(Value::from_string(result)))
606 }
607
608 "gensub" => {
610 let pattern = args.first().map(|v| v.to_string_val()).unwrap_or_default();
611 let replacement = args.get(1).map(|v| v.to_string_val()).unwrap_or_default();
612 let how = args
613 .get(2)
614 .map(|v| v.to_string_val())
615 .unwrap_or_else(|| "g".to_string());
616 let target = args
617 .get(3)
618 .map(|v| v.to_string_val())
619 .unwrap_or_else(|| self.record.clone());
620
621 let re = self.get_regex(&pattern)?.clone();
622
623 let result = if how.eq_ignore_ascii_case("g") {
625 let mut result = String::new();
626 let mut last_end = 0;
627 for caps in re.captures_iter(&target) {
628 let m = caps.get(0).unwrap();
629 result.push_str(&target[last_end..m.start()]);
630 result.push_str(&apply_replacement(&replacement, &caps, true));
631 last_end = m.end();
632 }
633 result.push_str(&target[last_end..]);
634 result
635 } else {
636 let n = how.parse::<usize>().unwrap_or(1).max(1);
638 let mut count = 0;
639 let mut last_end = 0;
640 let mut result = String::new();
641 let mut replaced = false;
642 for caps in re.captures_iter(&target) {
643 count += 1;
644 if count == n {
645 let m = caps.get(0).unwrap();
646 result.push_str(&target[last_end..m.start()]);
647 result.push_str(&apply_replacement(&replacement, &caps, true));
648 last_end = m.end();
649 replaced = true;
650 break;
651 }
652 }
653 if !replaced {
654 target.clone()
655 } else {
656 result.push_str(&target[last_end..]);
657 result
658 }
659 };
660
661 Ok(Some(Value::from_string(result)))
662 }
663
664 _ => Ok(None), }
666 }
667
668 fn call_user_function<W: Write>(
669 &mut self,
670 func: &crate::ast::FunctionDef,
671 args: Vec<Value>,
672 array_refs: Vec<Option<String>>,
673 output: &mut W,
674 ) -> Result<Value> {
675 let saved_vars: std::collections::HashMap<String, Value> = func
677 .params
678 .iter()
679 .filter_map(|name| self.variables.get(name).map(|v| (name.clone(), v.clone())))
680 .collect();
681
682 let saved_arrays: std::collections::HashMap<
684 String,
685 std::collections::HashMap<String, Value>,
686 > = func
687 .params
688 .iter()
689 .filter_map(|name| self.arrays.get(name).map(|a| (name.clone(), a.clone())))
690 .collect();
691
692 let mut array_aliases: std::collections::HashMap<String, String> =
695 std::collections::HashMap::new();
696 for (i, param) in func.params.iter().enumerate() {
697 if let Some(Some(array_name)) = array_refs.get(i) {
698 if param != array_name {
701 array_aliases.insert(param.clone(), array_name.clone());
702 }
703 }
704 }
705 self.array_aliases = array_aliases;
706
707 for (i, param) in func.params.iter().enumerate() {
709 if let Some(Some(_)) = array_refs.get(i) {
711 continue;
712 }
713 let value = args.get(i).cloned().unwrap_or(Value::Uninitialized);
714 self.set_variable_value(param, value);
715 }
716
717 let result = match self.execute_block(&func.body, output)? {
719 super::stmt::StmtResult::Return(v) => v,
720 _ => Value::Uninitialized,
721 };
722
723 self.array_aliases.clear();
725
726 for param in &func.params {
728 if let Some(value) = saved_vars.get(param) {
729 self.set_variable_value(param, value.clone());
730 } else {
731 self.variables.remove(param);
732 }
733 }
734
735 for param in &func.params {
737 if let Some(arr) = saved_arrays.get(param) {
738 self.arrays.insert(param.clone(), arr.clone());
739 } else if !array_refs
740 .get(
741 func.params
742 .iter()
743 .position(|p| p == param)
744 .unwrap_or(usize::MAX),
745 )
746 .map(|r| r.is_some())
747 .unwrap_or(false)
748 {
749 self.arrays.remove(param);
750 }
751 }
752
753 Ok(result)
754 }
755
756 fn next_random(&mut self) -> f64 {
758 let mut x = self.rand_state;
759 x ^= x << 13;
760 x ^= x >> 7;
761 x ^= x << 17;
762 self.rand_state = x;
763 (x as f64) / (u64::MAX as f64)
764 }
765}
766
767fn regex_sub_helper(
768 re: ®ex::Regex,
769 replacement: &str,
770 target: &str,
771 global: bool,
772) -> (String, usize) {
773 let mut count = 0;
776
777 if global {
778 let result = re.replace_all(target, |caps: ®ex::Captures| {
779 count += 1;
780 apply_replacement(replacement, caps, false)
781 });
782 (result.to_string(), count)
783 } else {
784 let result = re.replace(target, |caps: ®ex::Captures| {
785 count += 1;
786 apply_replacement(replacement, caps, false)
787 });
788 (result.to_string(), count)
789 }
790}
791
792fn apply_replacement(replacement: &str, caps: ®ex::Captures, allow_groups: bool) -> String {
802 let mut result = String::new();
803 let mut chars = replacement.chars().peekable();
804
805 while let Some(c) = chars.next() {
806 match c {
807 '\\' => match chars.peek().copied() {
808 Some('&') => {
809 result.push('&');
810 chars.next();
811 }
812 Some('\\') => {
813 result.push('\\');
814 chars.next();
815 }
816 Some(d) if allow_groups && d.is_ascii_digit() && d != '0' => {
817 chars.next();
818 let idx = d.to_digit(10).unwrap() as usize;
819 if let Some(m) = caps.get(idx) {
820 result.push_str(m.as_str());
821 }
822 }
823 _ => result.push('\\'),
824 },
825 '&' => {
826 if let Some(m) = caps.get(0) {
827 result.push_str(m.as_str());
828 }
829 }
830 other => result.push(other),
831 }
832 }
833
834 result
835}
836
837fn simple_mktime(year: i64, month: i64, day: i64, hour: i64, min: i64, sec: i64) -> i64 {
839 const DAYS_IN_MONTH: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
841
842 fn is_leap_year(year: i64) -> bool {
843 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
844 }
845
846 fn days_in_year(year: i64) -> i64 {
847 if is_leap_year(year) { 366 } else { 365 }
848 }
849
850 let mut days: i64 = 0;
852
853 for y in 1970..year {
855 days += days_in_year(y);
856 }
857 for y in year..1970 {
858 days -= days_in_year(y);
859 }
860
861 for m in 1..month {
863 let m_idx = (m - 1) as usize;
864 if m_idx < 12 {
865 days += DAYS_IN_MONTH[m_idx];
866 if m == 2 && is_leap_year(year) {
867 days += 1;
868 }
869 }
870 }
871
872 days += day - 1;
874
875 days * 86400 + hour * 3600 + min * 60 + sec
877}
878
879fn format_strftime(format: &str, timestamp: u64) -> String {
881 let secs = timestamp as i64;
883
884 let (year, month, day, hour, min, sec, wday, yday) = breakdown_time(secs);
886
887 let weekday_names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
888 let month_names = [
889 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
890 ];
891 let weekday_full = [
892 "Sunday",
893 "Monday",
894 "Tuesday",
895 "Wednesday",
896 "Thursday",
897 "Friday",
898 "Saturday",
899 ];
900 let month_full = [
901 "January",
902 "February",
903 "March",
904 "April",
905 "May",
906 "June",
907 "July",
908 "August",
909 "September",
910 "October",
911 "November",
912 "December",
913 ];
914
915 let mut result = String::new();
916 let mut chars = format.chars().peekable();
917
918 while let Some(ch) = chars.next() {
919 if ch == '%' {
920 match chars.next() {
921 Some('Y') => result.push_str(&format!("{:04}", year)),
922 Some('y') => result.push_str(&format!("{:02}", year % 100)),
923 Some('m') => result.push_str(&format!("{:02}", month)),
924 Some('d') => result.push_str(&format!("{:02}", day)),
925 Some('e') => result.push_str(&format!("{:2}", day)),
926 Some('H') => result.push_str(&format!("{:02}", hour)),
927 Some('M') => result.push_str(&format!("{:02}", min)),
928 Some('S') => result.push_str(&format!("{:02}", sec)),
929 Some('a') => result.push_str(weekday_names.get(wday as usize).unwrap_or(&"???")),
930 Some('A') => result.push_str(weekday_full.get(wday as usize).unwrap_or(&"???")),
931 Some('b') | Some('h') => {
932 result.push_str(month_names.get((month - 1) as usize).unwrap_or(&"???"))
933 }
934 Some('B') => {
935 result.push_str(month_full.get((month - 1) as usize).unwrap_or(&"???"))
936 }
937 Some('j') => result.push_str(&format!("{:03}", yday)),
938 Some('u') => result.push_str(&format!("{}", if wday == 0 { 7 } else { wday })),
939 Some('w') => result.push_str(&format!("{}", wday)),
940 Some('Z') => result.push_str("UTC"),
941 Some('z') => result.push_str("+0000"),
942 Some('%') => result.push('%'),
943 Some('n') => result.push('\n'),
944 Some('t') => result.push('\t'),
945 Some(c) => {
946 result.push('%');
947 result.push(c);
948 }
949 None => result.push('%'),
950 }
951 } else {
952 result.push(ch);
953 }
954 }
955
956 result
957}
958
959fn breakdown_time(secs: i64) -> (i64, i64, i64, i64, i64, i64, i64, i64) {
961 const DAYS_IN_MONTH: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
962
963 fn is_leap_year(year: i64) -> bool {
964 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
965 }
966
967 let sec = secs % 60;
968 let min = (secs / 60) % 60;
969 let hour = (secs / 3600) % 24;
970 let mut days = secs / 86400;
971
972 let wday = ((days + 4) % 7 + 7) % 7;
974
975 let mut year = 1970i64;
977 loop {
978 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
979 if days >= days_in_year {
980 days -= days_in_year;
981 year += 1;
982 } else if days < 0 {
983 year -= 1;
984 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
985 days += days_in_year;
986 } else {
987 break;
988 }
989 }
990
991 let yday = days + 1; let mut month = 1i64;
995 for (m, &dim) in DAYS_IN_MONTH.iter().enumerate() {
996 let mut days_in_month = dim;
997 if m == 1 && is_leap_year(year) {
998 days_in_month += 1;
999 }
1000 if days < days_in_month {
1001 month = m as i64 + 1;
1002 break;
1003 }
1004 days -= days_in_month;
1005 }
1006 let day = days + 1;
1007
1008 (year, month, day, hour, min, sec, wday, yday)
1009}