summaryrefslogtreecommitdiff
path: root/lib/chef/util/file_edit.rb
blob: ce37bdcdbf3cd8ca6fab79f0e4eb4bad8a2ca9aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#
# Author:: Nuo Yan (<nuo@opscode.com>)
# Copyright:: Copyright (c) 2009 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'fileutils'
require 'tempfile'

class Chef
  class Util
    class FileEdit

      private

      attr_accessor :original_pathname, :contents, :file_edited

      public

      def initialize(filepath)
        @original_pathname = filepath
        @file_edited = false

        raise ArgumentError, "File doesn't exist" unless File.exist? @original_pathname
        raise ArgumentError, "File is blank" unless (@contents = File.new(@original_pathname).readlines).length > 0
      end

      #search the file line by line and match each line with the given regex
      #if matched, replace the whole line with newline.
      def search_file_replace_line(regex, newline)
        search_match(regex, newline, 'r', 1)
      end

      #search the file line by line and match each line with the given regex
      #if matched, replace the match (all occurances)  with the replace parameter
      def search_file_replace(regex, replace)
        search_match(regex, replace, 'r', 2)
      end

      #search the file line by line and match each line with the given regex
      #if matched, delete the line
      def search_file_delete_line(regex)
        search_match(regex, " ", 'd', 1)
      end

      #search the file line by line and match each line with the given regex
      #if matched, delete the match (all occurances) from the line
      def search_file_delete(regex)
        search_match(regex, " ", 'd', 2)
      end

      #search the file line by line and match each line with the given regex
      #if matched, insert newline after each matching line
      def insert_line_after_match(regex, newline)
        search_match(regex, newline, 'i', 1)
      end

      #search the file line by line and match each line with the given regex
      #if not matched, insert newline at the end of the file
      def insert_line_if_no_match(regex, newline)
        search_match(regex, newline, 'i', 2)
      end

      #Make a copy of old_file and write new file out (only if file changed)
      def write_file

        # file_edited is false when there was no match in the whole file and thus no contents have changed.
        if file_edited
          backup_pathname = original_pathname + ".old"
          FileUtils.cp(original_pathname, backup_pathname, :preserve => true)
          File.open(original_pathname, "w") do |newfile|
            contents.each do |line|
              newfile.puts(line)
            end
            newfile.flush
          end
        end
        self.file_edited = false
      end

      private

      #helper method to do the match, replace, delete, and insert operations
      #command is the switch of delete, replace, and insert ('d', 'r', 'i')
      #method is to control operation on whole line or only the match (1 for line, 2 for match)
      def search_match(regex, replace, command, method)

        #convert regex to a Regexp object (if not already is one) and store it in exp.
        exp = Regexp.new(regex)

        #loop through contents and do the appropriate operation depending on 'command' and 'method'
        new_contents = []

        contents.each do |line|
          if line.match(exp)
            self.file_edited = true
            case
            when command == 'r'
              new_contents << ((method == 1) ? replace : line.gsub!(exp, replace))
            when command == 'd'
              if method == 2
                new_contents << line.gsub!(exp, "")
              end
            when command == 'i'
              new_contents << line
              new_contents << replace unless method == 2
            end
          else
            new_contents << line
          end
        end
        if command == 'i' && method == 2 && ! file_edited
          new_contents << replace
          self.file_edited = true
        end

        self.contents = new_contents
      end
    end
  end
end