- change max threads from 8 to 64, need to keep an eye on stack memory use here.
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /**
2  * $Id: idprop.c
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 /*
389  replaces all properties with the same name in a destination group from a source group.
390 */
391 void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
392 {
393         IDProperty *loop, *prop;
394         for (prop=src->data.group.first; prop; prop=prop->next) {
395                 for (loop=dest->data.group.first; loop; loop=loop->next) {
396                         if (BSTR_EQ(loop->name, prop->name)) {
397                                 IDProperty *copy = IDP_CopyProperty(prop);
398
399                                 BLI_insertlink(&dest->data.group, loop, copy);
400
401                                 BLI_remlink(&dest->data.group, loop);
402                                 IDP_FreeProperty(loop);
403                                 MEM_freeN(loop);
404                                 break;
405                         }
406                 }
407
408                 /* only add at end if not added yet */
409                 if (loop == NULL) {
410                         IDProperty *copy = IDP_CopyProperty(prop);
411                         dest->len++;
412                         BLI_addtail(&dest->data.group, copy);
413                 }
414         }
415 }
416 /*
417  replaces a property with the same name in a group, or adds 
418  it if the propery doesn't exist.
419 */
420 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
421 {
422         IDProperty *loop;
423         for (loop=group->data.group.first; loop; loop=loop->next) {
424                 if (BSTR_EQ(loop->name, prop->name)) {
425                         BLI_insertlink(&group->data.group, loop, prop);
426                         
427                         BLI_remlink(&group->data.group, loop);
428                         IDP_FreeProperty(loop);
429                         MEM_freeN(loop);                        
430                         return;
431                 }
432         }
433
434         group->len++;
435         BLI_addtail(&group->data.group, prop);
436 }
437
438 /*returns 0 if an id property with the same name exists and it failed,
439   or 1 if it succeeded in adding to the group.*/
440 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
441 {
442         IDProperty *loop;
443         for (loop=group->data.group.first; loop; loop=loop->next) {
444                 if (BSTR_EQ(loop->name, prop->name)) return 0;
445         }
446
447         group->len++;
448         BLI_addtail(&group->data.group, prop);
449
450         return 1;
451 }
452
453 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
454 {
455         IDProperty *loop;
456         for (loop=group->data.group.first; loop; loop=loop->next) {
457                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
458         }
459         
460         group->len++;
461
462         BLI_insertlink(&group->data.group, previous, pnew);
463         return 1;
464 }
465
466 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
467 {
468         group->len--;
469         BLI_remlink(&group->data.group, prop);
470 }
471
472 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
473 {
474         IDProperty *loop;
475         for (loop=prop->data.group.first; loop; loop=loop->next) {
476                 if (strcmp(loop->name, name)==0) return loop;
477         }
478         return NULL;
479 }
480
481 typedef struct IDPIter {
482         void *next;
483         IDProperty *parent;
484 } IDPIter;
485
486 void *IDP_GetGroupIterator(IDProperty *prop)
487 {
488         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
489         iter->next = prop->data.group.first;
490         iter->parent = prop;
491         return (void*) iter;
492 }
493
494 IDProperty *IDP_GroupIterNext(void *vself)
495 {
496         IDPIter *self = (IDPIter*) vself;
497         Link *next = (Link*) self->next;
498         if (self->next == NULL) {
499                 MEM_freeN(self);
500                 return NULL;
501         }
502
503         self->next = next->next;
504         return (void*) next;
505 }
506
507 void IDP_FreeIterBeforeEnd(void *vself)
508 {
509         MEM_freeN(vself);
510 }
511
512 /*Ok, the way things work, Groups free the ID Property structs of their children.
513   This is because all ID Property freeing functions free only direct data (not the ID Property
514   struct itself), but for Groups the child properties *are* considered
515   direct data.*/
516 static void IDP_FreeGroup(IDProperty *prop)
517 {
518         IDProperty *loop;
519         for (loop=prop->data.group.first; loop; loop=loop->next)
520         {
521                 IDP_FreeProperty(loop);
522         }
523         BLI_freelistN(&prop->data.group);
524 }
525
526
527 /*-------- Main Functions --------*/
528 IDProperty *IDP_CopyProperty(IDProperty *prop)
529 {
530         switch (prop->type) {
531                 case IDP_GROUP: return IDP_CopyGroup(prop);
532                 case IDP_STRING: return IDP_CopyString(prop);
533                 case IDP_ARRAY: return IDP_CopyArray(prop);
534                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
535                 default: return idp_generic_copy(prop);
536         }
537 }
538
539 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
540 {
541         if (id->properties) return id->properties;
542         else {
543                 if (create_if_needed) {
544                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
545                         id->properties->type = IDP_GROUP;
546                         /* dont overwite the data's name and type
547                          * some functions might need this if they
548                          * dont have a real ID, should be named elsewhere - Campbell */
549                         /* strcpy(id->name, "top_level_group");*/
550                 }
551                 return id->properties;
552         }
553 }
554
555 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
556 {
557         if(prop1 == NULL && prop2 == NULL)
558                 return 1;
559         else if(prop1 == NULL || prop2 == NULL)
560                 return 0;
561         else if(prop1->type != prop2->type)
562                 return 0;
563
564         if(prop1->type == IDP_INT)
565                 return (IDP_Int(prop1) == IDP_Int(prop2));
566         else if(prop1->type == IDP_FLOAT)
567                 return (IDP_Float(prop1) == IDP_Float(prop2));
568         else if(prop1->type == IDP_DOUBLE)
569                 return (IDP_Double(prop1) == IDP_Double(prop2));
570         else if(prop1->type == IDP_STRING)
571                 return BSTR_EQ(IDP_String(prop1), IDP_String(prop2));
572         else if(prop1->type == IDP_ARRAY) {
573                 if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
574                         return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
575                 else
576                         return 0;
577         }
578         else if(prop1->type == IDP_GROUP) {
579                 IDProperty *link1, *link2;
580
581                 if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
582                         return 0;
583
584                 for(link1=prop1->data.group.first; link1; link1=link1->next) {
585                         link2= IDP_GetPropertyFromGroup(prop2, link1->name);
586
587                         if(!IDP_EqualsProperties(link1, link2))
588                                 return 0;
589                 }
590
591                 return 1;
592         }
593         else if(prop1->type == IDP_IDPARRAY) {
594                 IDProperty *array1= IDP_IDPArray(prop1);
595                 IDProperty *array2= IDP_IDPArray(prop2);
596                 int i;
597
598                 if(prop1->len != prop2->len)
599                         return 0;
600                 
601                 for(i=0; i<prop1->len; i++)
602                         if(!IDP_EqualsProperties(&array1[i], &array2[i]))
603                                 return 0;
604         }
605         
606         return 1;
607 }
608
609 IDProperty *IDP_New(int type, IDPropertyTemplate val, const char *name)
610 {
611         IDProperty *prop=NULL;
612
613         switch (type) {
614                 case IDP_INT:
615                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
616                         prop->data.val = val.i;
617                         break;
618                 case IDP_FLOAT:
619                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
620                         *(float*)&prop->data.val = val.f;
621                         break;
622                 case IDP_DOUBLE:
623                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
624                         *(double*)&prop->data.val = val.d;
625                         break;          
626                 case IDP_ARRAY:
627                 {
628                         /*for now, we only support float and int and double arrays*/
629                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE || val.array.type == IDP_GROUP) {
630                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
631                                 prop->subtype = val.array.type;
632                                 if (val.array.len)
633                                         prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
634                                 prop->len = prop->totallen = val.array.len;
635                                 break;
636                         } else {
637                                 return NULL;
638                         }
639                 }
640                 case IDP_STRING:
641                 {
642                         char *st = val.str;
643                         int stlen;
644
645                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
646                         if (st == NULL) {
647                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
648                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
649                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
650                         } else {
651                                 stlen = strlen(st) + 1;
652                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
653                                 prop->len = prop->totallen = stlen;
654                                 strcpy(prop->data.pointer, st);
655                         }
656                         break;
657                 }
658                 case IDP_GROUP:
659                 {
660                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
661                         /* heh I think all needed values are set properly by calloc anyway :) */
662                         break;
663                 }
664                 default:
665                 {
666                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
667                         break;
668                 }
669         }
670
671         prop->type = type;
672         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
673         
674         /*security null byte*/
675         prop->name[MAX_IDPROP_NAME-1] = 0;
676         
677         return prop;
678 }
679
680 /*NOTE: this will free all child properties including list arrays and groups!
681   Also, note that this does NOT unlink anything!  Plus it doesn't free
682   the actual IDProperty struct either.*/
683 void IDP_FreeProperty(IDProperty *prop)
684 {
685         switch (prop->type) {
686                 case IDP_ARRAY:
687                         IDP_FreeArray(prop);
688                         break;
689                 case IDP_STRING:
690                         IDP_FreeString(prop);
691                         break;
692                 case IDP_GROUP:
693                         IDP_FreeGroup(prop);
694                         break;
695                 case IDP_IDPARRAY:
696                         IDP_FreeIDPArray(prop);
697                         break;
698         }
699 }
700
701 /*Unlinks any IDProperty<->ID linkage that might be going on.
702   note: currently unused.*/
703 void IDP_UnlinkProperty(IDProperty *prop)
704 {
705         switch (prop->type) {
706                 case IDP_ID:
707                         IDP_UnlinkID(prop);
708         }
709 }