File indexing completed on 2024-04-21 05:08:03
0001 # frozen_string_literal: true 0002 # 0003 # Copyright (C) 2015-2016 Harald Sitter <sitter@kde.org> 0004 # 0005 # This library is free software; you can redistribute it and/or 0006 # modify it under the terms of the GNU Lesser General Public 0007 # License as published by the Free Software Foundation; either 0008 # version 2.1 of the License, or (at your option) version 3, or any 0009 # later version accepted by the membership of KDE e.V. (or its 0010 # successor approved by the membership of KDE e.V.), which shall 0011 # act as a proxy defined in Section 6 of version 3 of the license. 0012 # 0013 # This library is distributed in the hope that it will be useful, 0014 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 # Lesser General Public License for more details. 0017 # 0018 # You should have received a copy of the GNU Lesser General Public 0019 # License along with this library. If not, see <http://www.gnu.org/licenses/>. 0020 0021 require 'insensitive_hash/minimal' 0022 0023 require_relative 'relationship' 0024 0025 module Debian 0026 # Deb822 specification parser. 0027 class Deb822 0028 def parse_relationships(line) 0029 ret = [] 0030 line.split(',').each do |string| 0031 rel_array = [] 0032 string.split('|').each do |entry| 0033 r = Relationship.new(entry) 0034 next unless r.name # Invalid name, ignore this bugger. 0035 rel_array << r 0036 end 0037 ret << rel_array unless rel_array.empty? 0038 end 0039 ret 0040 end 0041 0042 def parse_paragraph(lines, fields = {}) 0043 mandatory_fields = fields[:mandatory] || [] 0044 multiline_fields = fields[:multiline] || [] 0045 foldable_fields = fields[:foldable] || [] 0046 relationship_fields = fields[:relationship] || [] 0047 0048 current_header = nil 0049 data = InsensitiveHash.new 0050 0051 while (line = lines.shift) && line && !line.strip.empty? 0052 next if line.start_with?('#') # Comment 0053 0054 header_match = line.match(/^(\S+):(.*\n?)$/) 0055 unless header_match.nil? 0056 # 0 = full match 0057 # 1 = key match 0058 # 2 = value match 0059 key = header_match[1].lstrip 0060 value = header_match[2].lstrip 0061 current_header = key 0062 if foldable_fields.include?(key.downcase) 0063 # We do not care about whitespaces for folds, so strip everything. 0064 if relationship_fields.include?(key.downcase) 0065 value = parse_relationships(value) 0066 else 0067 value = [value.chomp(',').strip] 0068 end 0069 elsif multiline_fields.include?(key.downcase) 0070 # For multiline we want to preserve right hand side whitespaces. 0071 value 0072 else 0073 value.strip! 0074 end 0075 data[key] = value 0076 next 0077 end 0078 0079 fold_match = line.match(/^\s+(.+\n)$/) 0080 unless fold_match.nil? 0081 # Folding value encountered -> append to header. 0082 # 0 full match 0083 # 1 value match 0084 value = fold_match[1].lstrip 0085 0086 # Fold matches can either be proper RFC 5322 folds or 0087 # multiline continuations, latter wants to preserve 0088 # newlines and so forth. 0089 # The type is entirely dependent on what the header field is. 0090 if foldable_fields.include?(current_header.downcase) 0091 # We do not care about whitespaces for folds, so strip everything. 0092 if relationship_fields.include?(current_header.downcase) 0093 value = parse_relationships(value) 0094 else 0095 value = [value.strip] 0096 end 0097 data[current_header] += value 0098 elsif multiline_fields.include?(current_header.downcase) 0099 # For multiline we want to preserve right hand side whitespaces. 0100 data[current_header] << value 0101 else 0102 raise "A field is folding that is not allowed to #{current_header}" 0103 end 0104 0105 next 0106 end 0107 0108 # TODO: user defined fields 0109 0110 raise "Paragraph parsing ran into an unknown line: '#{line}'" 0111 end 0112 0113 # If the entire stanza was commented out we can end up with no data, it 0114 # is very sad. 0115 return nil if data.empty? 0116 0117 mandatory_fields.each do |field| 0118 # TODO: this should really make a list and complain all at once or 0119 # something. 0120 raise "Missing mandatory field #{field}" unless data.include?(field) 0121 end 0122 0123 data 0124 end 0125 0126 def parse! 0127 raise 'Not implemented' 0128 end 0129 0130 def dump_paragraph(data, fields = {}) 0131 # mandatory_fields = fields[:mandatory] || [] 0132 multiline_fields = fields[:multiline] || [] 0133 # foldable_fields = fields[:foldable] || [] 0134 relationship_fields = fields[:relationship] || [] 0135 0136 output = '' 0137 data.each do |field, value| 0138 key = "#{field}: " 0139 output += key 0140 field = field.downcase # normalize for include check 0141 if multiline_fields.include?(field) 0142 output += output_multiline(value) 0143 # elsif foldable_fields.include?(field) 0144 # output += output_foldable(value, key.length) 0145 elsif relationship_fields.include?(field) 0146 # relationships are always foldable 0147 output += output_relationship(value, key.length) 0148 else 0149 # FIXME: rstrip because multiline do not get their trailing newline 0150 # stripped in parsing 0151 output += (value || value.rstrip) 0152 end 0153 output += "\n" 0154 end 0155 output 0156 end 0157 0158 private 0159 0160 def output_multiline(data) 0161 data = data.join("\n") if data.respond_to?(:join) 0162 data = data.to_s unless data.is_a?(String) 0163 data.gsub("\n", "\n ").chomp(' ') 0164 end 0165 0166 def output_relationship(data, indent) 0167 # This implements output as per wrap-and-sort. That is: 0168 # - sort all 0169 # - substvars at the end 0170 # - output >80 => line break each entry 0171 data.sort 0172 joined_alternatives = data.collect do |entry| 0173 entry.join(' | ') 0174 end 0175 output = joined_alternatives.join(', ') 0176 return output if output.size < (80 - indent) 0177 joined_alternatives.join(",\n#{Array.new(indent, ' ').join}") 0178 end 0179 end 0180 end