The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*  You may distribute under the terms of either the GNU General Public License
 *  or the Artistic License (the same terms as Perl itself)
 *
 *  (C) Paul Evans, 2011 -- leonerd@leonerd.org.uk
 */


#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <wchar.h>

/* For pre-5.14 source compatibility */
#ifndef UNICODE_WARN_ILLEGAL_INTERCHANGE
#   define UNICODE_WARN_ILLEGAL_INTERCHANGE 0
#   define UTF8_DISALLOW_SURROGATE 0
#   define UTF8_WARN_SURROGATE 0
#   define UTF8_DISALLOW_FE_FF 0
#   define UTF8_WARN_FE_FF 0
#   define UTF8_WARN_NONCHAR 0
#endif

MODULE = Tickit::Utils      PACKAGE = Tickit::Utils

int textwidth(str)
    SV *str
  INIT:
    STRLEN len;
    const char *s, *e;

  CODE:
    RETVAL = 0;

    if(!SvUTF8(str)) {
      str = sv_mortalcopy(str);
      sv_utf8_upgrade(str);
    }

    s = SvPV_const(str, len);
    e = s + len;

    while(s < e) {
      UV ord = utf8n_to_uvchr(s, e-s, &len, (UTF8_DISALLOW_SURROGATE
                                               |UTF8_WARN_SURROGATE
                                               |UTF8_DISALLOW_FE_FF
                                               |UTF8_WARN_FE_FF
                                               |UTF8_WARN_NONCHAR));
      int width = wcwidth(ord);
      if(width == -1)
        XSRETURN_UNDEF;

      s += len;
      RETVAL += width;
    }

  OUTPUT:
    RETVAL

void chars2cols(str,...)
    SV *str;
  INIT:
    STRLEN len;
    const char *s, *e;
    int cp = 0, col = 0;
    int i;

  PPCODE:
    if(!SvUTF8(str)) {
      str = sv_mortalcopy(str);
      sv_utf8_upgrade(str);
    }

    s = SvPV_const(str, len);
    e = s + len;

    EXTEND(SP, items - 1);

    for(i = 1; i < items; i++ ) {
      int thiscp = SvUV(ST(i));
      if(thiscp < cp)
        croak("chars2cols requires a monotonically-increasing list of character numbers; %d is not greater than %d\n",
          thiscp, cp);

      while(s < e && cp < thiscp) {
        UV ord = utf8n_to_uvchr(s, e-s, &len, (UTF8_DISALLOW_SURROGATE
                                                 |UTF8_WARN_SURROGATE
                                                 |UTF8_DISALLOW_FE_FF
                                                 |UTF8_WARN_FE_FF
                                                 |UTF8_WARN_NONCHAR));

        int width = wcwidth(ord);
        if(width == -1)
          (GIMME_V == G_ARRAY) ? XSRETURN(0) : XSRETURN_UNDEF;

        s += len;
        cp += 1;
        col += width;
      }

      mPUSHu(col);

      if(GIMME_V != G_ARRAY)
        XSRETURN(1);
    }

    XSRETURN(items - 1);

void cols2chars(str,...)
    SV *str;
  INIT:
    STRLEN len;
    const char *s, *e;
    int cp = 0, col = 0;
    int i;

  PPCODE:
    if(!SvUTF8(str)) {
      str = sv_mortalcopy(str);
      sv_utf8_upgrade(str);
    }

    s = SvPV_const(str, len);
    e = s + len;

    EXTEND(SP, items - 1);

    for(i = 1; i < items; i++ ) {
      int thiscol = SvUV(ST(i));
      if(thiscol < col)
        croak("cols2chars requires a monotonically-increasing list of column numbers; %d is not greater than %d\n",
          thiscol, col);

      while(s < e) {
        UV ord = utf8n_to_uvchr(s, e-s, &len, (UTF8_DISALLOW_SURROGATE
                                                 |UTF8_WARN_SURROGATE
                                                 |UTF8_DISALLOW_FE_FF
                                                 |UTF8_WARN_FE_FF
                                                 |UTF8_WARN_NONCHAR));

        int width = wcwidth(ord);
        if(width == -1)
          (GIMME_V == G_ARRAY) ? XSRETURN(0) : XSRETURN_UNDEF;

        if(col + width > thiscol)
          break;

        s += len;
        cp += 1;
        col += width;
      }

      mPUSHu(cp);

      if(GIMME_V != G_ARRAY)
        XSRETURN(1);
    }

    XSRETURN(items - 1);