libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
acf_file.c
1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15/*
16 * Copyright 2023 Saso Kiselkov. All rights reserved.
17 */
18
19#include <stddef.h>
20#include <stdlib.h>
21#include <string.h>
22#include <errno.h>
23
24#include <acfutils/acf_file.h>
25#include <acfutils/avl.h>
26#include <acfutils/helpers.h>
27#include <acfutils/log.h>
28#include <acfutils/safe_alloc.h>
29
33typedef struct {
34 char *name;
35 char *value;
36 avl_node_t node;
38
44struct acf_file {
45 int version;
46 avl_tree_t props;
47};
48
49static int
50acf_prop_compar(const void *a, const void *b)
51{
52 const acf_prop_t *pa = a, *pb = b;
53 int x = strcmp(pa->name, pb->name);
54 if (x < 0)
55 return (-1);
56 else if (x == 0)
57 return (0);
58 return (1);
59}
60
70acf_file_t *
71acf_file_read(const char *filename)
72{
73 acf_file_t *acf;
74 FILE *fp = fopen(filename, "rb");
75 char *line = NULL;
76 size_t cap = 0;
77 bool_t parsing_props = B_FALSE;
78
79 if (fp == NULL) {
80 logMsg("Error reading acf file %s: %s", filename,
81 strerror(errno));
82 return (NULL);
83 }
84 acf = safe_calloc(1, sizeof (*acf));
85 avl_create(&acf->props, acf_prop_compar, sizeof (acf_prop_t),
86 offsetof(acf_prop_t, node));
87
88 for (int line_num = 1; getline(&line, &cap, fp) > 0; line_num++) {
89 char *name_end;
90 acf_prop_t *prop;
91 avl_index_t where;
92
93 strip_space(line);
94 if (line_num <= 3) {
95 size_t n_comps;
96 char **comps = strsplit(line, " ", B_TRUE, &n_comps);
97
98 if (n_comps >= 2 && strcmp(comps[1], "Version") == 0) {
99 acf->version = atoi(comps[0]);
100 } else if (line_num == 3 &&
101 strcmp(comps[0], "ACF") != 0) {
102 logMsg("Error reading acf file %s: missing "
103 "file header. Are you sure this is an "
104 "ACF file?", filename);
105 free_strlist(comps, n_comps);
106 goto errout;
107 }
108 free_strlist(comps, n_comps);
109 continue;
110 }
111 if (!parsing_props) {
112 if (strncmp(line, "PROPERTIES_BEGIN", 16) == 0)
113 parsing_props = B_TRUE;
114 continue;
115 }
116 if (strncmp(line, "PROPERTIES_END", 14) == 0)
117 break;
118 if (strncmp(line, "P ", 2) != 0)
119 continue;
120
121 name_end = strchr(&line[2], ' ');
122 if (name_end == NULL) {
123 logMsg("Error reading acf file %s:%d: bad parameter "
124 "line.", filename, line_num);
125 goto errout;
126 }
127 prop = safe_calloc(1, sizeof (*prop));
128 prop->name = safe_malloc(name_end - line - 1);
129 prop->value = safe_malloc(strlen(&name_end[1]) + 1);
130 lacf_strlcpy(prop->name, &line[2], name_end - line - 1);
131 lacf_strlcpy(prop->value, &name_end[1], strlen(&name_end[1]) +
132 1);
133 if (avl_find(&acf->props, prop, &where) != NULL) {
134 logMsg("Error reading acf file %s:%d duplicate "
135 "property \"%s\" found.", filename, line_num,
136 prop->name);
137 free(prop->name);
138 free(prop->value);
139 free(prop);
140 goto errout;
141 }
142 avl_insert(&acf->props, prop, where);
143 }
144
145 fclose(fp);
146 return (acf);
147errout:
148 acf_file_free(acf);
149 fclose(fp);
150 return (NULL);
151}
152
156void
157acf_file_free(acf_file_t *acf)
158{
159 acf_prop_t *prop;
160 void *cookie = NULL;
161
162 while ((prop = avl_destroy_nodes(&acf->props, &cookie)) != NULL) {
163 free(prop->name);
164 free(prop->value);
165 free(prop);
166 }
167 avl_destroy(&acf->props);
168 free(acf);
169}
170
182const char *
183acf_prop_find(const acf_file_t *acf, const char *prop_path)
184{
185 const acf_prop_t srch = { .name = (char *)prop_path };
186 acf_prop_t *prop = avl_find(&acf->props, &srch, NULL);
187
188 if (prop == NULL)
189 return (NULL);
190 return (prop->value);
191}
192
196int
197acf_file_get_version(const acf_file_t *acf)
198{
199 ASSERT(acf != NULL);
200 return (acf->version);
201}
#define ASSERT(x)
Definition assert.h:208
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
Definition avl.c:938
uintptr_t avl_index_t
Definition avl.h:119
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
Definition avl.c:471
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
Definition avl.c:867
void avl_destroy(avl_tree_t *tree)
Definition avl.c:890
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
Definition avl.c:244
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
Definition helpers.c:650
void lacf_strlcpy(char *dest, const char *src, size_t cap)
Definition helpers.c:1667
void free_strlist(char **comps, size_t num)
Definition helpers.c:703
#define logMsg(...)
Definition log.h:112
static void strip_space(char *line)
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
avl_tree_t props
Definition acf_file.c:46
int version
Definition acf_file.c:45
char * value
Definition acf_file.c:35
char * name
Definition acf_file.c:34