Avoid using temp file during write (#115)
Ethan Koenig authored 6 years ago
无闻 committed 6 years ago
23 | 23 | "os" |
24 | 24 | "regexp" |
25 | 25 | "runtime" |
26 | "strconv" | |
27 | 26 | "strings" |
28 | 27 | "sync" |
29 | "time" | |
30 | 28 | ) |
31 | 29 | |
32 | 30 | const ( |
397 | 395 | return f.Reload() |
398 | 396 | } |
399 | 397 | |
400 | // WriteToIndent writes content into io.Writer with given indention. | |
401 | // If PrettyFormat has been set to be true, | |
402 | // it will align "=" sign with spaces under each section. | |
403 | func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { | |
398 | func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { | |
404 | 399 | equalSign := "=" |
405 | 400 | if PrettyFormat { |
406 | 401 | equalSign = " = " |
414 | 409 | if sec.Comment[0] != '#' && sec.Comment[0] != ';' { |
415 | 410 | sec.Comment = "; " + sec.Comment |
416 | 411 | } |
417 | if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil { | |
418 | return 0, err | |
412 | if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { | |
413 | return nil, err | |
419 | 414 | } |
420 | 415 | } |
421 | 416 | |
422 | 417 | if i > 0 || DefaultHeader { |
423 | if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil { | |
424 | return 0, err | |
418 | if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { | |
419 | return nil, err | |
425 | 420 | } |
426 | 421 | } else { |
427 | 422 | // Write nothing if default section is empty |
431 | 426 | } |
432 | 427 | |
433 | 428 | if sec.isRawSection { |
434 | if _, err = buf.WriteString(sec.rawBody); err != nil { | |
435 | return 0, err | |
429 | if _, err := buf.WriteString(sec.rawBody); err != nil { | |
430 | return nil, err | |
436 | 431 | } |
437 | 432 | continue |
438 | 433 | } |
468 | 463 | if key.Comment[0] != '#' && key.Comment[0] != ';' { |
469 | 464 | key.Comment = "; " + key.Comment |
470 | 465 | } |
471 | if _, err = buf.WriteString(key.Comment + LineBreak); err != nil { | |
472 | return 0, err | |
466 | if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { | |
467 | return nil, err | |
473 | 468 | } |
474 | 469 | } |
475 | 470 | |
487 | 482 | } |
488 | 483 | |
489 | 484 | for _, val := range key.ValueWithShadows() { |
490 | if _, err = buf.WriteString(kname); err != nil { | |
491 | return 0, err | |
485 | if _, err := buf.WriteString(kname); err != nil { | |
486 | return nil, err | |
492 | 487 | } |
493 | 488 | |
494 | 489 | if key.isBooleanType { |
509 | 504 | } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { |
510 | 505 | val = "`" + val + "`" |
511 | 506 | } |
512 | if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil { | |
513 | return 0, err | |
507 | if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { | |
508 | return nil, err | |
514 | 509 | } |
515 | 510 | } |
516 | 511 | } |
517 | 512 | |
518 | 513 | if PrettySection { |
519 | 514 | // Put a line between sections |
520 | if _, err = buf.WriteString(LineBreak); err != nil { | |
521 | return 0, err | |
522 | } | |
523 | } | |
524 | } | |
525 | ||
515 | if _, err := buf.WriteString(LineBreak); err != nil { | |
516 | return nil, err | |
517 | } | |
518 | } | |
519 | } | |
520 | ||
521 | return buf, nil | |
522 | } | |
523 | ||
524 | // WriteToIndent writes content into io.Writer with given indention. | |
525 | // If PrettyFormat has been set to be true, | |
526 | // it will align "=" sign with spaces under each section. | |
527 | func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { | |
528 | buf, err := f.writeToBuffer(indent) | |
529 | if err != nil { | |
530 | return 0, err | |
531 | } | |
526 | 532 | return buf.WriteTo(w) |
527 | 533 | } |
528 | 534 | |
535 | 541 | func (f *File) SaveToIndent(filename, indent string) error { |
536 | 542 | // Note: Because we are truncating with os.Create, |
537 | 543 | // so it's safer to save to a temporary file location and rename afte done. |
538 | tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp" | |
539 | defer os.Remove(tmpPath) | |
540 | ||
541 | fw, err := os.Create(tmpPath) | |
544 | buf, err := f.writeToBuffer(indent); | |
542 | 545 | if err != nil { |
543 | 546 | return err |
544 | 547 | } |
545 | 548 | |
546 | if _, err = f.WriteToIndent(fw, indent); err != nil { | |
547 | fw.Close() | |
548 | return err | |
549 | } | |
550 | fw.Close() | |
551 | ||
552 | // Remove old file and rename the new one. | |
553 | os.Remove(filename) | |
554 | return os.Rename(tmpPath, filename) | |
549 | return ioutil.WriteFile(filename, buf.Bytes(), 0666) | |
555 | 550 | } |
556 | 551 | |
557 | 552 | // SaveTo writes content to file system. |