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