ClangFormat: apply to source, most of intern
[blender.git] / source / blender / blenkernel / intern / idprop_utils.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup bke
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "BLI_utildefines.h"
25 #include "BLI_string.h"
26 #include "BLI_dynstr.h"
27
28 #include "BKE_idprop.h"
29 #include "BKE_idcode.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_strict_flags.h"
34
35 /* -------------------------------------------------------------------- */
36 /** \name IDProp Repr
37  *
38  * Convert an IDProperty to a string.
39  *
40  * Output should be a valid Python literal
41  * (with minor exceptions - float nan for eg).
42  * \{ */
43
44 struct ReprState {
45   void (*str_append_fn)(void *user_data, const char *str, uint str_len);
46   void *user_data;
47   /* Big enough to format any primitive type. */
48   char buf[128];
49 };
50
51 static void idp_str_append_escape(struct ReprState *state,
52                                   const char *str,
53                                   const uint str_len,
54                                   bool quote)
55 {
56   if (quote) {
57     state->str_append_fn(state->user_data, "\"", 1);
58   }
59   uint i_prev = 0, i = 0;
60   while (i < str_len) {
61     const char c = str[i];
62     if (c == '"') {
63       if (i_prev != i) {
64         state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
65       }
66       state->str_append_fn(state->user_data, "\\\"", 2);
67       i_prev = i + 1;
68     }
69     else if (c == '\\') {
70       if (i_prev != i) {
71         state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
72       }
73       state->str_append_fn(state->user_data, "\\\\", 2);
74       i_prev = i + 1;
75     }
76     else if (c < 32) {
77       if (i_prev != i) {
78         state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
79       }
80       char buf[5];
81       uint len = (uint)BLI_snprintf_rlen(buf, sizeof(buf), "\\x%02x", c);
82       BLI_assert(len == 4);
83       state->str_append_fn(state->user_data, buf, len);
84       i_prev = i + 1;
85     }
86     i++;
87   }
88   state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
89   if (quote) {
90     state->str_append_fn(state->user_data, "\"", 1);
91   }
92 }
93
94 static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *prop)
95 {
96   /* Note: 'strlen' will be calculated at compile time for literals. */
97 #define STR_APPEND_STR(str) state->str_append_fn(state->user_data, str, (uint)strlen(str))
98
99 #define STR_APPEND_STR_QUOTE(str) idp_str_append_escape(state, str, (uint)strlen(str), true)
100 #define STR_APPEND_STR_LEN_QUOTE(str, str_len) idp_str_append_escape(state, str, str_len, true)
101
102 #define STR_APPEND_FMT(format, ...) \
103   state->str_append_fn( \
104       state->user_data, \
105       state->buf, \
106       (uint)BLI_snprintf_rlen(state->buf, sizeof(state->buf), format, __VA_ARGS__))
107
108   switch (prop->type) {
109     case IDP_STRING: {
110       STR_APPEND_STR_LEN_QUOTE(IDP_String(prop), (uint)MAX2(0, prop->len - 1));
111       break;
112     }
113     case IDP_INT: {
114       STR_APPEND_FMT("%d", IDP_Int(prop));
115       break;
116     }
117     case IDP_FLOAT: {
118       STR_APPEND_FMT("%g", (double)IDP_Float(prop));
119       break;
120     }
121     case IDP_DOUBLE: {
122       STR_APPEND_FMT("%g", IDP_Double(prop));
123       break;
124     }
125     case IDP_ARRAY: {
126       STR_APPEND_STR("[");
127       switch (prop->subtype) {
128         case IDP_INT:
129           for (const int *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
130             if (v != prop->data.pointer) {
131               STR_APPEND_STR(", ");
132             }
133             STR_APPEND_FMT("%d", *v);
134           }
135           break;
136         case IDP_FLOAT:
137           for (const float *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
138             if (v != prop->data.pointer) {
139               STR_APPEND_STR(", ");
140             }
141             STR_APPEND_FMT("%g", (double)*v);
142           }
143           break;
144         case IDP_DOUBLE:
145           for (const double *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
146             if (v != prop->data.pointer) {
147               STR_APPEND_STR(", ");
148             }
149             STR_APPEND_FMT("%g", *v);
150           }
151           break;
152       }
153       STR_APPEND_STR("]");
154       break;
155     }
156     case IDP_IDPARRAY: {
157       STR_APPEND_STR("[");
158       for (const IDProperty *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
159         if (v != prop->data.pointer) {
160           STR_APPEND_STR(", ");
161         }
162         idp_repr_fn_recursive(state, v);
163       }
164       STR_APPEND_STR("]");
165       break;
166     }
167     case IDP_GROUP: {
168       STR_APPEND_STR("{");
169       for (const IDProperty *subprop = prop->data.group.first; subprop; subprop = subprop->next) {
170         if (subprop != prop->data.group.first) {
171           STR_APPEND_STR(", ");
172         }
173         STR_APPEND_STR_QUOTE(subprop->name);
174         STR_APPEND_STR(": ");
175         idp_repr_fn_recursive(state, subprop);
176       }
177       STR_APPEND_STR("}");
178       break;
179     }
180     case IDP_ID: {
181       const ID *id = prop->data.pointer;
182       if (id != NULL) {
183         STR_APPEND_STR("bpy.data.");
184         STR_APPEND_STR(BKE_idcode_to_name_plural(GS(id->name)));
185         STR_APPEND_STR("[");
186         STR_APPEND_STR_QUOTE(id->name + 2);
187         STR_APPEND_STR("]");
188       }
189       else {
190         STR_APPEND_STR("None");
191       }
192       break;
193     }
194     default: {
195       BLI_assert(0);
196       break;
197     }
198   }
199
200 #undef STR_APPEND_STR
201 #undef STR_APPEND_STR_QUOTE
202 #undef STR_APPEND_STR_LEN_QUOTE
203 #undef STR_APPEND_FMT
204 }
205
206 void IDP_repr_fn(const IDProperty *prop,
207                  void (*str_append_fn)(void *user_data, const char *str, uint str_len),
208                  void *user_data)
209 {
210   struct ReprState state = {
211       .str_append_fn = str_append_fn,
212       .user_data = user_data,
213   };
214   idp_repr_fn_recursive(&state, prop);
215 }
216
217 static void repr_str(void *user_data, const char *str, uint len)
218 {
219   BLI_dynstr_nappend(user_data, str, (int)len);
220 }
221
222 char *IDP_reprN(const IDProperty *prop, uint *r_len)
223 {
224   DynStr *ds = BLI_dynstr_new();
225   IDP_repr_fn(prop, repr_str, ds);
226   char *cstring = BLI_dynstr_get_cstring(ds);
227   if (r_len != NULL) {
228     *r_len = (uint)BLI_dynstr_get_len(ds);
229   }
230   BLI_dynstr_free(ds);
231   return cstring;
232 }
233
234 void IDP_print(const IDProperty *prop)
235 {
236   char *repr = IDP_reprN(prop, NULL);
237   printf("IDProperty(%p): ", prop);
238   puts(repr);
239   MEM_freeN(repr);
240 }
241
242 /** \} */