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 int32 count = value.CountItems(); 134 if (count == 1) { 135 _WriteListElement(value.ItemAt(0)); 136 } else { 137 _Write("{\n", 2); 138 139 int32 count = value.CountItems(); 140 for (int32 i = 0; i < count; i++) { 141 _Write('\t'); 142 _WriteListElement(value.ItemAt(i)); 143 _Write('\n'); 144 } 145 146 _Write('}'); 147 } 148 } 149 150 template<typename Value> 151 void _WriteListElement(const Value* value) 152 { 153 _Write(value->ToString()); 154 if (!fBasePackage.IsEmpty() 155 && value->Name() == fBasePackage) { 156 _Write(" base"); 157 } 158 } 159 160 void _WriteListElement(const BGlobalWritableFileInfo* value) 161 { 162 _WriteMaybeQuoted(value->Path()); 163 if (value->IsDirectory()) { 164 _Write(' '); 165 _Write("directory"); 166 } 167 if (value->IsIncluded()) { 168 _Write(' '); 169 _Write(kWritableFileUpdateTypes[value->UpdateType()]); 170 } 171 } 172 173 void _WriteListElement(const BUserSettingsFileInfo* value) 174 { 175 _WriteMaybeQuoted(value->Path()); 176 if (value->IsDirectory()) { 177 _Write(" directory"); 178 } else if (!value->TemplatePath().IsEmpty()) { 179 _Write(" template "); 180 _WriteMaybeQuoted(value->TemplatePath()); 181 } 182 } 183 184 void _WriteListElement(const BUser* value) 185 { 186 _WriteMaybeQuoted(value->Name()); 187 188 if (!value->RealName().IsEmpty()) { 189 _Write(" real-name "); 190 _WriteMaybeQuoted(value->RealName()); 191 } 192 193 if (!value->Home().IsEmpty()) { 194 _Write(" home "); 195 _WriteMaybeQuoted(value->Home()); 196 } 197 198 if (!value->Shell().IsEmpty()) { 199 _Write(" shell "); 200 _WriteMaybeQuoted(value->Shell()); 201 } 202 203 if (!value->Groups().IsEmpty()) { 204 _Write(" groups "); 205 BString groups = value->Groups().Join(" "); 206 if (groups.IsEmpty()) { 207 if (fError == B_OK) 208 fError = B_NO_MEMORY; 209 return; 210 } 211 _Write(groups); 212 } 213 } 214 215 static inline bool _IsValueEmpty(const char* value) 216 { 217 return value[0] == '\0'; 218 } 219 220 static inline bool _IsValueEmpty(const BPackageVersion& value) 221 { 222 return false; 223 } 224 225 template<typename List> 226 static inline bool _IsValueEmpty(const List& value) 227 { 228 return value.IsEmpty(); 229 } 230 231 void _WriteMaybeQuoted(const char* data) 232 { 233 // check whether quoting is needed 234 bool needsQuoting = false; 235 bool needsEscaping = false; 236 for (const char* it = data; *it != '\0'; it++) { 237 if (isalnum(*it) || *it == '.' || *it == '-' || *it == '_' 238 || *it == ':' || *it == '+') { 239 continue; 240 } 241 242 needsQuoting = true; 243 244 if (*it == '\t' || *it == '\n' || *it == '"' || *it == '\\') { 245 needsEscaping = true; 246 break; 247 } 248 } 249 250 if (!needsQuoting) { 251 _Write(data); 252 return; 253 } 254 255 // we need quoting 256 _Write('"'); 257 258 // escape the string, if necessary 259 if (needsEscaping) { 260 const char* start = data; 261 const char* end = data; 262 while (*end != '\0') { 263 char replacement[2]; 264 switch (*end) { 265 case '\t': 266 replacement[1] = 't'; 267 break; 268 case '\n': 269 replacement[1] = 'n'; 270 break; 271 case '"': 272 case '\\': 273 replacement[1] = *end; 274 break; 275 default: 276 end++; 277 continue; 278 } 279 280 if (start < end) 281 _Write(start, end - start); 282 283 replacement[0] = '\\'; 284 _Write(replacement, 2); 285 } 286 } else 287 _Write(data); 288 289 _Write('"'); 290 } 291 292 inline void _Write(char data) 293 { 294 _Write(&data, 1); 295 } 296 297 inline void _Write(const char* data) 298 { 299 _Write(data, strlen(data)); 300 } 301 302 inline void _Write(const BString& data) 303 { 304 _Write(data, data.Length()); 305 } 306 307 void _Write(const void* data, size_t size) 308 { 309 if (fError == B_OK) { 310 ssize_t bytesWritten = fData.Write(data, size); 311 if (bytesWritten < 0) 312 fError = bytesWritten; 313 } 314 } 315 316 private: 317 BMallocIO fData; 318 status_t fError; 319 BString fBasePackage; 320 }; 321 322 323 } // namespace BPackageKit 324 325 326 #endif // PACKAGE_INFO_STRING_BUILDER_H 327