1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/transformations.h"
24 #include "libpspp/str.h"
26 #include "gl/xalloc.h"
28 /* A single transformation. */
31 /* Offset to add to EXECUTE's return value, if it returns a
32 transformation index. Normally 0 but set to the starting
33 index of a spliced chain after splicing. */
35 trns_finalize_func
*finalize
; /* Finalize proc. */
36 trns_proc_func
*execute
; /* Executes the transformation. */
37 trns_free_func
*free
; /* Garbage collector proc. */
38 void *aux
; /* Auxiliary data. */
41 /* A chain of transformations. */
44 struct transformation
*trns
; /* Array of transformations. */
45 size_t trns_cnt
; /* Number of transformations. */
46 size_t trns_cap
; /* Allocated capacity. */
47 bool finalized
; /* Finalize functions called? */
50 /* Allocates and returns a new transformation chain. */
52 trns_chain_create (void)
54 struct trns_chain
*chain
= xmalloc (sizeof *chain
);
58 chain
->finalized
= false;
62 /* Finalizes all the un-finalized transformations in CHAIN.
63 Any given transformation is only finalized once. */
65 trns_chain_finalize (struct trns_chain
*chain
)
67 while (!chain
->finalized
)
71 chain
->finalized
= true;
72 for (i
= 0; i
< chain
->trns_cnt
; i
++)
74 struct transformation
*trns
= &chain
->trns
[i
];
75 trns_finalize_func
*finalize
= trns
->finalize
;
77 trns
->finalize
= NULL
;
84 /* Destroys CHAIN, finalizing it in the process if it has not
85 already been finalized. */
87 trns_chain_destroy (struct trns_chain
*chain
)
95 /* Needed to ensure that the control stack gets cleared. */
96 trns_chain_finalize (chain
);
98 for (i
= 0; i
< chain
->trns_cnt
; i
++)
100 struct transformation
*trns
= &chain
->trns
[i
];
101 if (trns
->free
!= NULL
)
102 ok
= trns
->free (trns
->aux
) && ok
;
111 /* Returns true if CHAIN contains any transformations,
114 trns_chain_is_empty (const struct trns_chain
*chain
)
116 return chain
->trns_cnt
== 0;
119 /* Adds a transformation to CHAIN with finalize function
120 FINALIZE, execute function EXECUTE, free function FREE, and
121 auxiliary data AUX. */
123 trns_chain_append (struct trns_chain
*chain
, trns_finalize_func
*finalize
,
124 trns_proc_func
*execute
, trns_free_func
*free
,
127 struct transformation
*trns
;
129 chain
->finalized
= false;
131 if (chain
->trns_cnt
== chain
->trns_cap
)
132 chain
->trns
= x2nrealloc (chain
->trns
, &chain
->trns_cap
,
133 sizeof *chain
->trns
);
135 trns
= &chain
->trns
[chain
->trns_cnt
++];
137 trns
->finalize
= finalize
;
138 trns
->execute
= execute
;
143 /* Appends the transformations in SRC to those in DST,
145 Both DST and SRC must already be finalized. */
147 trns_chain_splice (struct trns_chain
*dst
, struct trns_chain
*src
)
151 assert (dst
->finalized
);
152 assert (src
->finalized
);
154 if (dst
->trns_cnt
+ src
->trns_cnt
> dst
->trns_cap
)
156 dst
->trns_cap
= dst
->trns_cnt
+ src
->trns_cnt
;
157 dst
->trns
= xnrealloc (dst
->trns
, dst
->trns_cap
, sizeof *dst
->trns
);
160 for (i
= 0; i
< src
->trns_cnt
; i
++)
162 struct transformation
*d
= &dst
->trns
[i
+ dst
->trns_cnt
];
163 const struct transformation
*s
= &src
->trns
[i
];
165 d
->idx_ofs
+= src
->trns_cnt
;
167 dst
->trns_cnt
+= src
->trns_cnt
;
170 trns_chain_destroy (src
);
173 /* Returns the index that a transformation execution function may
174 return to "jump" to the next transformation to be added. */
176 trns_chain_next (struct trns_chain
*chain
)
178 return chain
->trns_cnt
;
181 /* Executes the given CHAIN of transformations on *C,
182 passing CASE_NR as the case number.
183 *C may be replaced by a new case.
184 Returns the result code that caused the transformations to
185 terminate, or TRNS_CONTINUE if the transformations finished
186 due to "falling off the end" of the set of transformations. */
188 trns_chain_execute (const struct trns_chain
*chain
, enum trns_result start
,
189 struct ccase
**c
, casenumber case_nr
)
193 assert (chain
->finalized
);
194 for (i
= start
< 0 ? 0 : start
; i
< chain
->trns_cnt
; )
196 struct transformation
*trns
= &chain
->trns
[i
];
197 int retval
= trns
->execute (trns
->aux
, c
, case_nr
);
198 if (retval
== TRNS_CONTINUE
)
200 else if (retval
>= 0)
201 i
= retval
+ trns
->idx_ofs
;
203 return retval
== TRNS_END_CASE
? i
+ 1 : retval
;
206 return TRNS_CONTINUE
;