1 /*
2 * Copyright (c) 2012-2015 Matthew Seaman <matthew@FreeBSD.org>
3 * Copyright (c) 2014-2020 Baptiste Daroussin <bapt@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "bsd_compat.h"
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 /* musl libc apparently does not have ALLPERMS */
33 #ifndef ALLPERMS
34 #define ALLPERMS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
35 #endif
36
37 #include <assert.h>
38 #include <ctype.h>
39 #include <inttypes.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <time.h>
44 #include <utlist.h>
45
46 #include "pkg.h"
47 #include <xstring.h>
48 #include <private/pkg_printf.h>
49 #include <private/pkg.h>
50
51 /*
52 * Format codes
53 * Arg Type What
54 * A pkg Package annotations
55 * An pkg_note Annotation tag name
56 * Av pkg_note Annotation value
57 *
58 * B pkg List of required shared libraries
59 * Bn pkg_shlib Shared library name
60 *
61 * C pkg List of categories
62 * Cn pkg_category Category name
63 *
64 * D pkg List of directories
65 * Dg pkg_dir Group owner of directory
66 * Dk pkg_dir Keep flag
67 * Dn pkg_dir Directory path name
68 * Dp pkg_dir Directory permissions
69 * Dt pkg_dir Try flag (@dirrmtry in plist)
70 * Du pkg_dir User owner of directory
71 *
72 * E
73 *
74 * F pkg List of files
75 * Fg pkg_file Group owner of file
76 * Fk pkg_file Keep flag
77 * Fn pkg_file File path name
78 * Fp pkg_file File permissions
79 * Fs pkg_file File SHA256 checksum
80 * Fu pkg_file User owner of file
81 *
82 * G pkg List of groups
83 * Gn pkg_group Group name
84 *
85 * H
86 *
87 * I int* Row counter
88 *
89 * J
90 * K
91 *
92 * L pkg List of licenses
93 * Ln pkg_license Licence name
94 *
95 * M pkg Message
96 * N pkg Reponame
97 *
98 * O pkg List of options
99 * On pkg_option Option name (key)
100 * Ov pkg_option Option value
101 * Od pkg_option Option default value (if known)
102 * OD pkg_option Option description
103 *
104 * P pkg
105 * Q
106 *
107 * R pkg Repopath
108 * S char* Arbitrary character string
109 *
110 * T
111 *
112 * U pkg List of users
113 * Un pkg_user User name
114 *
115 * V pkg old version
116 * W
117 * X pkg Internal Checksum
118 * Y pkg List of requires
119 * Yn pkg_provide Name of the require
120 * Z
121 *
122 * a pkg autoremove flag
123 *
124 * b pkg List of provided shared libraries
125 * bn pkg_shlib Shared library name
126 *
127 * c pkg comment
128 *
129 * d pkg List of dependencies
130 * dk pkg_dep dependency lock status
131 * dn pkg_dep dependency name
132 * do pkg_dep dependency origin
133 * dv pkg_dep dependency version
134 *
135 * e pkg Package description
136 *
137 * f
138 * g
139 * h
140 * i
141 * j
142 *
143 * k pkg lock status
144 * l pkg license logic
145 * m pkg maintainer
146 * n pkg name
147 * o pkg origin
148 * p pkg prefix
149 * q pkg architecture / ABI
150 * r pkg List of requirements
151 * rk pkg_dep requirement lock status
152 * rn pkg_dep requirement name
153 * ro pkg_dep requirement origin
154 * rv pkg_dep requirement version
155 *
156 * s pkg flatsize
157 * t pkg install timestamp
158 * u pkg checksum
159 * v pkg version
160 * w pkg home page URL
161 *
162 * x pkg pkg tarball size
163 * y pkg List of provides
164 * yn pkg_provide name of the provide
165 *
166 * z pkg short checksum
167 */
168 static xstring *pkg_xstring_vprintf(xstring * restrict buf, const char * restrict format, va_list ap);
169
170 struct pkg_printf_fmt {
171 char fmt_main;
172 char fmt_sub;
173 bool has_trailer;
174 bool struct_pkg; /* or else a sub-type? */
175 unsigned context;
176 xstring *(*fmt_handler)(xstring *, const void *,
177 struct percent_esc *);
178 };
179
180 /*
181 * These are in pkg_fmt_t order, which is necessary for the parsing
182 * algorithm.
183 */
184
185 static const struct pkg_printf_fmt fmt[] = {
186 [PP_PKG_ANNOTATION_NAME] =
187 {
188 'A',
189 'n',
190 false,
191 false,
192 PP_PKG|PP_A,
193 &format_annotation_name,
194 },
195 [PP_PKG_ANNOTATION_VALUE] =
196 {
197 'A',
198 'v',
199 false,
200 false,
201 PP_PKG|PP_A,
202 &format_annotation_value,
203 },
204 [PP_PKG_ANNOTATIONS] =
205 {
206 'A',
207 '\0',
208 true,
209 true,
210 PP_PKG,
211 &format_annotations,
212 },
213 [PP_PKG_SHLIB_REQUIRED_NAME] =
214 {
215 'B',
216 'n',
217 false,
218 false,
219 PP_PKG|PP_B,
220 &format_shlib_name,
221 },
222 [PP_PKG_SHLIBS_REQUIRED] =
223 {
224 'B',
225 '\0',
226 true,
227 true,
228 PP_PKG,
229 &format_shlibs_required,
230 },
231 [PP_PKG_CATEGORY_NAME] =
232 {
233 'C',
234 'n',
235 false,
236 false,
237 PP_PKG|PP_C,
238 &format_category_name,
239 },
240 [PP_PKG_CATEGORIES] =
241 {
242 'C',
243 '\0',
244 true,
245 true,
246 PP_PKG,
247 &format_categories,
248 },
249 [PP_PKG_DIRECTORY_GROUP] =
250 {
251 'D',
252 'g',
253 false,
254 false,
255 PP_PKG|PP_D,
256 &format_directory_group,
257 },
258 [PP_PKG_DIRECTORY_PATH] =
259 {
260 'D',
261 'n',
262 false,
263 false,
264 PP_PKG|PP_D,
265 &format_directory_path,
266 },
267 [PP_PKG_DIRECTORY_PERMS] =
268 {
269 'D',
270 'p',
271 false,
272 false,
273 PP_PKG|PP_D,
274 &format_directory_perms,
275 },
276 [PP_PKG_DIRECTORY_USER] =
277 {
278 'D',
279 'u',
280 false,
281 false,
282 PP_PKG|PP_D,
283 &format_directory_user,
284 },
285 [PP_PKG_DIRECTORIES] =
286 {
287 'D',
288 '\0',
289 true,
290 true,
291 PP_PKG,
292 &format_directories,
293 },
294 [PP_PKG_FILE_GROUP] =
295 {
296 'F',
297 'g',
298 false,
299 false,
300 PP_PKG|PP_F,
301 &format_file_group,
302 },
303 [PP_PKG_FILE_PATH] =
304 {
305 'F',
306 'n',
307 false,
308 false,
309 PP_PKG|PP_F,
310 &format_file_path,
311 },
312 [PP_PKG_FILE_PERMS] =
313 {
314 'F',
315 'p',
316 false,
317 false,
318 PP_PKG|PP_F,
319 &format_file_perms,
320 },
321 [PP_PKG_FILE_SHA256] =
322 {
323 'F',
324 's',
325 false,
326 false,
327 PP_PKG|PP_F,
328 &format_file_sha256,
329 },
330 [PP_PKG_FILE_USER] =
331 {
332 'F',
333 'u',
334 false,
335 false,
336 PP_PKG|PP_F,
337 &format_file_user,
338 },
339 [PP_PKG_FILES] =
340 {
341 'F',
342 '\0',
343 true,
344 true,
345 PP_PKG,
346 &format_files,
347 },
348 [PP_PKG_GROUP_NAME] =
349 {
350 'G',
351 'n',
352 false,
353 false,
354 PP_PKG|PP_G,
355 &format_group_name,
356 },
357 [PP_PKG_GROUPS] =
358 {
359 'G',
360 '\0',
361 true,
362 true,
363 PP_PKG,
364 &format_groups,
365 },
366 [PP_ROW_COUNTER] =
367 {
368 'I',
369 '\0',
370 false,
371 false,
372 PP_TRAILER,
373 &format_row_counter,
374 },
375 [PP_PKG_LICENSE_NAME] =
376 {
377 'L',
378 'n',
379 false,
380 false,
381 PP_PKG|PP_L,
382 &format_license_name,
383 },
384 [PP_PKG_LICENSES] =
385 {
386 'L',
387 '\0',
388 true,
389 true,
390 PP_PKG,
391 &format_licenses,
392 },
393 [PP_PKG_MESSAGE] =
394 {
395 'M',
396 '\0',
397 false,
398 true,
399 PP_ALL,
400 &format_message,
401 },
402 [PP_PKG_REPO_IDENT] =
403 {
404 'N',
405 '\0',
406 false,
407 true,
408 PP_ALL,
409 &format_repo_ident,
410 },
411 [PP_PKG_OPTION_NAME] =
412 {
413 'O',
414 'n',
415 false,
416 false,
417 PP_PKG|PP_O,
418 &format_option_name,
419 },
420 [PP_PKG_OPTION_VALUE] =
421 {
422 'O',
423 'v',
424 false,
425 false,
426 PP_PKG|PP_O,
427 &format_option_value,
428 },
429 [PP_PKG_OPTION_DEFAULT] =
430 {
431 'O',
432 'd',
433 false,
434 false,
435 PP_PKG|PP_O,
436 &format_option_default,
437 },
438 [PP_PKG_OPTION_DESCRIPTION] =
439 {
440 'O',
441 'D',
442 false,
443 false,
444 PP_PKG|PP_O,
445 &format_option_description,
446 },
447 [PP_PKG_OPTIONS] =
448 {
449 'O',
450 '\0',
451 true,
452 true,
453 PP_PKG,
454 &format_options,
455 },
456 [PP_PKG_ALTABI] =
457 {
458 'Q',
459 '\0',
460 false,
461 true,
462 PP_ALL,
463 &format_altabi,
464 },
465 [PP_PKG_REPO_PATH] =
466 {
467 'R',
468 '\0',
469 false,
470 true,
471 PP_ALL,
472 &format_repo_path,
473 },
474 [PP_PKG_CHAR_STRING] =
475 {
476 'S',
477 '\0',
478 false,
479 false,
480 PP_PKG,
481 &format_char_string,
482 },
483 [PP_PKG_USER_NAME] =
484 {
485 'U',
486 'n',
487 false,
488 false,
489 PP_PKG|PP_U,
490 &format_user_name,
491 },
492 [PP_PKG_USERS] =
493 {
494 'U',
495 '\0',
496 true,
497 true,
498 PP_PKG,
499 &format_users,
500 },
501 [PP_PKG_OLD_VERSION] =
502 {
503 'V',
504 '\0',
505 false,
506 true,
507 PP_ALL,
508 &format_old_version,
509 },
510 [PP_PKG_REQUIRED_NAME] = {
511 'Y',
512 'n',
513 false,
514 false,
515 PP_PKG|PP_Y,
516 &format_provide_name,
517 },
518 [PP_PKG_REQUIRED] = {
519 'Y',
520 '\0',
521 true,
522 true,
523 PP_PKG,
524 &format_required,
525 },
526 [PP_PKG_AUTOREMOVE] =
527 {
528 'a',
529 '\0',
530 false,
531 true,
532 PP_ALL,
533 &format_autoremove,
534 },
535 [PP_PKG_SHLIB_PROVIDED_NAME] =
536 {
537 'b',
538 'n',
539 false,
540 false,
541 PP_PKG|PP_b,
542 &format_shlib_name,
543 },
544 [PP_PKG_SHLIBS_PROVIDED] =
545 {
546 'b',
547 '\0',
548 true,
549 true,
550 PP_PKG,
551 &format_shlibs_provided,
552 },
553 [PP_PKG_COMMENT] =
554 {
555 'c',
556 '\0',
557 false,
558 true,
559 PP_ALL,
560 &format_comment,
561 },
562 [PP_PKG_DEPENDENCY_LOCK] =
563 {
564 'd',
565 'k',
566 false,
567 false,
568 PP_PKG|PP_d,
569 &format_dependency_lock,
570 },
571 [PP_PKG_DEPENDENCY_NAME] =
572 {
573 'd',
574 'n',
575 false,
576 false,
577 PP_PKG|PP_d,
578 &format_dependency_name,
579 },
580 [PP_PKG_DEPENDENCY_ORIGIN] =
581 {
582 'd',
583 'o',
584 false,
585 false,
586 PP_PKG|PP_d,
587 &format_dependency_origin,
588 },
589 [PP_PKG_DEPENDENCY_VERSION] =
590 {
591 'd',
592 'v',
593 false,
594 false,
595 PP_PKG|PP_d,
596 &format_dependency_version,
597 },
598 [PP_PKG_DEPENDENCIES] =
599 {
600 'd',
601 '\0',
602 true,
603 true,
604 PP_PKG,
605 &format_dependencies,
606 },
607 [PP_PKG_DESCRIPTION] =
608 {
609 'e',
610 '\0',
611 false,
612 true,
613 PP_ALL,
614 &format_description,
615 },
616 [PP_PKG_LOCK_STATUS] =
617 {
618 'k',
619 '\0',
620 false,
621 true,
622 PP_ALL,
623 &format_lock_status,
624 },
625 [PP_PKG_LICENSE_LOGIC] =
626 {
627 'l',
628 '\0',
629 false,
630 true,
631 PP_ALL,
632 &format_license_logic,
633 },
634 [PP_PKG_MAINTAINER] =
635 {
636 'm',
637 '\0',
638 false,
639 true,
640 PP_ALL,
641 &format_maintainer,
642 },
643 [PP_PKG_NAME] =
644 {
645 'n',
646 '\0',
647 false,
648 true,
649 PP_ALL,
650 &format_name, },
651 [PP_PKG_ORIGIN] =
652 {
653 'o',
654 '\0',
655 false,
656 true,
657 PP_ALL,
658 &format_origin,
659 },
660 [PP_PKG_PREFIX] =
661 {
662 'p',
663 '\0',
664 false,
665 true,
666 PP_ALL,
667 &format_prefix,
668 },
669 [PP_PKG_ARCHITECTURE] =
670 {
671 'q',
672 '\0',
673 false,
674 true,
675 PP_ALL,
676 &format_architecture,
677 },
678 [PP_PKG_REQUIREMENT_LOCK] =
679 {
680 'r',
681 'k',
682 false,
683 false,
684 PP_PKG|PP_r,
685 &format_dependency_lock,
686 },
687 [PP_PKG_REQUIREMENT_NAME] =
688 {
689 'r',
690 'n',
691 false,
692 false,
693 PP_PKG|PP_r,
694 &format_dependency_name,
695 },
696 [PP_PKG_REQUIREMENT_ORIGIN] =
697 {
698 'r',
699 'o',
700 false,
701 false,
702 PP_PKG|PP_r,
703 &format_dependency_origin,
704 },
705 [PP_PKG_REQUIREMENT_VERSION] =
706 {
707 'r',
708 'v',
709 false,
710 false,
711 PP_PKG|PP_r,
712 &format_dependency_version,
713 },
714 [PP_PKG_REQUIREMENTS] =
715 {
716 'r',
717 '\0',
718 true,
719 true,
720 PP_PKG,
721 &format_requirements,
722 },
723 [PP_PKG_FLATSIZE] =
724 {
725 's',
726 '\0',
727 false,
728 true,
729 PP_ALL,
730 &format_flatsize,
731 },
732 [PP_PKG_INSTALL_TIMESTAMP] =
733 {
734 't',
735 '\0',
736 true,
737 true,
738 PP_ALL,
739 &format_install_tstamp,
740 },
741 [PP_PKG_CHECKSUM] =
742 {
743 'u',
744 '\0',
745 false,
746 true,
747 PP_ALL,
748 &format_checksum,
749 },
750 [PP_PKG_VERSION] =
751 {
752 'v',
753 '\0',
754 false,
755 true,
756 PP_ALL,
757 &format_version,
758 },
759 [PP_PKG_HOME_PAGE] =
760 {
761 'w',
762 '\0',
763 false,
764 true,
765 PP_ALL,
766 &format_home_url,
767 },
768 [PP_PKG_PKGSIZE] =
769 {
770 'x',
771 '\0',
772 false,
773 true,
774 PP_ALL,
775 &format_pkgsize,
776 },
777 [PP_PKG_PROVIDED_NAME] = {
778 'y',
779 'n',
780 false,
781 false,
782 PP_PKG|PP_y,
783 &format_provide_name,
784 },
785 [PP_PKG_PROVIDED] = {
786 'y',
787 '\0',
788 true,
789 true,
790 PP_PKG,
791 &format_provided,
792 },
793 [PP_PKG_SHORT_CHECKSUM] =
794 {
795 'z',
796 '\0',
797 false,
798 true,
799 PP_ALL,
800 &format_short_checksum,
801 },
802 [PP_PKG_INT_CHECKSUM] =
803 {
804 'X',
805 '\0',
806 false,
807 true,
808 PP_ALL,
809 &format_int_checksum,
810 },
811 [PP_LITERAL_PERCENT] =
812 {
813 '%',
814 '\0',
815 false,
816 false,
817 PP_ALL,
818 &format_literal_percent,
819 },
820 [PP_UNKNOWN] =
821 {
822 '\0',
823 '\0',
824 false,
825 false,
826 PP_ALL,
827 &format_unknown,
828 },
829 [PP_END_MARKER] =
830 {
831 '\0',
832 '\0',
833 false,
834 false,
835 0,
836 NULL,
837 },
838 };
839
840 /*
841 * Note: List values -- special behaviour with ? and # modifiers.
842 * Affects %A %B %C %D %F %G %L %O %U %b %d %r
843 *
844 * With ? -- Flag values. Boolean. %?X returns 0 if the %X list is
845 * empty, 1 otherwise.
846 *
847 * With # -- Count values. Integer. %#X returns the number of items in
848 * the %X list.
849 */
850
851 /*
852 * %A -- Annotations. Free-form tag+value text that can be added to
853 * packages. Optionally accepts per-field format in %{ %| %} Default
854 * %{%An: %Av\n%|%}
855 */
856 xstring *
format_annotations(xstring * buf,const void * data,struct percent_esc * p)857 format_annotations(xstring *buf, const void *data, struct percent_esc *p)
858 {
859 const struct pkg *pkg = data;
860 struct pkg_kv *kv;
861 int count;
862
863 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) {
864 LL_COUNT(pkg->annotations, kv, count);
865 return (list_count(buf, count, p));
866 } else {
867 set_list_defaults(p, "%An: %Av\n", "");
868
869 count = 1;
870 fflush(p->sep_fmt->fp);
871 fflush(p->item_fmt->fp);
872 LL_FOREACH(pkg->annotations, kv) {
873 if (count > 1)
874 iterate_item(buf, pkg, p->sep_fmt->buf,
875 kv, count, PP_A);
876
877 iterate_item(buf, pkg, p->item_fmt->buf,
878 kv, count, PP_A);
879 count++;
880 }
881 }
882 return (buf);
883 }
884
885 /*
886 * %An -- Annotation tag name.
887 */
888 xstring *
format_annotation_name(xstring * buf,const void * data,struct percent_esc * p)889 format_annotation_name(xstring *buf, const void *data, struct percent_esc *p)
890 {
891 const struct pkg_kv *kv = data;
892
893 return (string_val(buf, kv->key, p));
894 }
895
896 /*
897 * %Av -- Annotation value.
898 */
899 xstring *
format_annotation_value(xstring * buf,const void * data,struct percent_esc * p)900 format_annotation_value(xstring *buf, const void *data, struct percent_esc *p)
901 {
902 const struct pkg_kv *kv = data;
903
904 return (string_val(buf, kv->value, p));
905 }
906
907 /*
908 * %B -- Required Shared Libraries. List of shlibs required by
909 * binaries in the pkg. Optionally accepts per-field format in %{ %|
910 * %}. Default %{%Bn\n%|%}
911 */
912 xstring *
format_shlibs_required(xstring * buf,const void * data,struct percent_esc * p)913 format_shlibs_required(xstring *buf, const void *data, struct percent_esc *p)
914 {
915 const struct pkg *pkg = data;
916
917 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
918 return (list_count(buf, pkg_list_count(pkg, PKG_SHLIBS_REQUIRED), p));
919 else {
920 char *buffer = NULL;
921 int count;
922
923 set_list_defaults(p, "%Bn\n", "");
924
925 count = 1;
926 fflush(p->sep_fmt->fp);
927 fflush(p->item_fmt->fp);
928 while (pkg_shlibs_required(pkg, &buffer) == EPKG_OK) {
929 if (count > 1)
930 iterate_item(buf, pkg, p->sep_fmt->buf,
931 buffer, count, PP_B);
932
933 iterate_item(buf, pkg, p->item_fmt->buf,
934 buffer, count, PP_B);
935 count++;
936 }
937 }
938 return (buf);
939 }
940
941 /*
942 * %Bn -- Required Shared Library name or %bn -- Provided Shared
943 * Library name
944 */
945 xstring *
format_shlib_name(xstring * buf,const void * data,struct percent_esc * p)946 format_shlib_name(xstring *buf, const void *data, struct percent_esc *p)
947 {
948 const char *shlib = data;
949
950 return (string_val(buf, shlib, p));
951 }
952
953 /*
954 * %C -- Categories. List of Category names (strings). 1ary category
955 * is not distinguished -- look at the package origin for that.
956 * Optionally accepts per-field format in %{ %| %}, where %n is
957 * replaced by the category name. Default %{%Cn%|, %}
958 */
959 xstring *
format_categories(xstring * buf,const void * data,struct percent_esc * p)960 format_categories(xstring *buf, const void *data, struct percent_esc *p)
961 {
962 const struct pkg *pkg = data;
963 int count = 0;
964 char *cat;
965
966 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) {
967 return (list_count(buf, pkg_list_count(pkg, PKG_CATEGORIES), p));
968 } else {
969 set_list_defaults(p, "%Cn", ", ");
970
971 count = 1;
972 fflush(p->sep_fmt->fp);
973 fflush(p->item_fmt->fp);
974 kh_each_value(pkg->categories, cat, {
975 if (count > 1)
976 iterate_item(buf, pkg, p->sep_fmt->buf,
977 cat, count, PP_C);
978
979 iterate_item(buf, pkg, p->item_fmt->buf, cat,
980 count, PP_C);
981 count++;
982 });
983 }
984 return (buf);
985 }
986
987 /*
988 * %Cn -- Category name.
989 */
990 xstring *
format_category_name(xstring * buf,const void * data,struct percent_esc * p)991 format_category_name(xstring *buf, const void *data, struct percent_esc *p)
992 {
993 const char *cat = data;
994
995 return (string_val(buf, cat, p));
996 }
997
998 /*
999 * %D -- Directories. List of directory names (strings) possibly with
1000 * other meta-data. Optionally accepts following per-field format in
1001 * %{ %| %}, where %Dn is replaced by the directory name. Default
1002 * %{%Dn\n%|%}
1003 */
1004 xstring *
format_directories(xstring * buf,const void * data,struct percent_esc * p)1005 format_directories(xstring *buf, const void *data, struct percent_esc *p)
1006 {
1007 const struct pkg *pkg = data;
1008
1009 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1010 return (list_count(buf, pkg_list_count(pkg, PKG_DIRS), p));
1011 else {
1012 struct pkg_dir *dir = NULL;
1013 int count;
1014
1015 set_list_defaults(p, "%Dn\n", "");
1016
1017 count = 1;
1018 fflush(p->sep_fmt->fp);
1019 fflush(p->item_fmt->fp);
1020 while (pkg_dirs(pkg, &dir) == EPKG_OK) {
1021 if (count > 1)
1022 iterate_item(buf, pkg, p->sep_fmt->buf,
1023 dir, count, PP_D);
1024
1025 iterate_item(buf, pkg, p->item_fmt->buf,
1026 dir, count, PP_D);
1027 count++;
1028 }
1029 }
1030 return (buf);
1031 }
1032
1033 /*
1034 * %Dg -- Directory group. TODO: numeric gid
1035 */
1036 xstring *
format_directory_group(xstring * buf,const void * data,struct percent_esc * p)1037 format_directory_group(xstring *buf, const void *data,
1038 struct percent_esc *p)
1039 {
1040 const struct pkg_dir *dir = data;
1041
1042 return (string_val(buf, dir->gname, p));
1043 }
1044
1045 /*
1046 * %Dn -- Directory path name.
1047 */
1048 xstring *
format_directory_path(xstring * buf,const void * data,struct percent_esc * p)1049 format_directory_path(xstring *buf, const void *data, struct percent_esc *p)
1050 {
1051 const struct pkg_dir *dir = data;
1052
1053 return (string_val(buf, dir->path, p));
1054 }
1055
1056 /*
1057 * %Dp -- Directory permissions.
1058 */
1059 xstring *
format_directory_perms(xstring * buf,const void * data,struct percent_esc * p)1060 format_directory_perms(xstring *buf, const void *data,
1061 struct percent_esc *p)
1062 {
1063 const struct pkg_dir *dir = data;
1064
1065 return (mode_val(buf, dir->perm, p));
1066 }
1067
1068 /*
1069 * %Du -- Directory user. TODO: numeric UID
1070 */
1071 xstring *
format_directory_user(xstring * buf,const void * data,struct percent_esc * p)1072 format_directory_user(xstring *buf, const void *data,
1073 struct percent_esc *p)
1074 {
1075 const struct pkg_dir *dir = data;
1076
1077 return (string_val(buf, dir->uname, p));
1078 }
1079
1080 /*
1081 * %F -- Files. List of filenames (strings) possibly with other
1082 * meta-data. Optionally accepts following per-field format in %{ %|
1083 * %}, where %n is replaced by the filename, %s by the checksum, etc.
1084 * Default %{%Fn\n%|%}
1085 */
1086 xstring *
format_files(xstring * buf,const void * data,struct percent_esc * p)1087 format_files(xstring *buf, const void *data, struct percent_esc *p)
1088 {
1089 const struct pkg *pkg = data;
1090
1091 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1092 return (list_count(buf, pkg_list_count(pkg, PKG_FILES), p));
1093 else {
1094 struct pkg_file *file = NULL;
1095 int count;
1096
1097 set_list_defaults(p, "%Fn\n", "");
1098
1099 count = 1;
1100 fflush(p->sep_fmt->fp);
1101 fflush(p->item_fmt->fp);
1102 LL_FOREACH(pkg->files, file) {
1103 if (count > 1)
1104 iterate_item(buf, pkg, p->sep_fmt->buf,
1105 file, count, PP_F);
1106
1107 iterate_item(buf, pkg, p->item_fmt->buf,
1108 file, count, PP_F);
1109 count++;
1110 }
1111 }
1112 return (buf);
1113 }
1114
1115 /*
1116 * %Fg -- File group.
1117 */
1118 xstring *
format_file_group(xstring * buf,const void * data,struct percent_esc * p)1119 format_file_group(xstring *buf, const void *data, struct percent_esc *p)
1120 {
1121 const struct pkg_file *file = data;
1122
1123 return (string_val(buf, file->gname, p));
1124 }
1125
1126 /*
1127 * %Fn -- File path name.
1128 */
1129 xstring *
format_file_path(xstring * buf,const void * data,struct percent_esc * p)1130 format_file_path(xstring *buf, const void *data, struct percent_esc *p)
1131 {
1132 const struct pkg_file *file = data;
1133
1134 return (string_val(buf, file->path, p));
1135 }
1136
1137 /*
1138 * %Fp -- File permissions.
1139 */
1140 xstring *
format_file_perms(xstring * buf,const void * data,struct percent_esc * p)1141 format_file_perms(xstring *buf, const void *data, struct percent_esc *p)
1142 {
1143 const struct pkg_file *file = data;
1144
1145 return (mode_val(buf, file->perm, p));
1146 }
1147
1148 /*
1149 * %Fs -- File SHA256 Checksum.
1150 */
1151 xstring *
format_file_sha256(xstring * buf,const void * data,struct percent_esc * p)1152 format_file_sha256(xstring *buf, const void *data, struct percent_esc *p)
1153 {
1154 const struct pkg_file *file = data;
1155
1156 return (string_val(buf, file->sum, p));
1157 }
1158
1159 /*
1160 * %Fu -- File user.
1161 */
1162 xstring *
format_file_user(xstring * buf,const void * data,struct percent_esc * p)1163 format_file_user(xstring *buf, const void *data, struct percent_esc *p)
1164 {
1165 const struct pkg_file *file = data;
1166
1167 return (string_val(buf, file->uname, p));
1168 }
1169
1170 /*
1171 * %G -- Groups. list of string values. Optionally accepts following
1172 * per-field format in %{ %| %} where %Gn will be replaced by each
1173 * groupname or %#Gn by the gid -- a line from
1174 * /etc/group. Default %{%Gn\n%|%}
1175 */
1176 xstring *
format_groups(xstring * buf,const void * data,struct percent_esc * p)1177 format_groups(xstring *buf, const void *data, struct percent_esc *p)
1178 {
1179 const struct pkg *pkg = data;
1180
1181 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1182 return (list_count(buf, pkg_list_count(pkg, PKG_GROUPS), p));
1183 else {
1184 char *group = NULL;
1185 int count;
1186
1187 set_list_defaults(p, "%Gn\n", "");
1188
1189 count = 1;
1190 fflush(p->sep_fmt->fp);
1191 fflush(p->item_fmt->fp);
1192 while(pkg_groups(pkg, &group) == EPKG_OK) {
1193 if (count > 1)
1194 iterate_item(buf, pkg, p->sep_fmt->buf,
1195 group, count, PP_G);
1196
1197 iterate_item(buf, pkg,p->item_fmt->buf,
1198 group, count, PP_G);
1199 count++;
1200 }
1201 }
1202 return (buf);
1203 }
1204
1205 /*
1206 * %Gn -- Group name.
1207 */
1208 xstring *
format_group_name(xstring * buf,const void * data,struct percent_esc * p)1209 format_group_name(xstring *buf, const void *data, struct percent_esc *p)
1210 {
1211 const char *group = data;
1212
1213 return (string_val(buf, group, p));
1214 }
1215
1216 /*
1217 * %I -- Row counter (integer*). Usually used only in per-field format.
1218 */
1219 xstring *
format_row_counter(xstring * buf,const void * data,struct percent_esc * p)1220 format_row_counter(xstring *buf, const void *data, struct percent_esc *p)
1221 {
1222 const int *counter = data;
1223
1224 return (int_val(buf, *counter, p));
1225 }
1226
1227 /*
1228 * %L -- Licences. List of string values. Optionally accepts
1229 * following per-field format in %{ %| %} where %Ln is replaced by the
1230 * license name and %l by the license logic. Default %{%n%| %l %}
1231 */
1232 xstring *
format_licenses(xstring * buf,const void * data,struct percent_esc * p)1233 format_licenses(xstring *buf, const void *data, struct percent_esc *p)
1234 {
1235 const struct pkg *pkg = data;
1236 char *lic;
1237 int count = 0;
1238
1239 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) {
1240 return (list_count(buf, pkg_list_count(pkg, PKG_LICENSES), p));
1241 } else {
1242 set_list_defaults(p, "%Ln", " %l ");
1243
1244 count = 1;
1245 fflush(p->sep_fmt->fp);
1246 fflush(p->item_fmt->fp);
1247 kh_each_value(pkg->licenses, lic, {
1248 if (count > 1)
1249 iterate_item(buf, pkg, p->sep_fmt->buf,
1250 lic, count, PP_L);
1251
1252 iterate_item(buf, pkg, p->item_fmt->buf, lic,
1253 count, PP_L);
1254 count++;
1255 });
1256 }
1257 return (buf);
1258 }
1259
1260 /*
1261 * %Ln -- License name.
1262 */
1263 xstring *
format_license_name(xstring * buf,const void * data,struct percent_esc * p)1264 format_license_name(xstring *buf, const void *data, struct percent_esc *p)
1265 {
1266 const char *lic = data;
1267
1268 return (string_val(buf, lic, p));
1269 }
1270
1271 /*
1272 * %M -- Pkg message. string. Accepts field-width, left-align
1273 */
1274 xstring *
format_message(xstring * buffer,const void * data,struct percent_esc * p)1275 format_message(xstring *buffer, const void *data, struct percent_esc *p)
1276 {
1277 xstring *buf, *bufmsg = NULL;
1278 const struct pkg *pkg = data;
1279 struct pkg_message *msg;
1280 char *message;
1281
1282 LL_FOREACH(pkg->message, msg) {
1283 if (bufmsg == NULL) {
1284 bufmsg = xstring_new();
1285 } else {
1286 fputc('\n', bufmsg->fp);
1287 }
1288 switch(msg->type) {
1289 case PKG_MESSAGE_ALWAYS:
1290 fprintf(bufmsg->fp, "Always:\n");
1291 break;
1292 case PKG_MESSAGE_UPGRADE:
1293 fprintf(bufmsg->fp, "On upgrade");
1294 if (msg->minimum_version != NULL ||
1295 msg->maximum_version != NULL) {
1296 fprintf(bufmsg->fp, " from %s", pkg->name);
1297 }
1298 if (msg->minimum_version != NULL) {
1299 fprintf(bufmsg->fp, ">%s", msg->minimum_version);
1300 }
1301 if (msg->maximum_version != NULL) {
1302 fprintf(bufmsg->fp, "<%s", msg->maximum_version);
1303 }
1304 fprintf(bufmsg->fp, ":\n");
1305 break;
1306 case PKG_MESSAGE_INSTALL:
1307 fprintf(bufmsg->fp, "On install:\n");
1308 break;
1309 case PKG_MESSAGE_REMOVE:
1310 fprintf(bufmsg->fp, "On remove:\n");
1311 break;
1312 }
1313 fprintf(bufmsg->fp, "%s\n", msg->str);
1314 }
1315 if (bufmsg == NULL)
1316 message = NULL;
1317 else {
1318 fflush(bufmsg->fp);
1319 message = bufmsg->buf;
1320 }
1321
1322 buf = string_val(buffer, message, p);
1323 xstring_free(bufmsg);
1324
1325 return (buf);
1326 }
1327
1328 /*
1329 * %N -- Repository identity. string. Accepts field-width, left-align
1330 */
1331 xstring *
format_repo_ident(xstring * buf,const void * data,struct percent_esc * p)1332 format_repo_ident(xstring *buf, const void *data, struct percent_esc *p)
1333 {
1334 const struct pkg *pkg = data;
1335 const char *reponame;
1336
1337 reponame = pkg->reponame;
1338 if (reponame == NULL) {
1339 reponame = pkg_kv_get(&pkg->annotations, "repository");
1340 if (reponame == NULL)
1341 reponame = "unknown-repository";
1342 }
1343 return (string_val(buf, reponame, p));
1344 }
1345
1346 /*
1347 * %O -- Options. list of {option,value} tuples. Optionally accepts
1348 * following per-field format in %{ %| %}, where %On is replaced by the
1349 * option name and %Ov by the value. Default %{%On %Ov\n%|%}
1350 */
1351 xstring *
format_options(xstring * buf,const void * data,struct percent_esc * p)1352 format_options(xstring *buf, const void *data, struct percent_esc *p)
1353 {
1354 const struct pkg *pkg = data;
1355
1356 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1357 return (list_count(buf, pkg_list_count(pkg, PKG_OPTIONS), p));
1358 else {
1359 struct pkg_option *opt = NULL;
1360 int count;
1361
1362 set_list_defaults(p, "%On %Ov\n", "");
1363
1364 count = 1;
1365 fflush(p->sep_fmt->fp);
1366 fflush(p->item_fmt->fp);
1367 while (pkg_options(pkg, &opt) == EPKG_OK) {
1368 if (count > 1)
1369 iterate_item(buf, pkg, p->sep_fmt->buf,
1370 opt, count, PP_O);
1371
1372 iterate_item(buf, pkg, p->item_fmt->buf,
1373 opt, count, PP_O);
1374 count++;
1375 }
1376 }
1377 return (buf);
1378 }
1379
1380 /*
1381 * %On -- Option name.
1382 */
1383 xstring *
format_option_name(xstring * buf,const void * data,struct percent_esc * p)1384 format_option_name(xstring *buf, const void *data, struct percent_esc *p)
1385 {
1386 const struct pkg_option *option = data;
1387
1388 return (string_val(buf, option->key, p));
1389 }
1390
1391 /*
1392 * %Ov -- Option value.
1393 */
1394 xstring *
format_option_value(xstring * buf,const void * data,struct percent_esc * p)1395 format_option_value(xstring *buf, const void *data, struct percent_esc *p)
1396 {
1397 const struct pkg_option *option = data;
1398
1399 return (string_val(buf, option->value, p));
1400 }
1401
1402 /*
1403 * %Od -- Option default value.
1404 */
1405 xstring *
format_option_default(xstring * buf,const void * data,struct percent_esc * p)1406 format_option_default(xstring *buf, const void *data, struct percent_esc *p)
1407 {
1408 const struct pkg_option *option = data;
1409
1410 return (string_val(buf, option->value, p));
1411 }
1412
1413 /*
1414 * %OD -- Option description
1415 */
1416 xstring *
format_option_description(xstring * buf,const void * data,struct percent_esc * p)1417 format_option_description(xstring *buf, const void *data, struct percent_esc *p)
1418 {
1419 const struct pkg_option *option = data;
1420
1421 return (string_val(buf, option->description, p));
1422 }
1423
1424 /*
1425 * %Q -- pkg architecture a.k.a ABI string. Accepts field-width, left-align
1426 */
1427 xstring *
format_altabi(xstring * buf,const void * data,struct percent_esc * p)1428 format_altabi(xstring *buf, const void *data, struct percent_esc *p)
1429 {
1430 const struct pkg *pkg = data;
1431
1432 return (string_val(buf, pkg->arch, p));
1433 }
1434
1435 /*
1436 * %R -- Repo path. string.
1437 */
1438 xstring *
format_repo_path(xstring * buf,const void * data,struct percent_esc * p)1439 format_repo_path(xstring *buf, const void *data, struct percent_esc *p)
1440 {
1441 const struct pkg *pkg = data;
1442
1443 return (string_val(buf, pkg->repopath, p));
1444 }
1445
1446 /*
1447 * %S -- Character string.
1448 */
1449 xstring *
format_char_string(xstring * buf,const void * data,struct percent_esc * p)1450 format_char_string(xstring *buf, const void *data, struct percent_esc *p)
1451 {
1452 const char *charstring = data;
1453
1454 return (string_val(buf, charstring, p));
1455 }
1456
1457 /*
1458 * %U -- Users. list of string values. Optionally accepts following
1459 * per-field format in %{ %| %} where %Un will be replaced by each
1460 * username or %#Un by the uid -- a line from
1461 * /etc/passwd. Default %{%Un\n%|%}
1462 */
1463 xstring *
format_users(xstring * buf,const void * data,struct percent_esc * p)1464 format_users(xstring *buf, const void *data, struct percent_esc *p)
1465 {
1466 const struct pkg *pkg = data;
1467
1468 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1469 return (list_count(buf, pkg_list_count(pkg, PKG_USERS), p));
1470 else {
1471 char *user = NULL;
1472 int count;
1473
1474 set_list_defaults(p, "%Un\n", "");
1475
1476 count = 1;
1477 fflush(p->sep_fmt->fp);
1478 fflush(p->item_fmt->fp);
1479 while (pkg_users(pkg, &user) == EPKG_OK) {
1480 if (count > 1)
1481 iterate_item(buf, pkg, p->sep_fmt->buf,
1482 user, count, PP_U);
1483
1484 iterate_item(buf, pkg, p->item_fmt->buf,
1485 user, count, PP_U);
1486 count++;
1487 }
1488 }
1489 return (buf);
1490 }
1491
1492 /*
1493 * %Un -- User name.
1494 */
1495 xstring *
format_user_name(xstring * buf,const void * data,struct percent_esc * p)1496 format_user_name(xstring *buf, const void *data, struct percent_esc *p)
1497 {
1498 const char *user = data;
1499
1500 return (string_val(buf, user, p));
1501 }
1502
1503 /*
1504 * %V -- Old package version. string. Accepts field width, left align
1505 */
1506 xstring *
format_old_version(xstring * buf,const void * data,struct percent_esc * p)1507 format_old_version(xstring *buf, const void *data, struct percent_esc *p)
1508 {
1509 const struct pkg *pkg = data;
1510
1511 return (string_val(buf, pkg->old_version, p));
1512 }
1513
1514 /*
1515 * %X -- Package checksum. string. Accepts field width, left align
1516 */
1517 xstring *
format_int_checksum(xstring * buf,const void * data,struct percent_esc * p)1518 format_int_checksum(xstring *buf, const void *data, struct percent_esc *p)
1519 {
1520 struct pkg *pkg = (struct pkg *)data;
1521
1522 pkg_checksum_calculate(pkg, NULL, true, false, true);
1523 return (string_val(buf, pkg->digest, p));
1524 }
1525
1526 /*
1527 * %Y -- Required pattern. List of pattern required by
1528 * binaries in the pkg. Optionally accepts per-field format in %{ %|
1529 * %}. Default %{%Yn\n%|%}
1530 */
1531 xstring *
format_required(xstring * buf,const void * data,struct percent_esc * p)1532 format_required(xstring *buf, const void *data, struct percent_esc *p)
1533 {
1534 const struct pkg *pkg = data;
1535
1536 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1537 return (list_count(buf, pkg_list_count(pkg, PKG_REQUIRES), p));
1538 else {
1539 char *provide = NULL;
1540 int count;
1541
1542 set_list_defaults(p, "%Yn\n", "");
1543
1544 count = 1;
1545 fflush(p->sep_fmt->fp);
1546 fflush(p->item_fmt->fp);
1547 while (pkg_requires(pkg, &provide) == EPKG_OK) {
1548 if (count > 1)
1549 iterate_item(buf, pkg, p->sep_fmt->buf,
1550 provide, count, PP_Y);
1551
1552 iterate_item(buf, pkg, p->item_fmt->buf,
1553 provide, count, PP_Y);
1554 count++;
1555 }
1556 }
1557 return (buf);
1558 }
1559
1560 /*
1561 * %Yn -- Required name or %yn -- Provided name
1562 */
1563 xstring *
format_provide_name(xstring * buf,const void * data,struct percent_esc * p)1564 format_provide_name(xstring *buf, const void *data, struct percent_esc *p)
1565 {
1566 const char *provide = data;
1567
1568 return (string_val(buf, provide, p));
1569 }
1570 /*
1571 * %a -- Autoremove flag. boolean. Accepts field-width, left-align.
1572 * Standard form: 0, 1. Alternate form1: no, yes. Alternate form2:
1573 * false, true
1574 */
1575 xstring *
format_autoremove(xstring * buf,const void * data,struct percent_esc * p)1576 format_autoremove(xstring *buf, const void *data, struct percent_esc *p)
1577 {
1578 const struct pkg *pkg = data;
1579
1580 return (bool_val(buf, pkg->automatic, p));
1581 }
1582
1583
1584 /*
1585 * %b -- Provided Shared Libraries. List of shlibs provided by
1586 * binaries in the pkg. Optionally accepts per-field format in %{ %|
1587 * %}, where %n is replaced by the shlib name. Default %{%bn\n%|%}
1588 */
1589 xstring *
format_shlibs_provided(xstring * buf,const void * data,struct percent_esc * p)1590 format_shlibs_provided(xstring *buf, const void *data, struct percent_esc *p)
1591 {
1592 const struct pkg *pkg = data;
1593
1594 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1595 return (list_count(buf, pkg_list_count(pkg, PKG_SHLIBS_PROVIDED), p));
1596 else {
1597 char *shlib = NULL;
1598 int count;
1599
1600 set_list_defaults(p, "%bn\n", "");
1601
1602 count = 1;
1603 fflush(p->sep_fmt->fp);
1604 fflush(p->item_fmt->fp);
1605 while (pkg_shlibs_provided(pkg, &shlib) == EPKG_OK) {
1606 if (count > 1)
1607 iterate_item(buf, pkg, p->sep_fmt->buf,
1608 shlib, count, PP_b);
1609
1610 iterate_item(buf, pkg, p->item_fmt->buf,
1611 shlib, count, PP_b);
1612 count++;
1613 }
1614 }
1615 return (buf);
1616 }
1617
1618 /*
1619 * %c -- Comment. string. Accepts field-width, left-align
1620 */
1621 xstring *
format_comment(xstring * buf,const void * data,struct percent_esc * p)1622 format_comment(xstring *buf, const void *data, struct percent_esc *p)
1623 {
1624 const struct pkg *pkg = data;
1625
1626 return (string_val(buf, pkg->comment, p));
1627 }
1628
1629 /*
1630 * %d -- Dependencies. List of pkgs. Can be optionally followed by
1631 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
1632 * formats. Defaults to printing "%dn-%dv\n" for each dependency.
1633 */
1634 xstring *
format_dependencies(xstring * buf,const void * data,struct percent_esc * p)1635 format_dependencies(xstring *buf, const void *data, struct percent_esc *p)
1636 {
1637 const struct pkg *pkg = data;
1638
1639 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1640 return (list_count(buf, pkg_list_count(pkg, PKG_DEPS), p));
1641 else {
1642 struct pkg_dep *dep = NULL;
1643 int count;
1644
1645 set_list_defaults(p, "%dn-%dv\n", "");
1646
1647 count = 1;
1648 fflush(p->sep_fmt->fp);
1649 fflush(p->item_fmt->fp);
1650 while (pkg_deps(pkg, &dep) == EPKG_OK) {
1651 if (count > 1)
1652 iterate_item(buf, pkg, p->sep_fmt->buf,
1653 dep, count, PP_d);
1654
1655 iterate_item(buf, pkg, p->item_fmt->buf,
1656 dep, count, PP_d);
1657 count++;
1658 }
1659 }
1660 return (buf);
1661 }
1662
1663 /*
1664 * %dk -- Dependency lock status or %rk -- Requirement lock status.
1665 */
1666 xstring *
format_dependency_lock(xstring * buf,const void * data,struct percent_esc * p)1667 format_dependency_lock(xstring *buf, const void *data,
1668 struct percent_esc *p)
1669 {
1670 const struct pkg_dep *dep = data;
1671
1672 return (bool_val(buf, pkg_dep_is_locked(dep), p));
1673 }
1674
1675 /*
1676 * %dn -- Dependency name or %rn -- Requirement name.
1677 */
1678 xstring *
format_dependency_name(xstring * buf,const void * data,struct percent_esc * p)1679 format_dependency_name(xstring *buf, const void *data,
1680 struct percent_esc *p)
1681 {
1682 const struct pkg_dep *dep = data;
1683
1684 return (string_val(buf, dep->name, p));
1685 }
1686
1687 /*
1688 * %do -- Dependency origin or %ro -- Requirement origin.
1689 */
1690 xstring *
format_dependency_origin(xstring * buf,const void * data,struct percent_esc * p)1691 format_dependency_origin(xstring *buf, const void *data,
1692 struct percent_esc *p)
1693 {
1694 const struct pkg_dep *dep = data;
1695
1696 return (string_val(buf, dep->origin, p));
1697 }
1698
1699 /*
1700 * %dv -- Dependency version or %rv -- Requirement version.
1701 */
1702 xstring *
format_dependency_version(xstring * buf,const void * data,struct percent_esc * p)1703 format_dependency_version(xstring *buf, const void *data,
1704 struct percent_esc *p)
1705 {
1706 const struct pkg_dep *dep = data;
1707
1708 return (string_val(buf, dep->version, p));
1709 }
1710
1711 /*
1712 * %e -- Description. string. Accepts field-width, left-align
1713 */
1714 xstring *
format_description(xstring * buf,const void * data,struct percent_esc * p)1715 format_description(xstring *buf, const void *data, struct percent_esc *p)
1716 {
1717 const struct pkg *pkg = data;
1718
1719 return (string_val(buf, pkg->desc, p));
1720 }
1721
1722 /*
1723 * %k -- Locked flag. boolean. Accepts field-width, left-align.
1724 * Standard form: 0, 1. Alternate form1: no, yes. Alternate form2:
1725 * false, true
1726 */
1727 xstring *
format_lock_status(xstring * buf,const void * data,struct percent_esc * p)1728 format_lock_status(xstring *buf, const void *data, struct percent_esc *p)
1729 {
1730 const struct pkg *pkg = data;
1731
1732 return (bool_val(buf, pkg->locked, p));
1733 }
1734
1735 /*
1736 * %l -- Licence logic. string. Accepts field-width, left-align.
1737 * Standard form: and, or, single. Alternate form 1: &, |, ''.
1738 * Alternate form 2: &&, ||, ==
1739 */
1740 xstring *
format_license_logic(xstring * buf,const void * data,struct percent_esc * p)1741 format_license_logic(xstring *buf, const void *data, struct percent_esc *p)
1742 {
1743 const struct pkg *pkg = data;
1744
1745 return (liclog_val(buf, pkg->licenselogic, p));
1746 }
1747
1748 /*
1749 * %m -- Maintainer e-mail address. string. Accepts field-width, left-align
1750 */
1751 xstring *
format_maintainer(xstring * buf,const void * data,struct percent_esc * p)1752 format_maintainer(xstring *buf, const void *data, struct percent_esc *p)
1753 {
1754 const struct pkg *pkg = data;
1755
1756 return (string_val(buf, pkg->maintainer, p));
1757 }
1758
1759 /*
1760 * %n -- Package name. string. Accepts field-width, left-align
1761 */
1762 xstring *
format_name(xstring * buf,const void * data,struct percent_esc * p)1763 format_name(xstring *buf, const void *data, struct percent_esc *p)
1764 {
1765 const struct pkg *pkg = data;
1766
1767 return (string_val(buf, pkg->name, p));
1768 }
1769
1770 /*
1771 * %o -- Package origin. string. Accepts field-width, left-align
1772 */
1773 xstring *
format_origin(xstring * buf,const void * data,struct percent_esc * p)1774 format_origin(xstring *buf, const void *data, struct percent_esc *p)
1775 {
1776 const struct pkg *pkg = data;
1777
1778 return (string_val(buf, pkg->origin, p));
1779 }
1780
1781 /*
1782 * %p -- Installation prefix. string. Accepts field-width, left-align
1783 */
1784 xstring *
format_prefix(xstring * buf,const void * data,struct percent_esc * p)1785 format_prefix(xstring *buf, const void *data, struct percent_esc *p)
1786 {
1787 const struct pkg *pkg = data;
1788
1789 return (string_val(buf, pkg->prefix, p));
1790 }
1791
1792 /*
1793 * %q -- pkg architecture a.k.a ABI string. Accepts field-width, left-align
1794 */
1795 xstring *
format_architecture(xstring * buf,const void * data,struct percent_esc * p)1796 format_architecture(xstring *buf, const void *data, struct percent_esc *p)
1797 {
1798 const struct pkg *pkg = data;
1799
1800 return (string_val(buf, pkg->abi, p));
1801 }
1802
1803 /*
1804 * %r -- Requirements. List of pkgs. Can be optionally followed by
1805 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
1806 * formats. Defaults to printing "%{%rn-%rv\n%|%}" for each dependency.
1807 */
1808 xstring *
format_requirements(xstring * buf,const void * data,struct percent_esc * p)1809 format_requirements(xstring *buf, const void *data, struct percent_esc *p)
1810 {
1811 const struct pkg *pkg = data;
1812
1813 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1814 return(list_count(buf, pkg_list_count(pkg, PKG_RDEPS), p));
1815 else {
1816 struct pkg_dep *req = NULL;
1817 int count;
1818
1819 set_list_defaults(p, "%rn-%rv\n", "");
1820
1821 count = 1;
1822 fflush(p->sep_fmt->fp);
1823 fflush(p->item_fmt->fp);
1824 while (pkg_rdeps(pkg, &req) == EPKG_OK) {
1825 if (count > 1)
1826 iterate_item(buf, pkg, p->sep_fmt->buf,
1827 req, count, PP_r);
1828
1829 iterate_item(buf, pkg, p->item_fmt->buf,
1830 req, count, PP_r);
1831 count++;
1832 }
1833 }
1834 return (buf);
1835 }
1836
1837 /*
1838 * %s -- Size of installed package. integer. Accepts field-width,
1839 * left-align, zero-fill, space-for-plus, explicit-plus and
1840 * alternate-form. Alternate form is a humanized number using decimal
1841 * exponents (k, M, G). Alternate form 2, ditto, but using binary
1842 * scale prefixes (ki, Mi, Gi etc.)
1843 */
1844 xstring *
format_flatsize(xstring * buf,const void * data,struct percent_esc * p)1845 format_flatsize(xstring *buf, const void *data, struct percent_esc *p)
1846 {
1847 const struct pkg *pkg = data;
1848
1849 return (int_val(buf, pkg->flatsize, p));
1850 }
1851
1852 /*
1853 * %t -- Installation timestamp (Unix time). integer. Accepts
1854 * field-width, left-align. Can be followed by optional strftime
1855 * format string in %{ %}. Default is to print seconds-since-epoch as
1856 * an integer applying our integer format modifiers.
1857 */
1858 xstring *
format_install_tstamp(xstring * buf,const void * data,struct percent_esc * p)1859 format_install_tstamp(xstring *buf, const void *data, struct percent_esc *p)
1860 {
1861 const struct pkg *pkg = data;
1862
1863 fflush(p->item_fmt->fp);
1864 if (strlen(p->item_fmt->buf) == 0)
1865 return (int_val(buf, pkg->timestamp, p));
1866 else {
1867 char buffer[1024];
1868 time_t tsv;
1869
1870 tsv = (time_t)pkg->timestamp;
1871 strftime(buffer, sizeof(buffer), p->item_fmt->buf,
1872 localtime(&tsv));
1873 fprintf(buf->fp, "%s", buffer);
1874 }
1875 return (buf);
1876 }
1877
1878 /*
1879 * %v -- Package version. string. Accepts field width, left align
1880 */
1881 xstring *
format_version(xstring * buf,const void * data,struct percent_esc * p)1882 format_version(xstring *buf, const void *data, struct percent_esc *p)
1883 {
1884 const struct pkg *pkg = data;
1885
1886 return (string_val(buf, pkg->version, p));
1887 }
1888
1889 /*
1890 * %u -- Package checksum. string. Accepts field width, left align
1891 */
1892 xstring *
format_checksum(xstring * buf,const void * data,struct percent_esc * p)1893 format_checksum(xstring *buf, const void *data, struct percent_esc *p)
1894 {
1895 const struct pkg *pkg = data;
1896
1897 return (string_val(buf, pkg->sum, p));
1898 }
1899
1900 /*
1901 * %w -- Home page URL. string. Accepts field width, left align
1902 */
1903 xstring *
format_home_url(xstring * buf,const void * data,struct percent_esc * p)1904 format_home_url(xstring *buf, const void *data, struct percent_esc *p)
1905 {
1906 const struct pkg *pkg = data;
1907
1908 return (string_val(buf, pkg->www, p));
1909 }
1910
1911 /*
1912 * %x - Package tarball size. Integer. Accepts field-width,
1913 * left-align, zero-fill, space-for-plus, explicit-plus and
1914 * alternate-form. Alternate form is a humanized number using decimal
1915 * exponents (k, M, G). Alternate form 2, ditto, but using binary
1916 * scale prefixes (ki, Mi, Gi etc.)
1917 */
1918 xstring *
format_pkgsize(xstring * buf,const void * data,struct percent_esc * p)1919 format_pkgsize(xstring *buf, const void *data, struct percent_esc *p)
1920 {
1921 const struct pkg *pkg = data;
1922
1923 return (int_val(buf, pkg->pkgsize, p));
1924 }
1925
1926 /*
1927 * %y -- Provided pattern. List of pattern provided by
1928 * binaries in the pkg. Optionally accepts per-field format in %{ %|
1929 * %}. Default %{%yn\n%|%}
1930 */
1931 xstring *
format_provided(xstring * buf,const void * data,struct percent_esc * p)1932 format_provided(xstring *buf, const void *data, struct percent_esc *p)
1933 {
1934 const struct pkg *pkg = data;
1935
1936 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
1937 return (list_count(buf, pkg_list_count(pkg, PKG_PROVIDES), p));
1938 else {
1939 char *provide = NULL;
1940 int count;
1941
1942 set_list_defaults(p, "%yn\n", "");
1943
1944 count = 1;
1945 fflush(p->sep_fmt->fp);
1946 fflush(p->item_fmt->fp);
1947 while (pkg_provides(pkg, &provide) == EPKG_OK) {
1948 if (count > 1)
1949 iterate_item(buf, pkg, p->sep_fmt->buf,
1950 provide, count, PP_y);
1951
1952 iterate_item(buf, pkg, p->item_fmt->buf,
1953 provide, count, PP_y);
1954 count++;
1955 }
1956 }
1957 return (buf);
1958 }
1959
1960 /*
1961 * %z -- Package short checksum. string. Accepts field width, left align
1962 */
1963 xstring *
format_short_checksum(xstring * buf,const void * data,struct percent_esc * p)1964 format_short_checksum(xstring *buf, const void *data, struct percent_esc *p)
1965 {
1966 const struct pkg *pkg = data;
1967 char csum[PKG_FILE_CKSUM_CHARS + 1];
1968 int slen;
1969
1970 if (pkg->sum != NULL)
1971 slen = MIN(PKG_FILE_CKSUM_CHARS, strlen(pkg->sum));
1972 else
1973 slen = 0;
1974 memcpy(csum, pkg->sum, slen);
1975 csum[slen] = '\0';
1976
1977 return (string_val(buf, csum, p));
1978 }
1979 /*
1980 * %% -- Output a literal '%' character
1981 */
1982 xstring *
format_literal_percent(xstring * buf,__unused const void * data,__unused struct percent_esc * p)1983 format_literal_percent(xstring *buf, __unused const void *data,
1984 __unused struct percent_esc *p)
1985 {
1986 fputc('%', buf->fp);
1987 return (buf);
1988 }
1989
1990 /*
1991 * Unknown format code -- return NULL to signal upper layers to pass
1992 * the text through unchanged.
1993 */
1994 xstring *
format_unknown(xstring * buf,__unused const void * data,__unused struct percent_esc * p)1995 format_unknown(xstring *buf, __unused const void *data,
1996 __unused struct percent_esc *p)
1997 {
1998 fputc('%', buf->fp);
1999 return (NULL);
2000 }
2001
2002 /* -------------------------------------------------------------- */
2003
2004 struct percent_esc *
new_percent_esc(void)2005 new_percent_esc(void)
2006 {
2007 struct percent_esc *p;
2008
2009 p = xcalloc(1, sizeof(struct percent_esc));
2010 p->item_fmt = xstring_new();
2011 p->sep_fmt = xstring_new();
2012 return (p);
2013 }
2014
2015 struct percent_esc *
clear_percent_esc(struct percent_esc * p)2016 clear_percent_esc(struct percent_esc *p)
2017 {
2018 p->flags = 0;
2019 p->width = 0;
2020 p->trailer_status = 0;
2021 xstring_reset(p->item_fmt);
2022 xstring_reset(p->sep_fmt);
2023
2024 p->fmt_code = '\0';
2025
2026 return (p);
2027 }
2028
2029 void
free_percent_esc(struct percent_esc * p)2030 free_percent_esc(struct percent_esc *p)
2031 {
2032 if (p) {
2033 if (p->item_fmt)
2034 xstring_free(p->item_fmt);
2035 if (p->sep_fmt)
2036 xstring_free(p->sep_fmt);
2037 free(p);
2038 }
2039 return;
2040 }
2041
2042 char *
gen_format(char * buf,size_t buflen,unsigned flags,const char * tail)2043 gen_format(char *buf, size_t buflen, unsigned flags, const char *tail)
2044 {
2045 int bp = 0;
2046 size_t tlen;
2047
2048 /* We need the length of tail plus at least 3 characters '%'
2049 '*' '\0' but maybe as many as 7 '%' '#' '-' '+' '\'' '*'
2050 '\0' */
2051
2052 tlen = strlen(tail);
2053
2054 if (buflen - bp < tlen + 3)
2055 return (NULL);
2056
2057 buf[bp++] = '%';
2058
2059 /* PP_ALTERNATE_FORM1 is not used by regular printf(3) */
2060
2061 /* If PP_EXPLICIT_PLUS and PP_SPACE_FOR_PLUS are both set,
2062 the result is formatted according to PP_EXPLICIT_PLUS */
2063
2064 if ((flags & (PP_EXPLICIT_PLUS|PP_SPACE_FOR_PLUS)) ==
2065 (PP_EXPLICIT_PLUS|PP_SPACE_FOR_PLUS))
2066 flags &= ~(PP_SPACE_FOR_PLUS);
2067
2068 /* If PP_LEFT_ALIGN and PP_ZERO_PAD are given together,
2069 PP_LEFT_ALIGN applies */
2070
2071 if ((flags & (PP_LEFT_ALIGN|PP_ZERO_PAD)) ==
2072 (PP_LEFT_ALIGN|PP_ZERO_PAD))
2073 flags &= ~(PP_ZERO_PAD);
2074
2075 if (flags & PP_ALTERNATE_FORM2)
2076 buf[bp++] = '#';
2077
2078 if (flags & PP_LEFT_ALIGN)
2079 buf[bp++] = '-';
2080
2081 if (flags & PP_ZERO_PAD)
2082 buf[bp++] = '0';
2083
2084 if (buflen - bp < tlen + 2)
2085 return (NULL);
2086
2087 if (flags & PP_EXPLICIT_PLUS)
2088 buf[bp++] = '+';
2089
2090 if (flags & PP_SPACE_FOR_PLUS)
2091 buf[bp++] = ' ';
2092
2093 if (flags & PP_THOUSANDS_SEP)
2094 buf[bp++] = '\'';
2095
2096 if (buflen - bp < tlen + 2)
2097 return (NULL);
2098
2099 /* The effect of 0 meaning 'zero fill' is indisinguishable
2100 from 0 meaning 'a field width of zero' */
2101
2102 buf[bp++] = '*';
2103 buf[bp] = '\0';
2104
2105 strlcat(buf, tail, buflen);
2106
2107 return (buf);
2108 }
2109
2110
2111 xstring *
human_number(xstring * buf,int64_t number,struct percent_esc * p)2112 human_number(xstring *buf, int64_t number, struct percent_esc *p)
2113 {
2114 double num;
2115 int sign;
2116 int width;
2117 int scale_width;
2118 int divisor;
2119 int scale;
2120 int precision;
2121 bool bin_scale;
2122
2123 #define MAXSCALE 7
2124
2125 const char *bin_pfx[MAXSCALE] =
2126 { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
2127 const char *si_pfx[MAXSCALE] =
2128 { "", "k", "M", "G", "T", "P", "E" };
2129 char format[16];
2130
2131 bin_scale = ((p->flags & PP_ALTERNATE_FORM2) != 0);
2132
2133 p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
2134
2135 if (gen_format(format, sizeof(format), p->flags, ".*f") == NULL)
2136 return (NULL);
2137
2138 if (number >= 0) {
2139 num = number;
2140 sign = 1;
2141 } else {
2142 num = -number;
2143 sign = -1;
2144 }
2145
2146 divisor = bin_scale ? 1024 : 1000;
2147
2148 for (scale = 0; scale < MAXSCALE; scale++) {
2149 if (num < divisor)
2150 break;
2151 num /= divisor;
2152 }
2153
2154 if (scale == MAXSCALE)
2155 scale--;
2156
2157 if (scale == 0)
2158 scale_width = 0;
2159 else if (bin_scale)
2160 scale_width = 2;
2161 else
2162 scale_width = 1;
2163
2164 if (p->width == 0)
2165 width = 0;
2166 else if (p->width <= scale_width)
2167 width = 1;
2168 else
2169 width = p->width - scale_width;
2170
2171 if (num >= 100)
2172 precision = 0;
2173 else if (num >= 10) {
2174 if (width == 0 || width > 3)
2175 precision = 1;
2176 else
2177 precision = 0;
2178 } else {
2179 if (width == 0 || width > 3)
2180 precision = 2;
2181 else if (width == 3)
2182 precision = 1;
2183 else
2184 precision = 0;
2185 }
2186
2187 fprintf(buf->fp, format, width, precision, num * sign);
2188
2189 if (scale > 0)
2190 fprintf(buf->fp, "%s",
2191 bin_scale ? bin_pfx[scale] : si_pfx[scale]);
2192
2193 return (buf);
2194 }
2195
2196 xstring *
string_val(xstring * buf,const char * str,struct percent_esc * p)2197 string_val(xstring *buf, const char *str, struct percent_esc *p)
2198 {
2199 char format[16];
2200
2201 /* The '#' '?' '+' ' ' '0' and '\'' modifiers have no meaning
2202 for strings */
2203
2204 p->flags &= ~(PP_ALTERNATE_FORM1 |
2205 PP_ALTERNATE_FORM2 |
2206 PP_EXPLICIT_PLUS |
2207 PP_SPACE_FOR_PLUS |
2208 PP_ZERO_PAD |
2209 PP_THOUSANDS_SEP);
2210
2211 if (gen_format(format, sizeof(format), p->flags, "s") == NULL)
2212 return (NULL);
2213
2214 fprintf(buf->fp, format, p->width, str);
2215 return (buf);
2216 }
2217
2218 xstring *
int_val(xstring * buf,int64_t value,struct percent_esc * p)2219 int_val(xstring *buf, int64_t value, struct percent_esc *p)
2220 {
2221 if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
2222 return (human_number(buf, value, p));
2223 else {
2224 char format[16];
2225
2226 if (gen_format(format, sizeof(format), p->flags, PRId64)
2227 == NULL)
2228 return (NULL);
2229
2230 fprintf(buf->fp, format, p->width, value);
2231 }
2232 return (buf);
2233 }
2234
2235 xstring *
bool_val(xstring * buf,bool value,struct percent_esc * p)2236 bool_val(xstring *buf, bool value, struct percent_esc *p)
2237 {
2238 static const char *boolean_str[2][3] = {
2239 [false] = { "false", "no", "" },
2240 [true] = { "true", "yes", "(*)" },
2241 };
2242 int alternate;
2243
2244 if (p->flags & PP_ALTERNATE_FORM2)
2245 alternate = 2;
2246 else if (p->flags & PP_ALTERNATE_FORM1)
2247 alternate = 1;
2248 else
2249 alternate = 0;
2250
2251 p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
2252
2253 return (string_val(buf, boolean_str[value][alternate], p));
2254 }
2255
2256 xstring *
mode_val(xstring * buf,mode_t mode,struct percent_esc * p)2257 mode_val(xstring *buf, mode_t mode, struct percent_esc *p)
2258 {
2259 /*
2260 * Print mode as an octal integer '%o' by default.
2261 * PP_ALTERNATE_FORM2 generates '%#o' pased to regular
2262 * printf(). PP_ALTERNATE_FORM1 will generate 'drwxr-x--- '
2263 * style from strmode(3).
2264 */
2265
2266 if (p->flags & PP_ALTERNATE_FORM1) {
2267 char modebuf[12];
2268
2269 strmode(mode, modebuf);
2270
2271 return (string_val(buf, modebuf, p));
2272 } else {
2273 char format[16];
2274
2275 /*
2276 * Should the mode when expressed as a numeric value
2277 * in octal include the bits that indicate the inode
2278 * type? Generally no, but since mode is
2279 * intrinsically an unsigned type, overload
2280 * PP_EXPLICIT_PLUS to mean 'show bits for the inode
2281 * type'
2282 */
2283
2284 if ( (p->flags & PP_EXPLICIT_PLUS) == 0 )
2285 mode &= ALLPERMS;
2286
2287 p->flags &= ~(PP_ALTERNATE_FORM1|PP_EXPLICIT_PLUS);
2288
2289 if (gen_format(format, sizeof(format), p->flags, PRIo16)
2290 == NULL)
2291 return (NULL);
2292
2293 fprintf(buf->fp, format, p->width, mode);
2294 }
2295 return (buf);
2296 }
2297
2298 xstring *
liclog_val(xstring * buf,lic_t licenselogic,struct percent_esc * p)2299 liclog_val(xstring *buf, lic_t licenselogic, struct percent_esc *p)
2300 {
2301 int alternate;
2302 int llogic = PP_LIC_SINGLE;
2303
2304 static const char *liclog_str[3][3] = {
2305 [PP_LIC_SINGLE] = { "single", "", "==" },
2306 [PP_LIC_OR] = { "or", "|", "||" },
2307 [PP_LIC_AND] = { "and", "&", "&&" },
2308 };
2309
2310 switch (licenselogic) {
2311 case LICENSE_SINGLE:
2312 llogic = PP_LIC_SINGLE;
2313 break;
2314 case LICENSE_OR:
2315 llogic = PP_LIC_OR;
2316 break;
2317 case LICENSE_AND:
2318 llogic = PP_LIC_AND;
2319 break;
2320 }
2321
2322 if (p->flags & PP_ALTERNATE_FORM2)
2323 alternate = 2;
2324 else if (p->flags & PP_ALTERNATE_FORM1)
2325 alternate = 1;
2326 else
2327 alternate = 0;
2328
2329 p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
2330
2331 return (string_val(buf, liclog_str[llogic][alternate], p));
2332 }
2333
2334 xstring *
list_count(xstring * buf,int64_t count,struct percent_esc * p)2335 list_count(xstring *buf, int64_t count, struct percent_esc *p)
2336 {
2337 /* Convert to 0 or 1 for %?X */
2338 if (p->flags & PP_ALTERNATE_FORM1)
2339 count = (count > 0);
2340
2341 /* Turn off %#X and %?X flags, then print as a normal integer */
2342 p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
2343
2344 return (int_val(buf, count, p));
2345 }
2346
2347 struct percent_esc *
set_list_defaults(struct percent_esc * p,const char * item_fmt,const char * sep_fmt)2348 set_list_defaults(struct percent_esc *p, const char *item_fmt,
2349 const char *sep_fmt)
2350 {
2351 if ((p->trailer_status & ITEM_FMT_SET) != ITEM_FMT_SET) {
2352 fprintf(p->item_fmt->fp, "%s", item_fmt);
2353 p->trailer_status |= ITEM_FMT_SET;
2354 }
2355 if ((p->trailer_status & SEP_FMT_SET) != SEP_FMT_SET) {
2356 fprintf(p->sep_fmt->fp, "%s", sep_fmt);
2357 p->trailer_status |= SEP_FMT_SET;
2358 }
2359 return (p);
2360 }
2361
2362 xstring *
iterate_item(xstring * buf,const struct pkg * pkg,const char * format,const void * data,int count,unsigned context)2363 iterate_item(xstring *buf, const struct pkg *pkg, const char *format,
2364 const void *data, int count, unsigned context)
2365 {
2366 const char *f;
2367 struct percent_esc *p;
2368
2369 /* Scan the format string and interpret any escapes */
2370
2371 f = format;
2372 p = new_percent_esc();
2373
2374 if (p == NULL) {
2375 xstring_reset(buf);
2376 return (buf); /* Out of memory */
2377 }
2378
2379 while ( *f != '\0' ) {
2380 switch(*f) {
2381 case '%':
2382 f = process_format_trailer(buf, p, f, pkg, data, count, context);
2383 break;
2384 case '\\':
2385 f = process_escape(buf, f);
2386 break;
2387 default:
2388 fprintf(buf->fp, "%c", *f);
2389 f++;
2390 break;
2391 }
2392 if (f == NULL) {
2393 xstring_reset(buf);
2394 break; /* Out of memory */
2395 }
2396 }
2397
2398 free_percent_esc(p);
2399 return (buf);
2400 }
2401
2402 const char *
field_modifier(const char * f,struct percent_esc * p)2403 field_modifier(const char *f, struct percent_esc *p)
2404 {
2405 bool done;
2406
2407 /* Field modifiers, if any:
2408 '?' alternate form 1
2409 '#' alternate form 2
2410 '-' left align
2411 '+' explicit plus sign (numerics only)
2412 ' ' space instead of plus sign (numerics only)
2413 '0' pad with zeroes (numerics only)
2414 '\'' use thousands separator (numerics only)
2415 Note '*' (dynamic field width) is not supported */
2416
2417 done = false;
2418 while (!done) {
2419 switch (*f) {
2420 case '?':
2421 p->flags |= PP_ALTERNATE_FORM1;
2422 break;
2423 case '#':
2424 p->flags |= PP_ALTERNATE_FORM2;
2425 break;
2426 case '-':
2427 p->flags |= PP_LEFT_ALIGN;
2428 break;
2429 case '+':
2430 p->flags |= PP_EXPLICIT_PLUS;
2431 break;
2432 case ' ':
2433 p->flags |= PP_SPACE_FOR_PLUS;
2434 break;
2435 case '0':
2436 p->flags |= PP_ZERO_PAD;
2437 break;
2438 case '\'':
2439 p->flags |= PP_THOUSANDS_SEP;
2440 break;
2441 default:
2442 done = true;
2443 break;
2444 }
2445 if (!done)
2446 f++;
2447 }
2448 return (f);
2449 }
2450
2451 const char *
field_width(const char * f,struct percent_esc * p)2452 field_width(const char *f, struct percent_esc *p)
2453 {
2454 bool done;
2455
2456 /* Field width, if any -- some number of decimal digits.
2457 Note: field width set to zero could be interpreted as using
2458 0 to request zero padding: it doesn't matter which -- the
2459 result on output is exactly the same. */
2460
2461 done = false;
2462 while (!done) {
2463 switch(*f) {
2464 case '0':
2465 p->width = p->width * 10 + 0;
2466 break;
2467 case '1':
2468 p->width = p->width * 10 + 1;
2469 break;
2470 case '2':
2471 p->width = p->width * 10 + 2;
2472 break;
2473 case '3':
2474 p->width = p->width * 10 + 3;
2475 break;
2476 case '4':
2477 p->width = p->width * 10 + 4;
2478 break;
2479 case '5':
2480 p->width = p->width * 10 + 5;
2481 break;
2482 case '6':
2483 p->width = p->width * 10 + 6;
2484 break;
2485 case '7':
2486 p->width = p->width * 10 + 7;
2487 break;
2488 case '8':
2489 p->width = p->width * 10 + 8;
2490 break;
2491 case '9':
2492 p->width = p->width * 10 + 9;
2493 break;
2494 default:
2495 done = true;
2496 break;
2497 }
2498 if (!done)
2499 f++;
2500 }
2501 return (f);
2502 }
2503
2504 const char *
format_trailer(const char * f,struct percent_esc * p)2505 format_trailer(const char *f, struct percent_esc *p)
2506 {
2507
2508 /* is the trailer even present? */
2509
2510 if (f[0] == '%' && f[1] == '{') {
2511 bool sep = false;
2512 bool done = false;
2513 const char *f1;
2514 const char *f2;
2515
2516 p->trailer_status |= ITEM_FMT_SET;
2517 f1 = f + 2;
2518
2519 for (f2 = f1; *f2 != '\0'; f2++) {
2520 if (f2[0] == '%' && ( f2[1] == '}' || f2[1] == '|')) {
2521 if (f2[1] == '|')
2522 sep = true;
2523 else
2524 done = true;
2525 f1 = f2 + 2;
2526 break;
2527 }
2528 fputc(*f2, p->item_fmt->fp);
2529 fflush(p->item_fmt->fp);
2530 }
2531
2532
2533 if (sep) {
2534 p->trailer_status |= SEP_FMT_SET;
2535 done = false;
2536
2537 for (f2 = f1; *f2 != '\0'; f2++) {
2538 if (f2[0] == '%' && f2[1] == '}') {
2539 done = true;
2540 f1 = f2 + 2;
2541 break;
2542 }
2543 fputc(*f2, p->sep_fmt->fp);
2544 fflush(p->sep_fmt->fp);
2545 }
2546
2547 }
2548
2549 if (done) {
2550 f = f1;
2551 } else {
2552 xstring_reset(p->item_fmt);
2553 xstring_reset(p->sep_fmt);
2554 }
2555 }
2556
2557 return (f);
2558 }
2559
2560 const char *
format_code(const char * f,unsigned context,struct percent_esc * p)2561 format_code(const char *f, unsigned context, struct percent_esc *p)
2562 {
2563 fmt_code_t fmt_code;
2564
2565 p->fmt_code = PP_UNKNOWN; /* Assume unknown, for contradiction */
2566
2567 /* The next character or two will be a format code -- look
2568 these up in the fmt table to make sure they are allowed in
2569 context. This could be optimized since the format codes
2570 are arranged alphabetically in the fmt[] array. */
2571
2572 for (fmt_code = 0; fmt_code < PP_END_MARKER; fmt_code++) {
2573 if ((fmt[fmt_code].context & context) != context)
2574 continue;
2575 if (fmt[fmt_code].fmt_main != f[0])
2576 continue;
2577 if (fmt[fmt_code].fmt_sub == f[1] && f[1] != '\0') {
2578 p->fmt_code = fmt_code;
2579 f += 2;
2580 break;
2581 }
2582 if (fmt[fmt_code].fmt_sub == '\0') {
2583 p->fmt_code = fmt_code;
2584 f++;
2585 break;
2586 }
2587 }
2588
2589 return (f);
2590 }
2591
2592 const char *
parse_format(const char * f,unsigned context,struct percent_esc * p)2593 parse_format(const char *f, unsigned context, struct percent_esc *p)
2594 {
2595 f++; /* Eat the % */
2596
2597 f = field_modifier(f, p);
2598
2599 f = field_width(f, p);
2600
2601 f = format_code(f, context, p);
2602
2603 /* Does this format take a trailing list item/separator format
2604 like %{...%|...%} ? It's only the list-valued items that
2605 do, and they can only take it at the top level (context ==
2606 PP_PKG). Also, they only take the trailing stuff in the
2607 absence of %?X or %#X modifiers. */
2608
2609 if ((context & PP_PKG) == PP_PKG &&
2610 fmt[p->fmt_code].has_trailer &&
2611 (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) == 0)
2612 f = format_trailer(f, p);
2613
2614 return (f);
2615 }
2616
2617 const char*
maybe_read_hex_byte(xstring * buf,const char * f)2618 maybe_read_hex_byte(xstring *buf, const char *f)
2619 {
2620 /* Hex escapes are of the form \xNN -- always two hex digits */
2621
2622 f++; /* eat the x */
2623
2624 if (isxdigit(f[0]) && isxdigit(f[1])) {
2625 int val;
2626
2627 switch(*f) {
2628 case '0':
2629 val = 0x0;
2630 break;
2631 case '1':
2632 val = 0x10;
2633 break;
2634 case '2':
2635 val = 0x20;
2636 break;
2637 case '3':
2638 val = 0x30;
2639 break;
2640 case '4':
2641 val = 0x40;
2642 break;
2643 case '5':
2644 val = 0x50;
2645 break;
2646 case '6':
2647 val = 0x60;
2648 break;
2649 case '7':
2650 val = 0x70;
2651 break;
2652 case '8':
2653 val = 0x80;
2654 break;
2655 case '9':
2656 val = 0x90;
2657 break;
2658 case 'a':
2659 case 'A':
2660 val = 0xa0;
2661 break;
2662 case 'b':
2663 case 'B':
2664 val = 0xb0;
2665 break;
2666 case 'c':
2667 case 'C':
2668 val = 0xc0;
2669 break;
2670 case 'd':
2671 case 'D':
2672 val = 0xd0;
2673 break;
2674 case 'e':
2675 case 'E':
2676 val = 0xe0;
2677 break;
2678 case 'f':
2679 case 'F':
2680 val = 0xf0;
2681 break;
2682 default:
2683 /* This case is to shut up the over-picky
2684 * compiler warnings about use of an
2685 * uninitialised value. It can't actually
2686 * be reached. */
2687 val = 0x0;
2688 break;
2689 }
2690
2691 f++;
2692
2693 switch(*f) {
2694 case '0':
2695 val += 0x0;
2696 break;
2697 case '1':
2698 val += 0x1;
2699 break;
2700 case '2':
2701 val += 0x2;
2702 break;
2703 case '3':
2704 val += 0x3;
2705 break;
2706 case '4':
2707 val += 0x4;
2708 break;
2709 case '5':
2710 val += 0x5;
2711 break;
2712 case '6':
2713 val += 0x6;
2714 break;
2715 case '7':
2716 val += 0x7;
2717 break;
2718 case '8':
2719 val += 0x8;
2720 break;
2721 case '9':
2722 val += 0x9;
2723 break;
2724 case 'a':
2725 case 'A':
2726 val += 0xa;
2727 break;
2728 case 'b':
2729 case 'B':
2730 val += 0xb;
2731 break;
2732 case 'c':
2733 case 'C':
2734 val += 0xc;
2735 break;
2736 case 'd':
2737 case 'D':
2738 val += 0xd;
2739 break;
2740 case 'e':
2741 case 'E':
2742 val += 0xe;
2743 break;
2744 case 'f':
2745 case 'F':
2746 val += 0xf;
2747 break;
2748 }
2749
2750 fputc(val, buf->fp);
2751 f++;
2752 } else {
2753 /* Pass through unchanged if it's not a recognizable
2754 hex byte. */
2755 fputc('\\', buf->fp);
2756 fputc('x', buf->fp);
2757 }
2758 return (f);
2759 }
2760
2761 const char*
read_oct_byte(xstring * buf,const char * f)2762 read_oct_byte(xstring *buf, const char *f)
2763 {
2764 int val = 0;
2765 int count = 0;
2766
2767 /* Octal escapes are upto three octal digits: \N, \NN or \NNN
2768 up to a max of \377. Note: this treats \400 as \40
2769 followed by character 0 passed through unchanged. */
2770
2771 while (val < 32 && count++ < 3) {
2772 switch (*f) {
2773 case '0':
2774 val = val * 8 + 0;
2775 break;
2776 case '1':
2777 val = val * 8 + 1;
2778 break;
2779 case '2':
2780 val = val * 8 + 2;
2781 break;
2782 case '3':
2783 val = val * 8 + 3;
2784 break;
2785 case '4':
2786 val = val * 8 + 4;
2787 break;
2788 case '5':
2789 val = val * 8 + 5;
2790 break;
2791 case '6':
2792 val = val * 8 + 6;
2793 break;
2794 case '7':
2795 val = val * 8 + 7;
2796 break;
2797 default: /* Non-octal digit */
2798 goto done;
2799 }
2800
2801 f++;
2802 }
2803 done:
2804 fputc(val, buf->fp);
2805
2806 return (f);
2807 }
2808
2809 const char *
process_escape(xstring * buf,const char * f)2810 process_escape(xstring *buf, const char *f)
2811 {
2812 f++; /* Eat the \ */
2813
2814 switch (*f) {
2815 case 'a':
2816 fputc('\a', buf->fp);
2817 f++;
2818 break;
2819 case 'b':
2820 fputc('\b', buf->fp);
2821 f++;
2822 break;
2823 case 'f':
2824 fputc('\f', buf->fp);
2825 f++;
2826 break;
2827 case 'n':
2828 fputc('\n', buf->fp);
2829 f++;
2830 break;
2831 case 't':
2832 fputc('\t', buf->fp);
2833 f++;
2834 break;
2835 case 'v':
2836 fputc('\v', buf->fp);
2837 f++;
2838 break;
2839 case '\'':
2840 fputc('\'', buf->fp);
2841 f++;
2842 break;
2843 case '"':
2844 fputc('"', buf->fp);
2845 f++;
2846 break;
2847 case '\\':
2848 fputc('\\', buf->fp);
2849 f++;
2850 break;
2851 case 'x': /* Hex escape: \xNN */
2852 f = maybe_read_hex_byte(buf, f);
2853 break;
2854 case '0':
2855 case '1':
2856 case '2':
2857 case '3':
2858 case '4':
2859 case '5':
2860 case '6':
2861 case '7': /* Oct escape: all fall through */
2862 f = read_oct_byte(buf, f);
2863 break;
2864 default: /* If it's not a recognised escape,
2865 leave f pointing at the escaped
2866 character */
2867 fputc('\\', buf->fp);
2868 break;
2869 }
2870
2871 return (f);
2872 }
2873
2874 const char *
process_format_trailer(xstring * buf,struct percent_esc * p,const char * f,const struct pkg * pkg,const void * data,int count,unsigned context)2875 process_format_trailer(xstring *buf, struct percent_esc *p,
2876 const char *f, const struct pkg *pkg,
2877 const void *data, int count, unsigned context)
2878 {
2879 const char *fstart;
2880 xstring *s;
2881
2882 fstart = f;
2883 f = parse_format(f, context, p);
2884
2885 if (p->fmt_code == PP_ROW_COUNTER)
2886 s = fmt[p->fmt_code].fmt_handler(buf, &count, p);
2887 else if (p->fmt_code > PP_LAST_FORMAT)
2888 s = fmt[p->fmt_code].fmt_handler(buf, NULL, p);
2889 else if (fmt[p->fmt_code].struct_pkg)
2890 s = fmt[p->fmt_code].fmt_handler(buf, pkg, p);
2891 else
2892 s = fmt[p->fmt_code].fmt_handler(buf, data, p);
2893
2894
2895 if (s == NULL) {
2896 f = fstart + 1; /* Eat just the % on error */
2897 }
2898
2899 clear_percent_esc(p);
2900
2901 return (f);
2902 }
2903
2904 const char *
process_format_main(xstring * buf,struct percent_esc * p,const char * fstart,const char * fend,void * data)2905 process_format_main(xstring *buf, struct percent_esc *p,
2906 const char *fstart, const char *fend, void *data)
2907 {
2908 xstring *s;
2909
2910 s = fmt[p->fmt_code].fmt_handler(buf, data, p);
2911
2912 clear_percent_esc(p);
2913
2914 /* Pass through unprocessed on error */
2915 return (s == NULL ? fstart : fend);
2916 }
2917
2918 /**
2919 * print to stdout data from pkg as indicated by the format code format
2920 * @param ... Varargs list of struct pkg etc. supplying the data
2921 * @param format String with embedded %-escapes indicating what to print
2922 * @return count of the number of characters printed
2923 */
2924 int
pkg_printf(const char * restrict format,...)2925 pkg_printf(const char * restrict format, ...)
2926 {
2927 int count;
2928 va_list ap;
2929
2930 va_start(ap, format);
2931 count = pkg_vprintf(format, ap);
2932 va_end(ap);
2933
2934 return (count);
2935 }
2936
2937 /**
2938 * print to stdout data from pkg as indicated by the format code format
2939 * @param ap Varargs list of struct pkg etc. supplying the data
2940 * @param format String with embedded %-escapes indicating what to print
2941 * @return count of the number of characters printed
2942 */
2943 int
pkg_vprintf(const char * restrict format,va_list ap)2944 pkg_vprintf(const char * restrict format, va_list ap)
2945 {
2946 xstring *buf;
2947 int count;
2948
2949 buf = xstring_new();
2950
2951 if (buf)
2952 buf = pkg_xstring_vprintf(buf, format, ap);
2953 fflush(buf->fp);
2954 if (buf && strlen(buf->buf) > 0) {
2955 count = printf("%s", buf->buf);
2956 } else
2957 count = -1;
2958 if (buf)
2959 xstring_free(buf);
2960 return (count);
2961 }
2962
2963 /**
2964 * print to named stream from pkg as indicated by the format code format
2965 * @param ... Varargs list of struct pkg etc. supplying the data
2966 * @param format String with embedded %-escapes indicating what to output
2967 * @return count of the number of characters printed
2968 */
2969 int
pkg_fprintf(FILE * restrict stream,const char * restrict format,...)2970 pkg_fprintf(FILE * restrict stream, const char * restrict format, ...)
2971 {
2972 int count;
2973 va_list ap;
2974
2975 va_start(ap, format);
2976 count = pkg_vfprintf(stream, format, ap);
2977 va_end(ap);
2978
2979 return (count);
2980 }
2981
2982 /**
2983 * print to named stream from pkg as indicated by the format code format
2984 * @param ap Varargs list of struct pkg etc. supplying the data
2985 * @param format String with embedded %-escapes indicating what to output
2986 * @return count of the number of characters printed
2987 */
2988 int
pkg_vfprintf(FILE * restrict stream,const char * restrict format,va_list ap)2989 pkg_vfprintf(FILE * restrict stream, const char * restrict format, va_list ap)
2990 {
2991 xstring *buf;
2992 int count;
2993
2994 buf = xstring_new();
2995
2996 if (buf)
2997 buf = pkg_xstring_vprintf(buf, format, ap);
2998 fflush(buf->fp);
2999 if (buf && strlen(buf->buf) > 0) {
3000 count = fprintf(stream, "%s", buf->buf);
3001 } else
3002 count = -1;
3003 if (buf)
3004 xstring_free(buf);
3005 return (count);
3006 }
3007
3008 /**
3009 * print to file descriptor fd data from pkg as indicated by the format
3010 * code format
3011 * @param fd Previously opened file descriptor to print to
3012 * @param ... Varargs list of struct pkg etc. supplying the data
3013 * @param format String with embedded %-escapes indicating what to print
3014 * @return count of the number of characters printed
3015 */
3016 int
pkg_dprintf(int fd,const char * restrict format,...)3017 pkg_dprintf(int fd, const char * restrict format, ...)
3018 {
3019 int count;
3020 va_list ap;
3021
3022 va_start(ap, format);
3023 count = pkg_vdprintf(fd, format, ap);
3024 va_end(ap);
3025
3026 return (count);
3027 }
3028
3029 /**
3030 * print to file descriptor fd data from pkg as indicated by the format
3031 * code format
3032 * @param fd Previously opened file descriptor to print to
3033 * @param ap Varargs list of struct pkg etc. supplying the data
3034 * @param format String with embedded %-escapes indicating what to print
3035 * @return count of the number of characters printed
3036 */
3037 int
pkg_vdprintf(int fd,const char * restrict format,va_list ap)3038 pkg_vdprintf(int fd, const char * restrict format, va_list ap)
3039 {
3040 xstring *buf;
3041 int count;
3042
3043 buf = xstring_new();
3044
3045 if (buf)
3046 buf = pkg_xstring_vprintf(buf, format, ap);
3047 fflush(buf->fp);
3048 if (buf && strlen(buf->buf) > 0) {
3049 count = dprintf(fd, "%s", buf->buf);
3050 } else
3051 count = -1;
3052 if (buf)
3053 xstring_free(buf);
3054 return (count);
3055 }
3056
3057 /**
3058 * print to buffer str of given size data from pkg as indicated by the
3059 * format code format as a NULL-terminated string
3060 * @param str Character array buffer to receive output
3061 * @param size Length of the buffer str
3062 * @param ... Varargs list of struct pkg etc. supplying the data
3063 * @param format String with embedded %-escapes indicating what to output
3064 * @return count of the number of characters that would have been output
3065 * disregarding truncation to fit size
3066 */
3067 int
pkg_snprintf(char * restrict str,size_t size,const char * restrict format,...)3068 pkg_snprintf(char * restrict str, size_t size, const char * restrict format, ...)
3069 {
3070 int count;
3071 va_list ap;
3072
3073 va_start(ap, format);
3074 count = pkg_vsnprintf(str, size, format, ap);
3075 va_end(ap);
3076
3077 return (count);
3078 }
3079
3080 /**
3081 * print to buffer str of given size data from pkg as indicated by the
3082 * format code format as a NULL-terminated string
3083 * @param str Character array buffer to receive output
3084 * @param size Length of the buffer str
3085 * @param ap Varargs list of struct pkg etc. supplying the data
3086 * @param format String with embedded %-escapes indicating what to output
3087 * @return count of the number of characters that would have been output
3088 * disregarding truncation to fit size
3089 */
3090 int
pkg_vsnprintf(char * restrict str,size_t size,const char * restrict format,va_list ap)3091 pkg_vsnprintf(char * restrict str, size_t size, const char * restrict format,
3092 va_list ap)
3093 {
3094 xstring *buf;
3095 int count;
3096
3097 buf = xstring_new();
3098
3099 if (buf)
3100 buf = pkg_xstring_vprintf(buf, format, ap);
3101 fflush(buf->fp);
3102 if (buf && strlen(buf->buf) > 0) {
3103 count = snprintf(str, size, "%s", buf->buf);
3104 } else
3105 count = -1;
3106 if (buf)
3107 xstring_free(buf);
3108
3109 return (count);
3110 }
3111
3112 /**
3113 * Allocate a string buffer ret sufficiently big to contain formatted
3114 * data data from pkg as indicated by the format code format
3115 * @param ret location of pointer to be set to point to buffer containing
3116 * result
3117 * @param ... Varargs list of struct pkg etc. supplying the data
3118 * @param format String with embedded %-escapes indicating what to output
3119 * @return count of the number of characters printed
3120 */
3121 int
pkg_asprintf(char ** ret,const char * restrict format,...)3122 pkg_asprintf(char **ret, const char * restrict format, ...)
3123 {
3124 int count;
3125 va_list ap;
3126
3127 va_start(ap, format);
3128 count = pkg_vasprintf(ret, format, ap);
3129 va_end(ap);
3130
3131 return (count);
3132 }
3133
3134 /**
3135 * Allocate a string buffer ret sufficiently big to contain formatted
3136 * data data from pkg as indicated by the format code format
3137 * @param ret location of pointer to be set to point to buffer containing
3138 * result
3139 * @param ap Varargs list of struct pkg etc. supplying the data
3140 * @param format String with embedded %-escapes indicating what to output
3141 * @return count of the number of characters printed
3142 */
3143 int
pkg_vasprintf(char ** ret,const char * restrict format,va_list ap)3144 pkg_vasprintf(char **ret, const char * restrict format, va_list ap)
3145 {
3146 xstring *buf;
3147 int count;
3148
3149 buf = xstring_new();
3150
3151 if (buf)
3152 buf = pkg_xstring_vprintf(buf, format, ap);
3153 fflush(buf->fp);
3154 if (buf && strlen(buf->buf) > 0) {
3155 count = xasprintf(ret, "%s", buf->buf);
3156 } else {
3157 count = -1;
3158 *ret = NULL;
3159 }
3160 if (buf)
3161 xstring_free(buf);
3162 return (count);
3163 }
3164
3165 /**
3166 * store data from pkg into buf as indicated by the format code format.
3167 * This is the core function called by all the other pkg_printf() family.
3168 * @param buf contains the result
3169 * @param ap Arglist with struct pkg etc. supplying the data
3170 * @param format String with embedded %-escapes indicating what to output
3171 * @return count of the number of characters in the result
3172 */
3173 static xstring *
pkg_xstring_vprintf(xstring * restrict buf,const char * restrict format,va_list ap)3174 pkg_xstring_vprintf(xstring * restrict buf, const char * restrict format,
3175 va_list ap)
3176 {
3177 const char *f, *fend;
3178 struct percent_esc *p;
3179 void *data;
3180
3181 assert(buf != NULL);
3182 assert(format != NULL);
3183
3184 f = format;
3185 p = new_percent_esc();
3186
3187 if (p == NULL) {
3188 xstring_reset(buf);
3189 return (buf); /* Out of memory */
3190 }
3191
3192 while ( *f != '\0' ) {
3193 switch(*f) {
3194 case '%':
3195 fend = parse_format(f, PP_PKG, p);
3196
3197 if (p->fmt_code <= PP_LAST_FORMAT)
3198 data = va_arg(ap, void *);
3199 else
3200 data = NULL;
3201 f = process_format_main(buf, p, f, fend, data);
3202 break;
3203 case '\\':
3204 f = process_escape(buf, f);
3205 break;
3206 default:
3207 fputc(*f, buf->fp);
3208 f++;
3209 break;
3210 }
3211 if (f == NULL) {
3212 xstring_reset(buf);
3213 break; /* Error: out of memory */
3214 }
3215 }
3216
3217 free_percent_esc(p);
3218 return (buf);
3219 }
3220 /*
3221 * That's All Folks!
3222 */
3223