/src/r-source/src/main/character.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 1997--2026 The R Core Team |
4 | | * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Pulic License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (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, a copy is available at |
18 | | * https://www.R-project.org/Licenses/ |
19 | | */ |
20 | | |
21 | | /* The character functions in this file are |
22 | | |
23 | | nzchar nchar substr substr<- abbreviate tolower toupper chartr strtrim |
24 | | |
25 | | and the utility |
26 | | |
27 | | make.names |
28 | | |
29 | | The regex functions |
30 | | |
31 | | strsplit grep [g]sub [g]regexpr agrep |
32 | | |
33 | | here prior to 2.10.0 are now in grep.c and agrep.c |
34 | | |
35 | | make.unique, duplicated, unique, match, pmatch, charmatch are in unique.c |
36 | | iconv is in sysutils.c |
37 | | |
38 | | Character strings in R are at most 2^31-1 bytes, so we use int not size_t. |
39 | | |
40 | | Support for UTF-8-encoded strings in non-UTF-8 locales |
41 | | ====================================================== |
42 | | |
43 | | Comparison is done directly unless you happen to be comparing the same |
44 | | string in different encodings. |
45 | | |
46 | | nzchar and nchar(, "bytes") are independent of the encoding |
47 | | nchar(, "char") nchar(, "width") handle UTF-8 and Latin-1 directly |
48 | | substr substr<- handle UTF-8 and Latin-1 directly |
49 | | tolower toupper chartr translate UTF-8 and Latin-1 to wchar (which needs |
50 | | Unicode wide characters), rest to current charset |
51 | | abbreviate translates non-ASCII inputs to UTF-8 then wchar_t*. |
52 | | strtrim translates to the native encoding |
53 | | make.names translates to the native encoding, works in wchar_t in a MBCS. |
54 | | |
55 | | All the string matching functions handle UTF-8 directly, otherwise |
56 | | translate (latin1 to UTF-8, otherwise to native). |
57 | | |
58 | | Support for "bytes" marked encoding |
59 | | =================================== |
60 | | |
61 | | nzchar and nchar(, "bytes") are independent of the encoding. |
62 | | |
63 | | nchar(, "char") nchar(, "width") give NA (if allowed) or error. |
64 | | substr substr<- work in bytes |
65 | | |
66 | | abbreviate chartr make.names strtrim tolower toupper give error. |
67 | | |
68 | | */ |
69 | | |
70 | | #ifdef HAVE_CONFIG_H |
71 | | # include <config.h> |
72 | | #endif |
73 | | |
74 | | /* Used to indicate that we can safely convert marked UTF-8 strings |
75 | | to wchar_t* -- not currently used. |
76 | | */ |
77 | | #if defined(Win32) || defined(__STDC_ISO_10646__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) |
78 | | # define TO_WCS_OK 1 |
79 | | #else |
80 | | /* |
81 | | Maybe warn if utf8towcs is used, but no known platforms. |
82 | | */ |
83 | | #endif |
84 | | |
85 | | #include <Defn.h> |
86 | | #include <Internal.h> |
87 | | #include <errno.h> |
88 | | #include <R_ext/RS.h> // for R_Calloc/R_Free |
89 | | #include <R_ext/Itermacros.h> |
90 | | #include <rlocale.h> // overrides iswxxxx on some platforms. |
91 | | |
92 | | /* We use a shared buffer here to avoid reallocing small buffers, and |
93 | | keep a standard-size (MAXELTSIZE = 8192) buffer allocated shared |
94 | | between the various functions. |
95 | | |
96 | | If we want to make this thread-safe, we would need to initialize an |
97 | | instance non-statically in each using function, but this would add |
98 | | to the overhead. |
99 | | */ |
100 | | |
101 | | #include "RBufferUtils.h" |
102 | | static R_StringBuffer cbuff = {NULL, 0, MAXELTSIZE}; |
103 | | |
104 | | static int int_max = INT_MAX; |
105 | | |
106 | | /* Functions to perform analogues of the standard C string library. */ |
107 | | /* Most are vectorized */ |
108 | | |
109 | | /* primitive, nzchar(x, keepNA = FALSE) where the second argument is optional. |
110 | | Encoding of x is immaterial. |
111 | | */ |
112 | | attribute_hidden SEXP do_nzchar(SEXP call, SEXP op, SEXP args, SEXP env) |
113 | 16.5k | { |
114 | 16.5k | int nargs = length(args); |
115 | | |
116 | | // checkArity(op, args); .Primitive() and may have 1 or 2 args |
117 | 16.5k | if (nargs < 1 || nargs > 2) |
118 | 0 | errorcall(call, |
119 | 0 | ngettext("%d argument passed to '%s' which requires %d to %d", |
120 | 0 | "%d arguments passed to '%s' which requires %d to %d", |
121 | 0 | (unsigned long) nargs), |
122 | 0 | nargs, PRIMNAME(op), 1, 2); |
123 | 16.5k | check1arg(args, call, "x"); |
124 | | |
125 | 16.5k | if (isFactor(CAR(args))) |
126 | 0 | error(_("'%s' requires a character vector"), "nzchar()"); |
127 | 16.5k | SEXP x = PROTECT(coerceVector(CAR(args), STRSXP)); |
128 | 16.5k | if (!isString(x)) |
129 | 0 | error(_("'%s' requires a character vector"), "nzchar()"); |
130 | | |
131 | 16.5k | int keepNA = FALSE; // the default |
132 | 16.5k | if(nargs > 1) { |
133 | 0 | keepNA = asLogical(CADR(args)); |
134 | 0 | if (keepNA == NA_LOGICAL) keepNA = FALSE; |
135 | 0 | } |
136 | 16.5k | R_xlen_t i, len = XLENGTH(x); |
137 | 16.5k | SEXP ans = PROTECT(allocVector(LGLSXP, len)); |
138 | 16.5k | if (keepNA) |
139 | 0 | for (i = 0; i < len; i++) { |
140 | 0 | SEXP sxi = STRING_ELT(x, i); |
141 | 0 | LOGICAL(ans)[i] = (sxi == NA_STRING) ? NA_LOGICAL : LENGTH(sxi) > 0; |
142 | 0 | } |
143 | 16.5k | else |
144 | 33.1k | for (i = 0; i < len; i++) |
145 | 16.5k | LOGICAL(ans)[i] = LENGTH(STRING_ELT(x, i)) > 0; |
146 | 16.5k | UNPROTECT(2); |
147 | 16.5k | return ans; |
148 | 16.5k | } |
149 | | |
150 | | /* R strings are limited to 2^31 - 1 bytes on all platforms */ |
151 | | |
152 | | /* when msg_name is not NULL, |
153 | | error handling is via error() with a message including msg_name |
154 | | semi-internal buffer cbuff is freed if over default size |
155 | | |
156 | | when msg_name is NULL, (for use where performance matters) |
157 | | error handling is via negative return value (other than NA_INTEGER): |
158 | | -1 ... invalid multi-byte string |
159 | | -2 ... the quantity is not computable (bytes encoding) |
160 | | semi-internal buffer cbuff is never freed, should be freed by caller |
161 | | */ |
162 | | // in Defn.h |
163 | | attribute_hidden |
164 | | int R_nchar(SEXP string, nchar_type type_, |
165 | | Rboolean allowNA, Rboolean keepNA, const char* msg_name) |
166 | 18 | { |
167 | 18 | if (string == NA_STRING) |
168 | 0 | return keepNA ? NA_INTEGER : 2; |
169 | | // else : |
170 | 18 | switch(type_) { |
171 | 0 | case Bytes: |
172 | 0 | return LENGTH(string); |
173 | 0 | break; |
174 | 18 | case Chars: |
175 | 18 | if (IS_UTF8(string)) { |
176 | 0 | const char *p = CHAR(string); |
177 | 0 | if (!utf8Valid(p)) { |
178 | 0 | if (!allowNA) { |
179 | 0 | if (msg_name) |
180 | 0 | error(_("invalid multibyte string, %s"), msg_name); |
181 | 0 | else |
182 | 0 | return -1; |
183 | 0 | } |
184 | 0 | return NA_INTEGER; |
185 | 0 | } else { |
186 | 0 | int nc = 0; |
187 | 0 | for( ; *p; p += utf8clen(*p)) nc++; |
188 | 0 | return nc; |
189 | 0 | } |
190 | 18 | } else if (IS_LATIN1(string)) { |
191 | | // just count bytes |
192 | 0 | return (int) strlen(CHAR(string)); |
193 | 18 | } else if (IS_BYTES(string)) { |
194 | 0 | if (!allowNA) /* could do chars 0 */ { |
195 | 0 | if (msg_name) |
196 | 0 | error(_("number of characters is not computable in \"bytes\" encoding, %s"), |
197 | 0 | msg_name); |
198 | 0 | else |
199 | 0 | return -2; |
200 | 0 | } |
201 | 0 | return NA_INTEGER; |
202 | 18 | } else if (mbcslocale) { |
203 | 0 | int nc = (int) mbstowcs(NULL, translateChar(string), 0); |
204 | 0 | if (!allowNA && nc < 0) { |
205 | 0 | if (msg_name) |
206 | 0 | error(_("invalid multibyte string, %s"), msg_name); |
207 | 0 | else |
208 | 0 | return -1; |
209 | 0 | } |
210 | 0 | return (nc >= 0 ? nc : NA_INTEGER); |
211 | 0 | } else |
212 | 18 | return ((int) strlen(translateChar(string))); |
213 | 0 | break; |
214 | 0 | case Width: |
215 | 0 | if (IS_UTF8(string)) { |
216 | 0 | const char *p = CHAR(string); |
217 | 0 | if (!utf8Valid(p)) { |
218 | 0 | if (!allowNA) { |
219 | 0 | if (msg_name) |
220 | 0 | error(_("invalid multibyte string, %s"), msg_name); |
221 | 0 | else |
222 | 0 | return -1; |
223 | 0 | } |
224 | 0 | return NA_INTEGER; |
225 | 0 | } else { |
226 | 0 | int nc = 0; |
227 | 0 | for( ; *p; p += utf8clen(*p)) { |
228 | 0 | wchar_t wc1; |
229 | 0 | utf8toucs(&wc1, p); |
230 | 0 | R_wchar_t ucs; |
231 | 0 | if (IS_HIGH_SURROGATE(wc1)) |
232 | 0 | ucs = utf8toucs32(wc1, p); |
233 | 0 | else |
234 | 0 | ucs = wc1; |
235 | 0 | #ifdef USE_RI18N_WIDTH |
236 | 0 | nc += Ri18n_wcwidth(ucs); |
237 | | #else |
238 | | { |
239 | | int this = wcwidth(ucs); |
240 | | if (this >= 0) nc += this; |
241 | | } |
242 | | #endif |
243 | 0 | } |
244 | 0 | return nc; |
245 | 0 | } |
246 | 0 | } else if (IS_BYTES(string)) { |
247 | 0 | if (!allowNA) { /* could do width 0 */ |
248 | 0 | if (msg_name) |
249 | 0 | error(_("width is not computable for %s in \"bytes\" encoding"), |
250 | 0 | msg_name); |
251 | 0 | else |
252 | 0 | return -2; |
253 | 0 | } |
254 | 0 | return NA_INTEGER; |
255 | 0 | } else if (IS_LATIN1(string)) { |
256 | | // just count bytes as they are all width-1 chars |
257 | | // FIXME, well not control chars but there is ambiguity for most of 0x80-9F |
258 | 0 | return (int) strlen(CHAR(string)); |
259 | 0 | } else if (mbcslocale) { |
260 | 0 | const char *xi = translateChar(string); |
261 | 0 | int nc = (int) mbstowcs(NULL, xi, 0); |
262 | 0 | if (nc >= 0) { |
263 | 0 | const void *vmax = vmaxget(); |
264 | | /* working in wchar_t restricts this to the BMP on |
265 | | Windows, but maybe that is all current native |
266 | | charsets cover. */ |
267 | 0 | wchar_t *wc = (wchar_t *) |
268 | 0 | R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
269 | 0 | mbstowcs(wc, xi, nc + 1); |
270 | | // FIXME: width could conceivably exceed MAX_INT. |
271 | 0 | #ifdef USE_RI18N_WIDTH |
272 | 0 | int nci18n = Ri18n_wcswidth(wc, 2147483647); |
273 | | #else |
274 | | // We do not use this unless R_wchar_t is wchar_t |
275 | | // This could be -1 if there are non-printable chars, |
276 | | // then this is ignored |
277 | | int nci18n = wcswidth(wc, 2147483647); |
278 | | #endif |
279 | 0 | if (msg_name) |
280 | 0 | R_FreeStringBufferL(&cbuff); |
281 | 0 | vmaxset(vmax); |
282 | 0 | return (nci18n < 0) ? nc : nci18n; |
283 | 0 | } else if (!allowNA) { |
284 | 0 | if (msg_name) |
285 | 0 | error(_("invalid multibyte string, %s"), msg_name); |
286 | 0 | else |
287 | 0 | return -1; |
288 | 0 | } |
289 | 0 | else |
290 | 0 | return NA_INTEGER; |
291 | 0 | } else |
292 | | // See Latin-1 comment. |
293 | 0 | return (int) strlen(translateChar(string)); |
294 | | |
295 | 18 | } // switch |
296 | 0 | return NA_INTEGER; // -Wall |
297 | 18 | } // R_nchar() |
298 | | |
299 | | attribute_hidden SEXP do_nchar(SEXP call, SEXP op, SEXP args, SEXP env) |
300 | 18 | { |
301 | 18 | SEXP d, s, x, stype, ans; |
302 | 18 | int nargs = length(args); |
303 | | |
304 | | #ifdef R_version_3_4_or_so |
305 | | checkArity(op, args); |
306 | | #else |
307 | | // will work also for code byte-compiled *before* 'keepNA' was introduced |
308 | 18 | if (nargs < 3 || nargs > 4) |
309 | 0 | error(ngettext("%d argument passed to '%s' which requires %d to %d", |
310 | 0 | "%d arguments passed to '%s' which requires %d to %d", |
311 | 0 | (unsigned long) nargs), |
312 | 0 | nargs, PRIMNAME(op), 3, 4); |
313 | 18 | #endif |
314 | | /* DispatchOrEval internal generic: nchar */ |
315 | 18 | if (DispatchOrEval(call, op, "nchar", args, env, &ans, 0, 1)) |
316 | 0 | return(ans); |
317 | 18 | if (isFactor(CAR(args))) |
318 | 0 | error(_("'%s' requires a character vector"), "nchar()"); |
319 | 18 | PROTECT(x = coerceVector(CAR(args), STRSXP)); |
320 | 18 | if (!isString(x)) |
321 | 0 | error(_("'%s' requires a character vector"), "nchar()"); |
322 | 18 | R_xlen_t len = XLENGTH(x); |
323 | 18 | stype = CADR(args); |
324 | 18 | if (!isString(stype) || LENGTH(stype) != 1) |
325 | 0 | error(_("invalid '%s' argument"), "type"); |
326 | 18 | const char *type = CHAR(STRING_ELT(stype, 0)); /* always ASCII */ |
327 | 18 | size_t ntype = strlen(type); |
328 | 18 | if (ntype == 0) error(_("invalid '%s' argument"), "type"); |
329 | 18 | nchar_type type_; |
330 | 18 | if (strncmp(type, "bytes", ntype) == 0) type_ = Bytes; |
331 | 18 | else if (strncmp(type, "chars", ntype) == 0) type_ = Chars; |
332 | 0 | else if (strncmp(type, "width", ntype) == 0) type_ = Width; |
333 | 0 | else error(_("invalid '%s' argument"), "type"); |
334 | 18 | int allowNA = asLogical(CADDR(args)); |
335 | 18 | if (allowNA == NA_LOGICAL) allowNA = 0; |
336 | 18 | int keepNA; |
337 | 18 | if(nargs >= 4) { |
338 | 18 | keepNA = asLogical(CADDDR(args)); |
339 | 18 | if (keepNA == NA_LOGICAL) // default |
340 | 18 | keepNA = (type_ == Width) ? FALSE : TRUE; |
341 | 18 | } else keepNA = (type_ == Width) ? FALSE : TRUE; |
342 | 18 | PROTECT(s = allocVector(INTSXP, len)); |
343 | 18 | int *s_ = INTEGER(s); |
344 | 36 | for (R_xlen_t i = 0; i < len; i++) { |
345 | 18 | SEXP sxi = STRING_ELT(x, i); |
346 | | // NA_LOGICAL has now been excluded |
347 | 18 | int res = R_nchar(sxi, type_, |
348 | 18 | (Rboolean) allowNA, (Rboolean) keepNA, NULL); |
349 | 18 | switch(res) { |
350 | 0 | case -1: |
351 | 0 | error(_("invalid multibyte string, element %lld"), (long long)i+1); |
352 | 0 | case -2: |
353 | 0 | if (type_ == Chars) |
354 | 0 | error(_("number of characters is not computable in \"bytes\" encoding, element %lld"), |
355 | 0 | (long long)i+1); |
356 | 0 | else /* type_ == Width */ |
357 | 0 | error(_("width is not computable in \"bytes\" encoding, element %lld"), |
358 | 0 | (long long)i+1); |
359 | 18 | default: |
360 | 18 | s_[i] = res; |
361 | 18 | break; |
362 | 18 | } |
363 | 18 | } |
364 | 18 | R_FreeStringBufferL(&cbuff); |
365 | 18 | if ((d = getAttrib(x, R_NamesSymbol)) != R_NilValue) |
366 | 0 | setAttrib(s, R_NamesSymbol, d); |
367 | 18 | if ((d = getAttrib(x, R_DimSymbol)) != R_NilValue) |
368 | 0 | setAttrib(s, R_DimSymbol, d); |
369 | 18 | if ((d = getAttrib(x, R_DimNamesSymbol)) != R_NilValue) |
370 | 0 | setAttrib(s, R_DimNamesSymbol, d); |
371 | 18 | UNPROTECT(2); |
372 | 18 | return s; |
373 | 18 | } |
374 | | |
375 | | /* Assumes sa < so; sa, so are 1-based indices in character units to str, |
376 | | len is length of str in bytes, excluding the terminator. |
377 | | |
378 | | Returns pointer to result string in rfrom, of length rlen (in bytes, |
379 | | excluding the terminator - the string is not terminated). |
380 | | |
381 | | *rfrom may be invalid pointer when rlen is zero. |
382 | | */ |
383 | | static void substr(const char *str, int len, int ienc, int sa, int so, |
384 | | R_xlen_t idx, int isascii, const char **rfrom, |
385 | | int *rlen, int assumevalid) |
386 | 0 | { |
387 | 0 | int i; |
388 | 0 | const char *end = str + len; |
389 | |
|
390 | 0 | if (ienc == CE_UTF8) { |
391 | 0 | if (!assumevalid && !utf8Valid(str)) { |
392 | 0 | char msg[40]; |
393 | 0 | snprintf(msg, 40, "element %lld", (long long)idx+1); |
394 | 0 | error(_("invalid multibyte string, %s"), msg); |
395 | 0 | } |
396 | 0 | for (i = 0; i < sa - 1 && str < end; i++) |
397 | 0 | str += utf8clen(*str); |
398 | 0 | *rfrom = str; |
399 | 0 | for(; i < so && str < end; i++) |
400 | 0 | str += utf8clen(*str); |
401 | 0 | *rlen = (int) (str - *rfrom); |
402 | 0 | } else if (!isascii && ienc != CE_LATIN1 && ienc != CE_BYTES |
403 | 0 | && mbcslocale) { |
404 | 0 | mbstate_t mb_st; |
405 | 0 | mbs_init(&mb_st); |
406 | 0 | for (i = 0; i < sa - 1 && str < end; i++) |
407 | | /* throws error on invalid multi-byte string */ |
408 | 0 | str += Mbrtowc(NULL, str, R_MB_CUR_MAX, &mb_st); |
409 | 0 | *rfrom = str; |
410 | 0 | for (; i < so && str < end; i++) |
411 | | /* throws error on invalid multi-byte string */ |
412 | 0 | str += (int) Mbrtowc(NULL, str, R_MB_CUR_MAX, &mb_st); |
413 | 0 | *rlen = (int) (str - *rfrom); |
414 | 0 | } else { |
415 | 0 | if (so - 1 < len) { |
416 | 0 | *rfrom = str + sa - 1; |
417 | 0 | *rlen = so - sa + 1; |
418 | 0 | } else if (sa - 1 < len) { |
419 | 0 | *rfrom = str + sa - 1; |
420 | 0 | *rlen = len - (sa - 1); |
421 | 0 | } else { |
422 | 0 | *rfrom = NULL; |
423 | 0 | *rlen = 0; |
424 | 0 | } |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | | attribute_hidden SEXP |
429 | | do_substr(SEXP call, SEXP op, SEXP args, SEXP env) |
430 | 0 | { |
431 | 0 | checkArity(op, args); |
432 | 0 | SEXP x = CAR(args); |
433 | 0 | if (!isString(x)) |
434 | 0 | error(_("extracting substrings from a non-character object")); |
435 | 0 | R_xlen_t len = XLENGTH(x); |
436 | 0 | SEXP s = PROTECT(allocVector(STRSXP, len)); |
437 | |
|
438 | 0 | if (len > 0) { |
439 | 0 | SEXP sa = CADR(args), // start |
440 | 0 | so = CADDR(args); // stop |
441 | 0 | int |
442 | 0 | k = LENGTH(sa), |
443 | 0 | l = LENGTH(so); |
444 | 0 | if (!isInteger(sa) || k == 0 || |
445 | 0 | (so != R_NilValue && (!isInteger(so) || l == 0))) |
446 | 0 | error(_("invalid substring arguments")); |
447 | |
|
448 | 0 | int *starts = INTEGER(sa), *stops = NULL; |
449 | 0 | if (so == R_NilValue) { |
450 | 0 | stops = &int_max; |
451 | 0 | l = 1; |
452 | 0 | } else { // as.integer(.) in R |
453 | 0 | stops = INTEGER(so); |
454 | 0 | } |
455 | |
|
456 | 0 | SEXP lastel = NULL; |
457 | 0 | for (R_xlen_t i = 0; i < len; i++) { |
458 | |
|
459 | 0 | int start = starts[i % k], |
460 | 0 | stop = stops [i % l]; |
461 | 0 | SEXP el = STRING_ELT(x,i); |
462 | 0 | if (el == NA_STRING || start == NA_INTEGER || stop == NA_INTEGER) { |
463 | 0 | SET_STRING_ELT(s, i, NA_STRING); |
464 | 0 | continue; |
465 | 0 | } |
466 | 0 | cetype_t ienc = getCharCE(el); |
467 | 0 | const char *ss = CHAR(el); |
468 | 0 | int slen = LENGTH(el); |
469 | 0 | if (start < 1) start = 1; |
470 | 0 | if (start > stop) { |
471 | 0 | SET_STRING_ELT(s, i, R_BlankString); |
472 | 0 | } else { |
473 | 0 | const char *rfrom; |
474 | 0 | int rlen; |
475 | | /* Skip checking UTF-8 validity if the string is the same |
476 | | R object as previously. This improves performance of |
477 | | substring() used on a single string but many substrings |
478 | | to be extracted from it */ |
479 | 0 | substr(ss, slen, ienc, start, stop, i, |
480 | 0 | IS_ASCII(el), &rfrom, &rlen, el == lastel); |
481 | 0 | SET_STRING_ELT(s, i, mkCharLenCE(rfrom, rlen, ienc)); |
482 | 0 | } |
483 | 0 | lastel = el; |
484 | 0 | } |
485 | 0 | } |
486 | 0 | SHALLOW_DUPLICATE_ATTRIB(s, x); |
487 | | /* This copied the class, if any */ |
488 | 0 | UNPROTECT(1); |
489 | 0 | return s; |
490 | 0 | } |
491 | | |
492 | | // .Internal( startsWith(x, prefix) ) and |
493 | | // .Internal( endsWith (x, suffix) ) |
494 | | attribute_hidden SEXP |
495 | | do_startsWith(SEXP call, SEXP op, SEXP args, SEXP env) |
496 | 492 | { |
497 | 492 | checkArity(op, args); |
498 | | |
499 | 492 | SEXP x = CAR(args), Xfix = CADR(args); // 'prefix' or 'suffix' |
500 | 492 | if (!isString(x) || !isString(Xfix)) |
501 | 0 | error(_("non-character object(s)")); |
502 | 492 | R_xlen_t |
503 | 492 | n1 = XLENGTH(x), |
504 | 492 | n2 = XLENGTH(Xfix), |
505 | 492 | n = (n1 > 0 && n2 > 0) ? ((n1 >= n2) ? n1 : n2) : 0; |
506 | 492 | if (n == 0) return allocVector(LGLSXP, 0); |
507 | 420 | SEXP ans = PROTECT(allocVector(LGLSXP, n)); |
508 | | |
509 | 420 | typedef const char * cp; |
510 | 420 | if (n2 == 1) { // optimize the most common case |
511 | 420 | SEXP el = STRING_ELT(Xfix, 0); |
512 | 420 | if (el == NA_STRING) { |
513 | 0 | for (R_xlen_t i = 0; i < n1; i++) |
514 | 0 | LOGICAL(ans)[i] = NA_LOGICAL; |
515 | 420 | } else { |
516 | | // ASCII matching will do for ASCII Xfix except in non-UTF-8 MBCS |
517 | 420 | Rboolean need_translate = TRUE; |
518 | 420 | if (IS_ASCII(el) && (utf8locale || !mbcslocale)) |
519 | 420 | need_translate = FALSE; |
520 | 420 | cp y0 = need_translate ? translateCharUTF8(el) : CHAR(el); |
521 | 420 | int ylen = (int) strlen(y0); |
522 | 127k | for (R_xlen_t i = 0; i < n1; i++) { |
523 | 126k | SEXP el = STRING_ELT(x, i); |
524 | 126k | if (el == NA_STRING) { |
525 | 0 | LOGICAL(ans)[i] = NA_LOGICAL; |
526 | 126k | } else { |
527 | 126k | cp x0 = need_translate ? translateCharUTF8(el) : CHAR(el); |
528 | 126k | if(PRIMVAL(op) == 0) { // startsWith |
529 | 126k | LOGICAL(ans)[i] = strncmp(x0, y0, ylen) == 0; |
530 | 126k | } else { // endsWith |
531 | 0 | int off = (int)strlen(x0) - ylen; |
532 | 0 | if (off < 0) |
533 | 0 | LOGICAL(ans)[i] = 0; |
534 | 0 | else { |
535 | 0 | LOGICAL(ans)[i] = memcmp(x0 + off, y0, ylen) == 0; |
536 | 0 | } |
537 | 0 | } |
538 | 126k | } |
539 | 126k | } |
540 | 420 | } |
541 | 420 | } else { // n2 > 1 |
542 | | // convert both inputs to UTF-8 |
543 | 0 | cp *x0 = (cp *) R_alloc(n1, sizeof(char *)); |
544 | 0 | cp *y0 = (cp *) R_alloc(n2, sizeof(char *)); |
545 | | // and record lengths, -1 for NA |
546 | 0 | int *x1 = (int *) R_alloc(n1, sizeof(int)); |
547 | 0 | int *y1 = (int *) R_alloc(n2, sizeof(int)); |
548 | 0 | for (R_xlen_t i = 0; i < n1; i++) { |
549 | 0 | SEXP el = STRING_ELT(x, i); |
550 | 0 | if (el == NA_STRING) |
551 | 0 | x1[i] = -1; |
552 | 0 | else { |
553 | 0 | x0[i] = translateCharUTF8(el); |
554 | 0 | x1[i] = (int) strlen(x0[i]); |
555 | 0 | } |
556 | 0 | } |
557 | 0 | for (R_xlen_t i = 0; i < n2; i++) { |
558 | 0 | SEXP el = STRING_ELT(Xfix, i); |
559 | 0 | if (el == NA_STRING) |
560 | 0 | y1[i] = -1; |
561 | 0 | else { |
562 | 0 | y0[i] = translateCharUTF8(el); |
563 | 0 | y1[i] = (int) strlen(y0[i]); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | R_xlen_t i, i1, i2; |
567 | 0 | if(PRIMVAL(op) == 0) { // 0 = startsWith, 1 = endsWith |
568 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
569 | 0 | if (x1[i1] < 0 || y1[i2] < 0) |
570 | 0 | LOGICAL(ans)[i] = NA_LOGICAL; |
571 | 0 | else if (x1[i1] < y1[i2]) |
572 | 0 | LOGICAL(ans)[i] = 0; |
573 | 0 | else // memcmp should be faster than strncmp |
574 | 0 | LOGICAL(ans)[i] = |
575 | 0 | memcmp(x0[i1], y0[i2], y1[i2]) == 0; |
576 | 0 | }); |
577 | 0 | } else { // endsWith |
578 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
579 | 0 | if (x1[i1] < 0 || y1[i2] < 0) |
580 | 0 | LOGICAL(ans)[i] = NA_LOGICAL; |
581 | 0 | else { |
582 | 0 | int off = x1[i1] - y1[i2]; |
583 | 0 | if (off < 0) |
584 | 0 | LOGICAL(ans)[i] = 0; |
585 | 0 | else { |
586 | 0 | LOGICAL(ans)[i] = |
587 | 0 | memcmp(x0[i1] + off, y0[i2], y1[i2]) == 0; |
588 | 0 | } |
589 | 0 | } |
590 | 0 | }); |
591 | 0 | } |
592 | 0 | } |
593 | 420 | UNPROTECT(1); |
594 | 420 | return ans; |
595 | 492 | } |
596 | | |
597 | | |
598 | | static void |
599 | | substrset(char *buf, const char *const str, cetype_t ienc, int sa, int so, |
600 | | R_xlen_t xidx, R_xlen_t vidx) |
601 | 0 | { |
602 | | /* Replace the substring buf[sa:so] by str[] */ |
603 | 0 | int i, in = 0, out = 0; |
604 | |
|
605 | 0 | if (ienc == CE_UTF8) { |
606 | 0 | if (!utf8Valid(buf)) { |
607 | 0 | char msg[40]; |
608 | 0 | snprintf(msg, 40, "element %lld", (long long)xidx+1); |
609 | 0 | error(_("invalid multibyte string, %s"), msg); |
610 | 0 | } |
611 | 0 | if (!utf8Valid(str)) { |
612 | 0 | char msg[40]; |
613 | 0 | snprintf(msg, 40, "value element %lld", (long long)vidx+1); |
614 | 0 | error(_("invalid multibyte string, %s"), msg); |
615 | 0 | } |
616 | 0 | for (i = 1; i < sa; i++) buf += utf8clen(*buf); |
617 | 0 | for (i = sa; i <= so && buf[out] && str[in]; i++) { |
618 | 0 | in += utf8clen(str[in]); |
619 | 0 | out += utf8clen(buf[out]); |
620 | 0 | } |
621 | 0 | if (in != out) memmove(buf+in, buf+out, strlen(buf+out)+1); |
622 | 0 | memcpy(buf, str, in); |
623 | 0 | } else if (ienc == CE_LATIN1 || ienc == CE_BYTES) { |
624 | 0 | in = (int) strlen(str); |
625 | 0 | out = so - sa + 1; |
626 | 0 | memcpy(buf + sa - 1, str, (in < out) ? in : out); |
627 | 0 | } else { |
628 | | /* This cannot work for stateful encodings */ |
629 | 0 | if (mbcslocale) { |
630 | 0 | mbstate_t mb_st_in; |
631 | 0 | mbs_init(&mb_st_in); |
632 | 0 | for (i = 1; i < sa; i++) |
633 | 0 | buf += Mbrtowc(NULL, buf, R_MB_CUR_MAX, &mb_st_in); |
634 | | /* now work out how many bytes to replace by how many */ |
635 | 0 | mbstate_t mb_st_out; |
636 | 0 | mbs_init(&mb_st_out); |
637 | 0 | for (i = sa; i <= so && buf[out] && str[in]; i++) { |
638 | 0 | in += (int) Mbrtowc(NULL, str+in, R_MB_CUR_MAX, &mb_st_in); |
639 | 0 | out += (int) Mbrtowc(NULL, buf+out, R_MB_CUR_MAX, &mb_st_out); |
640 | 0 | } |
641 | 0 | if (in != out) memmove(buf+in, buf+out, strlen(buf+out)+1); |
642 | 0 | memcpy(buf, str, in); |
643 | 0 | } else { |
644 | 0 | in = (int) strlen(str); |
645 | 0 | out = so - sa + 1; |
646 | 0 | memcpy(buf + sa - 1, str, (in < out) ? in : out); |
647 | 0 | } |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | attribute_hidden SEXP |
652 | | do_substrgets(SEXP call, SEXP op, SEXP args, SEXP env) |
653 | 0 | { |
654 | 0 | checkArity(op, args); |
655 | 0 | SEXP x = CAR(args); |
656 | 0 | if (!isString(x)) |
657 | 0 | error(_("replacing substrings in a non-character object")); |
658 | 0 | R_xlen_t len = XLENGTH(x); |
659 | 0 | SEXP s = PROTECT(allocVector(STRSXP, len)); |
660 | |
|
661 | 0 | if (len > 0) { |
662 | 0 | SEXP sa = CADR(args), // start |
663 | 0 | so = CADDR(args); // stop |
664 | 0 | int |
665 | 0 | k = LENGTH(sa), |
666 | 0 | l = LENGTH(so); |
667 | 0 | if (!isInteger(sa) || k == 0 || |
668 | 0 | (so != R_NilValue && (!isInteger(so) || l == 0))) |
669 | 0 | error(_("invalid substring arguments")); |
670 | |
|
671 | 0 | int *starts = INTEGER(sa), *stops = NULL; |
672 | 0 | if (so == R_NilValue) { |
673 | 0 | stops = &int_max; |
674 | 0 | l = 1; |
675 | 0 | } else { |
676 | 0 | stops = INTEGER(so); |
677 | 0 | } |
678 | |
|
679 | 0 | SEXP value = CADDDR(args); |
680 | 0 | int v = LENGTH(value); |
681 | 0 | if (!isString(value) || v == 0) |
682 | 0 | error(_("invalid value")); |
683 | |
|
684 | 0 | void* vmax = vmaxget(); |
685 | 0 | for (R_xlen_t i = 0; i < len; i++) { |
686 | |
|
687 | 0 | int start = starts[i % k], |
688 | 0 | stop = stops [i % l]; |
689 | 0 | SEXP el = STRING_ELT(x, i); |
690 | 0 | SEXP v_el = STRING_ELT(value, i % v); |
691 | 0 | if (el == NA_STRING || v_el == NA_STRING || |
692 | 0 | start == NA_INTEGER || stop == NA_INTEGER) { |
693 | 0 | SET_STRING_ELT(s, i, NA_STRING); |
694 | 0 | continue; |
695 | 0 | } |
696 | | |
697 | 0 | cetype_t ienc = getCharCE(el); |
698 | 0 | const char* ss = CHAR(el); |
699 | 0 | int slen = (int) strlen(ss); |
700 | 0 | if (start < 1) start = 1; |
701 | 0 | if (stop > (int) slen) stop = (int) slen; /* SBCS optimization */ |
702 | 0 | if (start > stop) { |
703 | | /* just copy element across */ |
704 | 0 | SET_STRING_ELT(s, i, STRING_ELT(x, i)); |
705 | 0 | } else { |
706 | 0 | int ienc2 = ienc; |
707 | 0 | const char* v_ss = CHAR(v_el); |
708 | | /* is the value in the same encoding? |
709 | | FIXME: could re-encode to UTF-8 rather than to native. |
710 | | */ |
711 | 0 | cetype_t venc = getCharCE(v_el); |
712 | 0 | if (venc != ienc && !IS_ASCII(v_el)) { |
713 | 0 | ss = translateChar(el); |
714 | 0 | slen = (int) strlen(ss); |
715 | 0 | v_ss = translateChar(v_el); |
716 | 0 | ienc2 = CE_NATIVE; |
717 | 0 | } |
718 | | /* might expand under MBCS */ |
719 | 0 | char* buf = R_AllocStringBuffer(slen+strlen(v_ss), &cbuff); |
720 | 0 | strcpy(buf, ss); |
721 | 0 | substrset(buf, v_ss, ienc2, start, stop, i, i % v); |
722 | 0 | SET_STRING_ELT(s, i, mkCharCE(buf, ienc2)); |
723 | 0 | } |
724 | 0 | } |
725 | 0 | vmaxset(vmax); |
726 | |
|
727 | 0 | R_FreeStringBufferL(&cbuff); |
728 | 0 | } |
729 | 0 | SHALLOW_DUPLICATE_ATTRIB(s, x); |
730 | | /* This copied the class, if any */ |
731 | 0 | UNPROTECT(1); |
732 | 0 | return s; |
733 | 0 | } |
734 | | |
735 | | /* Abbreviate |
736 | | long names in the S-designated fashion: |
737 | | 1) spaces |
738 | | 2) lower case vowels |
739 | | 3) lower case consonants |
740 | | 4) upper case letters |
741 | | 5) special characters. |
742 | | |
743 | | Letters are dropped from the end of words |
744 | | and at least one letter is retained from each word. |
745 | | |
746 | | If unique abbreviations are not produced letters are added until the |
747 | | results are unique (duplicated names are removed prior to entry). |
748 | | names, minlength, use.classes, dot |
749 | | */ |
750 | | |
751 | | |
752 | 0 | #define FIRSTCHAR(i) (isspace((int)s[i-1])) |
753 | 0 | #define LASTCHAR(i) (!isspace((int)s[i-1]) && (!s[i+1] || isspace((int)s[i+1]))) |
754 | 0 | #define LC_VOWEL(i) (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || \ |
755 | 0 | s[i] == 'o' || s[i] == 'u') |
756 | 0 | #define UPPER (int)(strlen(s) - 1) |
757 | | |
758 | | /* memmove does allow overlapping src and dest */ |
759 | | static void mystrcpy(char *dest, const char *src) |
760 | 0 | { |
761 | 0 | memmove(dest, src, strlen(src)+1); |
762 | 0 | } |
763 | | |
764 | | static SEXP stripchars(const char * const inchar, int minlen, int usecl) |
765 | 0 | { |
766 | 0 | int i, j, nspace = 0; |
767 | 0 | char *s = cbuff.data; |
768 | | |
769 | | /* The R wrapper removed leading and trailing spces */ |
770 | 0 | mystrcpy(s, inchar); |
771 | 0 | if (strlen(s) < minlen) goto donesc; |
772 | | |
773 | | /* The for() loops never touch the first character */ |
774 | | |
775 | | /* record spaces for removal later (as they act as word boundaries) */ |
776 | 0 | for (i = UPPER, j = 1; i > 0; i--) { |
777 | 0 | if (isspace((int)s[i])) { |
778 | 0 | if (j) s[i] = '\0'; // trailing space |
779 | 0 | else nspace++; |
780 | 0 | } else j = 0; |
781 | 0 | if (strlen(s) - nspace <= minlen) |
782 | 0 | goto donesc; |
783 | 0 | } |
784 | | |
785 | 0 | if(usecl) { |
786 | | /* remove l/case vowels, |
787 | | which are not at the beginning of a word but are at the end */ |
788 | 0 | for (i = UPPER; i > 0; i--) { |
789 | 0 | if (LC_VOWEL(i) && LASTCHAR(i)) |
790 | 0 | mystrcpy(s + i, s + i + 1); |
791 | 0 | if (strlen(s) - nspace <= minlen) |
792 | 0 | goto donesc; |
793 | 0 | } |
794 | | |
795 | | /* remove those not at the beginning of a word */ |
796 | 0 | for (i = UPPER; i > 0; i--) { |
797 | 0 | if (LC_VOWEL(i) && !FIRSTCHAR(i)) |
798 | 0 | mystrcpy(s + i, s + i + 1); |
799 | 0 | if (strlen(s) - nspace <= minlen) |
800 | 0 | goto donesc; |
801 | 0 | } |
802 | | |
803 | | /* Now do the same for remaining l/case chars */ |
804 | 0 | for (i = UPPER; i > 0; i--) { |
805 | 0 | if (islower((int)s[i]) && LASTCHAR(i)) |
806 | 0 | mystrcpy(s + i, s + i + 1); |
807 | 0 | if (strlen(s) - nspace <= minlen) |
808 | 0 | goto donesc; |
809 | 0 | } |
810 | | |
811 | 0 | for (i = UPPER; i > 0; i--) { |
812 | 0 | if (islower((int)s[i]) && !FIRSTCHAR(i)) |
813 | 0 | mystrcpy(s + i, s + i + 1); |
814 | 0 | if (strlen(s) - nspace <= minlen) |
815 | 0 | goto donesc; |
816 | 0 | } |
817 | 0 | } |
818 | | |
819 | | /* all else has failed so we use brute force */ |
820 | | |
821 | 0 | for (i = UPPER; i > 0; i--) { |
822 | 0 | if (!FIRSTCHAR(i) && !isspace((int)s[i])) |
823 | 0 | mystrcpy(s + i, s + i + 1); |
824 | 0 | if (strlen(s) - nspace <= minlen) |
825 | 0 | goto donesc; |
826 | 0 | } |
827 | | |
828 | 0 | donesc: |
829 | 0 | { // remove internal spaces as required |
830 | 0 | int upper = (int) strlen(s); |
831 | 0 | if (upper > minlen) |
832 | 0 | for (i = upper - 1; i > 0; i--) |
833 | 0 | if (isspace((int)s[i])) |
834 | 0 | mystrcpy(s + i, s + i + 1); |
835 | 0 | } |
836 | |
|
837 | 0 | return mkChar(s); |
838 | 0 | } |
839 | | |
840 | 0 | #define FIRSTCHARW(i) (iswspace((int)wc[i-1])) |
841 | 0 | #define LASTCHARW(i) (!iswspace((int)wc[i-1]) && (!wc[i+1] || iswspace((int)wc[i+1]))) |
842 | 0 | #define WUP (int)(wcslen(wc) - 1) |
843 | | |
844 | | // lower-case vowels in English plus accented versions |
845 | | static int vowels[] = { |
846 | | 0x61, 0x65, 0x69, 0x6f, 0x75, |
847 | | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, |
848 | | 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, |
849 | | 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, |
850 | | 0x101, 0x103, 0x105, 0x113, 0x115, 0x117, 0x119, 0x11b, |
851 | | 0x129, 0x12b, 0x12d, 0x12f, 0x131, 0x14d, 0x14f, 0x151, |
852 | | 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173 |
853 | | }; |
854 | | |
855 | | static Rboolean iswvowel(wchar_t w) |
856 | 0 | { |
857 | 0 | int v = (int) w, n = sizeof(vowels)/sizeof(int); |
858 | 0 | Rboolean found = FALSE; |
859 | 0 | for(int i = 0; i < n; i++) |
860 | 0 | if(v == vowels[i]) {found = TRUE; break;} |
861 | |
|
862 | 0 | return found; |
863 | 0 | } |
864 | | |
865 | | static void mywcscpy(wchar_t *dest, const wchar_t *src) |
866 | 0 | { |
867 | 0 | memmove(dest, src, sizeof(wchar_t) * (wcslen(src)+1)); |
868 | 0 | } |
869 | | |
870 | | static SEXP wstripchars(const wchar_t * const inchar, int minlen, int usecl) |
871 | 0 | { |
872 | 0 | int i, j, nspace = 0; |
873 | 0 | wchar_t *wc = (wchar_t *)cbuff.data; |
874 | |
|
875 | 0 | mywcscpy(wc, inchar); |
876 | 0 | if (wcslen(wc) < minlen) goto donewsc; |
877 | | |
878 | 0 | for (i = WUP, j = 1; i > 0; i--) { |
879 | 0 | if (iswspace((int)wc[i])) { |
880 | 0 | if (j) wc[i] = '\0' ; else nspace++; |
881 | 0 | } else j = 0; |
882 | 0 | if (wcslen(wc) - nspace <= minlen) |
883 | 0 | goto donewsc; |
884 | 0 | } |
885 | | |
886 | 0 | if(usecl) { |
887 | 0 | for (i = WUP; i > 0; i--) { |
888 | 0 | if (iswvowel(wc[i]) && LASTCHARW(i)) |
889 | 0 | mywcscpy(wc + i, wc + i + 1); |
890 | 0 | if (wcslen(wc) - nspace <= minlen) |
891 | 0 | goto donewsc; |
892 | 0 | } |
893 | | |
894 | 0 | for (i = WUP; i > 0; i--) { |
895 | 0 | if (iswvowel(wc[i]) && !FIRSTCHARW(i)) |
896 | 0 | mywcscpy(wc + i, wc + i + 1); |
897 | 0 | if (wcslen(wc) - nspace <= minlen) |
898 | 0 | goto donewsc; |
899 | 0 | } |
900 | | |
901 | 0 | for (i = WUP; i > 0; i--) { |
902 | 0 | if (iswlower((wint_t)wc[i]) && LASTCHARW(i)) |
903 | 0 | mywcscpy(wc + i, wc + i + 1); |
904 | 0 | if (wcslen(wc) - nspace <= minlen) |
905 | 0 | goto donewsc; |
906 | 0 | } |
907 | | |
908 | 0 | for (i = WUP; i > 0; i--) { |
909 | 0 | if (iswlower((wint_t)wc[i]) && !FIRSTCHARW(i)) |
910 | 0 | mywcscpy(wc + i, wc + i + 1); |
911 | 0 | if (wcslen(wc) - nspace <= minlen) |
912 | 0 | goto donewsc; |
913 | 0 | } |
914 | 0 | } |
915 | | |
916 | 0 | for (i = WUP; i > 0; i--) { |
917 | 0 | if (!FIRSTCHARW(i) && !iswspace((int)wc[i])) |
918 | 0 | mywcscpy(wc + i, wc + i + 1); |
919 | 0 | if (wcslen(wc) - nspace <= minlen) |
920 | 0 | goto donewsc; |
921 | 0 | } |
922 | | |
923 | 0 | donewsc: |
924 | |
|
925 | 0 | { |
926 | 0 | int upper = (int) wcslen(wc); |
927 | 0 | if (upper > minlen) |
928 | 0 | for (i = upper - 1; i > 0; i--) |
929 | 0 | if (iswspace((int)wc[i])) mywcscpy(wc + i, wc + i + 1); |
930 | 0 | } |
931 | |
|
932 | 0 | size_t nb = wcstoutf8(NULL, wc, (size_t)INT_MAX + 2); |
933 | 0 | char *cbuf = CallocCharBuf(nb); |
934 | 0 | wcstoutf8(cbuf, wc, nb); |
935 | 0 | SEXP ans = mkCharCE(cbuf, CE_UTF8); |
936 | 0 | R_Free(cbuf); |
937 | 0 | return ans; |
938 | 0 | } |
939 | | |
940 | | |
941 | | attribute_hidden SEXP do_abbrev(SEXP call, SEXP op, SEXP args, SEXP env) |
942 | 0 | { |
943 | 0 | checkArity(op,args); |
944 | 0 | SEXP x = CAR(args); |
945 | |
|
946 | 0 | if (!isString(x)) |
947 | 0 | error(_("the first argument must be a character vector")); |
948 | 0 | int minlen = asInteger(CADR(args)); |
949 | 0 | if (minlen == NA_INTEGER) |
950 | 0 | error(_("invalid '%s' argument"), "minlength"); |
951 | 0 | int usecl = asLogical(CADDR(args)); |
952 | 0 | if (usecl == NA_INTEGER) |
953 | 0 | error(_("invalid '%s' argument"), "use.classes"); |
954 | |
|
955 | 0 | R_xlen_t len = XLENGTH(x); |
956 | 0 | SEXP ans = PROTECT(allocVector(STRSXP, len)); |
957 | 0 | const void *vmax = vmaxget(); |
958 | 0 | Rboolean warn = FALSE; |
959 | 0 | for (R_xlen_t i = 0 ; i < len ; i++) { |
960 | 0 | SEXP el = STRING_ELT(x, i); |
961 | 0 | if (el == NA_STRING) |
962 | 0 | SET_STRING_ELT(ans, i, NA_STRING); |
963 | 0 | else { |
964 | 0 | const char *s = CHAR(el); |
965 | 0 | if (IS_ASCII(el)) { |
966 | 0 | if(strlen(s) > minlen) { |
967 | 0 | R_AllocStringBuffer(strlen(s)+1, &cbuff); |
968 | 0 | SET_STRING_ELT(ans, i, stripchars(s, minlen, usecl)); |
969 | 0 | } else SET_STRING_ELT(ans, i, el); |
970 | 0 | } else { |
971 | 0 | s = translateCharUTF8(el); |
972 | 0 | int nc = (int) utf8towcs(NULL, s, 0); |
973 | 0 | if (nc > minlen) { |
974 | 0 | warn = TRUE; |
975 | 0 | const wchar_t *wc = wtransChar(el); // to WCS-2 on Windows |
976 | 0 | nc = (int) wcslen(wc); |
977 | 0 | R_AllocStringBuffer(sizeof(wchar_t)*(nc+1), &cbuff); |
978 | 0 | SET_STRING_ELT(ans, i, wstripchars(wc, minlen, usecl)); |
979 | 0 | } else SET_STRING_ELT(ans, i, el); |
980 | 0 | } |
981 | 0 | } |
982 | 0 | vmaxset(vmax); // this throws away the result of wtransChar |
983 | 0 | } |
984 | 0 | if (usecl && warn) warning(_("abbreviate used with non-ASCII chars")); |
985 | 0 | SHALLOW_DUPLICATE_ATTRIB(ans, x); |
986 | | /* This copied the class, if any */ |
987 | 0 | R_FreeStringBufferL(&cbuff); |
988 | 0 | UNPROTECT(1); |
989 | 0 | return ans; |
990 | 0 | } |
991 | | |
992 | | attribute_hidden SEXP do_makenames(SEXP call, SEXP op, SEXP args, SEXP env) |
993 | 12 | { |
994 | 12 | SEXP arg, ans; |
995 | 12 | R_xlen_t i, n; |
996 | 12 | int l, allow_; |
997 | 12 | char *p, *tmp = NULL, *cbuf; |
998 | 12 | const char *This; |
999 | 12 | Rboolean need_prefix; |
1000 | 12 | const void *vmax; |
1001 | | |
1002 | 12 | checkArity(op ,args); |
1003 | 12 | arg = CAR(args); |
1004 | 12 | if (!isString(arg)) |
1005 | 0 | error(_("non-character names")); |
1006 | 12 | n = XLENGTH(arg); |
1007 | 12 | allow_ = asLogical(CADR(args)); |
1008 | 12 | if (allow_ == NA_LOGICAL) |
1009 | 0 | error(_("invalid '%s' value"), "allow_"); |
1010 | 12 | PROTECT(ans = allocVector(STRSXP, n)); |
1011 | 12 | vmax = vmaxget(); |
1012 | 84 | for (i = 0 ; i < n ; i++) { |
1013 | 72 | This = translateChar(STRING_ELT(arg, i)); |
1014 | 72 | l = (int) strlen(This); |
1015 | | /* need to prefix names not beginning with alpha or ., as |
1016 | | well as . followed by a number */ |
1017 | 72 | need_prefix = FALSE; |
1018 | 72 | if (mbcslocale && This[0]) { |
1019 | 0 | int nc = l, used; |
1020 | 0 | wchar_t wc; |
1021 | 0 | mbstate_t mb_st; |
1022 | 0 | const char *pp = This; |
1023 | 0 | mbs_init(&mb_st); |
1024 | 0 | used = (int) Mbrtowc(&wc, pp, R_MB_CUR_MAX, &mb_st); |
1025 | 0 | pp += used; nc -= used; |
1026 | 0 | if (wc == L'.') { |
1027 | 0 | if (nc > 0) { |
1028 | 0 | Mbrtowc(&wc, pp, R_MB_CUR_MAX, &mb_st); |
1029 | 0 | if (iswdigit(wc)) need_prefix = TRUE; |
1030 | 0 | } |
1031 | 0 | } else if (!iswalpha(wc)) need_prefix = TRUE; |
1032 | 72 | } else { |
1033 | 72 | if (This[0] == '.') { |
1034 | 0 | if (l >= 1 && isdigit(0xff & (int) This[1])) need_prefix = TRUE; |
1035 | 72 | } else if (!isalpha(0xff & (int) This[0])) need_prefix = TRUE; |
1036 | 72 | } |
1037 | 72 | if (need_prefix) { |
1038 | 0 | tmp = R_Calloc(l+2, char); |
1039 | 0 | strcpy(tmp, "X"); |
1040 | 0 | strcat(tmp, translateChar(STRING_ELT(arg, i))); |
1041 | 72 | } else { |
1042 | 72 | tmp = R_Calloc(l+1, char); |
1043 | 72 | strcpy(tmp, translateChar(STRING_ELT(arg, i))); |
1044 | 72 | } |
1045 | 72 | if (mbcslocale) { |
1046 | | /* This cannot lengthen the string, so safe to overwrite it. */ |
1047 | 0 | int nc = (int) mbstowcs(NULL, tmp, 0); |
1048 | 0 | if (nc >= 0) { |
1049 | 0 | wchar_t *wstr = R_Calloc(nc+1, wchar_t); |
1050 | 0 | mbstowcs(wstr, tmp, nc+1); |
1051 | 0 | for (wchar_t * wc = wstr; *wc; wc++) { |
1052 | 0 | if (*wc == L'.' || (allow_ && *wc == L'_')) |
1053 | 0 | /* leave alone */; |
1054 | 0 | else if (!iswalnum((int)*wc)) *wc = L'.'; |
1055 | 0 | } |
1056 | 0 | wcstombs(tmp, wstr, strlen(tmp)+1); |
1057 | 0 | R_Free(wstr); |
1058 | 0 | } else error(_("invalid multibyte string %lld"), (long long)i+1); |
1059 | 72 | } else { |
1060 | 612 | for (p = tmp; *p; p++) { |
1061 | 540 | if (*p == '.' || (allow_ && *p == '_')) /* leave alone */; |
1062 | 504 | else if (!isalnum(0xff & (int)*p)) *p = '.'; |
1063 | | /* else leave alone */ |
1064 | 540 | } |
1065 | 72 | } |
1066 | 72 | SET_STRING_ELT(ans, i, mkChar(tmp)); |
1067 | | /* do we have a reserved word? If so the name is invalid */ |
1068 | 72 | if (!isValidName(tmp)) { |
1069 | | /* FIXME: could use R_Realloc instead */ |
1070 | 0 | cbuf = CallocCharBuf(strlen(tmp) + 1); |
1071 | 0 | strcpy(cbuf, tmp); |
1072 | 0 | strcat(cbuf, "."); |
1073 | 0 | SET_STRING_ELT(ans, i, mkChar(cbuf)); |
1074 | 0 | R_Free(cbuf); |
1075 | 0 | } |
1076 | 72 | R_Free(tmp); |
1077 | 72 | vmaxset(vmax); |
1078 | 72 | } |
1079 | 12 | UNPROTECT(1); |
1080 | 12 | return ans; |
1081 | 12 | } |
1082 | | |
1083 | | |
1084 | | attribute_hidden SEXP do_tolower(SEXP call, SEXP op, SEXP args, SEXP env) |
1085 | 0 | { |
1086 | 0 | SEXP x, y; |
1087 | 0 | R_xlen_t i, n; |
1088 | 0 | int ul; |
1089 | 0 | char *p; |
1090 | 0 | SEXP el; |
1091 | 0 | cetype_t ienc; |
1092 | 0 | Rboolean use_UTF8 = FALSE; |
1093 | 0 | const void *vmax; |
1094 | |
|
1095 | 0 | checkArity(op, args); |
1096 | 0 | ul = PRIMVAL(op); /* 0 = tolower, 1 = toupper */ |
1097 | |
|
1098 | 0 | x = CAR(args); |
1099 | | /* coercion is done in wrapper */ |
1100 | 0 | if (!isString(x)) error(_("non-character argument")); |
1101 | 0 | n = XLENGTH(x); |
1102 | 0 | PROTECT(y = allocVector(STRSXP, n)); |
1103 | 0 | for (i = 0; i < n; i++) { |
1104 | 0 | SEXP xi = STRING_ELT(x, i); |
1105 | 0 | if (IS_UTF8(xi) || |
1106 | 0 | (!latin1locale && IS_LATIN1(xi))) use_UTF8 = TRUE; |
1107 | 0 | } |
1108 | 0 | if (mbcslocale || use_UTF8 == TRUE) { |
1109 | 0 | int nb, nc, j; |
1110 | 0 | #ifndef USE_RI18N_CASE |
1111 | 0 | wctrans_t tr = wctrans(ul ? "toupper" : "tolower"); |
1112 | 0 | #endif |
1113 | 0 | wchar_t * wc; |
1114 | 0 | char * cbuf; |
1115 | |
|
1116 | 0 | vmax = vmaxget(); |
1117 | | /* the translated string need not be the same length in bytes */ |
1118 | 0 | for (i = 0; i < n; i++) { |
1119 | 0 | el = STRING_ELT(x, i); |
1120 | 0 | if (el == NA_STRING) SET_STRING_ELT(y, i, NA_STRING); |
1121 | 0 | else { |
1122 | | /* FIXME: in Windows UTF-8 locales, use UTF-8 branch */ |
1123 | 0 | const char *xi; |
1124 | 0 | ienc = getCharCE(el); |
1125 | 0 | if (use_UTF8 && ienc == CE_UTF8) { |
1126 | 0 | xi = CHAR(el); |
1127 | | // could overcount if there are conjugate pairs |
1128 | 0 | nc = (int) utf8towcs(NULL, xi, 0); |
1129 | 0 | } else if (use_UTF8 && ienc == CE_LATIN1) { |
1130 | 0 | xi = translateCharUTF8(el); // in case it is really in CP1252 |
1131 | 0 | nc = (int) utf8towcs(NULL, xi, 0); |
1132 | 0 | ienc = CE_UTF8; |
1133 | 0 | } else { |
1134 | 0 | xi = translateChar(el); |
1135 | 0 | nc = (int) mbstowcs(NULL, xi, 0); |
1136 | 0 | ienc = CE_NATIVE; |
1137 | 0 | } |
1138 | 0 | if (nc >= 0) { |
1139 | 0 | if (ienc == CE_UTF8) { |
1140 | | #ifdef USE_RI18N_CASE |
1141 | | R_wchar_t *wcr = (R_wchar_t *) |
1142 | | R_AllocStringBuffer((nc+1)*sizeof(R_wchar_t), &cbuff); |
1143 | | utf8towcs4(wcr, xi, nc + 1); |
1144 | | if (ul) |
1145 | | for (j = 0; j < nc; j++) |
1146 | | wcr[j] = Ri18n_towupper(wcr[j]); |
1147 | | else |
1148 | | for (j = 0; j < nc; j++) |
1149 | | wcr[j] = Ri18n_towlower(wcr[j]); |
1150 | | nb = (int) wcs4toutf8(NULL, wcr, INT_MAX); |
1151 | | cbuf = CallocCharBuf(nb); |
1152 | | wcs4toutf8(cbuf, wcr, nb); |
1153 | | SET_STRING_ELT(y, i, mkCharCE(cbuf, CE_UTF8)); |
1154 | | #else |
1155 | 0 | wc = (wchar_t *) |
1156 | 0 | R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1157 | 0 | utf8towcs(wc, xi, nc + 1); |
1158 | 0 | for (j = 0; j < nc; j++) wc[j] = towctrans(wc[j], tr); |
1159 | 0 | nb = (int) wcstoutf8(NULL, wc, INT_MAX); |
1160 | 0 | cbuf = CallocCharBuf(nb); |
1161 | 0 | wcstoutf8(cbuf, wc, nb); |
1162 | 0 | SET_STRING_ELT(y, i, mkCharCE(cbuf, CE_UTF8)); |
1163 | 0 | #endif |
1164 | 0 | } else { |
1165 | 0 | wc = (wchar_t *) |
1166 | 0 | R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1167 | 0 | mbstowcs(wc, xi, nc + 1); |
1168 | | #ifdef USE_RI18N_CASE |
1169 | | if (ul) |
1170 | | for (j = 0; j < nc; j++) |
1171 | | wc[j] = Ri18n_towupper(wc[j]); |
1172 | | else |
1173 | | for (j = 0; j < nc; j++) |
1174 | | wc[j] = Ri18n_towlower(wc[j]); |
1175 | | #else |
1176 | | /* This cannot cope with surrogate pairs, |
1177 | | if mbstowcs can make them. */ |
1178 | 0 | for (j = 0; j < nc; j++) wc[j] = towctrans(wc[j], tr); |
1179 | 0 | #endif |
1180 | 0 | nb = (int) wcstombs(NULL, wc, 0); |
1181 | 0 | cbuf = CallocCharBuf(nb); |
1182 | 0 | wcstombs(cbuf, wc, nb + 1); |
1183 | 0 | SET_STRING_ELT(y, i, markKnown(cbuf, el)); |
1184 | 0 | } |
1185 | 0 | R_Free(cbuf); |
1186 | 0 | } else { |
1187 | 0 | error(_("invalid multibyte string %lld"), (long long)i+1); |
1188 | 0 | } |
1189 | 0 | } |
1190 | 0 | vmaxset(vmax); |
1191 | 0 | } |
1192 | 0 | R_FreeStringBufferL(&cbuff); |
1193 | 0 | } else { |
1194 | 0 | char *xi; |
1195 | 0 | vmax = vmaxget(); |
1196 | 0 | for (i = 0; i < n; i++) { |
1197 | 0 | if (STRING_ELT(x, i) == NA_STRING) |
1198 | 0 | SET_STRING_ELT(y, i, NA_STRING); |
1199 | 0 | else { |
1200 | 0 | xi = CallocCharBuf(strlen(CHAR(STRING_ELT(x, i)))); |
1201 | 0 | strcpy(xi, translateChar(STRING_ELT(x, i))); |
1202 | 0 | for (p = xi; *p != '\0'; p++) |
1203 | 0 | *p = (char) (ul ? toupper(*p) : tolower(*p)); |
1204 | 0 | SET_STRING_ELT(y, i, markKnown(xi, STRING_ELT(x, i))); |
1205 | 0 | R_Free(xi); |
1206 | 0 | } |
1207 | 0 | vmaxset(vmax); |
1208 | 0 | } |
1209 | 0 | } |
1210 | 0 | SHALLOW_DUPLICATE_ATTRIB(y, x); |
1211 | | /* This copied the class, if any */ |
1212 | 0 | UNPROTECT(1); |
1213 | 0 | return(y); |
1214 | 0 | } |
1215 | | |
1216 | | |
1217 | | /* These assume one wchar_t per char so will not work with surrogate pairs */ |
1218 | | typedef enum { WTR_INIT, WTR_CHAR, WTR_RANGE } wtr_type; |
1219 | | struct wtr_spec { |
1220 | | wtr_type type; |
1221 | | struct wtr_spec *next; |
1222 | | union { |
1223 | | wchar_t c; |
1224 | | struct { |
1225 | | wchar_t first; |
1226 | | wchar_t last; |
1227 | | } r; |
1228 | | } u; |
1229 | | }; |
1230 | | |
1231 | | static void |
1232 | 0 | wtr_build_spec(const wchar_t *s, struct wtr_spec *trs) { |
1233 | 0 | int i, len = (int) wcslen(s); |
1234 | 0 | struct wtr_spec *This, *_new; |
1235 | |
|
1236 | 0 | This = trs; |
1237 | 0 | for (i = 0; i < len - 2; ) { |
1238 | 0 | _new = R_Calloc(1, struct wtr_spec); |
1239 | 0 | _new->next = NULL; |
1240 | 0 | if (s[i + 1] == L'-') { |
1241 | 0 | _new->type = WTR_RANGE; |
1242 | 0 | if (s[i] > s[i + 2]) |
1243 | 0 | error(_("decreasing range specification ('%lc-%lc')"), |
1244 | 0 | (wint_t)s[i], (wint_t)s[i + 2]); |
1245 | 0 | _new->u.r.first = s[i]; |
1246 | 0 | _new->u.r.last = s[i + 2]; |
1247 | 0 | i = i + 3; |
1248 | 0 | } else { |
1249 | 0 | _new->type = WTR_CHAR; |
1250 | 0 | _new->u.c = s[i]; |
1251 | 0 | i++; |
1252 | 0 | } |
1253 | 0 | This = This->next = _new; |
1254 | 0 | } |
1255 | 0 | for ( ; i < len; i++) { |
1256 | 0 | _new = R_Calloc(1, struct wtr_spec); |
1257 | 0 | _new->next = NULL; |
1258 | 0 | _new->type = WTR_CHAR; |
1259 | 0 | _new->u.c = s[i]; |
1260 | 0 | This = This->next = _new; |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | | static void |
1265 | 0 | wtr_free_spec(struct wtr_spec *trs) { |
1266 | 0 | struct wtr_spec *This, *next; |
1267 | 0 | This = trs; |
1268 | 0 | while(This) { |
1269 | 0 | next = This->next; |
1270 | 0 | R_Free(This); |
1271 | 0 | This = next; |
1272 | 0 | } |
1273 | 0 | } |
1274 | | |
1275 | | static wchar_t |
1276 | 0 | wtr_get_next_char_from_spec(struct wtr_spec **p) { |
1277 | 0 | wchar_t c; |
1278 | 0 | struct wtr_spec *This; |
1279 | |
|
1280 | 0 | This = *p; |
1281 | 0 | if (!This) |
1282 | 0 | return('\0'); |
1283 | 0 | switch(This->type) { |
1284 | | /* Note: this code does not deal with the WTR_INIT case. */ |
1285 | 0 | case WTR_CHAR: |
1286 | 0 | c = This->u.c; |
1287 | 0 | *p = This->next; |
1288 | 0 | break; |
1289 | 0 | case WTR_RANGE: |
1290 | 0 | c = This->u.r.first; |
1291 | 0 | if (c == This->u.r.last) { |
1292 | 0 | *p = This->next; |
1293 | 0 | } else { |
1294 | 0 | (This->u.r.first)++; |
1295 | 0 | } |
1296 | 0 | break; |
1297 | 0 | default: |
1298 | 0 | c = L'\0'; |
1299 | 0 | break; |
1300 | 0 | } |
1301 | 0 | return(c); |
1302 | 0 | } |
1303 | | |
1304 | | typedef enum { TR_INIT, TR_CHAR, TR_RANGE } tr_spec_type; |
1305 | | struct tr_spec { |
1306 | | tr_spec_type type; |
1307 | | struct tr_spec *next; |
1308 | | union { |
1309 | | unsigned char c; |
1310 | | struct { |
1311 | | unsigned char first; |
1312 | | unsigned char last; |
1313 | | } r; |
1314 | | } u; |
1315 | | }; |
1316 | | |
1317 | | static void |
1318 | 0 | tr_build_spec(const char *s, struct tr_spec *trs) { |
1319 | 0 | int i, len = (int) strlen(s); |
1320 | 0 | struct tr_spec *This, *_new; |
1321 | |
|
1322 | 0 | This = trs; |
1323 | 0 | for (i = 0; i < len - 2; ) { |
1324 | 0 | _new = R_Calloc(1, struct tr_spec); |
1325 | 0 | _new->next = NULL; |
1326 | 0 | if (s[i + 1] == '-') { |
1327 | 0 | _new->type = TR_RANGE; |
1328 | 0 | if (s[i] > s[i + 2]) |
1329 | 0 | error(_("decreasing range specification ('%c-%c')"), |
1330 | 0 | s[i], s[i + 2]); |
1331 | 0 | _new->u.r.first = s[i]; |
1332 | 0 | _new->u.r.last = s[i + 2]; |
1333 | 0 | i = i + 3; |
1334 | 0 | } else { |
1335 | 0 | _new->type = TR_CHAR; |
1336 | 0 | _new->u.c = s[i]; |
1337 | 0 | i++; |
1338 | 0 | } |
1339 | 0 | This = This->next = _new; |
1340 | 0 | } |
1341 | 0 | for ( ; i < len; i++) { |
1342 | 0 | _new = R_Calloc(1, struct tr_spec); |
1343 | 0 | _new->next = NULL; |
1344 | 0 | _new->type = TR_CHAR; |
1345 | 0 | _new->u.c = s[i]; |
1346 | 0 | This = This->next = _new; |
1347 | 0 | } |
1348 | 0 | } |
1349 | | |
1350 | | static void |
1351 | 0 | tr_free_spec(struct tr_spec *trs) { |
1352 | 0 | struct tr_spec *This, *next; |
1353 | 0 | This = trs; |
1354 | 0 | while(This) { |
1355 | 0 | next = This->next; |
1356 | 0 | R_Free(This); |
1357 | 0 | This = next; |
1358 | 0 | } |
1359 | 0 | } |
1360 | | |
1361 | | static unsigned char |
1362 | 0 | tr_get_next_char_from_spec(struct tr_spec **p) { |
1363 | 0 | unsigned char c; |
1364 | 0 | struct tr_spec *This; |
1365 | |
|
1366 | 0 | This = *p; |
1367 | 0 | if (!This) |
1368 | 0 | return('\0'); |
1369 | 0 | switch(This->type) { |
1370 | | /* Note: this code does not deal with the TR_INIT case. */ |
1371 | 0 | case TR_CHAR: |
1372 | 0 | c = This->u.c; |
1373 | 0 | *p = This->next; |
1374 | 0 | break; |
1375 | 0 | case TR_RANGE: |
1376 | 0 | c = This->u.r.first; |
1377 | 0 | if (c == This->u.r.last) { |
1378 | 0 | *p = This->next; |
1379 | 0 | } else { |
1380 | 0 | (This->u.r.first)++; |
1381 | 0 | } |
1382 | 0 | break; |
1383 | 0 | default: |
1384 | 0 | c = '\0'; |
1385 | 0 | break; |
1386 | 0 | } |
1387 | 0 | return(c); |
1388 | 0 | } |
1389 | | |
1390 | | typedef struct { wchar_t c_old, c_new; } xtable_t; |
1391 | | |
1392 | | static R_INLINE int xtable_comp(const void *a, const void *b) |
1393 | 0 | { |
1394 | 0 | return ((xtable_t *)a)->c_old - ((xtable_t *)b)->c_old; |
1395 | 0 | } |
1396 | | |
1397 | | static R_INLINE int xtable_key_comp(const void *a, const void *b) |
1398 | 0 | { |
1399 | 0 | return *((wchar_t *)a) - ((xtable_t *)b)->c_old; |
1400 | 0 | } |
1401 | | |
1402 | 0 | #define SWAP(_a, _b, _TYPE) \ |
1403 | 0 | { \ |
1404 | 0 | _TYPE _t; \ |
1405 | 0 | _t = *(_a); \ |
1406 | 0 | *(_a) = *(_b); \ |
1407 | 0 | *(_b) = _t; \ |
1408 | 0 | } |
1409 | | |
1410 | 0 | #define ISORT(_base,_num,_TYPE,_comp) \ |
1411 | 0 | { \ |
1412 | 0 | /* insert sort */ \ |
1413 | 0 | /* require stable data */ \ |
1414 | 0 | int _i, _j ; \ |
1415 | 0 | for ( _i = 1 ; _i < _num ; _i++ ) \ |
1416 | 0 | for ( _j = _i; _j > 0 && \ |
1417 | 0 | (*_comp)(_base+_j-1, _base+_j)>0; _j--) \ |
1418 | 0 | SWAP(_base+_j-1, _base+_j, _TYPE); \ |
1419 | 0 | } |
1420 | | |
1421 | 0 | #define COMPRESS(_base,_num,_TYPE,_comp) \ |
1422 | 0 | { \ |
1423 | 0 | /* suppress even c_old. last use */ \ |
1424 | 0 | int _i,_j ; \ |
1425 | 0 | for ( _i = 0 ; _i < (*(_num)) - 1 ; _i++ ){ \ |
1426 | 0 | int rc = (*_comp)(_base+_i, _base+_i+1); \ |
1427 | 0 | if (rc == 0){ \ |
1428 | 0 | for ( _j = _i, _i-- ; _j < (*(_num)) - 1; _j++ ) \ |
1429 | 0 | *((_base)+_j) = *((_base)+_j+1); \ |
1430 | 0 | (*(_num))--; \ |
1431 | 0 | } \ |
1432 | 0 | } \ |
1433 | 0 | } |
1434 | | |
1435 | 0 | #define BSEARCH(_rc,_key,_base,_nmemb,_TYPE,_comp) \ |
1436 | 0 | { \ |
1437 | 0 | size_t l, u, idx; \ |
1438 | 0 | _TYPE *p; \ |
1439 | 0 | int comp; \ |
1440 | 0 | l = 0; \ |
1441 | 0 | u = _nmemb; \ |
1442 | 0 | _rc = NULL; \ |
1443 | 0 | while (l < u) \ |
1444 | 0 | { \ |
1445 | 0 | idx = (l + u) / 2; \ |
1446 | 0 | p = (_base) + idx; \ |
1447 | 0 | comp = (*_comp)(_key, p); \ |
1448 | 0 | if (comp < 0) \ |
1449 | 0 | u = idx; \ |
1450 | 0 | else if (comp > 0) \ |
1451 | 0 | l = idx + 1; \ |
1452 | 0 | else{ \ |
1453 | 0 | _rc = p; \ |
1454 | 0 | break; \ |
1455 | 0 | } \ |
1456 | 0 | } \ |
1457 | 0 | } |
1458 | | |
1459 | | attribute_hidden SEXP do_chartr(SEXP call, SEXP op, SEXP args, SEXP env) |
1460 | 0 | { |
1461 | 0 | SEXP old, _new, x, y; |
1462 | 0 | R_xlen_t i, n; |
1463 | 0 | char *cbuf; |
1464 | 0 | SEXP el; |
1465 | 0 | cetype_t ienc; |
1466 | 0 | Rboolean use_WC = FALSE; |
1467 | 0 | const void *vmax; |
1468 | |
|
1469 | 0 | checkArity(op, args); |
1470 | 0 | old = CAR(args); args = CDR(args); |
1471 | 0 | _new = CAR(args); args = CDR(args); |
1472 | 0 | x = CAR(args); |
1473 | 0 | n = XLENGTH(x); |
1474 | 0 | if (!isString(old) || LENGTH(old) < 1 || STRING_ELT(old, 0) == NA_STRING) |
1475 | 0 | error(_("invalid '%s' argument"), "old"); |
1476 | 0 | if (LENGTH(old) > 1) |
1477 | 0 | warning(_("argument '%s' has length > 1 and only the first element will be used"), "old"); |
1478 | 0 | if (!isString(_new) || LENGTH(_new) < 1 || STRING_ELT(_new, 0) == NA_STRING) |
1479 | 0 | error(_("invalid '%s' argument"), "new"); |
1480 | 0 | if (LENGTH(_new) > 1) |
1481 | 0 | warning(_("argument '%s' has length > 1 and only the first element will be used"), "new"); |
1482 | 0 | if (!isString(x)) error("invalid '%s' argument", "x"); |
1483 | | |
1484 | | /* If we have marked strings we want to do this in Unicode as some |
1485 | | * of them might be mis-represented by translateChar. But |
1486 | | * utf8towcs may not be reliable unless TO_WCS_OK is defined. |
1487 | | */ |
1488 | 0 | for (i = 0; i < n; i++) { |
1489 | 0 | SEXP xi = STRING_ELT(x, i); |
1490 | 0 | if (IS_UTF8(xi) || (!latin1locale && IS_LATIN1(xi))) use_WC = TRUE; |
1491 | 0 | } |
1492 | |
|
1493 | 0 | if (IS_UTF8(STRING_ELT(old, 0)) || |
1494 | 0 | (!latin1locale && IS_LATIN1(STRING_ELT(old, 0)))) use_WC = TRUE; |
1495 | 0 | if (IS_UTF8(STRING_ELT(_new, 0)) || |
1496 | 0 | (!latin1locale && IS_LATIN1(STRING_ELT(_new, 0)))) use_WC = TRUE; |
1497 | |
|
1498 | 0 | if (mbcslocale || use_WC == TRUE) { |
1499 | 0 | int j, nb, nc; |
1500 | 0 | xtable_t *xtable, *tbl; |
1501 | 0 | int xtable_cnt; |
1502 | 0 | struct wtr_spec *trs_cnt, **trs_cnt_ptr; |
1503 | 0 | wchar_t c_old, c_new, *wc; |
1504 | 0 | const char *xi, *s; |
1505 | 0 | struct wtr_spec *trs_old, **trs_old_ptr; |
1506 | 0 | struct wtr_spec *trs_new, **trs_new_ptr; |
1507 | | |
1508 | | /* Initialize the old and new wtr_spec lists. */ |
1509 | 0 | trs_old = R_Calloc(1, struct wtr_spec); |
1510 | 0 | trs_old->type = WTR_INIT; |
1511 | 0 | trs_old->next = NULL; |
1512 | 0 | trs_new = R_Calloc(1, struct wtr_spec); |
1513 | 0 | trs_new->type = WTR_INIT; |
1514 | 0 | trs_new->next = NULL; |
1515 | | /* Build the old and new wtr_spec lists. */ |
1516 | 0 | if (use_WC && IS_UTF8(STRING_ELT(old, 0))) { |
1517 | 0 | s = CHAR(STRING_ELT(old, 0)); |
1518 | 0 | nc = (int) utf8towcs(NULL, s, 0); |
1519 | 0 | if (nc < 0) error(_("invalid UTF-8 string 'old'")); |
1520 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1521 | 0 | utf8towcs(wc, s, nc + 1); |
1522 | 0 | } else if (use_WC && IS_LATIN1(STRING_ELT(old, 0))) { |
1523 | 0 | s = translateCharUTF8(STRING_ELT(old, 0)); |
1524 | 0 | nc = (int) utf8towcs(NULL, s, 0); |
1525 | 0 | if (nc < 0) error(_("invalid UTF-8 string 'old'")); // but must be valid |
1526 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1527 | 0 | utf8towcs(wc, s, nc + 1); |
1528 | 0 | } else { |
1529 | 0 | s = translateChar(STRING_ELT(old, 0)); |
1530 | 0 | nc = (int) mbstowcs(NULL, s, 0); |
1531 | 0 | if (nc < 0) error(_("invalid multibyte string 'old'")); |
1532 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1533 | 0 | mbstowcs(wc, s, nc + 1); |
1534 | 0 | } |
1535 | 0 | wtr_build_spec(wc, trs_old); |
1536 | 0 | trs_cnt = R_Calloc(1, struct wtr_spec); |
1537 | 0 | trs_cnt->type = WTR_INIT; |
1538 | 0 | trs_cnt->next = NULL; |
1539 | 0 | wtr_build_spec(wc, trs_cnt); /* use count only */ |
1540 | |
|
1541 | 0 | if (use_WC && IS_UTF8(STRING_ELT(_new, 0))) { |
1542 | 0 | s = CHAR(STRING_ELT(_new, 0)); |
1543 | 0 | nc = (int) utf8towcs(NULL, s, 0); |
1544 | 0 | if (nc < 0) error(_("invalid UTF-8 string 'new'")); |
1545 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1546 | 0 | utf8towcs(wc, s, nc + 1); |
1547 | 0 | } else if (use_WC && IS_LATIN1(STRING_ELT(_new, 0))) { |
1548 | 0 | s = translateCharUTF8(STRING_ELT(_new, 0)); |
1549 | 0 | nc = (int) utf8towcs(NULL, s, 0); |
1550 | 0 | if (nc < 0) error(_("invalid UTF-8 string 'new'")); |
1551 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1552 | 0 | utf8towcs(wc, s, nc + 1); |
1553 | 0 | } else { |
1554 | 0 | s = translateChar(STRING_ELT(_new, 0)); |
1555 | 0 | nc = (int) mbstowcs(NULL, s, 0); |
1556 | 0 | if (nc < 0) error(_("invalid multibyte string 'new'")); |
1557 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), &cbuff); |
1558 | 0 | mbstowcs(wc, s, nc + 1); |
1559 | 0 | } |
1560 | 0 | wtr_build_spec(wc, trs_new); |
1561 | | |
1562 | | /* Initialize the pointers for walking through the old and new |
1563 | | wtr_spec lists and retrieving the next chars from the lists. |
1564 | | */ |
1565 | |
|
1566 | 0 | trs_cnt_ptr = R_Calloc(1, struct wtr_spec *); |
1567 | 0 | *trs_cnt_ptr = trs_cnt->next; |
1568 | 0 | for (xtable_cnt = 0 ; wtr_get_next_char_from_spec(trs_cnt_ptr); |
1569 | 0 | xtable_cnt++) ; |
1570 | 0 | wtr_free_spec(trs_cnt); |
1571 | 0 | R_Free(trs_cnt_ptr); |
1572 | 0 | xtable = (xtable_t *) R_alloc(xtable_cnt+1, sizeof(xtable_t)); |
1573 | |
|
1574 | 0 | trs_old_ptr = R_Calloc(1, struct wtr_spec *); |
1575 | 0 | *trs_old_ptr = trs_old->next; |
1576 | 0 | trs_new_ptr = R_Calloc(1, struct wtr_spec *); |
1577 | 0 | *trs_new_ptr = trs_new->next; |
1578 | 0 | for (i = 0; ; i++) { |
1579 | 0 | c_old = wtr_get_next_char_from_spec(trs_old_ptr); |
1580 | 0 | c_new = wtr_get_next_char_from_spec(trs_new_ptr); |
1581 | 0 | if (c_old == '\0') |
1582 | 0 | break; |
1583 | 0 | else if (c_new == '\0') |
1584 | 0 | error(_("'old' is longer than 'new'")); |
1585 | 0 | else { |
1586 | 0 | xtable[i].c_old = c_old; |
1587 | 0 | xtable[i].c_new = c_new; |
1588 | 0 | } |
1589 | 0 | } |
1590 | | |
1591 | | /* Free the memory occupied by the wtr_spec lists. */ |
1592 | 0 | wtr_free_spec(trs_old); |
1593 | 0 | wtr_free_spec(trs_new); |
1594 | 0 | R_Free(trs_old_ptr); R_Free(trs_new_ptr); |
1595 | |
|
1596 | 0 | ISORT(xtable, xtable_cnt, xtable_t , xtable_comp); |
1597 | 0 | COMPRESS(xtable, &xtable_cnt, xtable_t, xtable_comp); |
1598 | |
|
1599 | 0 | PROTECT(y = allocVector(STRSXP, n)); |
1600 | 0 | vmax = vmaxget(); |
1601 | 0 | for (i = 0; i < n; i++) { |
1602 | 0 | el = STRING_ELT(x,i); |
1603 | 0 | if (el == NA_STRING) |
1604 | 0 | SET_STRING_ELT(y, i, NA_STRING); |
1605 | 0 | else { |
1606 | 0 | ienc = getCharCE(el); |
1607 | 0 | if (use_WC && ienc == CE_UTF8) { |
1608 | 0 | xi = CHAR(el); |
1609 | 0 | nc = (int) utf8towcs(NULL, xi, 0); |
1610 | 0 | } else { |
1611 | 0 | xi = translateChar(el); |
1612 | 0 | nc = (int) mbstowcs(NULL, xi, 0); |
1613 | 0 | ienc = CE_NATIVE; |
1614 | 0 | } |
1615 | 0 | if (nc < 0) |
1616 | 0 | error(_("invalid input multibyte string %lld"), |
1617 | 0 | (long long)i+1); |
1618 | 0 | wc = (wchar_t *) R_AllocStringBuffer((nc+1)*sizeof(wchar_t), |
1619 | 0 | &cbuff); |
1620 | 0 | if (ienc == CE_UTF8) utf8towcs(wc, xi, nc + 1); |
1621 | 0 | else mbstowcs(wc, xi, nc + 1); |
1622 | 0 | for (j = 0; j < nc; j++){ |
1623 | 0 | BSEARCH(tbl,&wc[j], xtable, xtable_cnt, |
1624 | 0 | xtable_t, xtable_key_comp); |
1625 | 0 | if (tbl) wc[j] = tbl->c_new; |
1626 | 0 | } |
1627 | 0 | if (ienc == CE_UTF8) { |
1628 | 0 | nb = (int) wcstoutf8(NULL, wc, INT_MAX); |
1629 | 0 | cbuf = CallocCharBuf(nb); |
1630 | 0 | wcstoutf8(cbuf, wc, nb); |
1631 | 0 | SET_STRING_ELT(y, i, mkCharCE(cbuf, CE_UTF8)); |
1632 | 0 | } else { |
1633 | 0 | nb = (int) wcstombs(NULL, wc, 0); |
1634 | 0 | cbuf = CallocCharBuf(nb); |
1635 | 0 | wcstombs(cbuf, wc, nb + 1); |
1636 | 0 | SET_STRING_ELT(y, i, markKnown(cbuf, el)); |
1637 | 0 | } |
1638 | 0 | R_Free(cbuf); |
1639 | 0 | } |
1640 | 0 | vmaxset(vmax); |
1641 | 0 | } |
1642 | 0 | R_FreeStringBufferL(&cbuff); |
1643 | 0 | } else { |
1644 | 0 | unsigned char xtable[UCHAR_MAX + 1], *p, c_old, c_new; |
1645 | 0 | struct tr_spec *trs_old, **trs_old_ptr; |
1646 | 0 | struct tr_spec *trs_new, **trs_new_ptr; |
1647 | |
|
1648 | 0 | for (unsigned int ii = 0; ii <= UCHAR_MAX; ii++) |
1649 | 0 | xtable[ii] = (unsigned char) ii; |
1650 | | |
1651 | | /* Initialize the old and new tr_spec lists. */ |
1652 | 0 | trs_old = R_Calloc(1, struct tr_spec); |
1653 | 0 | trs_old->type = TR_INIT; |
1654 | 0 | trs_old->next = NULL; |
1655 | 0 | trs_new = R_Calloc(1, struct tr_spec); |
1656 | 0 | trs_new->type = TR_INIT; |
1657 | 0 | trs_new->next = NULL; |
1658 | | /* Build the old and new tr_spec lists. */ |
1659 | 0 | tr_build_spec(translateChar(STRING_ELT(old, 0)), trs_old); |
1660 | 0 | tr_build_spec(translateChar(STRING_ELT(_new, 0)), trs_new); |
1661 | | /* Initialize the pointers for walking through the old and new |
1662 | | tr_spec lists and retrieving the next chars from the lists. |
1663 | | */ |
1664 | 0 | trs_old_ptr = R_Calloc(1, struct tr_spec *); |
1665 | 0 | *trs_old_ptr = trs_old->next; |
1666 | 0 | trs_new_ptr = R_Calloc(1, struct tr_spec *); |
1667 | 0 | *trs_new_ptr = trs_new->next; |
1668 | 0 | for (;;) { |
1669 | 0 | c_old = tr_get_next_char_from_spec(trs_old_ptr); |
1670 | 0 | c_new = tr_get_next_char_from_spec(trs_new_ptr); |
1671 | 0 | if (c_old == '\0') |
1672 | 0 | break; |
1673 | 0 | else if (c_new == '\0') |
1674 | 0 | error(_("'old' is longer than 'new'")); |
1675 | 0 | else |
1676 | 0 | xtable[c_old] = c_new; |
1677 | 0 | } |
1678 | | /* Free the memory occupied by the tr_spec lists. */ |
1679 | 0 | tr_free_spec(trs_old); |
1680 | 0 | tr_free_spec(trs_new); |
1681 | 0 | R_Free(trs_old_ptr); R_Free(trs_new_ptr); |
1682 | |
|
1683 | 0 | n = LENGTH(x); |
1684 | 0 | PROTECT(y = allocVector(STRSXP, n)); |
1685 | 0 | vmax = vmaxget(); |
1686 | 0 | for (i = 0; i < n; i++) { |
1687 | 0 | if (STRING_ELT(x,i) == NA_STRING) |
1688 | 0 | SET_STRING_ELT(y, i, NA_STRING); |
1689 | 0 | else { |
1690 | 0 | const char *xi = translateChar(STRING_ELT(x, i)); |
1691 | 0 | cbuf = CallocCharBuf(strlen(xi)); |
1692 | 0 | strcpy(cbuf, xi); |
1693 | 0 | for (p = (unsigned char *) cbuf; *p != '\0'; p++) |
1694 | 0 | *p = xtable[*p]; |
1695 | 0 | SET_STRING_ELT(y, i, markKnown(cbuf, STRING_ELT(x, i))); |
1696 | 0 | R_Free(cbuf); |
1697 | 0 | } |
1698 | 0 | } |
1699 | 0 | vmaxset(vmax); |
1700 | 0 | } |
1701 | |
|
1702 | 0 | SHALLOW_DUPLICATE_ATTRIB(y, x); |
1703 | | /* This copied the class, if any */ |
1704 | 0 | UNPROTECT(1); |
1705 | 0 | return(y); |
1706 | 0 | } |
1707 | | |
1708 | | attribute_hidden SEXP do_strtrim(SEXP call, SEXP op, SEXP args, SEXP env) |
1709 | 0 | { |
1710 | 0 | SEXP s, x, width; |
1711 | 0 | R_xlen_t i, len; |
1712 | 0 | int nw, w, nc; |
1713 | 0 | const char *This; |
1714 | 0 | char *buf; |
1715 | 0 | const char *p; char *q; |
1716 | 0 | int w0, wsum, k, nb; |
1717 | 0 | mbstate_t mb_st; |
1718 | 0 | const void *vmax; |
1719 | |
|
1720 | 0 | checkArity(op, args); |
1721 | | /* as.character happens at R level now */ |
1722 | 0 | if (!isString(x = CAR(args))) |
1723 | 0 | error(_("strtrim() requires a character vector")); |
1724 | 0 | len = XLENGTH(x); |
1725 | 0 | PROTECT(s = allocVector(STRSXP, len)); |
1726 | 0 | if(len > 0) { |
1727 | 0 | PROTECT(width = coerceVector(CADR(args), INTSXP)); |
1728 | 0 | nw = LENGTH(width); |
1729 | 0 | if (!nw || (nw < len && len % nw)) |
1730 | 0 | error(_("invalid '%s' argument"), "width"); |
1731 | 0 | for (i = 0; i < nw; i++) |
1732 | 0 | if (INTEGER(width)[i] == NA_INTEGER || |
1733 | 0 | INTEGER(width)[i] < 0) |
1734 | 0 | error(_("invalid '%s' argument"), "width"); |
1735 | 0 | vmax = vmaxget(); |
1736 | 0 | for (i = 0; i < len; i++) { |
1737 | 0 | if (STRING_ELT(x, i) == NA_STRING) { |
1738 | 0 | SET_STRING_ELT(s, i, STRING_ELT(x, i)); |
1739 | 0 | continue; |
1740 | 0 | } |
1741 | 0 | w = INTEGER(width)[i % nw]; |
1742 | | // FIXME: this could do a better job with UTF-8 or Latin-1 input |
1743 | 0 | This = translateChar(STRING_ELT(x, i)); |
1744 | 0 | nc = (int) strlen(This); |
1745 | 0 | buf = R_AllocStringBuffer(nc, &cbuff); |
1746 | 0 | wsum = 0; |
1747 | 0 | mbs_init(&mb_st); |
1748 | 0 | for (p = This, w0 = 0, q = buf; *p ;) { |
1749 | 0 | wchar_t wc; |
1750 | 0 | nb = (int) Mbrtowc(&wc, p, R_MB_CUR_MAX, &mb_st); |
1751 | 0 | #ifdef USE_RI18N_WIDTH |
1752 | 0 | w0 = Ri18n_wcwidth((R_wchar_t) wc); |
1753 | | #else |
1754 | | w0 = wcwidth(wc); |
1755 | | #endif |
1756 | 0 | if (w0 < 0) { p += nb; continue; } /* skip non-printable chars */ |
1757 | 0 | wsum += w0; |
1758 | 0 | if (wsum <= w) { |
1759 | 0 | for (k = 0; k < nb; k++) *q++ = *p++; |
1760 | 0 | } else break; |
1761 | 0 | } |
1762 | 0 | *q = '\0'; |
1763 | 0 | SET_STRING_ELT(s, i, markKnown(buf, STRING_ELT(x, i))); |
1764 | 0 | vmaxset(vmax); |
1765 | 0 | } |
1766 | 0 | R_FreeStringBufferL(&cbuff); |
1767 | 0 | UNPROTECT(1); |
1768 | 0 | } |
1769 | 0 | SHALLOW_DUPLICATE_ATTRIB(s, x); |
1770 | | /* This copied the class, if any */ |
1771 | 0 | UNPROTECT(1); |
1772 | 0 | return s; |
1773 | 0 | } |
1774 | | |
1775 | | static int strtoi(SEXP s, int base) |
1776 | 0 | { |
1777 | 0 | if(s == NA_STRING || CHAR(s)[0] == '\0') return(NA_INTEGER); |
1778 | | |
1779 | | /* strtol might return extreme values on error */ |
1780 | 0 | errno = 0; |
1781 | 0 | char *endp; |
1782 | 0 | long int res = strtol(CHAR(s), &endp, base); /* ASCII */ |
1783 | 0 | return (errno || *endp != '\0' || |
1784 | 0 | res > INT_MAX || res < INT_MIN) |
1785 | 0 | ? NA_INTEGER |
1786 | 0 | : (int) res; |
1787 | 0 | } |
1788 | | |
1789 | | attribute_hidden SEXP do_strtoi(SEXP call, SEXP op, SEXP args, SEXP env) |
1790 | 0 | { |
1791 | 0 | SEXP ans, x, b; |
1792 | 0 | R_xlen_t i, n; |
1793 | 0 | int base; |
1794 | |
|
1795 | 0 | checkArity(op, args); |
1796 | |
|
1797 | 0 | x = CAR(args); args = CDR(args); |
1798 | 0 | b = CAR(args); |
1799 | |
|
1800 | 0 | if(!isInteger(b) || (LENGTH(b) < 1)) |
1801 | 0 | error(_("invalid '%s' argument"), "base"); |
1802 | 0 | base = INTEGER(b)[0]; |
1803 | 0 | if((base != 0) && ((base < 2) || (base > 36))) |
1804 | 0 | error(_("invalid '%s' argument"), "base"); |
1805 | |
|
1806 | 0 | PROTECT(ans = allocVector(INTSXP, n = LENGTH(x))); |
1807 | 0 | for(i = 0; i < n; i++) |
1808 | 0 | INTEGER(ans)[i] = strtoi(STRING_ELT(x, i), base); |
1809 | 0 | UNPROTECT(1); |
1810 | |
|
1811 | 0 | return ans; |
1812 | 0 | } |
1813 | | |
1814 | | /* creates a new STRSXP which is a suffix of string, starting |
1815 | | with given index; the result is returned unprotected */ |
1816 | | |
1817 | 39.2k | attribute_hidden SEXP stringSuffix(SEXP string, int fromIndex) { |
1818 | | |
1819 | 39.2k | int origLen = LENGTH(string); |
1820 | 39.2k | int newLen = origLen - fromIndex; |
1821 | | |
1822 | 39.2k | SEXP res = PROTECT(allocVector(STRSXP, newLen)); |
1823 | 39.2k | int i; |
1824 | 78.2k | for(i = 0; i < newLen; i++) { |
1825 | 38.9k | SET_STRING_ELT(res, i, STRING_ELT(string, fromIndex++)); |
1826 | 38.9k | } |
1827 | | |
1828 | 39.2k | UNPROTECT(1); /* res */ |
1829 | 39.2k | return res; |
1830 | 39.2k | } |
1831 | | |
1832 | | attribute_hidden SEXP do_strrep(SEXP call, SEXP op, SEXP args, SEXP env) |
1833 | 0 | { |
1834 | 0 | SEXP d, s, x, n, el; |
1835 | 0 | R_xlen_t is, ix, in, ns, nx, nn; |
1836 | 0 | const char *xi; |
1837 | 0 | int j, ni, nc; |
1838 | 0 | const char *cbuf; |
1839 | 0 | char *buf; |
1840 | 0 | const void *vmax; |
1841 | |
|
1842 | 0 | checkArity(op, args); |
1843 | |
|
1844 | 0 | x = CAR(args); args = CDR(args); |
1845 | 0 | n = CAR(args); |
1846 | |
|
1847 | 0 | nx = XLENGTH(x); |
1848 | 0 | nn = XLENGTH(n); |
1849 | 0 | if((nx == 0) || (nn == 0)) |
1850 | 0 | return allocVector(STRSXP, 0); |
1851 | | |
1852 | 0 | ns = (nx > nn) ? nx : nn; |
1853 | |
|
1854 | 0 | PROTECT(s = allocVector(STRSXP, ns)); |
1855 | 0 | vmax = vmaxget(); |
1856 | 0 | is = ix = in = 0; |
1857 | 0 | for(; is < ns; is++) { |
1858 | 0 | el = STRING_ELT(x, ix); |
1859 | 0 | ni = INTEGER(n)[in]; |
1860 | 0 | if((el == NA_STRING) || (ni == NA_INTEGER)) { |
1861 | 0 | SET_STRING_ELT(s, is, NA_STRING); |
1862 | 0 | } else { |
1863 | 0 | if(ni < 0) |
1864 | 0 | error(_("invalid '%s' value"), "times"); |
1865 | 0 | xi = CHAR(el); |
1866 | 0 | nc = (int) strlen(xi); |
1867 | | |
1868 | | /* check for feasible result length; use double to protect |
1869 | | against integer overflow */ |
1870 | 0 | double len = ((double) nc) * ni; |
1871 | 0 | if (len > INT_MAX) |
1872 | 0 | error("R character strings are limited to 2^31-1 bytes"); |
1873 | |
|
1874 | 0 | cbuf = buf = CallocCharBuf(nc * ni); |
1875 | 0 | for(j = 0; j < ni; j++) { |
1876 | 0 | strcpy(buf, xi); |
1877 | 0 | buf += nc; |
1878 | 0 | } |
1879 | 0 | SET_STRING_ELT(s, is, mkCharCE(cbuf, getCharCE(el))); |
1880 | 0 | R_Free(cbuf); |
1881 | 0 | vmaxset(vmax); |
1882 | 0 | } |
1883 | 0 | ix = (++ix == nx) ? 0 : ix; |
1884 | 0 | in = (++in == nn) ? 0 : in; |
1885 | 0 | } |
1886 | | /* Copy names if not recycled. */ |
1887 | 0 | if((ns == nx) && |
1888 | 0 | (d = getAttrib(x, R_NamesSymbol)) != R_NilValue) |
1889 | 0 | setAttrib(s, R_NamesSymbol, d); |
1890 | 0 | UNPROTECT(1); |
1891 | 0 | return s; |
1892 | 0 | } |