this repo has no description
1/*
2This file is part of Darling.
3
4Copyright (C) 2017 Lubos Dolezel
5
6Darling is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11Darling is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Darling. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <CoreFoundation/CoreFoundation.h>
23#include <stdbool.h>
24
25bool processArgs(int argc, const char** argv, const char** command);
26void resolvePlistEntry(const char* whatStr, CFPropertyListRef* parent, CFPropertyListRef* entry, char** last, bool autoCreate);
27void setEntry(const char* entryName, const char* entryValue);
28void printUsage(void);
29void printHelp(void);
30bool revertToFile(void);
31bool saveToFile(void);
32void runCommand(const char* cmd);
33void doPrint(const char* what);
34
35enum PropertyType {
36 typeUnknown = 0, typeString, typeArray, typeDict, typeBool, typeReal, typeInteger, typeDate, typeData
37};
38void addEntry(const char* entry, enum PropertyType type, const char* value);
39CFPropertyListRef parseValue(enum PropertyType type, const char* string);
40void deleteEntry(const char* entry);
41void copyEntry(const char* src, const char* dst);
42void mergeFile(const char* path, const char* entry);
43void importFile(const char* entry, const char* path);
44
45// Forces XML output when printing to screen
46bool forceXML = false;
47CFPropertyListRef plist = NULL;
48const char* outputFile = NULL;
49
50int main(int argc, const char **argv)
51{
52 const char* command = NULL;
53
54 if (!processArgs(argc, argv, &command))
55 {
56 printUsage();
57 return 1;
58 }
59 if (!outputFile)
60 return 0;
61
62 if (!revertToFile())
63 return 1;
64
65 if (command != NULL)
66 {
67 runCommand(command);
68 if (!saveToFile())
69 return 1;
70 }
71 else
72 {
73 while (true)
74 {
75 char buffer[200];
76
77 printf("Command: ");
78
79 if (!fgets(buffer, sizeof(buffer), stdin))
80 break;
81
82 size_t len = strlen(buffer);
83 if (buffer[len-1] == '\n')
84 buffer[len-1] = '\0';
85 runCommand(buffer);
86 }
87 }
88
89 return EXIT_SUCCESS;
90}
91
92void printUsage()
93{
94 puts("Usage: PlistBuddy [-cxh] <file.plist>\n"
95 " -c \"<command>\" execute command, otherwise run in interactive mode\n"
96 " -x output will be in the form of an xml plist where appropriate\n"
97 " -h print the complete help info, with command guide\n");
98}
99
100void printHelp()
101{
102 puts("Command Format:\n\n"
103 " Help - Prints this information\n"
104 " Exit - Exits the program, changes are not saved to the file\n"
105 " Save - Saves the current changes to the file\n"
106 " Revert - Reloads the last saved version of the file\n"
107 " Clear [<Type>] - Clears out all existing entries, and creates root of Type\n"
108 " Print [<Entry>] - Prints value of Entry. Otherwise, prints file\n"
109 " Set <Entry> <Value> - Sets the value at Entry to Value\n"
110 " Add <Entry> <Type> [<Value>] - Adds Entry to the plist, with value Value\n"
111 " Copy <EntrySrc> <EntryDst> - Copies the EntrySrc property to EntryDst\n"
112 " Delete <Entry> - Deletes Entry from the plist\n"
113 " Merge <file.plist> [<Entry>] - Adds the contents of file.plist to Entry\n"
114 " Import <Entry> <file> - Creates or sets Entry the contents of file\n"
115 "\n"
116 "Entry Format:\n"
117 " Entries consist of property key names delimited by colons. Array items\n"
118 " are specified by a zero-based integer index. Examples:\n"
119 " :CFBundleShortVersionString\n"
120 " :CFBundleDocumentTypes:2:CFBundleTypeExtensions\n"
121 "\n"
122 "Types:\n"
123 " string\n"
124 " array\n"
125 " dict\n"
126 " bool\n"
127 " real\n"
128 " integer\n"
129 " date\n"
130 " data\n"
131 "\n"
132 "Examples:\n"
133 " Set :CFBundleIdentifier com.apple.plistbuddy\n"
134 " Sets the CFBundleIdentifier property to com.apple.plistbuddy\n"
135 " Add :CFBundleGetInfoString string \"App version 1.0.1\"\n"
136 " Adds the CFBundleGetInfoString property to the plist\n"
137 " Add :CFBundleDocumentTypes: dict\n"
138 " Adds a new item of type dict to the CFBundleDocumentTypes array\n"
139 " Add :CFBundleDocumentTypes:0 dict\n"
140 " Adds the new item to the beginning of the array\n"
141 " Delete :CFBundleDocumentTypes:0 dict\n"
142 " Deletes the FIRST item in the array\n"
143 " Delete :CFBundleDocumentTypes\n"
144 " Deletes the ENTIRE CFBundleDocumentTypes array\n");
145}
146
147bool processArgs(int argc, const char** argv, const char** command)
148{
149 if (argc <= 1)
150 return false;
151
152 for (int i = 1; i < argc; i++)
153 {
154 if (strcmp(argv[i], "-c") == 0)
155 {
156 if (i+1 >= argc)
157 return false;
158 *command = argv[++i];
159 }
160 else if (strcmp(argv[i], "-x") == 0)
161 {
162 forceXML = true;
163 }
164 else if (strcmp(argv[i], "-h") == 0)
165 {
166 printHelp();
167 return true;
168 }
169 else if (i+1 >= argc)
170 {
171 outputFile = argv[i];
172 }
173 else
174 {
175 puts("Invalid Arguments");
176 exit(1);
177 }
178 }
179
180 return outputFile != NULL;
181}
182
183CFPropertyListRef loadPlist(const char* filePath)
184{
185 SInt32 errorCode = 0;
186 CFStringRef errorString = NULL;
187 CFDataRef data = NULL;
188 CFPropertyListRef plist;
189 CFStringRef path = CFStringCreateWithCString(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8);
190 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
191
192 CFRelease(path);
193
194 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &data, NULL, NULL, &errorCode);
195 CFRelease(url);
196
197 if (errorCode != 0)
198 {
199 printf("Error Reading File: %s\n", filePath);
200 return false;
201 }
202
203 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListMutableContainers, &errorString);
204
205 if (errorString != NULL)
206 {
207 CFShow(errorString);
208 CFRelease(errorString);
209
210 printf("Error Reading File: %s\n", filePath);
211 }
212
213 return plist;
214}
215
216bool revertToFile(void)
217{
218 if (plist != NULL)
219 CFRelease(plist);
220
221 if (access(outputFile, F_OK) != 0)
222 {
223 printf("File Doesn't Exist, Will Create: %s\n", outputFile);
224 plist = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
225 return true;
226 }
227 else
228 {
229 bool rv;
230
231 plist = loadPlist(outputFile);
232 rv = plist != NULL;
233
234 if (plist == NULL)
235 plist = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
236
237 return rv;
238 }
239}
240
241bool saveToFile(void)
242{
243 bool rv = true;
244 CFDataRef data;
245 CFStringRef path = CFStringCreateWithCString(kCFAllocatorDefault, outputFile, kCFStringEncodingUTF8);
246 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
247
248 CFRelease(path);
249
250 data = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
251 if (data != NULL)
252 {
253 SInt32 errorCode = 0;
254
255 CFURLWriteDataAndPropertiesToResource(url, data, NULL, &errorCode);
256 CFRelease(data);
257
258 if (errorCode != 0)
259 {
260 puts("Cannot Write File");
261 rv = false;
262 }
263 }
264 else
265 {
266 puts("Cannot Format Plist");
267 rv = false;
268 }
269
270 CFRelease(url);
271 return rv;
272}
273
274static const char* nextWord(const char* input)
275{
276 int i;
277
278 for (i = 0; isspace(input[i]) && input[i] != '\0'; i++)
279 ;
280
281 if (input[i])
282 return &input[i];
283 else
284 return NULL;
285}
286
287bool isCommand(const char* input, const char* cmd, const char** next)
288{
289 size_t len = strlen(cmd);
290
291 if (strncasecmp(input, cmd, len) == 0 && (input[len] == ' ' || input[len] == '\0'))
292 {
293 if (next != NULL)
294 *next = nextWord(input + len);
295 return true;
296 }
297 else
298 return false;
299}
300
301static enum PropertyType parseType(const char* type)
302{
303 if (strcasecmp(type, "string") == 0)
304 return typeString;
305 else if (strcasecmp(type, "array") == 0)
306 return typeArray;
307 else if (strcasecmp(type, "dict") == 0)
308 return typeDict;
309 else if (strcasecmp(type, "bool") == 0)
310 return typeBool;
311 else if (strcasecmp(type, "real") == 0)
312 return typeReal;
313 else if (strcasecmp(type, "integer") == 0)
314 return typeInteger;
315 else if (strcasecmp(type, "date") == 0)
316 return typeDate;
317 else if (strcasecmp(type, "data") == 0)
318 return typeData;
319 else
320 {
321 printf("Unrecognized Type: %s\n", type);
322 return typeUnknown;
323 }
324}
325
326static enum PropertyType inferType(CFPropertyListRef obj)
327{
328 CFTypeID typeId = CFGetTypeID(obj);
329
330 if (typeId == CFStringGetTypeID())
331 return typeString;
332 else if (typeId == CFArrayGetTypeID())
333 return typeArray;
334 else if (typeId == CFDictionaryGetTypeID())
335 return typeDict;
336 else if (typeId == CFBooleanGetTypeID())
337 return typeBool;
338 else if (typeId == CFNumberGetTypeID())
339 {
340 if (CFNumberIsFloatType((CFNumberRef) obj))
341 return typeReal;
342 else
343 return typeInteger;
344 }
345 else if (typeId == CFDateGetTypeID())
346 return typeDate;
347 else if (typeId == CFDataGetTypeID())
348 return typeData;
349 else
350 return typeUnknown;
351}
352
353char* getWord(const char* cmd, const char** next)
354{
355 if (cmd[0] == '"')
356 {
357 int i = 1, j = 0;
358 char* out = strdup(cmd);
359
360 for (; cmd[i] != '\0'; i++)
361 {
362 if (cmd[i] == '\\' && cmd[i+1] != '\0')
363 {
364 i++;
365 switch (cmd[i])
366 {
367 case '"':
368 out[j++] = '"';
369 break;
370 case 'n':
371 out[j++] = '\n';
372 break;
373 case 't':
374 out[j++] = '\t';
375 break;
376 default:
377 out[j++] = '\\';
378 out[j++] = cmd[i];
379 }
380 }
381 else if (cmd[i] == '"')
382 {
383 out[j++] = '\0';
384 break;
385 }
386 else
387 {
388 out[j++] = cmd[i];
389 }
390 }
391
392 if (j > 0 && out[j-1] != '\0')
393 {
394 free(out);
395 puts("Unterminated Quotes");
396 return NULL;
397 }
398 if (next)
399 *next = nextWord(cmd + i + 1);
400
401 return out;
402 }
403 else
404 {
405 char* p = strchr(cmd, ' ');
406 if (p == NULL)
407 {
408 if (next)
409 *next = NULL;
410 return strdup(cmd);
411 }
412 else
413 {
414 char* rv = strndup(cmd, p-cmd);
415 if (next)
416 *next = nextWord(p);
417 return rv;
418 }
419 }
420}
421
422void runCommand(const char* cmd)
423{
424 const char* next = NULL;
425
426 if (isCommand(cmd, "Exit", NULL) || isCommand(cmd, "Quit", NULL) || isCommand(cmd, "Bye", NULL))
427 {
428 exit(0);
429 }
430 else if (isCommand(cmd, "Help", NULL))
431 {
432 printHelp();
433 }
434 else if (isCommand(cmd, "Save", &next))
435 {
436 puts("Saving...");
437 saveToFile();
438 }
439 else if (isCommand(cmd, "Revert", NULL))
440 {
441 puts("Reverting to last saved state...");
442 revertToFile();
443 }
444 else if (isCommand(cmd, "Print", &next) || isCommand(cmd, "ls", &next))
445 {
446 doPrint(next);
447 }
448 else if (isCommand(cmd, "Clear", &next))
449 {
450 enum PropertyType type = typeUnknown;
451 if (next != NULL)
452 type = parseType(next);
453 if (type == typeUnknown)
454 type = typeDict;
455 }
456 else if (isCommand(cmd, "Set", &next) || isCommand(cmd, "=", &next))
457 {
458 // entry value
459 if (!next)
460 {
461 puts("Missing arguments");
462 return;
463 }
464
465 char* entry = getWord(next, &next);
466
467 if (!next)
468 next = "";
469
470 setEntry(entry, next);
471
472 free(entry);
473 }
474 else if (isCommand(cmd, "Add", &next) || isCommand(cmd, "+", &next))
475 {
476 // entry type [value]
477 if (!next)
478 {
479 puts("Missing arguments");
480 return;
481 }
482
483 char* entry = getWord(next, &next);
484
485 if (!next)
486 {
487 free(entry);
488 puts("Missing arguments");
489 return;
490 }
491
492 char* type = getWord(next, &next);
493 enum PropertyType typeV = parseType(type);
494
495 if (typeV == typeUnknown)
496 {
497 free(entry);
498 free(type);
499 return;
500 }
501
502 if (!next)
503 next = "";
504
505 addEntry(entry, typeV, next);
506
507 free(entry);
508 free(type);
509 }
510 else if (isCommand(cmd, "Copy", &next) || isCommand(cmd, "cp", &next))
511 {
512 // entrySrc entryDst
513 if (!next)
514 {
515 puts("Missing arguments");
516 return;
517 }
518
519 char* src = getWord(next, &next);
520 if (!next)
521 {
522 puts("Missing arguments");
523 return;
524 }
525
526 char* dst = getWord(next, NULL);
527
528 copyEntry(src, dst);
529
530 free(src);
531 free(dst);
532 }
533 else if (isCommand(cmd, "Delete", &next) || isCommand(cmd, "rm", &next) || isCommand(cmd, "-", &next))
534 {
535 // entry
536 if (!next)
537 {
538 puts("Missing arguments");
539 return;
540 }
541
542 deleteEntry(next);
543 }
544 else if (isCommand(cmd, "Merge", &next))
545 {
546 // file.plist [entryDst]
547 if (!next)
548 {
549 puts("Missing arguments");
550 return;
551 }
552
553 char* path = getWord(next, &next);
554
555 mergeFile(path, next);
556 free(path);
557 }
558 else if (isCommand(cmd, "Import", &next))
559 {
560 // entry file.plist
561 if (!next)
562 {
563 puts("Missing arguments");
564 return;
565 }
566 char* entry = getWord(next, &next);
567
568 if (!next)
569 {
570 puts("Missing arguments");
571 free(entry);
572 return;
573 }
574 char* path = getWord(next, &next);
575
576 importFile(entry, path);
577
578 free(path);
579 free(entry);
580 }
581 else
582 {
583 puts("Unrecognized Command");
584 }
585}
586
587void addEntry(const char* entry, enum PropertyType type, const char* value)
588{
589 CFPropertyListRef parent, existing;
590 CFPropertyListRef newValue;
591 char* leafName = NULL;
592 CFTypeID parentType;
593
594 // printf("add entry (type %d) with value: %s\n", type, value);
595
596 if (type == typeArray)
597 {
598 newValue = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
599 }
600 else if (type == typeDict)
601 {
602 newValue = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
603 }
604 else
605 {
606 newValue = parseValue(type, value);
607 if (newValue == NULL)
608 return;
609 }
610
611 resolvePlistEntry(entry, &parent, &existing, &leafName, true);
612
613 if (parent == NULL)
614 {
615 printf("Add: Entry, \"%s\", Does Not Exist\n", entry);
616 free(leafName);
617 return;
618 }
619
620 parentType = CFGetTypeID(parent);
621
622 // in case of arrays, we just insert at given position
623 if (existing != NULL && parentType != CFArrayGetTypeID())
624 {
625 printf("Add: \"%s\" Entry Already Exists\n", entry);
626 goto out;
627 }
628
629 if (parentType == CFArrayGetTypeID())
630 {
631 CFMutableArrayRef array = (CFMutableArrayRef) parent;
632 CFIndex index = atoi(leafName);
633
634 if (index < 0)
635 index = 0;
636 else if (index > CFArrayGetCount(array))
637 index = CFArrayGetCount(array);
638
639 CFArrayInsertValueAtIndex(array, index, newValue);
640 }
641 else if (parentType == CFDictionaryGetTypeID())
642 {
643 // printf("Add new entry named %s into dict %p, root is %p\n", leafName, parent, plist);
644 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault, leafName, kCFStringEncodingUTF8);
645 CFDictionaryAddValue((CFMutableDictionaryRef) parent, key, newValue);
646 CFRelease(key);
647 }
648 else
649 {
650 printf("Add: Can't Add Entry, \"%s\", to Parent\n", entry);
651 }
652
653out:
654 free(leafName);
655 CFRelease(newValue);
656}
657
658CFPropertyListRef parseValue(enum PropertyType type, const char* string)
659{
660 switch (type)
661 {
662 case typeString:
663 case typeData:
664 {
665 char* stringOut;
666 bool hasQuotes = string[0] == '"';
667
668 size_t len = strlen(string);
669 size_t i = 0, j = 0;
670
671 if (hasQuotes)
672 {
673 if (string[len-1] != '"')
674 {
675 puts("Parse Error: Unclosed Quotes");
676 return NULL;
677 }
678 i++;
679 len--;
680 }
681
682 stringOut = (char*) malloc(len+1);
683
684 for (; i < len; i++)
685 {
686 if (string[i] != '\\')
687 {
688 stringOut[j] = string[i];
689 j++;
690 }
691 else
692 {
693 if (i+1 == len)
694 {
695 free(stringOut);
696 puts("Parse Error: Unclosed Quotes");
697 return NULL;
698 }
699
700 switch (string[++i])
701 {
702 case '"':
703 stringOut[j++] = '"';
704 break;
705 case 'n':
706 stringOut[j++] = '\n';
707 break;
708 case 't':
709 stringOut[j++] = '\t';
710 break;
711 default:
712 stringOut[j++] = string[i];
713 }
714 }
715 }
716 stringOut[j] = '\0';
717
718 CFPropertyListRef rv;
719 if (type == typeString)
720 rv = CFStringCreateWithCString(kCFAllocatorDefault, stringOut, kCFStringEncodingUTF8);
721 else
722 rv = CFDataCreate(kCFAllocatorDefault, (const UInt8*) string, strlen(string));
723
724 free(stringOut);
725 return rv;
726 }
727 case typeBool:
728 if (strcasecmp(string, "true") == 0 || strcasecmp(string, "yes") == 0 || strcmp(string, "1") == 0)
729 return kCFBooleanTrue;
730 return kCFBooleanFalse;
731
732 case typeInteger:
733 {
734 char* endptr;
735 long long v;
736
737 v = strtoll(string, &endptr, 0);
738 if (endptr == string)
739 {
740 puts("Unrecognized Integer Format");
741 return NULL;
742 }
743
744 return CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &v);
745 }
746
747 case typeReal:
748 {
749 char* endptr;
750 double v;
751
752 v = strtod(string, &endptr);
753 if (endptr == string)
754 {
755 puts("Unrecognized Real Format");
756 return NULL;
757 }
758
759 return CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat64Type, &v);
760 }
761 case typeDate:
762 {
763 struct tm tm;
764
765 if (!strptime(string, "%a %b %d %H:%M:%S %Z %Y", &tm)
766 && !strptime(string, "%c", &tm)
767 && !strptime(string, "%D", &tm))
768 {
769 puts("Unrecognized Date Format");
770 return NULL;
771 }
772
773 CFTimeZoneRef tz = CFTimeZoneCopyDefault();
774 CFAbsoluteTime at;
775 CFGregorianDate date;
776
777 date.day = tm.tm_mday;
778 date.hour = tm.tm_hour;
779 date.minute = tm.tm_min;
780 date.month = tm.tm_mon + 1;
781 date.second = tm.tm_sec;
782 date.year = tm.tm_year + 1900;
783
784 at = CFGregorianDateGetAbsoluteTime(date, tz);
785
786 CFDateRef rv = CFDateCreate(kCFAllocatorDefault, at);
787
788 CFRelease(tz);
789 return rv;
790 }
791 default:
792 {
793 puts("Cannot parse this entry type");
794 return NULL;
795 }
796 }
797}
798
799void setEntry(const char* entry, const char* entryValue)
800{
801 CFPropertyListRef parent, existing;
802 CFPropertyListRef newValue;
803 char* leafName = NULL;
804 enum PropertyType type;
805
806 resolvePlistEntry(entry, &parent, &existing, &leafName, false);
807 if (existing == NULL)
808 {
809 free(leafName);
810 printf("Set: Entry, \"%s\", Does Not Exist\n", entry);
811 return;
812 }
813
814 type = inferType(existing);
815 if (type == typeArray || type == typeDict)
816 {
817 free(leafName);
818 puts("Set: Cannot Perform Set On Containers");
819 return;
820 }
821
822 newValue = parseValue(type, entryValue);
823 if (newValue == NULL)
824 {
825 free(leafName);
826 return;
827 }
828
829 if (CFGetTypeID(parent) == CFDictionaryGetTypeID())
830 {
831 CFStringRef leafNameStr = CFStringCreateWithCString(kCFAllocatorDefault, leafName, kCFStringEncodingUTF8);
832 CFDictionaryReplaceValue((CFMutableDictionaryRef) parent, leafNameStr, newValue);
833 CFRelease(leafNameStr);
834 }
835 else
836 {
837 int index = atoi(leafName);
838
839 CFArraySetValueAtIndex((CFMutableArrayRef) parent, index, newValue);
840 }
841
842 free(leafName);
843 CFRelease(newValue);
844}
845
846static const char* strtok_empty(char** pstr, char delim)
847{
848 char* p;
849 char* str = *pstr;
850
851 if (str == NULL)
852 return NULL;
853
854 while (str[0] == delim)
855 str++;
856
857 if ((p = strchr (str, delim)) != NULL)
858 {
859 *p = '\0';
860 *pstr = p + 1;
861 } else {
862 *pstr = NULL;
863 }
864
865 return str;
866}
867
868void resolvePlistEntry(const char* whatStr, CFPropertyListRef* parent, CFPropertyListRef* entry, char** last, bool autoCreate)
869{
870 CFPropertyListRef pos = plist;
871 CFPropertyListRef lastPos = NULL;
872
873 const char *tok, *lastTok = "";
874
875 // trim head
876 while (whatStr[0] == ':')
877 whatStr++;
878
879 // trim tail
880 size_t len = strlen(whatStr);
881 while(len > 0 && whatStr[len - 1] == ':') {
882 len--;
883 }
884
885 char* whatStr2 = strndup(whatStr, len);
886 char* p = whatStr2;
887
888 while ((tok = strtok_empty(&p, ':')) && pos != NULL)
889 {
890 lastPos = pos;
891 if (*tok)
892 {
893 CFTypeID typeId = CFGetTypeID(pos);
894
895 if (typeId == CFDictionaryGetTypeID())
896 {
897 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault, tok, kCFStringEncodingUTF8);
898 pos = (CFPropertyListRef) CFDictionaryGetValue((CFDictionaryRef) pos, key);
899 if (pos == NULL && autoCreate && p != NULL)
900 {
901 pos = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
902 CFDictionaryAddValue((CFMutableDictionaryRef) lastPos, key, pos);
903 }
904 CFRelease(key);
905 }
906 else if (typeId == CFArrayGetTypeID())
907 {
908 int index = atoi(tok);
909 if (index < 0 || index >= CFArrayGetCount((CFArrayRef) pos))
910 {
911 pos = NULL;
912 }
913 else
914 {
915 pos = (CFPropertyListRef) CFArrayGetValueAtIndex((CFArrayRef) pos, index);
916 }
917 }
918 else
919 {
920 pos = NULL;
921 }
922 }
923
924 lastTok = tok;
925 }
926
927 if (last != NULL)
928 *last = strdup(lastTok);
929
930 if (entry != NULL)
931 *entry = pos;
932
933 if (parent != NULL)
934 *parent = (!tok) ? lastPos : NULL;
935
936 free(whatStr2);
937}
938
939void prettyPrintPlist(CFPropertyListRef what, int indentNum)
940{
941 CFTypeID typeId = CFGetTypeID(what);
942 char* indent = __builtin_alloca(indentNum + 1);
943
944 memset(indent, ' ', indentNum);
945 indent[indentNum] = '\0';
946
947 if (typeId == CFStringGetTypeID())
948 {
949 const char* str = CFStringGetCStringPtr((CFStringRef) what, kCFStringEncodingUTF8);
950 printf("%s", str);
951 }
952 else if (typeId == CFArrayGetTypeID())
953 {
954 CFArrayRef array = (CFArrayRef) what;
955 CFIndex count = CFArrayGetCount(array);
956
957 printf("Array {\n");
958 for (CFIndex i = 0; i < count; i++)
959 {
960 CFPropertyListRef elem = (CFPropertyListRef) CFArrayGetValueAtIndex(array, i);
961 printf("%s ", indent);
962
963 prettyPrintPlist(elem, indentNum + 4);
964 printf("\n");
965 }
966
967 printf("%s}", indent);
968 }
969 else if (typeId == CFDictionaryGetTypeID())
970 {
971 CFDictionaryRef dict = (CFDictionaryRef) what;
972 CFStringRef* keys;
973 CFPropertyListRef* values;
974
975 CFIndex count = CFDictionaryGetCount(dict);
976
977 keys = malloc(sizeof(CFStringRef*) * count);
978 values = malloc(sizeof(CFStringRef*) * count);
979
980 CFDictionaryGetKeysAndValues(dict, (const void**) keys, (const void**) values);
981
982 printf("Dict {\n");
983 for (CFIndex i = 0; i < count; i++)
984 {
985 printf("%s ", indent);
986 prettyPrintPlist(keys[i], 0);
987 printf(" = ");
988 prettyPrintPlist(values[i], indentNum + 4);
989 printf("\n");
990 }
991
992 printf("%s}", indent);
993
994 free(keys);
995 free(values);
996 }
997 else if (typeId == CFBooleanGetTypeID())
998 {
999 if (what == kCFBooleanTrue)
1000 printf("true");
1001 else
1002 printf("false");
1003 }
1004 else if (typeId == CFNumberGetTypeID())
1005 {
1006 CFNumberRef num = (CFNumberRef) what;
1007
1008 if (CFNumberIsFloatType(num))
1009 {
1010 Float64 d;
1011 CFNumberGetValue(num, kCFNumberFloat64Type, &d);
1012 printf("%f", d);
1013 }
1014 else
1015 {
1016 long long l;
1017 CFNumberGetValue(num, kCFNumberLongLongType, &l);
1018 printf("%lld", l);
1019 }
1020 }
1021 else if (typeId == CFDateGetTypeID())
1022 {
1023 CFAbsoluteTime t = CFDateGetAbsoluteTime((CFDateRef) what);
1024 CFTimeZoneRef tz = CFTimeZoneCopyDefault();
1025 CFGregorianDate d = CFAbsoluteTimeGetGregorianDate(t, tz);
1026 struct tm tm;
1027 char buf[150];
1028
1029 tm.tm_mday = d.day;
1030 tm.tm_mon = d.month - 1;
1031 tm.tm_year = d.year - 1900;
1032 tm.tm_hour = d.hour;
1033 tm.tm_min = d.minute;
1034 tm.tm_sec = d.second;
1035 tm.tm_wday = CFAbsoluteTimeGetDayOfWeek(t, tz);
1036
1037 if (tm.tm_wday == 7)
1038 tm.tm_wday = 0;
1039
1040 tm.tm_zone = 0;
1041 tm.tm_isdst = 0;
1042
1043 strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Z %Y", &tm);
1044 printf("%s", buf);
1045
1046 CFRelease(tz);
1047 }
1048 else if (typeId == CFDataGetTypeID())
1049 {
1050 CFDataRef data = (CFDataRef) what;
1051 const UInt8* bytes = CFDataGetBytePtr(data);
1052 CFIndex len = CFDataGetLength(data);
1053
1054 fwrite(bytes, 1, len, stdout);
1055 }
1056}
1057
1058void doPrint(const char* whatStr)
1059{
1060 CFPropertyListRef what = plist;
1061 CFPropertyListRef entry = plist;
1062
1063 if (whatStr != NULL)
1064 {
1065 resolvePlistEntry(whatStr, &what, &entry, NULL, false);
1066 if (entry == NULL)
1067 {
1068 printf("Print: Entry, \"%s\", Does Not Exist\n", whatStr);
1069 return;
1070 }
1071 }
1072
1073 if (!forceXML) {
1074 prettyPrintPlist(entry, 0);
1075 printf("\n");
1076 }
1077 else
1078 {
1079 CFDataRef data = CFPropertyListCreateXMLData(kCFAllocatorDefault, entry);
1080
1081 if (data != NULL)
1082 {
1083 prettyPrintPlist(data, 0);
1084 printf("\n");
1085
1086 CFRelease(data);
1087 }
1088 }
1089}
1090
1091void deleteEntry(const char* entry)
1092{
1093 CFPropertyListRef parent, existing;
1094 CFPropertyListRef newValue;
1095 char* leafName = NULL;
1096
1097 resolvePlistEntry(entry, &parent, &existing, &leafName, false);
1098 if (existing == NULL)
1099 {
1100 printf("Delete: Entry, \"%s\", Does Not Exist\n", entry);
1101 goto out;
1102 }
1103
1104 CFTypeID typeID = CFGetTypeID(parent);
1105 if (typeID == CFDictionaryGetTypeID())
1106 {
1107 CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, leafName, kCFStringEncodingUTF8);
1108 CFDictionaryRemoveValue((CFMutableDictionaryRef) parent, str);
1109 CFRelease(str);
1110 }
1111 else
1112 {
1113 CFIndex idx = atoi(leafName);
1114 CFArrayRemoveValueAtIndex((CFMutableArrayRef) parent, idx);
1115 }
1116
1117out:
1118 free(leafName);
1119}
1120
1121void copyEntry(const char* src, const char* dst)
1122{
1123 CFPropertyListRef existing, entry, newParent, entryCopy;
1124 char* leafName = NULL;
1125
1126 resolvePlistEntry(src, NULL, &entry, NULL, false);
1127 resolvePlistEntry(dst, &newParent, &existing, &leafName, true);
1128
1129 if (entry == NULL)
1130 {
1131 printf("Copy: Entry, \"%s\", Does Not Exist\n", src);
1132 goto out;
1133 }
1134 if (existing != NULL)
1135 {
1136 printf("Copy: \"%s\" Entry Already Exists\n", dst);
1137 goto out;
1138 }
1139
1140 entryCopy = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, entry, kCFPropertyListMutableContainers);
1141
1142 CFTypeID typeID = CFGetTypeID(newParent);
1143 if (typeID == CFDictionaryGetTypeID())
1144 {
1145 CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, leafName, kCFStringEncodingUTF8);
1146 CFDictionaryAddValue((CFMutableDictionaryRef) newParent, str, entryCopy);
1147 CFRelease(str);
1148 }
1149 else
1150 {
1151 CFIndex idx = atoi(leafName);
1152 CFArrayInsertValueAtIndex((CFMutableArrayRef) newParent, idx, entryCopy);
1153 }
1154
1155 CFRelease(entryCopy);
1156out:
1157 free(leafName);
1158}
1159
1160void mergeFile(const char* path, const char* entry)
1161{
1162 CFPropertyListRef dest, fileContents;
1163
1164 fileContents = loadPlist(path);
1165 if (fileContents == NULL)
1166 return;
1167
1168 if (entry != NULL && *entry)
1169 {
1170 resolvePlistEntry(entry, NULL, &dest, NULL, true);
1171 if (dest == NULL)
1172 {
1173 printf("Merge: Entry, \"%s\", Does Not Exist\n", entry);
1174 CFRelease(fileContents);
1175 return;
1176 }
1177 }
1178 else
1179 dest = plist;
1180
1181 CFTypeID typeID = CFGetTypeID(dest);
1182 CFTypeID sourceTypeID = CFGetTypeID(fileContents);
1183
1184 if (typeID == CFArrayGetTypeID())
1185 {
1186 if (sourceTypeID == CFArrayGetTypeID())
1187 {
1188 CFArrayAppendArray((CFMutableArrayRef) dest, fileContents, CFRangeMake(0, CFArrayGetCount(fileContents)));
1189 }
1190 else if (sourceTypeID == CFDictionaryGetTypeID())
1191 {
1192 CFIndex count = CFDictionaryGetCount((CFDictionaryRef) fileContents);
1193 void** values = malloc(sizeof(void*) * count);
1194 CFDictionaryGetKeysAndValues((CFDictionaryRef) fileContents, NULL, (const void**) values);
1195
1196 CFArrayReplaceValues((CFMutableArrayRef) dest, CFRangeMake(CFArrayGetCount((CFArrayRef) dest) - 1, 0), (const void**) values, count);
1197 free(values);
1198 }
1199 else
1200 {
1201 CFArrayAppendValue((CFMutableArrayRef) dest, fileContents);
1202 }
1203 }
1204 else if (typeID == CFDictionaryGetTypeID())
1205 {
1206 if (sourceTypeID == CFDictionaryGetTypeID())
1207 {
1208 CFIndex count = CFDictionaryGetCount((CFDictionaryRef) fileContents);
1209 void** values = malloc(sizeof(void*) * count);
1210 void** keys = malloc(sizeof(void*) * count);
1211
1212 CFDictionaryGetKeysAndValues((CFDictionaryRef) fileContents, (const void**) keys, (const void**) values);
1213
1214 for (CFIndex i = 0; i < count; i++)
1215 CFDictionarySetValue((CFMutableDictionaryRef) dest, keys[i], values[i]);
1216
1217 free(keys);
1218 free(values);
1219 }
1220 else if (sourceTypeID == CFArrayGetTypeID())
1221 {
1222 puts("Merge: Can't Add array Entries to dict");
1223 }
1224 else
1225 {
1226 CFDictionarySetValue((CFMutableDictionaryRef) dest, CFSTR(""), fileContents);
1227 }
1228 }
1229 else
1230 {
1231 puts("Merge: Specified Entry Must Be a Container");
1232 }
1233
1234 CFRelease(fileContents);
1235}
1236
1237// NOTE: This function doesn't seem to work at all in the original program
1238void importFile(const char* entry, const char* path)
1239{
1240 CFPropertyListRef dest, fileContents;
1241 char* leafName;
1242
1243 fileContents = loadPlist(path);
1244 if (fileContents == NULL)
1245 return;
1246
1247 resolvePlistEntry(entry, &dest, NULL, &leafName, true);
1248 if (dest == NULL)
1249 {
1250 printf("Import: Entry, \"%s\", Does Not Exist\n", entry);
1251 CFRelease(fileContents);
1252 return;
1253 }
1254
1255 CFTypeID typeID = CFGetTypeID(dest);
1256 if (typeID == CFDictionaryGetTypeID())
1257 {
1258 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault, leafName, kCFStringEncodingUTF8);
1259 CFDictionarySetValue((CFMutableDictionaryRef) dest, key, fileContents);
1260 CFRelease(key);
1261 }
1262 else if (typeID == CFArrayGetTypeID())
1263 {
1264 CFIndex index = atoi(leafName);
1265 if (index < 0)
1266 index = 0;
1267
1268 if (index >= CFArrayGetCount((CFArrayRef) dest))
1269 CFArrayAppendValue((CFMutableArrayRef) dest, fileContents);
1270 else
1271 CFArraySetValueAtIndex((CFMutableArrayRef) dest, index, fileContents);
1272 }
1273
1274 free(leafName);
1275 CFRelease(fileContents);
1276}
1277