Coverage Report

Created: 2026-09-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}