1/* Locale dependent memory area transformation for comparison. 2 Copyright (C) 2009, 2010 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@clisp.org>, 2009. 4 5 This program is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Lesser General Public License as published 7 by the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18#include <config.h> 19 20/* Specification. */ 21#include "memxfrm.h" 22 23#include <errno.h> 24#include <stdlib.h> 25#include <string.h> 26 27char * 28memxfrm (char *s, size_t n, char *resultbuf, size_t *lengthp) 29{ 30 /* Result accumulator. */ 31 char *result; 32 size_t length; 33 size_t allocated; 34 35 char orig_sentinel; 36 37 /* Initial memory allocation. */ 38 if (resultbuf != NULL && *lengthp > 0) 39 { 40 result = resultbuf; 41 allocated = *lengthp; 42 } 43 else 44 { 45 allocated = (n > 0 ? n : 1); 46 result = (char *) malloc (allocated); 47 if (result == NULL) 48 goto out_of_memory_2; 49 } 50 length = 0; 51 52 /* Add sentinel.byte. */ 53 orig_sentinel = s[n]; 54 s[n] = '\0'; 55 56 /* Iterate through S, transforming each NUL terminated segment. 57 Accumulate the resulting transformed segments in result, separated by 58 NULs. */ 59 { 60 const char *p_end = s + n + 1; 61 const char *p; 62 63 p = s; 64 for (;;) 65 { 66 /* Search next NUL byte. */ 67 const char *q = p + strlen (p); 68 69 for (;;) 70 { 71 size_t k; 72 73 errno = 0; 74 k = strxfrm (result + length, p, allocated - length); 75 if (errno != 0) 76 goto fail; 77 if (k >= allocated - length) 78 { 79 /* Grow the result buffer. */ 80 char *new_result; 81 82 allocated = 2 * allocated; 83 if (allocated < 64) 84 allocated = 64; 85 if (result == resultbuf) 86 new_result = (char *) malloc (allocated); 87 else 88 new_result = (char *) realloc (result, allocated); 89 if (new_result == NULL) 90 goto out_of_memory_1; 91 result = new_result; 92 } 93 else 94 { 95 length += k; 96 break; 97 } 98 } 99 100 p = q + 1; 101 if (p == p_end) 102 break; 103 result[length] = '\0'; 104 length++; 105 } 106 } 107 108 /* Shrink the allocated memory if possible. */ 109 if (result != resultbuf && (length > 0 ? length : 1) < allocated) 110 { 111 char *memory = (char *) realloc (result, length > 0 ? length : 1); 112 if (memory != NULL) 113 result = memory; 114 } 115 116 s[n] = orig_sentinel; 117 *lengthp = length; 118 return result; 119 120 fail: 121 { 122 int saved_errno = errno; 123 if (result != resultbuf) 124 free (result); 125 s[n] = orig_sentinel; 126 errno = saved_errno; 127 return NULL; 128 } 129 130 out_of_memory_1: 131 if (result != resultbuf) 132 free (result); 133 s[n] = orig_sentinel; 134 out_of_memory_2: 135 errno = ENOMEM; 136 return NULL; 137} 138