Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Sorted array routines for CUPS. |
3 | | * |
4 | | * Copyright 2007-2014 by Apple Inc. |
5 | | * Copyright 1997-2007 by Easy Software Products. |
6 | | * |
7 | | * These coded instructions, statements, and computer programs are the |
8 | | * property of Apple Inc. and are protected by Federal copyright |
9 | | * law. Distribution and use rights are outlined in the file "LICENSE.txt" |
10 | | * which should have been included with this file. If this file is |
11 | | * missing or damaged, see the license at "http://www.cups.org/". |
12 | | * |
13 | | * This file is subject to the Apple OS-Developed Software exception. |
14 | | */ |
15 | | |
16 | | /* |
17 | | * Include necessary headers... |
18 | | */ |
19 | | |
20 | | #include <cups/cups.h> |
21 | | #include "string-private.h" |
22 | | #include "debug-private.h" |
23 | | #include "array-private.h" |
24 | | |
25 | | |
26 | | /* |
27 | | * Limits... |
28 | | */ |
29 | | |
30 | 0 | #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/ |
31 | | |
32 | | |
33 | | /* |
34 | | * Types and structures... |
35 | | */ |
36 | | |
37 | | struct _cups_array_s /**** CUPS array structure ****/ |
38 | | { |
39 | | /* |
40 | | * The current implementation uses an insertion sort into an array of |
41 | | * sorted pointers. We leave the array type private/opaque so that we |
42 | | * can change the underlying implementation without affecting the users |
43 | | * of this API. |
44 | | */ |
45 | | |
46 | | int num_elements, /* Number of array elements */ |
47 | | alloc_elements, /* Allocated array elements */ |
48 | | current, /* Current element */ |
49 | | insert, /* Last inserted element */ |
50 | | unique, /* Are all elements unique? */ |
51 | | num_saved, /* Number of saved elements */ |
52 | | saved[_CUPS_MAXSAVE]; |
53 | | /* Saved elements */ |
54 | | void **elements; /* Array elements */ |
55 | | cups_array_func_t compare; /* Element comparison function */ |
56 | | void *data; /* User data passed to compare */ |
57 | | cups_ahash_func_t hashfunc; /* Hash function */ |
58 | | int hashsize, /* Size of hash */ |
59 | | *hash; /* Hash array */ |
60 | | cups_acopy_func_t copyfunc; /* Copy function */ |
61 | | cups_afree_func_t freefunc; /* Free function */ |
62 | | }; |
63 | | |
64 | | |
65 | | /* |
66 | | * Local functions... |
67 | | */ |
68 | | |
69 | | static int cups_array_add(cups_array_t *a, void *e, int insert); |
70 | | static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff); |
71 | | |
72 | | |
73 | | /* |
74 | | * 'cupsArrayAdd()' - Add an element to the array. |
75 | | * |
76 | | * When adding an element to a sorted array, non-unique elements are |
77 | | * appended at the end of the run of identical elements. For unsorted arrays, |
78 | | * the element is appended to the end of the array. |
79 | | * |
80 | | * @since CUPS 1.2/macOS 10.5@ |
81 | | */ |
82 | | |
83 | | int /* O - 1 on success, 0 on failure */ |
84 | | cupsArrayAdd(cups_array_t *a, /* I - Array */ |
85 | | void *e) /* I - Element */ |
86 | 0 | { |
87 | 0 | DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", (void *)a, e)); |
88 | | |
89 | | /* |
90 | | * Range check input... |
91 | | */ |
92 | |
|
93 | 0 | if (!a || !e) |
94 | 0 | { |
95 | 0 | DEBUG_puts("3cupsArrayAdd: returning 0"); |
96 | 0 | return (0); |
97 | 0 | } |
98 | | |
99 | | /* |
100 | | * Append the element... |
101 | | */ |
102 | | |
103 | 0 | return (cups_array_add(a, e, 0)); |
104 | 0 | } |
105 | | |
106 | | |
107 | | /* |
108 | | * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array. |
109 | | * |
110 | | * Note: The array MUST be created using the @link _cupsArrayNewStrings@ |
111 | | * function. Duplicate strings are NOT added. If the string pointer "s" is NULL |
112 | | * or the empty string, no strings are added to the array. |
113 | | */ |
114 | | |
115 | | int /* O - 1 on success, 0 on failure */ |
116 | | _cupsArrayAddStrings(cups_array_t *a, /* I - Array */ |
117 | | const char *s, /* I - Delimited strings or NULL */ |
118 | | char delim)/* I - Delimiter character */ |
119 | 0 | { |
120 | 0 | char *buffer, /* Copy of string */ |
121 | 0 | *start, /* Start of string */ |
122 | 0 | *end; /* End of string */ |
123 | 0 | int status = 1; /* Status of add */ |
124 | | |
125 | |
|
126 | 0 | DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", (void *)a, s, delim)); |
127 | |
|
128 | 0 | if (!a || !s || !*s) |
129 | 0 | { |
130 | 0 | DEBUG_puts("1_cupsArrayAddStrings: Returning 0"); |
131 | 0 | return (0); |
132 | 0 | } |
133 | | |
134 | 0 | if (delim == ' ') |
135 | 0 | { |
136 | | /* |
137 | | * Skip leading whitespace... |
138 | | */ |
139 | |
|
140 | 0 | DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace."); |
141 | |
|
142 | 0 | while (*s && isspace(*s & 255)) |
143 | 0 | s ++; |
144 | |
|
145 | 0 | DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s)); |
146 | 0 | } |
147 | |
|
148 | 0 | if (!strchr(s, delim) && |
149 | 0 | (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n')))) |
150 | 0 | { |
151 | | /* |
152 | | * String doesn't contain a delimiter, so add it as a single value... |
153 | | */ |
154 | |
|
155 | 0 | DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single " |
156 | 0 | "value."); |
157 | |
|
158 | 0 | if (!cupsArrayFind(a, (void *)s)) |
159 | 0 | status = cupsArrayAdd(a, (void *)s); |
160 | 0 | } |
161 | 0 | else if ((buffer = strdup(s)) == NULL) |
162 | 0 | { |
163 | 0 | DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string."); |
164 | 0 | status = 0; |
165 | 0 | } |
166 | 0 | else |
167 | 0 | { |
168 | 0 | for (start = end = buffer; *end; start = end) |
169 | 0 | { |
170 | | /* |
171 | | * Find the end of the current delimited string and see if we need to add |
172 | | * it... |
173 | | */ |
174 | |
|
175 | 0 | if (delim == ' ') |
176 | 0 | { |
177 | 0 | while (*end && !isspace(*end & 255)) |
178 | 0 | end ++; |
179 | 0 | while (*end && isspace(*end & 255)) |
180 | 0 | *end++ = '\0'; |
181 | 0 | } |
182 | 0 | else if ((end = strchr(start, delim)) != NULL) |
183 | 0 | *end++ = '\0'; |
184 | 0 | else |
185 | 0 | end = start + strlen(start); |
186 | |
|
187 | 0 | DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start, |
188 | 0 | end)); |
189 | |
|
190 | 0 | if (!cupsArrayFind(a, start)) |
191 | 0 | status &= cupsArrayAdd(a, start); |
192 | 0 | } |
193 | |
|
194 | 0 | free(buffer); |
195 | 0 | } |
196 | |
|
197 | 0 | DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status)); |
198 | |
|
199 | 0 | return (status); |
200 | 0 | } |
201 | | |
202 | | |
203 | | /* |
204 | | * 'cupsArrayClear()' - Clear the array. |
205 | | * |
206 | | * This function is equivalent to removing all elements in the array. |
207 | | * The caller is responsible for freeing the memory used by the |
208 | | * elements themselves. |
209 | | * |
210 | | * @since CUPS 1.2/macOS 10.5@ |
211 | | */ |
212 | | |
213 | | void |
214 | | cupsArrayClear(cups_array_t *a) /* I - Array */ |
215 | 0 | { |
216 | | /* |
217 | | * Range check input... |
218 | | */ |
219 | |
|
220 | 0 | if (!a) |
221 | 0 | return; |
222 | | |
223 | | /* |
224 | | * Free the existing elements as needed.. |
225 | | */ |
226 | | |
227 | 0 | if (a->freefunc) |
228 | 0 | { |
229 | 0 | int i; /* Looping var */ |
230 | 0 | void **e; /* Current element */ |
231 | |
|
232 | 0 | for (i = a->num_elements, e = a->elements; i > 0; i --, e ++) |
233 | 0 | (a->freefunc)(*e, a->data); |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * Set the number of elements to 0; we don't actually free the memory |
238 | | * here - that is done in cupsArrayDelete()... |
239 | | */ |
240 | |
|
241 | 0 | a->num_elements = 0; |
242 | 0 | a->current = -1; |
243 | 0 | a->insert = -1; |
244 | 0 | a->unique = 1; |
245 | 0 | a->num_saved = 0; |
246 | 0 | } |
247 | | |
248 | | |
249 | | /* |
250 | | * 'cupsArrayCount()' - Get the number of elements in the array. |
251 | | * |
252 | | * @since CUPS 1.2/macOS 10.5@ |
253 | | */ |
254 | | |
255 | | int /* O - Number of elements */ |
256 | | cupsArrayCount(cups_array_t *a) /* I - Array */ |
257 | 0 | { |
258 | | /* |
259 | | * Range check input... |
260 | | */ |
261 | |
|
262 | 0 | if (!a) |
263 | 0 | return (0); |
264 | | |
265 | | /* |
266 | | * Return the number of elements... |
267 | | */ |
268 | | |
269 | 0 | return (a->num_elements); |
270 | 0 | } |
271 | | |
272 | | |
273 | | /* |
274 | | * 'cupsArrayCurrent()' - Return the current element in the array. |
275 | | * |
276 | | * The current element is undefined until you call @link cupsArrayFind@, |
277 | | * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@. |
278 | | * |
279 | | * @since CUPS 1.2/macOS 10.5@ |
280 | | */ |
281 | | |
282 | | void * /* O - Element */ |
283 | | cupsArrayCurrent(cups_array_t *a) /* I - Array */ |
284 | 0 | { |
285 | | /* |
286 | | * Range check input... |
287 | | */ |
288 | |
|
289 | 0 | if (!a) |
290 | 0 | return (NULL); |
291 | | |
292 | | /* |
293 | | * Return the current element... |
294 | | */ |
295 | | |
296 | 0 | if (a->current >= 0 && a->current < a->num_elements) |
297 | 0 | return (a->elements[a->current]); |
298 | 0 | else |
299 | 0 | return (NULL); |
300 | 0 | } |
301 | | |
302 | | |
303 | | /* |
304 | | * 'cupsArrayDelete()' - Free all memory used by the array. |
305 | | * |
306 | | * The caller is responsible for freeing the memory used by the |
307 | | * elements themselves. |
308 | | * |
309 | | * @since CUPS 1.2/macOS 10.5@ |
310 | | */ |
311 | | |
312 | | void |
313 | | cupsArrayDelete(cups_array_t *a) /* I - Array */ |
314 | 0 | { |
315 | | /* |
316 | | * Range check input... |
317 | | */ |
318 | |
|
319 | 0 | if (!a) |
320 | 0 | return; |
321 | | |
322 | | /* |
323 | | * Free the elements if we have a free function (otherwise the caller is |
324 | | * responsible for doing the dirty work...) |
325 | | */ |
326 | | |
327 | 0 | if (a->freefunc) |
328 | 0 | { |
329 | 0 | int i; /* Looping var */ |
330 | 0 | void **e; /* Current element */ |
331 | |
|
332 | 0 | for (i = a->num_elements, e = a->elements; i > 0; i --, e ++) |
333 | 0 | (a->freefunc)(*e, a->data); |
334 | 0 | } |
335 | | |
336 | | /* |
337 | | * Free the array of element pointers... |
338 | | */ |
339 | |
|
340 | 0 | if (a->alloc_elements) |
341 | 0 | free(a->elements); |
342 | |
|
343 | 0 | if (a->hashsize) |
344 | 0 | free(a->hash); |
345 | |
|
346 | 0 | free(a); |
347 | 0 | } |
348 | | |
349 | | |
350 | | /* |
351 | | * 'cupsArrayDup()' - Duplicate the array. |
352 | | * |
353 | | * @since CUPS 1.2/macOS 10.5@ |
354 | | */ |
355 | | |
356 | | cups_array_t * /* O - Duplicate array */ |
357 | | cupsArrayDup(cups_array_t *a) /* I - Array */ |
358 | 0 | { |
359 | 0 | cups_array_t *da; /* Duplicate array */ |
360 | | |
361 | | |
362 | | /* |
363 | | * Range check input... |
364 | | */ |
365 | |
|
366 | 0 | if (!a) |
367 | 0 | return (NULL); |
368 | | |
369 | | /* |
370 | | * Allocate memory for the array... |
371 | | */ |
372 | | |
373 | 0 | da = calloc(1, sizeof(cups_array_t)); |
374 | 0 | if (!da) |
375 | 0 | return (NULL); |
376 | | |
377 | 0 | da->compare = a->compare; |
378 | 0 | da->data = a->data; |
379 | 0 | da->current = a->current; |
380 | 0 | da->insert = a->insert; |
381 | 0 | da->unique = a->unique; |
382 | 0 | da->num_saved = a->num_saved; |
383 | |
|
384 | 0 | memcpy(da->saved, a->saved, sizeof(a->saved)); |
385 | |
|
386 | 0 | if (a->num_elements) |
387 | 0 | { |
388 | | /* |
389 | | * Allocate memory for the elements... |
390 | | */ |
391 | |
|
392 | 0 | da->elements = malloc((size_t)a->num_elements * sizeof(void *)); |
393 | 0 | if (!da->elements) |
394 | 0 | { |
395 | 0 | free(da); |
396 | 0 | return (NULL); |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * Copy the element pointers... |
401 | | */ |
402 | | |
403 | 0 | if (a->copyfunc) |
404 | 0 | { |
405 | | /* |
406 | | * Use the copy function to make a copy of each element... |
407 | | */ |
408 | |
|
409 | 0 | int i; /* Looping var */ |
410 | |
|
411 | 0 | for (i = 0; i < a->num_elements; i ++) |
412 | 0 | da->elements[i] = (a->copyfunc)(a->elements[i], a->data); |
413 | 0 | } |
414 | 0 | else |
415 | 0 | { |
416 | | /* |
417 | | * Just copy raw pointers... |
418 | | */ |
419 | |
|
420 | 0 | memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *)); |
421 | 0 | } |
422 | |
|
423 | 0 | da->num_elements = a->num_elements; |
424 | 0 | da->alloc_elements = a->num_elements; |
425 | 0 | } |
426 | | |
427 | | /* |
428 | | * Return the new array... |
429 | | */ |
430 | | |
431 | 0 | return (da); |
432 | 0 | } |
433 | | |
434 | | |
435 | | /* |
436 | | * 'cupsArrayFind()' - Find an element in the array. |
437 | | * |
438 | | * @since CUPS 1.2/macOS 10.5@ |
439 | | */ |
440 | | |
441 | | void * /* O - Element found or @code NULL@ */ |
442 | | cupsArrayFind(cups_array_t *a, /* I - Array */ |
443 | | void *e) /* I - Element */ |
444 | 0 | { |
445 | 0 | int current, /* Current element */ |
446 | 0 | diff, /* Difference */ |
447 | 0 | hash; /* Hash index */ |
448 | | |
449 | | |
450 | | /* |
451 | | * Range check input... |
452 | | */ |
453 | |
|
454 | 0 | if (!a || !e) |
455 | 0 | return (NULL); |
456 | | |
457 | | /* |
458 | | * See if we have any elements... |
459 | | */ |
460 | | |
461 | 0 | if (!a->num_elements) |
462 | 0 | return (NULL); |
463 | | |
464 | | /* |
465 | | * Yes, look for a match... |
466 | | */ |
467 | | |
468 | 0 | if (a->hash) |
469 | 0 | { |
470 | 0 | hash = (*(a->hashfunc))(e, a->data); |
471 | |
|
472 | 0 | if (hash < 0 || hash >= a->hashsize) |
473 | 0 | { |
474 | 0 | current = a->current; |
475 | 0 | hash = -1; |
476 | 0 | } |
477 | 0 | else |
478 | 0 | { |
479 | 0 | current = a->hash[hash]; |
480 | |
|
481 | 0 | if (current < 0 || current >= a->num_elements) |
482 | 0 | current = a->current; |
483 | 0 | } |
484 | 0 | } |
485 | 0 | else |
486 | 0 | { |
487 | 0 | current = a->current; |
488 | 0 | hash = -1; |
489 | 0 | } |
490 | |
|
491 | 0 | current = cups_array_find(a, e, current, &diff); |
492 | 0 | if (!diff) |
493 | 0 | { |
494 | | /* |
495 | | * Found a match! If the array does not contain unique values, find |
496 | | * the first element that is the same... |
497 | | */ |
498 | |
|
499 | 0 | if (!a->unique && a->compare) |
500 | 0 | { |
501 | | /* |
502 | | * The array is not unique, find the first match... |
503 | | */ |
504 | |
|
505 | 0 | while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], |
506 | 0 | a->data)) |
507 | 0 | current --; |
508 | 0 | } |
509 | |
|
510 | 0 | a->current = current; |
511 | |
|
512 | 0 | if (hash >= 0) |
513 | 0 | a->hash[hash] = current; |
514 | |
|
515 | 0 | return (a->elements[current]); |
516 | 0 | } |
517 | 0 | else |
518 | 0 | { |
519 | | /* |
520 | | * No match... |
521 | | */ |
522 | |
|
523 | 0 | a->current = -1; |
524 | |
|
525 | 0 | return (NULL); |
526 | 0 | } |
527 | 0 | } |
528 | | |
529 | | |
530 | | /* |
531 | | * 'cupsArrayFirst()' - Get the first element in the array. |
532 | | * |
533 | | * @since CUPS 1.2/macOS 10.5@ |
534 | | */ |
535 | | |
536 | | void * /* O - First element or @code NULL@ if the array is empty */ |
537 | | cupsArrayFirst(cups_array_t *a) /* I - Array */ |
538 | 0 | { |
539 | | /* |
540 | | * Range check input... |
541 | | */ |
542 | |
|
543 | 0 | if (!a) |
544 | 0 | return (NULL); |
545 | | |
546 | | /* |
547 | | * Return the first element... |
548 | | */ |
549 | | |
550 | 0 | a->current = 0; |
551 | |
|
552 | 0 | return (cupsArrayCurrent(a)); |
553 | 0 | } |
554 | | |
555 | | |
556 | | /* |
557 | | * 'cupsArrayGetIndex()' - Get the index of the current element. |
558 | | * |
559 | | * The current element is undefined until you call @link cupsArrayFind@, |
560 | | * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@. |
561 | | * |
562 | | * @since CUPS 1.3/macOS 10.5@ |
563 | | */ |
564 | | |
565 | | int /* O - Index of the current element, starting at 0 */ |
566 | | cupsArrayGetIndex(cups_array_t *a) /* I - Array */ |
567 | 0 | { |
568 | 0 | if (!a) |
569 | 0 | return (-1); |
570 | 0 | else |
571 | 0 | return (a->current); |
572 | 0 | } |
573 | | |
574 | | |
575 | | /* |
576 | | * 'cupsArrayGetInsert()' - Get the index of the last inserted element. |
577 | | * |
578 | | * @since CUPS 1.3/macOS 10.5@ |
579 | | */ |
580 | | |
581 | | int /* O - Index of the last inserted element, starting at 0 */ |
582 | | cupsArrayGetInsert(cups_array_t *a) /* I - Array */ |
583 | 0 | { |
584 | 0 | if (!a) |
585 | 0 | return (-1); |
586 | 0 | else |
587 | 0 | return (a->insert); |
588 | 0 | } |
589 | | |
590 | | |
591 | | /* |
592 | | * 'cupsArrayIndex()' - Get the N-th element in the array. |
593 | | * |
594 | | * @since CUPS 1.2/macOS 10.5@ |
595 | | */ |
596 | | |
597 | | void * /* O - N-th element or @code NULL@ */ |
598 | | cupsArrayIndex(cups_array_t *a, /* I - Array */ |
599 | | int n) /* I - Index into array, starting at 0 */ |
600 | 0 | { |
601 | 0 | if (!a) |
602 | 0 | return (NULL); |
603 | | |
604 | 0 | a->current = n; |
605 | |
|
606 | 0 | return (cupsArrayCurrent(a)); |
607 | 0 | } |
608 | | |
609 | | |
610 | | /* |
611 | | * 'cupsArrayInsert()' - Insert an element in the array. |
612 | | * |
613 | | * When inserting an element in a sorted array, non-unique elements are |
614 | | * inserted at the beginning of the run of identical elements. For unsorted |
615 | | * arrays, the element is inserted at the beginning of the array. |
616 | | * |
617 | | * @since CUPS 1.2/macOS 10.5@ |
618 | | */ |
619 | | |
620 | | int /* O - 0 on failure, 1 on success */ |
621 | | cupsArrayInsert(cups_array_t *a, /* I - Array */ |
622 | | void *e) /* I - Element */ |
623 | 0 | { |
624 | 0 | DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", (void *)a, e)); |
625 | | |
626 | | /* |
627 | | * Range check input... |
628 | | */ |
629 | |
|
630 | 0 | if (!a || !e) |
631 | 0 | { |
632 | 0 | DEBUG_puts("3cupsArrayInsert: returning 0"); |
633 | 0 | return (0); |
634 | 0 | } |
635 | | |
636 | | /* |
637 | | * Insert the element... |
638 | | */ |
639 | | |
640 | 0 | return (cups_array_add(a, e, 1)); |
641 | 0 | } |
642 | | |
643 | | |
644 | | /* |
645 | | * 'cupsArrayLast()' - Get the last element in the array. |
646 | | * |
647 | | * @since CUPS 1.2/macOS 10.5@ |
648 | | */ |
649 | | |
650 | | void * /* O - Last element or @code NULL@ if the array is empty */ |
651 | | cupsArrayLast(cups_array_t *a) /* I - Array */ |
652 | 0 | { |
653 | | /* |
654 | | * Range check input... |
655 | | */ |
656 | |
|
657 | 0 | if (!a) |
658 | 0 | return (NULL); |
659 | | |
660 | | /* |
661 | | * Return the last element... |
662 | | */ |
663 | | |
664 | 0 | a->current = a->num_elements - 1; |
665 | |
|
666 | 0 | return (cupsArrayCurrent(a)); |
667 | 0 | } |
668 | | |
669 | | |
670 | | /* |
671 | | * 'cupsArrayNew()' - Create a new array. |
672 | | * |
673 | | * The comparison function ("f") is used to create a sorted array. The function |
674 | | * receives pointers to two elements and the user data pointer ("d") - the user |
675 | | * data pointer argument can safely be omitted when not required so functions |
676 | | * like @code strcmp@ can be used for sorted string arrays. |
677 | | * |
678 | | * @since CUPS 1.2/macOS 10.5@ |
679 | | */ |
680 | | |
681 | | cups_array_t * /* O - Array */ |
682 | | cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ |
683 | | void *d) /* I - User data pointer or @code NULL@ */ |
684 | 0 | { |
685 | 0 | return (cupsArrayNew3(f, d, 0, 0, 0, 0)); |
686 | 0 | } |
687 | | |
688 | | |
689 | | /* |
690 | | * 'cupsArrayNew2()' - Create a new array with hash. |
691 | | * |
692 | | * The comparison function ("f") is used to create a sorted array. The function |
693 | | * receives pointers to two elements and the user data pointer ("d") - the user |
694 | | * data pointer argument can safely be omitted when not required so functions |
695 | | * like @code strcmp@ can be used for sorted string arrays. |
696 | | * |
697 | | * The hash function ("h") is used to implement cached lookups with the |
698 | | * specified hash size ("hsize"). |
699 | | * |
700 | | * @since CUPS 1.3/macOS 10.5@ |
701 | | */ |
702 | | |
703 | | cups_array_t * /* O - Array */ |
704 | | cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ |
705 | | void *d, /* I - User data or @code NULL@ */ |
706 | | cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */ |
707 | | int hsize) /* I - Hash size (>= 0) */ |
708 | 0 | { |
709 | 0 | return (cupsArrayNew3(f, d, h, hsize, 0, 0)); |
710 | 0 | } |
711 | | |
712 | | |
713 | | /* |
714 | | * 'cupsArrayNew3()' - Create a new array with hash and/or free function. |
715 | | * |
716 | | * The comparison function ("f") is used to create a sorted array. The function |
717 | | * receives pointers to two elements and the user data pointer ("d") - the user |
718 | | * data pointer argument can safely be omitted when not required so functions |
719 | | * like @code strcmp@ can be used for sorted string arrays. |
720 | | * |
721 | | * The hash function ("h") is used to implement cached lookups with the |
722 | | * specified hash size ("hsize"). |
723 | | * |
724 | | * The copy function ("cf") is used to automatically copy/retain elements when |
725 | | * added or the array is copied. |
726 | | * |
727 | | * The free function ("cf") is used to automatically free/release elements when |
728 | | * removed or the array is deleted. |
729 | | * |
730 | | * @since CUPS 1.5/macOS 10.7@ |
731 | | */ |
732 | | |
733 | | cups_array_t * /* O - Array */ |
734 | | cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ |
735 | | void *d, /* I - User data or @code NULL@ */ |
736 | | cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */ |
737 | | int hsize, /* I - Hash size (>= 0) */ |
738 | | cups_acopy_func_t cf, /* I - Copy function */ |
739 | | cups_afree_func_t ff) /* I - Free function */ |
740 | 0 | { |
741 | 0 | cups_array_t *a; /* Array */ |
742 | | |
743 | | |
744 | | /* |
745 | | * Allocate memory for the array... |
746 | | */ |
747 | |
|
748 | 0 | a = calloc(1, sizeof(cups_array_t)); |
749 | 0 | if (!a) |
750 | 0 | return (NULL); |
751 | | |
752 | 0 | a->compare = f; |
753 | 0 | a->data = d; |
754 | 0 | a->current = -1; |
755 | 0 | a->insert = -1; |
756 | 0 | a->num_saved = 0; |
757 | 0 | a->unique = 1; |
758 | |
|
759 | 0 | if (hsize > 0 && h) |
760 | 0 | { |
761 | 0 | a->hashfunc = h; |
762 | 0 | a->hashsize = hsize; |
763 | 0 | a->hash = malloc((size_t)hsize * sizeof(int)); |
764 | |
|
765 | 0 | if (!a->hash) |
766 | 0 | { |
767 | 0 | free(a); |
768 | 0 | return (NULL); |
769 | 0 | } |
770 | | |
771 | 0 | memset(a->hash, -1, (size_t)hsize * sizeof(int)); |
772 | 0 | } |
773 | | |
774 | 0 | a->copyfunc = cf; |
775 | 0 | a->freefunc = ff; |
776 | |
|
777 | 0 | return (a); |
778 | 0 | } |
779 | | |
780 | | |
781 | | /* |
782 | | * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings. |
783 | | * |
784 | | * Note: The array automatically manages copies of the strings passed. If the |
785 | | * string pointer "s" is NULL or the empty string, no strings are added to the |
786 | | * newly created array. |
787 | | */ |
788 | | |
789 | | cups_array_t * /* O - Array */ |
790 | | _cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */ |
791 | | char delim) /* I - Delimiter character */ |
792 | 0 | { |
793 | 0 | cups_array_t *a; /* Array */ |
794 | | |
795 | |
|
796 | 0 | if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, |
797 | 0 | (cups_acopy_func_t)_cupsStrAlloc, |
798 | 0 | (cups_afree_func_t)_cupsStrFree)) != NULL) |
799 | 0 | _cupsArrayAddStrings(a, s, delim); |
800 | |
|
801 | 0 | return (a); |
802 | 0 | } |
803 | | |
804 | | |
805 | | /* |
806 | | * 'cupsArrayNext()' - Get the next element in the array. |
807 | | * |
808 | | * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)". |
809 | | * |
810 | | * The next element is undefined until you call @link cupsArrayFind@, |
811 | | * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ |
812 | | * to set the current element. |
813 | | * |
814 | | * @since CUPS 1.2/macOS 10.5@ |
815 | | */ |
816 | | |
817 | | void * /* O - Next element or @code NULL@ */ |
818 | | cupsArrayNext(cups_array_t *a) /* I - Array */ |
819 | 0 | { |
820 | | /* |
821 | | * Range check input... |
822 | | */ |
823 | |
|
824 | 0 | if (!a) |
825 | 0 | return (NULL); |
826 | | |
827 | | /* |
828 | | * Return the next element... |
829 | | */ |
830 | | |
831 | 0 | if (a->current < a->num_elements) |
832 | 0 | a->current ++; |
833 | |
|
834 | 0 | return (cupsArrayCurrent(a)); |
835 | 0 | } |
836 | | |
837 | | |
838 | | /* |
839 | | * 'cupsArrayPrev()' - Get the previous element in the array. |
840 | | * |
841 | | * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)". |
842 | | * |
843 | | * The previous element is undefined until you call @link cupsArrayFind@, |
844 | | * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ |
845 | | * to set the current element. |
846 | | * |
847 | | * @since CUPS 1.2/macOS 10.5@ |
848 | | */ |
849 | | |
850 | | void * /* O - Previous element or @code NULL@ */ |
851 | | cupsArrayPrev(cups_array_t *a) /* I - Array */ |
852 | 0 | { |
853 | | /* |
854 | | * Range check input... |
855 | | */ |
856 | |
|
857 | 0 | if (!a) |
858 | 0 | return (NULL); |
859 | | |
860 | | /* |
861 | | * Return the previous element... |
862 | | */ |
863 | | |
864 | 0 | if (a->current >= 0) |
865 | 0 | a->current --; |
866 | |
|
867 | 0 | return (cupsArrayCurrent(a)); |
868 | 0 | } |
869 | | |
870 | | |
871 | | /* |
872 | | * 'cupsArrayRemove()' - Remove an element from the array. |
873 | | * |
874 | | * If more than one element matches "e", only the first matching element is |
875 | | * removed. |
876 | | * |
877 | | * The caller is responsible for freeing the memory used by the |
878 | | * removed element. |
879 | | * |
880 | | * @since CUPS 1.2/macOS 10.5@ |
881 | | */ |
882 | | |
883 | | int /* O - 1 on success, 0 on failure */ |
884 | | cupsArrayRemove(cups_array_t *a, /* I - Array */ |
885 | | void *e) /* I - Element */ |
886 | 0 | { |
887 | 0 | ssize_t i, /* Looping var */ |
888 | 0 | current; /* Current element */ |
889 | 0 | int diff; /* Difference */ |
890 | | |
891 | | |
892 | | /* |
893 | | * Range check input... |
894 | | */ |
895 | |
|
896 | 0 | if (!a || !e) |
897 | 0 | return (0); |
898 | | |
899 | | /* |
900 | | * See if the element is in the array... |
901 | | */ |
902 | | |
903 | 0 | if (!a->num_elements) |
904 | 0 | return (0); |
905 | | |
906 | 0 | current = cups_array_find(a, e, a->current, &diff); |
907 | 0 | if (diff) |
908 | 0 | return (0); |
909 | | |
910 | | /* |
911 | | * Yes, now remove it... |
912 | | */ |
913 | | |
914 | 0 | a->num_elements --; |
915 | |
|
916 | 0 | if (a->freefunc) |
917 | 0 | (a->freefunc)(a->elements[current], a->data); |
918 | |
|
919 | 0 | if (current < a->num_elements) |
920 | 0 | memmove(a->elements + current, a->elements + current + 1, |
921 | 0 | (size_t)(a->num_elements - current) * sizeof(void *)); |
922 | |
|
923 | 0 | if (current <= a->current) |
924 | 0 | a->current --; |
925 | |
|
926 | 0 | if (current < a->insert) |
927 | 0 | a->insert --; |
928 | 0 | else if (current == a->insert) |
929 | 0 | a->insert = -1; |
930 | |
|
931 | 0 | for (i = 0; i < a->num_saved; i ++) |
932 | 0 | if (current <= a->saved[i]) |
933 | 0 | a->saved[i] --; |
934 | |
|
935 | 0 | if (a->num_elements <= 1) |
936 | 0 | a->unique = 1; |
937 | |
|
938 | 0 | return (1); |
939 | 0 | } |
940 | | |
941 | | |
942 | | /* |
943 | | * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@. |
944 | | * |
945 | | * @since CUPS 1.2/macOS 10.5@ |
946 | | */ |
947 | | |
948 | | void * /* O - New current element */ |
949 | | cupsArrayRestore(cups_array_t *a) /* I - Array */ |
950 | 0 | { |
951 | 0 | if (!a) |
952 | 0 | return (NULL); |
953 | | |
954 | 0 | if (a->num_saved <= 0) |
955 | 0 | return (NULL); |
956 | | |
957 | 0 | a->num_saved --; |
958 | 0 | a->current = a->saved[a->num_saved]; |
959 | |
|
960 | 0 | if (a->current >= 0 && a->current < a->num_elements) |
961 | 0 | return (a->elements[a->current]); |
962 | 0 | else |
963 | 0 | return (NULL); |
964 | 0 | } |
965 | | |
966 | | |
967 | | /* |
968 | | * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@. |
969 | | * |
970 | | * The current element is undefined until you call @link cupsArrayFind@, |
971 | | * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ |
972 | | * to set the current element. |
973 | | * |
974 | | * The save/restore stack is guaranteed to be at least 32 elements deep. |
975 | | * |
976 | | * @since CUPS 1.2/macOS 10.5@ |
977 | | */ |
978 | | |
979 | | int /* O - 1 on success, 0 on failure */ |
980 | | cupsArraySave(cups_array_t *a) /* I - Array */ |
981 | 0 | { |
982 | 0 | if (!a) |
983 | 0 | return (0); |
984 | | |
985 | 0 | if (a->num_saved >= _CUPS_MAXSAVE) |
986 | 0 | return (0); |
987 | | |
988 | 0 | a->saved[a->num_saved] = a->current; |
989 | 0 | a->num_saved ++; |
990 | |
|
991 | 0 | return (1); |
992 | 0 | } |
993 | | |
994 | | |
995 | | /* |
996 | | * 'cupsArrayUserData()' - Return the user data for an array. |
997 | | * |
998 | | * @since CUPS 1.2/macOS 10.5@ |
999 | | */ |
1000 | | |
1001 | | void * /* O - User data */ |
1002 | | cupsArrayUserData(cups_array_t *a) /* I - Array */ |
1003 | 0 | { |
1004 | 0 | if (a) |
1005 | 0 | return (a->data); |
1006 | 0 | else |
1007 | 0 | return (NULL); |
1008 | 0 | } |
1009 | | |
1010 | | |
1011 | | /* |
1012 | | * 'cups_array_add()' - Insert or append an element to the array. |
1013 | | * |
1014 | | * @since CUPS 1.2/macOS 10.5@ |
1015 | | */ |
1016 | | |
1017 | | static int /* O - 1 on success, 0 on failure */ |
1018 | | cups_array_add(cups_array_t *a, /* I - Array */ |
1019 | | void *e, /* I - Element to add */ |
1020 | | int insert) /* I - 1 = insert, 0 = append */ |
1021 | 0 | { |
1022 | 0 | int i, /* Looping var */ |
1023 | 0 | current; /* Current element */ |
1024 | 0 | int diff; /* Comparison with current element */ |
1025 | | |
1026 | |
|
1027 | 0 | DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", (void *)a, e, insert)); |
1028 | | |
1029 | | /* |
1030 | | * Verify we have room for the new element... |
1031 | | */ |
1032 | |
|
1033 | 0 | if (a->num_elements >= a->alloc_elements) |
1034 | 0 | { |
1035 | | /* |
1036 | | * Allocate additional elements; start with 16 elements, then |
1037 | | * double the size until 1024 elements, then add 1024 elements |
1038 | | * thereafter... |
1039 | | */ |
1040 | |
|
1041 | 0 | void **temp; /* New array elements */ |
1042 | 0 | int count; /* New allocation count */ |
1043 | | |
1044 | |
|
1045 | 0 | if (a->alloc_elements == 0) |
1046 | 0 | { |
1047 | 0 | count = 16; |
1048 | 0 | temp = malloc((size_t)count * sizeof(void *)); |
1049 | 0 | } |
1050 | 0 | else |
1051 | 0 | { |
1052 | 0 | if (a->alloc_elements < 1024) |
1053 | 0 | count = a->alloc_elements * 2; |
1054 | 0 | else |
1055 | 0 | count = a->alloc_elements + 1024; |
1056 | |
|
1057 | 0 | temp = realloc(a->elements, (size_t)count * sizeof(void *)); |
1058 | 0 | } |
1059 | |
|
1060 | 0 | DEBUG_printf(("9cups_array_add: count=" CUPS_LLFMT, CUPS_LLCAST count)); |
1061 | |
|
1062 | 0 | if (!temp) |
1063 | 0 | { |
1064 | 0 | DEBUG_puts("9cups_array_add: allocation failed, returning 0"); |
1065 | 0 | return (0); |
1066 | 0 | } |
1067 | | |
1068 | 0 | a->alloc_elements = count; |
1069 | 0 | a->elements = temp; |
1070 | 0 | } |
1071 | | |
1072 | | /* |
1073 | | * Find the insertion point for the new element; if there is no |
1074 | | * compare function or elements, just add it to the beginning or end... |
1075 | | */ |
1076 | | |
1077 | 0 | if (!a->num_elements || !a->compare) |
1078 | 0 | { |
1079 | | /* |
1080 | | * No elements or comparison function, insert/append as needed... |
1081 | | */ |
1082 | |
|
1083 | 0 | if (insert) |
1084 | 0 | current = 0; /* Insert at beginning */ |
1085 | 0 | else |
1086 | 0 | current = a->num_elements; /* Append to the end */ |
1087 | 0 | } |
1088 | 0 | else |
1089 | 0 | { |
1090 | | /* |
1091 | | * Do a binary search for the insertion point... |
1092 | | */ |
1093 | |
|
1094 | 0 | current = cups_array_find(a, e, a->insert, &diff); |
1095 | |
|
1096 | 0 | if (diff > 0) |
1097 | 0 | { |
1098 | | /* |
1099 | | * Insert after the current element... |
1100 | | */ |
1101 | |
|
1102 | 0 | current ++; |
1103 | 0 | } |
1104 | 0 | else if (!diff) |
1105 | 0 | { |
1106 | | /* |
1107 | | * Compared equal, make sure we add to the begining or end of |
1108 | | * the current run of equal elements... |
1109 | | */ |
1110 | |
|
1111 | 0 | a->unique = 0; |
1112 | |
|
1113 | 0 | if (insert) |
1114 | 0 | { |
1115 | | /* |
1116 | | * Insert at beginning of run... |
1117 | | */ |
1118 | |
|
1119 | 0 | while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], |
1120 | 0 | a->data)) |
1121 | 0 | current --; |
1122 | 0 | } |
1123 | 0 | else |
1124 | 0 | { |
1125 | | /* |
1126 | | * Append at end of run... |
1127 | | */ |
1128 | |
|
1129 | 0 | do |
1130 | 0 | { |
1131 | 0 | current ++; |
1132 | 0 | } |
1133 | 0 | while (current < a->num_elements && |
1134 | 0 | !(*(a->compare))(e, a->elements[current], a->data)); |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 | } |
1138 | | |
1139 | | /* |
1140 | | * Insert or append the element... |
1141 | | */ |
1142 | |
|
1143 | 0 | if (current < a->num_elements) |
1144 | 0 | { |
1145 | | /* |
1146 | | * Shift other elements to the right... |
1147 | | */ |
1148 | |
|
1149 | 0 | memmove(a->elements + current + 1, a->elements + current, |
1150 | 0 | (size_t)(a->num_elements - current) * sizeof(void *)); |
1151 | |
|
1152 | 0 | if (a->current >= current) |
1153 | 0 | a->current ++; |
1154 | |
|
1155 | 0 | for (i = 0; i < a->num_saved; i ++) |
1156 | 0 | if (a->saved[i] >= current) |
1157 | 0 | a->saved[i] ++; |
1158 | |
|
1159 | 0 | DEBUG_printf(("9cups_array_add: insert element at index " CUPS_LLFMT, CUPS_LLCAST current)); |
1160 | 0 | } |
1161 | | #ifdef DEBUG |
1162 | | else |
1163 | | DEBUG_printf(("9cups_array_add: append element at " CUPS_LLFMT, CUPS_LLCAST current)); |
1164 | | #endif /* DEBUG */ |
1165 | |
|
1166 | 0 | if (a->copyfunc) |
1167 | 0 | { |
1168 | 0 | if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL) |
1169 | 0 | { |
1170 | 0 | DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0"); |
1171 | 0 | return (0); |
1172 | 0 | } |
1173 | 0 | } |
1174 | 0 | else |
1175 | 0 | a->elements[current] = e; |
1176 | | |
1177 | 0 | a->num_elements ++; |
1178 | 0 | a->insert = current; |
1179 | |
|
1180 | | #ifdef DEBUG |
1181 | | for (current = 0; current < a->num_elements; current ++) |
1182 | | DEBUG_printf(("9cups_array_add: a->elements[" CUPS_LLFMT "]=%p", CUPS_LLCAST current, a->elements[current])); |
1183 | | #endif /* DEBUG */ |
1184 | |
|
1185 | 0 | DEBUG_puts("9cups_array_add: returning 1"); |
1186 | |
|
1187 | 0 | return (1); |
1188 | 0 | } |
1189 | | |
1190 | | |
1191 | | /* |
1192 | | * 'cups_array_find()' - Find an element in the array. |
1193 | | */ |
1194 | | |
1195 | | static int /* O - Index of match */ |
1196 | | cups_array_find(cups_array_t *a, /* I - Array */ |
1197 | | void *e, /* I - Element */ |
1198 | | int prev, /* I - Previous index */ |
1199 | | int *rdiff) /* O - Difference of match */ |
1200 | 0 | { |
1201 | 0 | int left, /* Left side of search */ |
1202 | 0 | right, /* Right side of search */ |
1203 | 0 | current, /* Current element */ |
1204 | 0 | diff; /* Comparison with current element */ |
1205 | | |
1206 | |
|
1207 | 0 | DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", (void *)a, e, prev, (void *)rdiff)); |
1208 | |
|
1209 | 0 | if (a->compare) |
1210 | 0 | { |
1211 | | /* |
1212 | | * Do a binary search for the element... |
1213 | | */ |
1214 | |
|
1215 | 0 | DEBUG_puts("9cups_array_find: binary search"); |
1216 | |
|
1217 | 0 | if (prev >= 0 && prev < a->num_elements) |
1218 | 0 | { |
1219 | | /* |
1220 | | * Start search on either side of previous... |
1221 | | */ |
1222 | |
|
1223 | 0 | if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 || |
1224 | 0 | (diff < 0 && prev == 0) || |
1225 | 0 | (diff > 0 && prev == (a->num_elements - 1))) |
1226 | 0 | { |
1227 | | /* |
1228 | | * Exact or edge match, return it! |
1229 | | */ |
1230 | |
|
1231 | 0 | DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff)); |
1232 | |
|
1233 | 0 | *rdiff = diff; |
1234 | |
|
1235 | 0 | return (prev); |
1236 | 0 | } |
1237 | 0 | else if (diff < 0) |
1238 | 0 | { |
1239 | | /* |
1240 | | * Start with previous on right side... |
1241 | | */ |
1242 | |
|
1243 | 0 | left = 0; |
1244 | 0 | right = prev; |
1245 | 0 | } |
1246 | 0 | else |
1247 | 0 | { |
1248 | | /* |
1249 | | * Start wih previous on left side... |
1250 | | */ |
1251 | |
|
1252 | 0 | left = prev; |
1253 | 0 | right = a->num_elements - 1; |
1254 | 0 | } |
1255 | 0 | } |
1256 | 0 | else |
1257 | 0 | { |
1258 | | /* |
1259 | | * Start search in the middle... |
1260 | | */ |
1261 | |
|
1262 | 0 | left = 0; |
1263 | 0 | right = a->num_elements - 1; |
1264 | 0 | } |
1265 | | |
1266 | 0 | do |
1267 | 0 | { |
1268 | 0 | current = (left + right) / 2; |
1269 | 0 | diff = (*(a->compare))(e, a->elements[current], a->data); |
1270 | |
|
1271 | 0 | DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d", |
1272 | 0 | left, right, current, diff)); |
1273 | |
|
1274 | 0 | if (diff == 0) |
1275 | 0 | break; |
1276 | 0 | else if (diff < 0) |
1277 | 0 | right = current; |
1278 | 0 | else |
1279 | 0 | left = current; |
1280 | 0 | } |
1281 | 0 | while ((right - left) > 1); |
1282 | | |
1283 | 0 | if (diff != 0) |
1284 | 0 | { |
1285 | | /* |
1286 | | * Check the last 1 or 2 elements... |
1287 | | */ |
1288 | |
|
1289 | 0 | if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0) |
1290 | 0 | current = left; |
1291 | 0 | else |
1292 | 0 | { |
1293 | 0 | diff = (*(a->compare))(e, a->elements[right], a->data); |
1294 | 0 | current = right; |
1295 | 0 | } |
1296 | 0 | } |
1297 | 0 | } |
1298 | 0 | else |
1299 | 0 | { |
1300 | | /* |
1301 | | * Do a linear pointer search... |
1302 | | */ |
1303 | |
|
1304 | 0 | DEBUG_puts("9cups_array_find: linear search"); |
1305 | |
|
1306 | 0 | diff = 1; |
1307 | |
|
1308 | 0 | for (current = 0; current < a->num_elements; current ++) |
1309 | 0 | if (a->elements[current] == e) |
1310 | 0 | { |
1311 | 0 | diff = 0; |
1312 | 0 | break; |
1313 | 0 | } |
1314 | 0 | } |
1315 | | |
1316 | | /* |
1317 | | * Return the closest element and the difference... |
1318 | | */ |
1319 | | |
1320 | 0 | DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff)); |
1321 | |
|
1322 | 0 | *rdiff = diff; |
1323 | |
|
1324 | 0 | return (current); |
1325 | 0 | } |