Fix syntax for ID keyword.
[blender-staging.git] / source / blender / blenkernel / intern / idprop.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27  
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "DNA_listBase.h"
33 #include "DNA_ID.h"
34
35 #include "BKE_idprop.h"
36 #include "BKE_global.h"
37 #include "BKE_library.h"
38 #include "BKE_utildefines.h"
39
40 #include "BLI_blenlib.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
45
46 /* IDPropertyTemplate is a union in DNA_ID.h */
47
48 /*local size table.*/
49 static char idp_size_table[] = {
50         1, /*strings*/
51         sizeof(int),
52         sizeof(float),
53         sizeof(float)*3, /*Vector type, deprecated*/
54         sizeof(float)*16, /*Matrix type, deprecated*/
55         0, /*arrays don't have a fixed size*/
56         sizeof(ListBase), /*Group type*/
57         sizeof(void*),
58         sizeof(double)
59 };
60
61 /* ------------Property Array Type ----------- */
62 #define GETPROP(prop, i) (((IDProperty*)(prop)->data.pointer)+(i))
63
64 /* --------- property array type -------------*/
65
66 /*note: as a start to move away from the stupid IDP_New function, this type
67   has it's own allocation function.*/
68 IDProperty *IDP_NewIDPArray(const char *name)
69 {
70         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
71         prop->type = IDP_IDPARRAY;
72         prop->len = 0;
73         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
74         
75         return prop;
76 }
77
78 IDProperty *IDP_CopyIDPArray(IDProperty *array)
79 {
80         IDProperty *narray = MEM_dupallocN(array), *tmp;
81         int i;
82         
83         narray->data.pointer = MEM_dupallocN(array->data.pointer);
84         for (i=0; i<narray->len; i++) {
85                 /*ok, the copy functions always allocate a new structure,
86                   which doesn't work here.  instead, simply copy the
87                   contents of the new structure into the array cell,
88                   then free it.  this makes for more maintainable
89                   code than simply reimplementing the copy functions
90                   in this loop.*/
91                 tmp = IDP_CopyProperty(GETPROP(narray, i));
92                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
93                 MEM_freeN(tmp);
94         }
95         
96         return narray;
97 }
98
99 void IDP_FreeIDPArray(IDProperty *prop)
100 {
101         int i;
102         
103         for (i=0; i<prop->len; i++)
104                 IDP_FreeProperty(GETPROP(prop, i));
105
106         if(prop->data.pointer)
107                 MEM_freeN(prop->data.pointer);
108 }
109
110 /*shallow copies item*/
111 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
112 {
113         IDProperty *old = GETPROP(prop, index);
114         if (index >= prop->len || index < 0) return;
115         if (item != old) IDP_FreeProperty(old);
116         
117         memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
118 }
119
120 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
121 {
122         return GETPROP(prop, index);
123 }
124
125 IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
126 {
127         IDP_ResizeIDPArray(prop, prop->len+1);
128         IDP_SetIndexArray(prop, prop->len-1, item);
129         return item;
130 }
131
132 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
133 {
134         void *newarr;
135         int newsize=newlen;
136
137         /*first check if the array buffer size has room*/
138         /*if newlen is 200 chars less then totallen, reallocate anyway*/
139         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
140                 int i;
141
142                 for(i=newlen; i<prop->len; i++)
143                         IDP_FreeProperty(GETPROP(prop, i));
144
145                 prop->len = newlen;
146                 return;
147         }
148
149         /* - Note: This code comes from python, here's the corrusponding comment. - */
150         /* This over-allocates proportional to the list size, making room
151          * for additional growth.  The over-allocation is mild, but is
152          * enough to give linear-time amortized behavior over a long
153          * sequence of appends() in the presence of a poorly-performing
154          * system realloc().
155          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
156          */
157         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
158
159         newarr = MEM_callocN(sizeof(IDProperty)*newsize, "idproperty array resized");
160         if (newlen >= prop->len) {
161                 /* newlen is bigger*/
162                 memcpy(newarr, prop->data.pointer, prop->len*sizeof(IDProperty));
163         }
164         else {
165                 int i;
166                 /* newlen is smaller*/
167                 for (i=newlen; i<prop->len; i++) {
168                         IDP_FreeProperty(GETPROP(prop, i));
169                 }
170                 memcpy(newarr, prop->data.pointer, newlen*sizeof(IDProperty));
171         }
172
173         if(prop->data.pointer)
174                 MEM_freeN(prop->data.pointer);
175         prop->data.pointer = newarr;
176         prop->len = newlen;
177         prop->totallen = newsize;
178 }
179
180 /* ----------- Numerical Array Type ----------- */
181 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
182 {
183         if(prop->subtype != IDP_GROUP)
184                 return;
185
186         if(newlen >= prop->len) {
187                 /* bigger */
188                 IDProperty **array= newarr;
189                 IDPropertyTemplate val;
190                 int a;
191
192                 for(a=prop->len; a<newlen; a++) {
193                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
194                         array[a]= IDP_New(IDP_GROUP, val, "IDP_ResizeArray group");
195                 }
196         }
197         else {
198                 /* smaller */
199                 IDProperty **array= prop->data.pointer;
200                 int a;
201
202                 for(a=newlen; a<prop->len; a++) {
203                         IDP_FreeProperty(array[a]);
204                         MEM_freeN(array[a]);
205                 }
206         }
207 }
208
209 /*this function works for strings too!*/
210 void IDP_ResizeArray(IDProperty *prop, int newlen)
211 {
212         void *newarr;
213         int newsize=newlen;
214
215         /*first check if the array buffer size has room*/
216         /*if newlen is 200 chars less then totallen, reallocate anyway*/
217         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
218                 idp_resize_group_array(prop, newlen, prop->data.pointer);
219                 prop->len = newlen;
220                 return;
221         }
222
223         /* - Note: This code comes from python, here's the corrusponding comment. - */
224         /* This over-allocates proportional to the list size, making room
225          * for additional growth.  The over-allocation is mild, but is
226          * enough to give linear-time amortized behavior over a long
227          * sequence of appends() in the presence of a poorly-performing
228          * system realloc().
229          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
230          */
231         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
232
233         newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized");
234         if (newlen >= prop->len) {
235                 /* newlen is bigger*/
236                 memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]);
237                 idp_resize_group_array(prop, newlen, newarr);
238         }
239         else {
240                 /* newlen is smaller*/
241                 idp_resize_group_array(prop, newlen, newarr);
242                 memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[(int)prop->subtype]);
243         }
244
245         MEM_freeN(prop->data.pointer);
246         prop->data.pointer = newarr;
247         prop->len = newlen;
248         prop->totallen = newsize;
249 }
250
251 void IDP_FreeArray(IDProperty *prop)
252 {
253         if (prop->data.pointer) {
254                 idp_resize_group_array(prop, 0, NULL);
255                 MEM_freeN(prop->data.pointer);
256         }
257 }
258
259
260  static IDProperty *idp_generic_copy(IDProperty *prop)
261  {
262         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
263
264         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
265         newp->type = prop->type;
266         newp->flag = prop->flag;
267         newp->data.val = prop->data.val;
268         newp->data.val2 = prop->data.val2;
269
270         return newp;
271  }
272
273 IDProperty *IDP_CopyArray(IDProperty *prop)
274 {
275         IDProperty *newp = idp_generic_copy(prop);
276
277         if (prop->data.pointer) {
278                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
279
280                 if(prop->type == IDP_GROUP) {
281                         IDProperty **array= newp->data.pointer;
282                         int a;
283
284                         for(a=0; a<prop->len; a++)
285                                 array[a]= IDP_CopyProperty(array[a]);
286                 }
287         }
288         newp->len = prop->len;
289         newp->subtype = prop->subtype;
290         newp->totallen = prop->totallen;
291
292         return newp;
293 }
294
295 /*taken from readfile.c*/
296 #define SWITCH_LONGINT(a) { \
297     char s_i, *p_i; \
298     p_i= (char *)&(a);  \
299     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
300     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
301     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
302     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
303
304
305
306 /* ---------- String Type ------------ */
307 IDProperty *IDP_CopyString(IDProperty *prop)
308 {
309         IDProperty *newp = idp_generic_copy(prop);
310
311         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
312         newp->len = prop->len;
313         newp->subtype = prop->subtype;
314         newp->totallen = prop->totallen;
315
316         return newp;
317 }
318
319
320 void IDP_AssignString(IDProperty *prop, char *st)
321 {
322         int stlen;
323
324         stlen = strlen(st);
325
326         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
327         strcpy(prop->data.pointer, st);
328 }
329
330 void IDP_ConcatStringC(IDProperty *prop, char *st)
331 {
332         int newlen;
333
334         newlen = prop->len + strlen(st);
335         /*we have to remember that prop->len includes the null byte for strings.
336          so there's no need to add +1 to the resize function.*/
337         IDP_ResizeArray(prop, newlen);
338         strcat(prop->data.pointer, st);
339 }
340
341 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
342 {
343         int newlen;
344
345         /*since ->len for strings includes the NULL byte, we have to subtract one or
346          we'll get an extra null byte after each concatination operation.*/
347         newlen = str1->len + append->len - 1;
348         IDP_ResizeArray(str1, newlen);
349         strcat(str1->data.pointer, append->data.pointer);
350 }
351
352 void IDP_FreeString(IDProperty *prop)
353 {
354         if(prop->data.pointer)
355                 MEM_freeN(prop->data.pointer);
356 }
357
358
359 /*-------- ID Type, not in use yet -------*/
360
361 void IDP_LinkID(IDProperty *prop, ID *id)
362 {
363         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
364         prop->data.pointer = id;
365         id_us_plus(id);
366 }
367
368 void IDP_UnlinkID(IDProperty *prop)
369 {
370         ((ID*)prop->data.pointer)->us--;
371 }
372
373 /*-------- Group Functions -------*/
374
375 /*checks if a property with the same name as prop exists, and if so replaces it.*/
376 IDProperty *IDP_CopyGroup(IDProperty *prop)
377 {
378         IDProperty *newp = idp_generic_copy(prop), *link;
379         newp->len = prop->len;
380         
381         for (link=prop->data.group.first; link; link=link->next) {
382                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
383         }
384
385         return newp;
386 }
387
388 /* use for syncing proxies.
389  * When values name and types match, copy the values, else ignore */
390 void IDP_SyncGroupValues(IDProperty *dest, IDProperty *src)
391 {
392         IDProperty *loop, *prop;
393         for (prop=src->data.group.first; prop; prop=prop->next) {
394                 for (loop=dest->data.group.first; loop; loop=loop->next) {
395                         if (BSTR_EQ(loop->name, prop->name)) {
396                                 int copy_done= 0;
397
398                                 if(prop->type==loop->type) {
399
400                                         switch (prop->type) {
401                                                 case IDP_INT:
402                                                 case IDP_FLOAT:
403                                                 case IDP_DOUBLE:
404                                                         loop->data= prop->data;
405                                                         copy_done= 1;
406                                                         break;
407                                                 case IDP_GROUP:
408                                                         IDP_SyncGroupValues(loop, prop);
409                                                         copy_done= 1;
410                                                         break;
411                                                 default:
412                                                 {
413                                                         IDProperty *tmp= loop;
414                                                         IDProperty *copy= IDP_CopyProperty(prop);
415
416                                                         BLI_insertlinkafter(&dest->data.group, loop, copy);
417                                                         BLI_remlink(&dest->data.group, tmp);
418                                                         loop = copy;
419
420                                                         IDP_FreeProperty(tmp);
421                                                         MEM_freeN(tmp);
422                                                 }
423                                         }
424                                 }
425                                 break;
426                         }
427                 }
428         }
429 }
430
431 /*
432  replaces all properties with the same name in a destination group from a source group.
433 */
434 void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
435 {
436         IDProperty *loop, *prop;
437         for (prop=src->data.group.first; prop; prop=prop->next) {
438                 for (loop=dest->data.group.first; loop; loop=loop->next) {
439                         if (BSTR_EQ(loop->name, prop->name)) {
440                                 IDProperty *copy = IDP_CopyProperty(prop);
441
442                                 BLI_insertlink(&dest->data.group, loop, copy);
443
444                                 BLI_remlink(&dest->data.group, loop);
445                                 IDP_FreeProperty(loop);
446                                 MEM_freeN(loop);
447                                 break;
448                         }
449                 }
450
451                 /* only add at end if not added yet */
452                 if (loop == NULL) {
453                         IDProperty *copy = IDP_CopyProperty(prop);
454                         dest->len++;
455                         BLI_addtail(&dest->data.group, copy);
456                 }
457         }
458 }
459 /*
460  replaces a property with the same name in a group, or adds 
461  it if the propery doesn't exist.
462 */
463 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
464 {
465         IDProperty *loop;
466         for (loop=group->data.group.first; loop; loop=loop->next) {
467                 if (BSTR_EQ(loop->name, prop->name)) {
468                         BLI_insertlink(&group->data.group, loop, prop);
469                         
470                         BLI_remlink(&group->data.group, loop);
471                         IDP_FreeProperty(loop);
472                         MEM_freeN(loop);                        
473                         return;
474                 }
475         }
476
477         group->len++;
478         BLI_addtail(&group->data.group, prop);
479 }
480
481 /*returns 0 if an id property with the same name exists and it failed,
482   or 1 if it succeeded in adding to the group.*/
483 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
484 {
485         IDProperty *loop;
486         for (loop=group->data.group.first; loop; loop=loop->next) {
487                 if (BSTR_EQ(loop->name, prop->name)) return 0;
488         }
489
490         group->len++;
491         BLI_addtail(&group->data.group, prop);
492
493         return 1;
494 }
495
496 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
497 {
498         IDProperty *loop;
499         for (loop=group->data.group.first; loop; loop=loop->next) {
500                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
501         }
502         
503         group->len++;
504
505         BLI_insertlink(&group->data.group, previous, pnew);
506         return 1;
507 }
508
509 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
510 {
511         group->len--;
512         BLI_remlink(&group->data.group, prop);
513 }
514
515 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
516 {
517         IDProperty *loop;
518         for (loop=prop->data.group.first; loop; loop=loop->next) {
519                 if (strcmp(loop->name, name)==0) return loop;
520         }
521         return NULL;
522 }
523
524 typedef struct IDPIter {
525         void *next;
526         IDProperty *parent;
527 } IDPIter;
528
529 void *IDP_GetGroupIterator(IDProperty *prop)
530 {
531         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
532         iter->next = prop->data.group.first;
533         iter->parent = prop;
534         return (void*) iter;
535 }
536
537 IDProperty *IDP_GroupIterNext(void *vself)
538 {
539         IDPIter *self = (IDPIter*) vself;
540         Link *next = (Link*) self->next;
541         if (self->next == NULL) {
542                 MEM_freeN(self);
543                 return NULL;
544         }
545
546         self->next = next->next;
547         return (void*) next;
548 }
549
550 void IDP_FreeIterBeforeEnd(void *vself)
551 {
552         MEM_freeN(vself);
553 }
554
555 /*Ok, the way things work, Groups free the ID Property structs of their children.
556   This is because all ID Property freeing functions free only direct data (not the ID Property
557   struct itself), but for Groups the child properties *are* considered
558   direct data.*/
559 static void IDP_FreeGroup(IDProperty *prop)
560 {
561         IDProperty *loop;
562         for (loop=prop->data.group.first; loop; loop=loop->next)
563         {
564                 IDP_FreeProperty(loop);
565         }
566         BLI_freelistN(&prop->data.group);
567 }
568
569
570 /*-------- Main Functions --------*/
571 IDProperty *IDP_CopyProperty(IDProperty *prop)
572 {
573         switch (prop->type) {
574                 case IDP_GROUP: return IDP_CopyGroup(prop);
575                 case IDP_STRING: return IDP_CopyString(prop);
576                 case IDP_ARRAY: return IDP_CopyArray(prop);
577                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
578                 default: return idp_generic_copy(prop);
579         }
580 }
581
582 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
583 {
584         if (id->properties) return id->properties;
585         else {
586                 if (create_if_needed) {
587                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
588                         id->properties->type = IDP_GROUP;
589                         /* dont overwite the data's name and type
590                          * some functions might need this if they
591                          * dont have a real ID, should be named elsewhere - Campbell */
592                         /* strcpy(id->name, "top_level_group");*/
593                 }
594                 return id->properties;
595         }
596 }
597
598 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
599 {
600         if(prop1 == NULL && prop2 == NULL)
601                 return 1;
602         else if(prop1 == NULL || prop2 == NULL)
603                 return 0;
604         else if(prop1->type != prop2->type)
605                 return 0;
606
607         if(prop1->type == IDP_INT)
608                 return (IDP_Int(prop1) == IDP_Int(prop2));
609         else if(prop1->type == IDP_FLOAT)
610                 return (IDP_Float(prop1) == IDP_Float(prop2));
611         else if(prop1->type == IDP_DOUBLE)
612                 return (IDP_Double(prop1) == IDP_Double(prop2));
613         else if(prop1->type == IDP_STRING)
614                 return BSTR_EQ(IDP_String(prop1), IDP_String(prop2));
615         else if(prop1->type == IDP_ARRAY) {
616                 if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
617                         return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
618                 else
619                         return 0;
620         }
621         else if(prop1->type == IDP_GROUP) {
622                 IDProperty *link1, *link2;
623
624                 if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
625                         return 0;
626
627                 for(link1=prop1->data.group.first; link1; link1=link1->next) {
628                         link2= IDP_GetPropertyFromGroup(prop2, link1->name);
629
630                         if(!IDP_EqualsProperties(link1, link2))
631                                 return 0;
632                 }
633
634                 return 1;
635         }
636         else if(prop1->type == IDP_IDPARRAY) {
637                 IDProperty *array1= IDP_IDPArray(prop1);
638                 IDProperty *array2= IDP_IDPArray(prop2);
639                 int i;
640
641                 if(prop1->len != prop2->len)
642                         return 0;
643                 
644                 for(i=0; i<prop1->len; i++)
645                         if(!IDP_EqualsProperties(&array1[i], &array2[i]))
646                                 return 0;
647         }
648         
649         return 1;
650 }
651
652 IDProperty *IDP_New(int type, IDPropertyTemplate val, const char *name)
653 {
654         IDProperty *prop=NULL;
655
656         switch (type) {
657                 case IDP_INT:
658                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
659                         prop->data.val = val.i;
660                         break;
661                 case IDP_FLOAT:
662                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
663                         *(float*)&prop->data.val = val.f;
664                         break;
665                 case IDP_DOUBLE:
666                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
667                         *(double*)&prop->data.val = val.d;
668                         break;          
669                 case IDP_ARRAY:
670                 {
671                         /*for now, we only support float and int and double arrays*/
672                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE || val.array.type == IDP_GROUP) {
673                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
674                                 prop->subtype = val.array.type;
675                                 if (val.array.len)
676                                         prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
677                                 prop->len = prop->totallen = val.array.len;
678                                 break;
679                         } else {
680                                 return NULL;
681                         }
682                 }
683                 case IDP_STRING:
684                 {
685                         char *st = val.str;
686                         int stlen;
687
688                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
689                         if (st == NULL) {
690                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
691                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
692                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
693                         } else {
694                                 stlen = strlen(st) + 1;
695                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
696                                 prop->len = prop->totallen = stlen;
697                                 strcpy(prop->data.pointer, st);
698                         }
699                         break;
700                 }
701                 case IDP_GROUP:
702                 {
703                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
704                         /* heh I think all needed values are set properly by calloc anyway :) */
705                         break;
706                 }
707                 default:
708                 {
709                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
710                         break;
711                 }
712         }
713
714         prop->type = type;
715         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
716         
717         /*security null byte*/
718         prop->name[MAX_IDPROP_NAME-1] = 0;
719         
720         return prop;
721 }
722
723 /*NOTE: this will free all child properties including list arrays and groups!
724   Also, note that this does NOT unlink anything!  Plus it doesn't free
725   the actual IDProperty struct either.*/
726 void IDP_FreeProperty(IDProperty *prop)
727 {
728         switch (prop->type) {
729                 case IDP_ARRAY:
730                         IDP_FreeArray(prop);
731                         break;
732                 case IDP_STRING:
733                         IDP_FreeString(prop);
734                         break;
735                 case IDP_GROUP:
736                         IDP_FreeGroup(prop);
737                         break;
738                 case IDP_IDPARRAY:
739                         IDP_FreeIDPArray(prop);
740                         break;
741         }
742 }
743
744 /*Unlinks any IDProperty<->ID linkage that might be going on.
745   note: currently unused.*/
746 void IDP_UnlinkProperty(IDProperty *prop)
747 {
748         switch (prop->type) {
749                 case IDP_ID:
750                         IDP_UnlinkID(prop);
751         }
752 }