1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 #ifndef PACKAGE_INFO_STRING_BUILDER_H 6 #define PACKAGE_INFO_STRING_BUILDER_H 7 8 9 #include <ctype.h> 10 11 #include <DataIO.h> 12 #include <package/PackageInfo.h> 13 14 15 namespace BPackageKit { 16 17 18 struct BPackageInfo::StringBuilder { 19 StringBuilder() 20 : 21 fData(), 22 fError(B_OK), 23 fBasePackage() 24 { 25 } 26 27 status_t Error() const 28 { 29 return fError; 30 } 31 32 status_t GetString(BString& _string) const 33 { 34 if (fError != B_OK) { 35 _string = BString(); 36 return fError; 37 } 38 39 _string.SetTo((const char*)fData.Buffer(), fData.BufferLength()); 40 return (size_t)_string.Length() == fData.BufferLength() 41 ? B_OK : B_NO_MEMORY; 42 } 43 44 template<typename Value> 45 StringBuilder& Write(const char* attribute, Value value) 46 { 47 if (_IsValueEmpty(value)) 48 return *this; 49 50 _Write(attribute); 51 _Write('\t'); 52 _WriteValue(value); 53 _Write('\n'); 54 return *this; 55 } 56 57 StringBuilder& WriteFlags(const char* attribute, uint32 flags) 58 { 59 if ((flags & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0 60 && (flags & B_PACKAGE_FLAG_SYSTEM_PACKAGE) == 0) { 61 return *this; 62 } 63 64 _Write(attribute); 65 _Write('\t'); 66 67 if ((flags & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0) 68 _Write(" approve_license"); 69 if ((flags & B_PACKAGE_FLAG_SYSTEM_PACKAGE) == 0) 70 _Write(" system_package"); 71 72 _Write('\n'); 73 return *this; 74 } 75 76 StringBuilder& BeginRequires(const BString& basePackage) 77 { 78 fBasePackage = basePackage; 79 return *this; 80 } 81 82 StringBuilder& EndRequires() 83 { 84 fBasePackage.Truncate(0); 85 return *this; 86 } 87 88 private: 89 void _WriteValue(const char* value) 90 { 91 _WriteMaybeQuoted(value); 92 } 93 94 void _WriteValue(const BPackageVersion& value) 95 { 96 if (fError != B_OK) 97 return; 98 99 if (value.InitCheck() != B_OK) { 100 fError = B_BAD_VALUE; 101 return; 102 } 103 104 _Write(value.ToString()); 105 } 106 107 void _WriteValue(const BStringList& value) 108 { 109 int32 count = value.CountStrings(); 110 if (count == 1) { 111 _WriteMaybeQuoted(value.StringAt(0)); 112 } else { 113 _Write("{\n", 2); 114 115 int32 count = value.CountStrings(); 116 for (int32 i = 0; i < count; i++) { 117 _Write('\t'); 118 _WriteMaybeQuoted(value.StringAt(i)); 119 _Write('\n'); 120 } 121 122 _Write('}'); 123 } 124 } 125 126 template<typename Value> 127 void _WriteValue(const BObjectList<Value>& value) 128 { 129 // Note: The fBasePackage solution is disgusting, but any attempt of 130 // encapsulating the stringification via templates seems to result in 131 // an Internal Compiler Error with gcc 2. 132 133 _Write("{\n", 2); 134 135 int32 count = value.CountItems(); 136 for (int32 i = 0; i < count; i++) { 137 _Write('\t'); 138 _WriteListElement(value.ItemAt(i)); 139 _Write('\n'); 140 } 141 142 _Write('}'); 143 } 144 145 template<typename Value> 146 void _WriteListElement(const Value* value) 147 { 148 _Write(value->ToString()); 149 if (!fBasePackage.IsEmpty() 150 && value->Name() == fBasePackage) { 151 _Write(" base"); 152 } 153 } 154 155 void _WriteListElement(const BGlobalWritableFileInfo* value) 156 { 157 _WriteMaybeQuoted(value->Path()); 158 if (value->IsDirectory()) { 159 _Write(' '); 160 _Write("directory"); 161 } 162 if (value->IsIncluded()) { 163 _Write(' '); 164 _Write(kWritableFileUpdateTypes[value->UpdateType()]); 165 } 166 } 167 168 void _WriteListElement(const BUserSettingsFileInfo* value) 169 { 170 _WriteMaybeQuoted(value->Path()); 171 if (value->IsDirectory()) { 172 _Write(" directory"); 173 } else if (!value->TemplatePath().IsEmpty()) { 174 _Write(" template "); 175 _WriteMaybeQuoted(value->TemplatePath()); 176 } 177 } 178 179 void _WriteListElement(const BUser* value) 180 { 181 _WriteMaybeQuoted(value->Name()); 182 183 if (!value->RealName().IsEmpty()) { 184 _Write(" real-name "); 185 _WriteMaybeQuoted(value->RealName()); 186 } 187 188 if (!value->Home().IsEmpty()) { 189 _Write(" home "); 190 _WriteMaybeQuoted(value->Home()); 191 } 192 193 if (!value->Shell().IsEmpty()) { 194 _Write(" shell "); 195 _WriteMaybeQuoted(value->Shell()); 196 } 197 198 if (!value->Groups().IsEmpty()) { 199 _Write(" groups "); 200 BString groups = value->Groups().Join(" "); 201 if (groups.IsEmpty()) { 202 if (fError == B_OK) 203 fError = B_NO_MEMORY; 204 return; 205 } 206 _Write(groups); 207 } 208 } 209 210 static inline bool _IsValueEmpty(const char* value) 211 { 212 return value[0] == '\0'; 213 } 214 215 static inline bool _IsValueEmpty(const BPackageVersion& value) 216 { 217 return false; 218 } 219 220 template<typename List> 221 static inline bool _IsValueEmpty(const List& value) 222 { 223 return value.IsEmpty(); 224 } 225 226 void _WriteMaybeQuoted(const char* data) 227 { 228 // check whether quoting is needed 229 bool needsQuoting = false; 230 bool needsEscaping = false; 231 for (const char* it = data; *it != '\0'; it++) { 232 if (isalnum(*it) || *it == '.' || *it == '-' || *it == '_' 233 || *it == ':' || *it == '+') { 234 continue; 235 } 236 237 needsQuoting = true; 238 239 if (*it == '\t' || *it == '\n' || *it == '"' || *it == '\\') { 240 needsEscaping = true; 241 break; 242 } 243 } 244 245 if (!needsQuoting) { 246 _Write(data); 247 return; 248 } 249 250 // we need quoting 251 _Write('"'); 252 253 // escape the string, if necessary 254 if (needsEscaping) { 255 const char* start = data; 256 const char* end = data; 257 while (*end != '\0') { 258 char replacement[2]; 259 switch (*end) { 260 case '\t': 261 replacement[1] = 't'; 262 break; 263 case '\n': 264 replacement[1] = 'n'; 265 break; 266 case '"': 267 case '\\': 268 replacement[1] = *end; 269 break; 270 default: 271 end++; 272 continue; 273 } 274 275 if (start < end) 276 _Write(start, end - start); 277 278 replacement[0] = '\\'; 279 _Write(replacement, 2); 280 start = ++end; 281 } 282 283 if (start < end) 284 _Write(start, end - start); 285 } else 286 _Write(data); 287 288 _Write('"'); 289 } 290 291 inline void _Write(char data) 292 { 293 _Write(&data, 1); 294 } 295 296 inline void _Write(const char* data) 297 { 298 _Write(data, strlen(data)); 299 } 300 301 inline void _Write(const BString& data) 302 { 303 _Write(data, data.Length()); 304 } 305 306 void _Write(const void* data, size_t size) 307 { 308 if (fError == B_OK) { 309 ssize_t bytesWritten = fData.Write(data, size); 310 if (bytesWritten < 0) 311 fError = bytesWritten; 312 } 313 } 314 315 private: 316 BMallocIO fData; 317 status_t fError; 318 BString fBasePackage; 319 }; 320 321 322 } // namespace BPackageKit 323 324 325 #endif // PACKAGE_INFO_STRING_BUILDER_H 326