PageRenderTime 119ms CodeModel.GetById 12ms app.highlight 97ms RepoModel.GetById 1ms app.codeStats 0ms

/src/core/pystring/pystring.cpp

http://github.com/imageworks/OpenColorIO
C++ | 1658 lines | 1150 code | 263 blank | 245 comment | 285 complexity | 5aa8bc88b9bb4a0c4413994c75579fea MD5 | raw file
   1///////////////////////////////////////////////////////////////////////////////
   2// Copyright (c) 2008-2010, Sony Pictures Imageworks Inc
   3// All rights reserved.
   4//
   5// Redistribution and use in source and binary forms, with or without
   6// modification, are permitted provided that the following conditions are
   7// met:
   8//
   9// Redistributions of source code must retain the above copyright notice,
  10// this list of conditions and the following disclaimer.
  11// Redistributions in binary form must reproduce the above copyright
  12// notice, this list of conditions and the following disclaimer in the
  13// documentation and/or other materials provided with the distribution.
  14// Neither the name of the organization Sony Pictures Imageworks nor the
  15// names of its contributors
  16// may be used to endorse or promote products derived from this software
  17// without specific prior written permission.
  18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19// "AS
  20// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  21// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  22// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23// OWNER
  24// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25// SPECIAL,
  26// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32///////////////////////////////////////////////////////////////////////////////
  33
  34#include <OpenColorIO/OpenColorIO.h>
  35
  36#include "pystring.h"
  37
  38#include <algorithm>
  39#include <cctype>
  40#include <cstring>
  41#include <iostream>
  42#include <sstream>
  43
  44OCIO_NAMESPACE_ENTER
  45{
  46
  47namespace pystring
  48{
  49
  50#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || defined(_MSC_VER)
  51#ifndef WINDOWS
  52#define WINDOWS
  53#endif
  54#endif
  55
  56// This definition codes from configure.in in the python src.
  57// Strictly speaking this limits us to str sizes of 2**31.
  58// Should we wish to handle this limit, we could use an architecture
  59// specific #defines and read from ssize_t (unistd.h) if the header exists.
  60// But in the meantime, the use of int assures maximum arch compatibility.
  61// This must also equal the size used in the end = MAX_32BIT_INT default arg.
  62
  63typedef int Py_ssize_t;
  64
  65/* helper macro to fixup start/end slice values */
  66#define ADJUST_INDICES(start, end, len)         \
  67    if (end > len)                          \
  68        end = len;                          \
  69    else if (end < 0) {                     \
  70        end += len;                         \
  71        if (end < 0)                        \
  72        end = 0;                        \
  73    }                                       \
  74    if (start < 0) {                        \
  75        start += len;                       \
  76        if (start < 0)                      \
  77        start = 0;                      \
  78    }
  79
  80
  81	namespace {
  82
  83		//////////////////////////////////////////////////////////////////////////////////////////////
  84		/// why doesn't the std::reverse work?
  85		///
  86		void reverse_strings( std::vector< std::string > & result)
  87		{
  88			for (std::vector< std::string >::size_type i = 0; i < result.size() / 2; i++ )
  89			{
  90				std::swap(result[i], result[result.size() - 1 - i]);
  91			}
  92		}
  93
  94		//////////////////////////////////////////////////////////////////////////////////////////////
  95		///
  96		///
  97		void split_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit )
  98		{
  99			std::string::size_type i, j, len = str.size();
 100			for (i = j = 0; i < len; )
 101			{
 102
 103				while ( i < len && ::isspace( str[i] ) ) i++;
 104				j = i;
 105
 106				while ( i < len && ! ::isspace( str[i]) ) i++;
 107
 108
 109
 110				if (j < i)
 111				{
 112					if ( maxsplit-- <= 0 ) break;
 113
 114					result.push_back( str.substr( j, i - j ));
 115
 116					while ( i < len && ::isspace( str[i])) i++;
 117					j = i;
 118				}
 119			}
 120			if (j < len)
 121			{
 122				result.push_back( str.substr( j, len - j ));
 123			}
 124		}
 125
 126
 127		//////////////////////////////////////////////////////////////////////////////////////////////
 128		///
 129		///
 130		void rsplit_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit )
 131		{
 132			std::string::size_type len = str.size();
 133			std::string::size_type i, j;
 134			for (i = j = len; i > 0; )
 135			{
 136
 137				while ( i > 0 && ::isspace( str[i - 1] ) ) i--;
 138				j = i;
 139
 140				while ( i > 0 && ! ::isspace( str[i - 1]) ) i--;
 141
 142
 143
 144				if (j > i)
 145				{
 146					if ( maxsplit-- <= 0 ) break;
 147
 148					result.push_back( str.substr( i, j - i ));
 149
 150					while ( i > 0 && ::isspace( str[i - 1])) i--;
 151					j = i;
 152				}
 153			}
 154			if (j > 0)
 155			{
 156				result.push_back( str.substr( 0, j ));
 157			}
 158			//std::reverse( result, result.begin(), result.end() );
 159			reverse_strings( result );
 160		}
 161
 162	} //anonymous namespace
 163
 164
 165    //////////////////////////////////////////////////////////////////////////////////////////////
 166    ///
 167    ///
 168    void split( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit )
 169    {
 170        result.clear();
 171
 172        if ( maxsplit < 0 ) maxsplit = MAX_32BIT_INT;//result.max_size();
 173
 174
 175        if ( sep.size() == 0 )
 176        {
 177            split_whitespace( str, result, maxsplit );
 178            return;
 179        }
 180
 181        std::string::size_type i,j, len = str.size(), n = sep.size();
 182
 183        i = j = 0;
 184
 185        while ( i+n <= len )
 186        {
 187            if ( str[i] == sep[0] && str.substr( i, n ) == sep )
 188            {
 189                if ( maxsplit-- <= 0 ) break;
 190
 191                result.push_back( str.substr( j, i - j ) );
 192                i = j = i + n;
 193            }
 194            else
 195            {
 196                i++;
 197            }
 198        }
 199
 200        result.push_back( str.substr( j, len-j ) );
 201    }
 202
 203    //////////////////////////////////////////////////////////////////////////////////////////////
 204    ///
 205    ///
 206    void rsplit( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit )
 207    {
 208        if ( maxsplit < 0 )
 209        {
 210            split( str, result, sep, 0 );
 211            return;
 212        }
 213
 214        result.clear();
 215
 216        if ( sep.size() == 0 )
 217        {
 218            rsplit_whitespace( str, result, maxsplit );
 219            return;
 220        }
 221
 222        std::string::size_type i,j, len = str.size(), n = sep.size();
 223
 224        i = j = len;
 225
 226        while ( i > n )
 227        {
 228            if ( str[i - 1] == sep[n - 1] && str.substr( i - n, n ) == sep )
 229            {
 230                if ( maxsplit-- <= 0 ) break;
 231
 232                result.push_back( str.substr( i, j - i ) );
 233                i = j = i - n;
 234            }
 235            else
 236            {
 237                i--;
 238            }
 239        }
 240
 241        result.push_back( str.substr( 0, j ) );
 242        reverse_strings( result );
 243    }
 244
 245    //////////////////////////////////////////////////////////////////////////////////////////////
 246    ///
 247    ///
 248    #define LEFTSTRIP 0
 249    #define RIGHTSTRIP 1
 250    #define BOTHSTRIP 2
 251
 252    //////////////////////////////////////////////////////////////////////////////////////////////
 253    ///
 254    ///
 255    std::string do_strip( const std::string & str, int striptype, const std::string & chars  )
 256    {
 257        Py_ssize_t len = (Py_ssize_t) str.size(), i, j, charslen = (Py_ssize_t) chars.size();
 258
 259        if ( charslen == 0 )
 260        {
 261            i = 0;
 262            if ( striptype != RIGHTSTRIP )
 263            {
 264                while ( i < len && ::isspace( str[i] ) )
 265                {
 266                    i++;
 267                }
 268            }
 269
 270            j = len;
 271            if ( striptype != LEFTSTRIP )
 272            {
 273                do
 274                {
 275                    j--;
 276                }
 277                while (j >= i && ::isspace(str[j]));
 278
 279                j++;
 280            }
 281
 282
 283        }
 284        else
 285        {
 286            const char * sep = chars.c_str();
 287
 288            i = 0;
 289            if ( striptype != RIGHTSTRIP )
 290            {
 291                while ( i < len && memchr(sep, str[i], charslen) )
 292                {
 293                    i++;
 294                }
 295            }
 296
 297            j = len;
 298            if (striptype != LEFTSTRIP)
 299            {
 300                do
 301                {
 302                    j--;
 303                }
 304                while (j >= i &&  memchr(sep, str[j], charslen)  );
 305                j++;
 306            }
 307
 308
 309        }
 310
 311        if ( i == 0 && j == len )
 312        {
 313            return str;
 314        }
 315        else
 316        {
 317            return str.substr( i, j - i );
 318        }
 319
 320    }
 321
 322    //////////////////////////////////////////////////////////////////////////////////////////////
 323    ///
 324    ///
 325    void partition( const std::string & str, const std::string & sep, std::vector< std::string > & result )
 326    {
 327        result.resize(3);
 328        int index = find( str, sep );
 329        if ( index < 0 )
 330        {
 331            result[0] = str;
 332            result[1] = "";
 333            result[2] = "";
 334        }
 335        else
 336        {
 337            result[0] = str.substr( 0, index );
 338            result[1] = sep;
 339            result[2] = str.substr( index + sep.size(), str.size() );
 340        }
 341    }
 342
 343    //////////////////////////////////////////////////////////////////////////////////////////////
 344    ///
 345    ///
 346    void rpartition( const std::string & str, const std::string & sep, std::vector< std::string > & result )
 347    {
 348        result.resize(3);
 349        int index = rfind( str, sep );
 350        if ( index < 0 )
 351        {
 352            result[0] = "";
 353            result[1] = "";
 354            result[2] = str;
 355        }
 356        else
 357        {
 358            result[0] = str.substr( 0, index );
 359            result[1] = sep;
 360            result[2] = str.substr( index + sep.size(), str.size() );
 361        }
 362    }
 363
 364    //////////////////////////////////////////////////////////////////////////////////////////////
 365    ///
 366    ///
 367    std::string strip( const std::string & str, const std::string & chars )
 368    {
 369        return do_strip( str, BOTHSTRIP, chars );
 370    }
 371
 372    //////////////////////////////////////////////////////////////////////////////////////////////
 373    ///
 374    ///
 375    std::string lstrip( const std::string & str, const std::string & chars )
 376    {
 377        return do_strip( str, LEFTSTRIP, chars );
 378    }
 379
 380    //////////////////////////////////////////////////////////////////////////////////////////////
 381    ///
 382    ///
 383    std::string rstrip( const std::string & str, const std::string & chars )
 384    {
 385        return do_strip( str, RIGHTSTRIP, chars );
 386    }
 387
 388    //////////////////////////////////////////////////////////////////////////////////////////////
 389    ///
 390    ///
 391    std::string join( const std::string & str, const std::vector< std::string > & seq )
 392    {
 393        std::vector< std::string >::size_type seqlen = seq.size(), i;
 394
 395        if ( seqlen == 0 ) return "";
 396        if ( seqlen == 1 ) return seq[0];
 397
 398        std::string result( seq[0] );
 399
 400        for ( i = 1; i < seqlen; ++i )
 401        {
 402            result += str + seq[i];
 403
 404        }
 405
 406
 407        return result;
 408    }
 409
 410
 411    //////////////////////////////////////////////////////////////////////////////////////////////
 412    ///
 413    ///
 414    
 415    namespace
 416    {
 417        /* Matches the end (direction >= 0) or start (direction < 0) of self
 418         * against substr, using the start and end arguments. Returns
 419         * -1 on error, 0 if not found and 1 if found.
 420         */
 421        
 422        int _string_tailmatch(const std::string & self, const std::string & substr,
 423                              Py_ssize_t start, Py_ssize_t end,
 424                              int direction)
 425        {
 426            Py_ssize_t len = (Py_ssize_t) self.size();
 427            Py_ssize_t slen = (Py_ssize_t) substr.size();
 428            
 429            const char* sub = substr.c_str();
 430            const char* str = self.c_str();
 431            
 432            ADJUST_INDICES(start, end, len);
 433            
 434            if (direction < 0) {
 435                // startswith
 436                if (start+slen > len)
 437                    return 0;
 438            } else {
 439                // endswith
 440                if (end-start < slen || start > len)
 441                    return 0;
 442                if (end-slen > start)
 443                    start = end - slen;
 444            }
 445            if (end-start >= slen)
 446                return (!std::memcmp(str+start, sub, slen));
 447            
 448            return 0;
 449        }
 450    }
 451    
 452    bool endswith( const std::string & str, const std::string & suffix, int start, int end )
 453    {
 454        int result = _string_tailmatch(str, suffix,
 455                                       (Py_ssize_t) start, (Py_ssize_t) end, +1);
 456        //if (result == -1) // TODO: Error condition
 457        
 458        return static_cast<bool>(result);
 459    }
 460    
 461    
 462    bool startswith( const std::string & str, const std::string & prefix, int start, int end )
 463    {
 464        int result = _string_tailmatch(str, prefix,
 465                                       (Py_ssize_t) start, (Py_ssize_t) end, -1);
 466        //if (result == -1) // TODO: Error condition
 467        
 468        return static_cast<bool>(result);
 469    }
 470
 471    //////////////////////////////////////////////////////////////////////////////////////////////
 472    ///
 473    ///
 474
 475    bool isalnum( const std::string & str )
 476    {
 477        std::string::size_type len = str.size(), i;
 478        if ( len == 0 ) return false;
 479
 480
 481        if( len == 1 )
 482        {
 483            return ::isalnum( str[0] );
 484        }
 485
 486        for ( i = 0; i < len; ++i )
 487        {
 488            if ( !::isalnum( str[i] ) ) return false;
 489        }
 490        return true;
 491    }
 492
 493    //////////////////////////////////////////////////////////////////////////////////////////////
 494    ///
 495    ///
 496    bool isalpha( const std::string & str )
 497    {
 498        std::string::size_type len = str.size(), i;
 499        if ( len == 0 ) return false;
 500        if( len == 1 ) return ::isalpha( (int) str[0] );
 501
 502        for ( i = 0; i < len; ++i )
 503        {
 504           if ( !::isalpha( (int) str[i] ) ) return false;
 505        }
 506        return true;
 507    }
 508
 509    //////////////////////////////////////////////////////////////////////////////////////////////
 510    ///
 511    ///
 512    bool isdigit( const std::string & str )
 513    {
 514        std::string::size_type len = str.size(), i;
 515        if ( len == 0 ) return false;
 516        if( len == 1 ) return ::isdigit( str[0] );
 517
 518        for ( i = 0; i < len; ++i )
 519        {
 520           if ( ! ::isdigit( str[i] ) ) return false;
 521        }
 522        return true;
 523    }
 524
 525    //////////////////////////////////////////////////////////////////////////////////////////////
 526    ///
 527    ///
 528    bool islower( const std::string & str )
 529    {
 530        std::string::size_type len = str.size(), i;
 531        if ( len == 0 ) return false;
 532        if( len == 1 ) return ::islower( str[0] );
 533
 534        for ( i = 0; i < len; ++i )
 535        {
 536           if ( !::islower( str[i] ) ) return false;
 537        }
 538        return true;
 539    }
 540
 541    //////////////////////////////////////////////////////////////////////////////////////////////
 542    ///
 543    ///
 544    bool isspace( const std::string & str )
 545    {
 546        std::string::size_type len = str.size(), i;
 547        if ( len == 0 ) return false;
 548        if( len == 1 ) return ::isspace( str[0] );
 549
 550        for ( i = 0; i < len; ++i )
 551        {
 552           if ( !::isspace( str[i] ) ) return false;
 553        }
 554        return true;
 555    }
 556
 557    //////////////////////////////////////////////////////////////////////////////////////////////
 558    ///
 559    ///
 560    bool istitle( const std::string & str )
 561    {
 562        std::string::size_type len = str.size(), i;
 563
 564        if ( len == 0 ) return false;
 565        if ( len == 1 ) return ::isupper( str[0] );
 566
 567        bool cased = false, previous_is_cased = false;
 568
 569        for ( i = 0; i < len; ++i )
 570        {
 571            if ( ::isupper( str[i] ) )
 572            {
 573                if ( previous_is_cased )
 574                {
 575                    return false;
 576                }
 577
 578                previous_is_cased = true;
 579                cased = true;
 580            }
 581            else if ( ::islower( str[i] ) )
 582            {
 583                if (!previous_is_cased)
 584                {
 585                    return false;
 586                }
 587
 588                previous_is_cased = true;
 589                cased = true;
 590
 591            }
 592            else
 593            {
 594                previous_is_cased = false;
 595            }
 596        }
 597
 598        return cased;
 599    }
 600
 601    //////////////////////////////////////////////////////////////////////////////////////////////
 602    ///
 603    ///
 604    bool isupper( const std::string & str )
 605    {
 606        std::string::size_type len = str.size(), i;
 607        if ( len == 0 ) return false;
 608        if( len == 1 ) return ::isupper( str[0] );
 609
 610        for ( i = 0; i < len; ++i )
 611        {
 612           if ( !::isupper( str[i] ) ) return false;
 613        }
 614        return true;
 615    }
 616
 617    //////////////////////////////////////////////////////////////////////////////////////////////
 618    ///
 619    ///
 620    std::string capitalize( const std::string & str )
 621    {
 622        std::string s( str );
 623        std::string::size_type len = s.size(), i;
 624
 625        if ( len > 0)
 626        {
 627            if (::islower(s[0])) s[0] = (char) ::toupper( s[0] );
 628        }
 629
 630        for ( i = 1; i < len; ++i )
 631        {
 632            if (::isupper(s[i])) s[i] = (char) ::tolower( s[i] );
 633        }
 634
 635        return s;
 636    }
 637
 638    //////////////////////////////////////////////////////////////////////////////////////////////
 639    ///
 640    ///
 641    std::string lower( const std::string & str )
 642    {
 643        std::string s( str );
 644        std::string::size_type len = s.size(), i;
 645
 646        for ( i = 0; i < len; ++i )
 647        {
 648            if ( ::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] );
 649        }
 650
 651        return s;
 652    }
 653
 654    //////////////////////////////////////////////////////////////////////////////////////////////
 655    ///
 656    ///
 657    std::string upper( const std::string & str )
 658    {
 659        std::string s( str ) ;
 660        std::string::size_type len = s.size(), i;
 661
 662        for ( i = 0; i < len; ++i )
 663        {
 664            if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] );
 665        }
 666
 667        return s;
 668    }
 669
 670    //////////////////////////////////////////////////////////////////////////////////////////////
 671    ///
 672    ///
 673    std::string swapcase( const std::string & str )
 674    {
 675        std::string s( str );
 676        std::string::size_type len = s.size(), i;
 677
 678        for ( i = 0; i < len; ++i )
 679        {
 680            if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] );
 681            else if (::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] );
 682        }
 683
 684        return s;
 685    }
 686
 687    //////////////////////////////////////////////////////////////////////////////////////////////
 688    ///
 689    ///
 690    std::string title( const std::string & str )
 691    {
 692        std::string s( str );
 693        std::string::size_type len = s.size(), i;
 694        bool previous_is_cased = false;
 695
 696        for ( i = 0; i < len; ++i )
 697        {
 698            int c = s[i];
 699            if ( ::islower(c) )
 700            {
 701                if ( !previous_is_cased )
 702                {
 703                    s[i] = (char) ::toupper(c);
 704                }
 705                previous_is_cased = true;
 706            }
 707            else if ( ::isupper(c) )
 708            {
 709                if ( previous_is_cased )
 710                {
 711                    s[i] = (char) ::tolower(c);
 712                }
 713                previous_is_cased = true;
 714            }
 715            else
 716            {
 717                previous_is_cased = false;
 718            }
 719        }
 720
 721        return s;
 722    }
 723
 724    //////////////////////////////////////////////////////////////////////////////////////////////
 725    ///
 726    ///
 727    std::string translate( const std::string & str, const std::string & table, const std::string & deletechars )
 728    {
 729        std::string s;
 730        std::string::size_type len = str.size(), dellen = deletechars.size();
 731
 732        if ( table.size() != 256 )
 733        {
 734            // TODO : raise exception instead
 735            return str;
 736        }
 737
 738        //if nothing is deleted, use faster code
 739        if ( dellen == 0 )
 740        {
 741            s = str;
 742            for ( std::string::size_type i = 0; i < len; ++i )
 743            {
 744                s[i] = table[ s[i] ];
 745            }
 746            return s;
 747        }
 748
 749
 750        int trans_table[256];
 751        for ( int i = 0; i < 256; i++)
 752        {
 753            trans_table[i] = table[i];
 754        }
 755
 756        for ( std::string::size_type i = 0; i < dellen; i++)
 757        {
 758            trans_table[(int) deletechars[i] ] = -1;
 759        }
 760
 761        for ( std::string::size_type i = 0; i < len; ++i )
 762        {
 763            if ( trans_table[ (int) str[i] ] != -1 )
 764            {
 765                s += table[ str[i] ];
 766            }
 767        }
 768
 769        return s;
 770
 771    }
 772
 773
 774    //////////////////////////////////////////////////////////////////////////////////////////////
 775    ///
 776    ///
 777    std::string zfill( const std::string & str, int width )
 778    {
 779        int len = (int)str.size();
 780
 781        if ( len >= width )
 782        {
 783            return str;
 784        }
 785
 786        std::string s( str );
 787
 788        int fill = width - len;
 789
 790        s = std::string( fill, '0' ) + s;
 791
 792
 793        if ( s[fill] == '+' || s[fill] == '-' )
 794        {
 795            s[0] = s[fill];
 796            s[fill] = '0';
 797        }
 798
 799        return s;
 800
 801    }
 802
 803    //////////////////////////////////////////////////////////////////////////////////////////////
 804    ///
 805    ///
 806    std::string ljust( const std::string & str, int width )
 807    {
 808        std::string::size_type len = str.size();
 809        if ( (( int ) len ) >= width ) return str;
 810        return str + std::string( width - len, ' ' );
 811    }
 812
 813    //////////////////////////////////////////////////////////////////////////////////////////////
 814    ///
 815    ///
 816    std::string rjust( const std::string & str, int width )
 817    {
 818        std::string::size_type len = str.size();
 819        if ( (( int ) len ) >= width ) return str;
 820        return std::string( width - len, ' ' ) + str;
 821    }
 822
 823    //////////////////////////////////////////////////////////////////////////////////////////////
 824    ///
 825    ///
 826    std::string center( const std::string & str, int width )
 827    {
 828        int len = (int) str.size();
 829        int marg, left;
 830
 831        if ( len >= width ) return str;
 832
 833        marg = width - len;
 834        left = marg / 2 + (marg & width & 1);
 835
 836        return std::string( left, ' ' ) + str + std::string( marg - left, ' ' );
 837
 838    }
 839
 840    //////////////////////////////////////////////////////////////////////////////////////////////
 841    ///
 842    ///
 843    std::string slice( const std::string & str, int start, int end )
 844    {
 845        ADJUST_INDICES(start, end, (int) str.size());
 846        if ( start >= end ) return "";
 847        return str.substr( start, end - start );
 848    }
 849    
 850    
 851    //////////////////////////////////////////////////////////////////////////////////////////////
 852    ///
 853    ///
 854    int find( const std::string & str, const std::string & sub, int start, int end  )
 855    {
 856        ADJUST_INDICES(start, end, (int) str.size());
 857        
 858        std::string::size_type result = str.find( sub, start );
 859        
 860        // If we cannot find the string, or if the end-point of our found substring is past
 861        // the allowed end limit, return that it can't be found.
 862        if( result == std::string::npos || 
 863           (result + sub.size() > (std::string::size_type)end) )
 864        {
 865            return -1;
 866        }
 867        
 868        return (int) result;
 869    }
 870
 871    //////////////////////////////////////////////////////////////////////////////////////////////
 872    ///
 873    ///
 874    int index( const std::string & str, const std::string & sub, int start, int end  )
 875    {
 876        return find( str, sub, start, end );
 877    }
 878
 879    //////////////////////////////////////////////////////////////////////////////////////////////
 880    ///
 881    ///
 882    int rfind( const std::string & str, const std::string & sub, int start, int end )
 883    {
 884        ADJUST_INDICES(start, end, (int) str.size());
 885        
 886        std::string::size_type result = str.rfind( sub, end );
 887        
 888        if( result == std::string::npos || 
 889            result < (std::string::size_type)start  || 
 890           (result + sub.size() > (std::string::size_type)end))
 891            return -1;
 892        
 893        return (int)result;
 894    }
 895
 896    //////////////////////////////////////////////////////////////////////////////////////////////
 897    ///
 898    ///
 899    int rindex( const std::string & str, const std::string & sub, int start, int end )
 900    {
 901        return rfind( str, sub, start, end );
 902    }
 903
 904    //////////////////////////////////////////////////////////////////////////////////////////////
 905    ///
 906    ///
 907    std::string expandtabs( const std::string & str, int tabsize )
 908    {
 909        std::string s( str );
 910
 911        std::string::size_type len = str.size(), i = 0;
 912        int offset = 0;
 913
 914        int j = 0;
 915
 916        for ( i = 0; i < len; ++i )
 917        {
 918            if ( str[i] == '\t' )
 919            {
 920
 921                if ( tabsize > 0 )
 922                {
 923                    int fillsize = tabsize - (j % tabsize);
 924                    j += fillsize;
 925                    s.replace( i + offset, 1, std::string( fillsize, ' ' ));
 926                    offset += fillsize - 1;
 927                }
 928                else
 929                {
 930                    s.replace( i + offset, 1, "" );
 931                    offset -= 1;
 932                }
 933
 934            }
 935            else
 936            {
 937                j++;
 938
 939                if (str[i] == '\n' || str[i] == '\r')
 940                {
 941                    j = 0;
 942                }
 943            }
 944        }
 945
 946        return s;
 947    }
 948
 949    //////////////////////////////////////////////////////////////////////////////////////////////
 950    ///
 951    ///
 952    int count( const std::string & str, const std::string & substr, int start, int end )
 953    {
 954        int nummatches = 0;
 955        int cursor = start;
 956
 957        while ( 1 )
 958        {
 959            cursor = find( str, substr, cursor, end );
 960
 961            if ( cursor < 0 ) break;
 962
 963            cursor += (int) substr.size();
 964            nummatches += 1;
 965        }
 966
 967        return nummatches;
 968
 969
 970    }
 971
 972    //////////////////////////////////////////////////////////////////////////////////////////////
 973    ///
 974    ///
 975    std::string replace( const std::string & str, const std::string & oldstr, const std::string & newstr, int count )
 976    {
 977        int sofar = 0;
 978        int cursor = 0;
 979        std::string s( str );
 980
 981        std::string::size_type oldlen = oldstr.size(), newlen = newstr.size();
 982
 983        while ( ( cursor = find( s, oldstr, cursor ) ) != -1 )
 984        {
 985            if ( count > -1 && sofar >= count )
 986            {
 987                break;
 988            }
 989
 990            s.replace( cursor, oldlen, newstr );
 991
 992            cursor += (int) newlen;
 993            ++sofar;
 994        }
 995
 996        return s;
 997
 998    }
 999
1000    //////////////////////////////////////////////////////////////////////////////////////////////
1001    ///
1002    ///
1003    void splitlines(  const std::string & str, std::vector< std::string > & result, bool keepends )
1004    {
1005        result.clear();
1006        std::string::size_type len = str.size(), i, j, eol;
1007
1008         for (i = j = 0; i < len; )
1009         {
1010            while (i < len && str[i] != '\n' && str[i] != '\r') i++;
1011
1012            eol = i;
1013            if (i < len)
1014            {
1015                if (str[i] == '\r' && i + 1 < len && str[i+1] == '\n')
1016                {
1017                    i += 2;
1018                }
1019                else
1020                {
1021                    i++;
1022                }
1023                if (keepends)
1024                eol = i;
1025
1026            }
1027
1028            result.push_back( str.substr( j, eol - j ) );
1029            j = i;
1030
1031        }
1032
1033        if (j < len)
1034        {
1035            result.push_back( str.substr( j, len - j ) );
1036        }
1037
1038    }
1039
1040    //////////////////////////////////////////////////////////////////////////////////////////////
1041    ///
1042    ///
1043    std::string mul( const std::string & str, int n )
1044    {
1045        // Early exits
1046        if (n <= 0) return "";
1047        if (n == 1) return str;
1048        
1049        std::ostringstream os;
1050        for(int i=0; i<n; ++i)
1051        {
1052            os << str;
1053        }
1054        return os.str();
1055    }
1056
1057
1058
1059namespace os
1060{
1061namespace path
1062{
1063    
1064    //////////////////////////////////////////////////////////////////////////////////////////////
1065    ///
1066    ///
1067    /// These functions are C++ ports of the python2.6 versions of os.path,
1068    /// and come from genericpath.py, ntpath.py, posixpath.py
1069
1070    /// Split a pathname into drive and path specifiers.
1071    /// Returns drivespec, pathspec. Either part may be empty.
1072    void splitdrive_nt(std::string & drivespec, std::string & pathspec,
1073                       const std::string & p)
1074    {
1075        if(pystring::slice(p, 1, 2) == ":")
1076        {
1077            std::string path = p; // In case drivespec == p
1078            drivespec = pystring::slice(path, 0, 2);
1079            pathspec = pystring::slice(path, 2);
1080        }
1081        else
1082        {
1083            drivespec = "";
1084            pathspec = p;
1085        }
1086    }
1087
1088    // On Posix, drive is always empty
1089    void splitdrive_posix(std::string & drivespec, std::string & pathspec,
1090                          const std::string & path)
1091    {
1092        drivespec = "";
1093        pathspec = path;
1094    }
1095
1096    void splitdrive(std::string & drivespec, std::string & pathspec,
1097                    const std::string & path)
1098    {
1099#ifdef WINDOWS
1100        return splitdrive_nt(drivespec, pathspec, path);
1101#else
1102        return splitdrive_posix(drivespec, pathspec, path);
1103#endif
1104    }
1105
1106    //////////////////////////////////////////////////////////////////////////////////////////////
1107    ///
1108    ///
1109
1110    // Test whether a path is absolute
1111    // In windows, if the character to the right of the colon
1112    // is a forward or backslash it's absolute.
1113    bool isabs_nt(const std::string & path)
1114    {
1115        std::string drivespec, pathspec;
1116        splitdrive_nt(drivespec, pathspec, path);
1117        if(pathspec.empty()) return false;
1118        return ((pathspec[0] == '/') || (pathspec[0] == '\\'));
1119    }
1120
1121    bool isabs_posix(const std::string & s)
1122    {
1123        return pystring::startswith(s, "/");
1124    }
1125
1126    bool isabs(const std::string & path)
1127    {
1128#ifdef WINDOWS
1129        return isabs_nt(path);
1130#else
1131        return isabs_posix(path);
1132#endif
1133    }
1134
1135
1136    //////////////////////////////////////////////////////////////////////////////////////////////
1137    ///
1138    ///
1139    
1140    std::string abspath_nt(const std::string & path, const std::string & cwd)
1141    {
1142        std::string p = path;
1143        if(!isabs_nt(p)) p = join_nt(cwd, p);
1144        return normpath_nt(p);
1145    }
1146    
1147    std::string abspath_posix(const std::string & path, const std::string & cwd)
1148    {
1149        std::string p = path;
1150        if(!isabs_posix(p)) p = join_posix(cwd, p);
1151        return normpath_posix(p);
1152    }
1153    
1154    std::string abspath(const std::string & path, const std::string & cwd)
1155    {
1156#ifdef WINDOWS
1157        return abspath_nt(path, cwd);
1158#else
1159        return abspath_posix(path, cwd);
1160#endif
1161    }
1162    
1163
1164    //////////////////////////////////////////////////////////////////////////////////////////////
1165    ///
1166    ///
1167
1168    std::string join_nt(const std::vector< std::string > & paths)
1169    {
1170        if(paths.empty()) return "";
1171        if(paths.size() == 1) return paths[0];
1172        
1173        std::string path = paths[0];
1174        
1175        for(unsigned int i=1; i<paths.size(); ++i)
1176        {
1177            std::string b = paths[i];
1178            
1179            bool b_nts = false;
1180            if(path.empty())
1181            {
1182                b_nts = true;
1183            }
1184            else if(isabs_nt(b))
1185            {
1186                // This probably wipes out path so far.  However, it's more
1187                // complicated if path begins with a drive letter:
1188                //     1. join('c:', '/a') == 'c:/a'
1189                //     2. join('c:/', '/a') == 'c:/a'
1190                // But
1191                //     3. join('c:/a', '/b') == '/b'
1192                //     4. join('c:', 'd:/') = 'd:/'
1193                //     5. join('c:/', 'd:/') = 'd:/'
1194                
1195                if( (pystring::slice(path, 1, 2) != ":") ||
1196                    (pystring::slice(b, 1, 2) == ":") )
1197                {
1198                    // Path doesnt start with a drive letter
1199                    b_nts = true;
1200                }
1201                // Else path has a drive letter, and b doesn't but is absolute.
1202                else if((path.size()>3) || 
1203                        ((path.size()==3) && !pystring::endswith(path, "/") && !pystring::endswith(path, "\\")))
1204                {
1205                    b_nts = true;
1206                }
1207            }
1208            
1209            if(b_nts)
1210            {
1211                path = b;
1212            }
1213            else
1214            {
1215                // Join, and ensure there's a separator.
1216                // assert len(path) > 0
1217                if( pystring::endswith(path, "/") || pystring::endswith(path, "\\"))
1218                {
1219                    if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1220                    {
1221                        path += pystring::slice(b, 1);
1222                    }
1223                    else
1224                    {
1225                        path += b;
1226                    }
1227                }
1228                else if(pystring::endswith(path, ":"))
1229                {
1230                    path += b;
1231                }
1232                else if(!b.empty())
1233                {
1234                    if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1235                    {
1236                        path += b;
1237                    }
1238                    else
1239                    {
1240                        path += "\\" + b;
1241                    }
1242                }
1243                else
1244                {
1245                    // path is not empty and does not end with a backslash,
1246                    // but b is empty; since, e.g., split('a/') produces
1247                    // ('a', ''), it's best if join() adds a backslash in
1248                    // this case.
1249                    path += "\\";
1250                }
1251            }
1252        }
1253        
1254        return path;
1255    }
1256    
1257    // Join two or more pathname components, inserting "\\" as needed.
1258    std::string join_nt(const std::string & a, const std::string & b)
1259    {
1260        std::vector< std::string > paths(2);
1261        paths[0] = a;
1262        paths[1] = b;
1263        return join_nt(paths);
1264    }
1265
1266    // Join pathnames.
1267    // If any component is an absolute path, all previous path components
1268    // will be discarded.
1269    // Ignore the previous parts if a part is absolute.
1270    // Insert a '/' unless the first part is empty or already ends in '/'.
1271
1272    std::string join_posix(const std::vector< std::string > & paths)
1273    {
1274        if(paths.empty()) return "";
1275        if(paths.size() == 1) return paths[0];
1276        
1277        std::string path = paths[0];
1278        
1279        for(unsigned int i=1; i<paths.size(); ++i)
1280        {
1281            std::string b = paths[i];
1282            if(pystring::startswith(b, "/"))
1283            {
1284                path = b;
1285            }
1286            else if(path.empty() || pystring::endswith(path, "/"))
1287            {
1288                path += b;
1289            }
1290            else
1291            {
1292                path += "/" + b;
1293            }
1294        }
1295        
1296        return path;
1297    }
1298
1299    std::string join_posix(const std::string & a, const std::string & b)
1300    {
1301        std::vector< std::string > paths(2);
1302        paths[0] = a;
1303        paths[1] = b;
1304        return join_posix(paths);
1305    }
1306    
1307    std::string join(const std::string & path1, const std::string & path2)
1308    {
1309#ifdef WINDOWS
1310        return join_nt(path1, path2);
1311#else
1312        return join_posix(path1, path2);
1313#endif
1314    }
1315
1316
1317    std::string join(const std::vector< std::string > & paths)
1318    {
1319#ifdef WINDOWS
1320        return join_nt(paths);
1321#else
1322        return join_posix(paths);
1323#endif
1324    }
1325    
1326    //////////////////////////////////////////////////////////////////////////////////////////////
1327    ///
1328    ///
1329
1330        
1331    // Split a pathname.
1332    // Return (head, tail) where tail is everything after the final slash.
1333    // Either part may be empty
1334
1335    void split_nt(std::string & head, std::string & tail, const std::string & path)
1336    {
1337        std::string d, p;
1338        splitdrive_nt(d, p, path);
1339        
1340        // set i to index beyond p's last slash
1341        int i = (int)p.size();
1342        
1343        while(i>0 && (p[i-1] != '\\') && (p[i-1] != '/'))
1344        {
1345            i = i - 1;
1346        }
1347
1348        head = pystring::slice(p,0,i);
1349        tail = pystring::slice(p,i); // now tail has no slashes
1350        
1351        // remove trailing slashes from head, unless it's all slashes
1352        std::string head2 = head;
1353        while(!head2.empty() && ((pystring::slice(head2,-1) == "/") ||
1354                                 (pystring::slice(head2,-1) == "\\")))
1355        {
1356            head2 = pystring::slice(head,0,-1);
1357        }
1358        
1359        if(!head2.empty()) head = head2;
1360        head = d + head;
1361    }
1362
1363
1364    // Split a path in head (everything up to the last '/') and tail (the
1365    // rest).  If the path ends in '/', tail will be empty.  If there is no
1366    // '/' in the path, head  will be empty.
1367    // Trailing '/'es are stripped from head unless it is the root.
1368
1369    void split_posix(std::string & head, std::string & tail, const std::string & p)
1370    {
1371        int i = pystring::rfind(p, "/") + 1;
1372        
1373        head = pystring::slice(p,0,i);
1374        tail = pystring::slice(p,i);
1375        
1376        if(!head.empty() && (head != pystring::mul("/", (int) head.size())))
1377        {
1378            head = pystring::rstrip(head, "/");
1379        }
1380    }
1381
1382    void split(std::string & head, std::string & tail, const std::string & path)
1383    {
1384#ifdef WINDOWS
1385        return split_nt(head, tail, path);
1386#else
1387        return split_posix(head, tail, path);
1388#endif
1389    }
1390
1391
1392    //////////////////////////////////////////////////////////////////////////////////////////////
1393    ///
1394    ///
1395
1396    std::string basename_nt(const std::string & path)
1397    {
1398        std::string head, tail;
1399        split_nt(head, tail, path);
1400        return tail;
1401    }
1402
1403    std::string basename_posix(const std::string & path)
1404    {
1405        std::string head, tail;
1406        split_posix(head, tail, path);
1407        return tail;
1408    }
1409
1410    std::string basename(const std::string & path)
1411    {
1412#ifdef WINDOWS
1413        return basename_nt(path);
1414#else
1415        return basename_posix(path);
1416#endif
1417    }
1418
1419    std::string dirname_nt(const std::string & path)
1420    {
1421        std::string head, tail;
1422        split_nt(head, tail, path);
1423        return head;
1424    }
1425    
1426    std::string dirname_posix(const std::string & path)
1427    {
1428        std::string head, tail;
1429        split_posix(head, tail, path);
1430        return head;
1431    }
1432    
1433    std::string dirname(const std::string & path)
1434    {
1435#ifdef WINDOWS
1436        return dirname_nt(path);
1437#else
1438        return dirname_posix(path);
1439#endif
1440    }
1441
1442
1443    //////////////////////////////////////////////////////////////////////////////////////////////
1444    ///
1445    ///
1446
1447    // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
1448    std::string normpath_nt(const std::string & p)
1449    {
1450        std::string path = p;
1451        path = pystring::replace(path, "/","\\");
1452        
1453        std::string prefix;
1454        splitdrive_nt(prefix, path, path);
1455        
1456        // We need to be careful here. If the prefix is empty, and the path starts
1457        // with a backslash, it could either be an absolute path on the current
1458        // drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
1459        // is therefore imperative NOT to collapse multiple backslashes blindly in
1460        // that case.
1461        // The code below preserves multiple backslashes when there is no drive
1462        // letter. This means that the invalid filename \\\a\b is preserved
1463        // unchanged, where a\\\b is normalised to a\b. It's not clear that there
1464        // is any better behaviour for such edge cases.
1465        
1466        if(prefix.empty())
1467        {
1468            // No drive letter - preserve initial backslashes
1469            while(pystring::slice(path,0,1) == "\\")
1470            {
1471                prefix = prefix + "\\";
1472                path = pystring::slice(path,1);
1473            }
1474        }
1475        else
1476        {
1477            // We have a drive letter - collapse initial backslashes
1478            if(pystring::startswith(path, "\\"))
1479            {
1480                prefix = prefix + "\\";
1481                path = pystring::lstrip(path, "\\");
1482            }
1483        }
1484        
1485        std::vector<std::string> comps;
1486        pystring::split(path, comps, "\\");
1487        
1488        int i = 0;
1489        
1490        while(i<(int)comps.size())
1491        {
1492            if(comps[i].empty() || comps[i] == ".")
1493            {
1494                comps.erase(comps.begin()+i);
1495            }
1496            else if(comps[i] == "..")
1497            {
1498                if(i>0 && comps[i-1] != "..")
1499                {
1500                    comps.erase(comps.begin()+i-1, comps.begin()+i+1);
1501                    i -= 1;
1502                }
1503                else if(i == 0 && pystring::endswith(prefix, "\\"))
1504                {
1505                    comps.erase(comps.begin()+i);
1506                }
1507                else
1508                {
1509                    i += 1;
1510                }
1511            }
1512            else
1513            {
1514                i += 1;
1515            }
1516        }
1517        
1518        // If the path is now empty, substitute '.'
1519        if(prefix.empty() && comps.empty())
1520        {
1521            comps.push_back(".");
1522        }
1523        
1524        return prefix + pystring::join("\\", comps);
1525    }
1526
1527    // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
1528    // It should be understood that this may change the meaning of the path
1529    // if it contains symbolic links!
1530    // Normalize path, eliminating double slashes, etc.
1531
1532    std::string normpath_posix(const std::string & p)
1533    {
1534        if(p.empty()) return ".";
1535        
1536        std::string path = p;
1537        
1538        int initial_slashes = pystring::startswith(path,"/") ? 1 : 0;
1539        
1540        // POSIX allows one or two initial slashes, but treats three or more
1541        // as single slash.
1542        
1543        if (initial_slashes && pystring::startswith(path,"//")
1544            && !pystring::startswith(path,"///"))
1545            initial_slashes = 2;
1546        
1547        std::vector<std::string> comps, new_comps;
1548        pystring::split(path, comps, "/");
1549        
1550        for(unsigned int i=0; i<comps.size(); ++i)
1551        {
1552            std::string comp = comps[i];
1553            if(comp.empty() || comp == ".")
1554                continue;
1555            
1556            if( (comp != "..") || ((initial_slashes == 0) && new_comps.empty()) ||
1557                (!new_comps.empty() && new_comps[new_comps.size()-1] == ".."))
1558            {
1559                new_comps.push_back(comp);
1560            }
1561            else if (!new_comps.empty())
1562            {
1563                new_comps.pop_back();
1564            }
1565        }
1566        
1567        path = pystring::join("/", new_comps);
1568        
1569        if (initial_slashes > 0)
1570            path = pystring::mul("/",initial_slashes) + path;
1571        
1572        if(path.empty()) return ".";
1573        return path;
1574    }
1575    
1576    std::string normpath(const std::string & path)
1577    {
1578#ifdef WINDOWS
1579        return normpath_nt(path);
1580#else
1581        return normpath_posix(path);
1582#endif
1583    }
1584
1585    //////////////////////////////////////////////////////////////////////////////////////////////
1586    ///
1587    ///
1588
1589    // Split the extension from a pathname.
1590    // Extension is everything from the last dot to the end, ignoring
1591    // leading dots.  Returns "(root, ext)"; ext may be empty.
1592    // It is always true that root + ext == p
1593
1594    void splitext_generic(std::string & root, std::string & ext,
1595                          const std::string & p,
1596                          const std::string & sep,
1597                          const std::string & altsep,
1598                          const std::string & extsep)
1599    {
1600        int sepIndex = pystring::rfind(p, sep);
1601        if(!altsep.empty())
1602        {
1603            int altsepIndex = pystring::rfind(p, altsep);
1604            sepIndex = std::max(sepIndex, altsepIndex);
1605        }
1606
1607        int dotIndex = pystring::rfind(p, extsep);
1608        if(dotIndex > sepIndex)
1609        {
1610            // Skip all leading dots
1611            int filenameIndex = sepIndex + 1;
1612
1613            while(filenameIndex < dotIndex)
1614            {
1615                if(pystring::slice(p,filenameIndex) != extsep)
1616                {
1617                    root = pystring::slice(p, 0, dotIndex);
1618                    ext = pystring::slice(p, dotIndex);
1619                    return;
1620                }
1621
1622                filenameIndex += 1;
1623            }
1624        }
1625
1626        root = p;
1627        ext = "";
1628    }
1629
1630    void splitext_nt(std::string & root, std::string & ext, const std::string & path)
1631    {
1632        return splitext_generic(root, ext, path,
1633                                "\\", "/", ".");
1634    }
1635
1636    void splitext_posix(std::string & root, std::string & ext, const std::string & path)
1637    {
1638        return splitext_generic(root, ext, path,
1639                                "/", "", ".");
1640    }
1641
1642    void splitext(std::string & root, std::string & ext, const std::string & path)
1643    {
1644#ifdef WINDOWS
1645        return splitext_nt(root, ext, path);
1646#else
1647        return splitext_posix(root, ext, path);
1648#endif
1649    }
1650
1651} // namespace path
1652} // namespace os
1653
1654
1655}//namespace pystring
1656
1657}
1658OCIO_NAMESPACE_EXIT