home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_11_11 / splash / splash.cpp < prev    next >
Text File  |  1993-01-15  |  17KB  |  645 lines

  1. /*
  2.  * Version 1.8
  3.  * Written by Jim Morris,  jegm@sgi.com
  4.  * Kudos to Larry Wall for inventing SP
  5.  * Copyrights only exist on the regex stuff,  and all
  6.  * have been left intact.
  7.  * The only thing I ask is that you let me know of any nifty fixes or
  8.  * additions.
  9.  * Credits:
  10.  * I'd like to thank Michael Golan <mg@Princeton.EDU> for his critiques
  11.  * and clever suggestions. Some of which have actually been implemented
  12.  */
  13. #include <iostream.h>
  14. #include <string.h>
  15. #include <malloc.h>
  16. #include <stdio.h>
  17.  
  18. #ifdef    __TURBOC__
  19. #pragma hdrstop
  20. #endif
  21.  
  22. #include "splash.h"
  23.  
  24. //************************************************************
  25. // VarString Implementation
  26. //************************************************************
  27.  
  28. VarString& VarString::operator=(const char *s)
  29. {
  30.     int nl= strlen(s);
  31.     if(nl+1 >= allocated) grow((nl-allocated)+allocinc);
  32.     assert(allocated > nl+1);
  33.     strcpy(a, s);
  34.     len= nl;
  35.     return *this;
  36. }
  37.  
  38. VarString& VarString::operator=(const VarString& n)
  39. {
  40.     if(this != &n){
  41.     if(n.len+1 >= allocated){ // if it is not big enough
  42. #        ifdef    DEBUG
  43.         fprintf(stderr, "~operator=(VarString&) a= %p\n", a);
  44. #        endif
  45.         delete [] a; // get rid of old one
  46.         allocated= n.allocated;
  47.         allocinc= n.allocinc;
  48.         a= new char[allocated];
  49. #        ifdef    DEBUG
  50.         fprintf(stderr, "operator=(VarString&) a= %p, source= %p\n", a, n.a);
  51. #        endif
  52.     }
  53.         len= n.len;
  54.     strcpy(a, n.a);
  55.     }
  56.     return *this;
  57. }
  58.  
  59. void VarString::grow(int n)
  60. {
  61.     if(n == 0) n= allocinc;
  62.     allocated += n;
  63.     char *tmp= new char[allocated];
  64.     strcpy(tmp, a);
  65. #ifdef    DEBUG
  66.     fprintf(stderr, "VarString::grow() a= %p, old= %p, allocinc= %d\n", tmp, a, allocinc);
  67.     fprintf(stderr, "~VarString::grow() a= %p\n", a);
  68. #endif
  69.     delete [] a;
  70.     a= tmp;
  71. }
  72.  
  73. void VarString::add(char c)
  74. {
  75.     if(len+1 >= allocated) grow();
  76.     assert(allocated > len+1);
  77.     a[len++]= c; a[len]= '\0';
  78. }
  79.  
  80. void VarString::add(const char *s)
  81. {
  82.     int nl= strlen(s);
  83.     if(len+nl >= allocated) grow(((len+nl)-allocated)+allocinc);
  84.     assert(allocated > len+nl);
  85.     strcat(a, s);
  86.     len+=nl;
  87. }
  88.  
  89. void VarString::add(int ip, const char *s)
  90. {
  91.     int nl= strlen(s);
  92.     if(len+nl >= allocated) grow(((len+nl)-allocated)+allocinc);
  93.     assert(allocated > len+nl);
  94.     memmove(&a[ip+nl], &a[ip], (len-ip)+1); // shuffle up
  95.     memcpy(&a[ip], s, nl);
  96.     len+=nl;
  97.     assert(a[len] == '\0');
  98. }
  99.  
  100. void VarString::remove(int ip, int n)
  101. {
  102.     assert(ip+n <= len);
  103.     memmove(&a[ip], &a[ip+n], len-ip); // shuffle down
  104.     len-=n;
  105.     assert(a[len] == '\0');    
  106. }
  107.  
  108. //************************************************************
  109. // SPString stuff
  110. //************************************************************
  111.  
  112. // assignments
  113. SPString& SPString::operator=(const SPString& n)
  114. {
  115.     if(this == &n) return *this;
  116.     pstr= n.pstr;
  117.     return *this;
  118. }
  119.  
  120. SPString& SPString::operator=(const substring& sb)
  121. {
  122.     VarString tmp(sb.pt, sb.len);
  123.     pstr= tmp;
  124.     return *this;
  125. }
  126.  
  127. // concatenations
  128. SPString SPString::operator+(const SPString& s) const
  129. {
  130.     SPString ts(*this);
  131.     ts.pstr.add(s);    
  132.     return ts; 
  133. }
  134.  
  135. SPString SPString::operator+(const char *s) const
  136. {
  137.     SPString ts(*this);
  138.     ts.pstr.add(s);
  139.     return ts; 
  140. }
  141.  
  142. SPString SPString::operator+(char c) const
  143. {
  144.     SPString ts(*this);
  145.     ts.pstr.add(c);
  146.     return ts; 
  147. }
  148.  
  149. SPString operator+(const char *s1, const SPString& s2)
  150. {
  151.     SPString ts(s1);
  152.     ts = ts + s2;
  153. //    cout << "s2[0] = " << s2[0] << endl; // gives incorrect error
  154.     return ts; 
  155. }
  156.  
  157. //************************************************************
  158. // other stuff
  159. //************************************************************
  160.  
  161. char SPString::chop(void)
  162. {
  163.     int n= length();
  164.     if(n <= 0) return '\0'; // empty
  165.     char tmp= pstr[n-1];
  166.     pstr.remove(n-1);
  167.     return tmp;
  168. }
  169.  
  170. int SPString::index(const SPString& s, int offset)
  171. {
  172.     if(offset < 0) offset= 0;
  173.     for(int i=offset;i<length();i++){
  174.     if(strncmp(&pstr[i], s, s.length()) == 0) return i;
  175.     }
  176.  
  177.     return -1;
  178. }
  179.  
  180. int SPString::rindex(const SPString& s, int offset)
  181. {
  182.     if(offset == -1) offset= length()-s.length();
  183.     else offset= offset-s.length()+1;
  184.     if(offset > length()-s.length()) offset= length()-s.length();
  185.       
  186.     for(int i=offset;i>=0;i--){
  187.     if(strncmp(&pstr[i], s, s.length()) == 0) return i;
  188.     }
  189.     return -1;
  190. }
  191.  
  192. SPString::substring SPString::substr(int offset, int len)
  193. {
  194.     if(len == -1) len= length() - offset; // default use rest of string
  195.     if(offset < 0){
  196.     offset= length() + offset;  // count from end of string
  197.     if(offset < 0) offset= 0;   // went too far, adjust to start
  198.     }
  199.     return substring(*this, offset, len);
  200. }
  201.  
  202. // this is private
  203. // it shrinks or expands string as required
  204. void SPString::insert(int pos, int len, const char *s, int nlen)
  205. {
  206.     if(pos < length()){ // nothing to delete if not true
  207.     if((len+pos) > length()) len= length() - pos;
  208.     pstr.remove(pos, len);  // first remove subrange
  209.     }else pos= length();
  210.  
  211.     VarString tmp(s, nlen);
  212.     pstr.add(pos, tmp);        // then insert new substring
  213. }
  214.  
  215. int SPString::m(Regexp& r)
  216. {
  217.     return r.match(*this);
  218. }
  219.  
  220. int SPString::m(const char *pat, const char *opts)
  221. {
  222. int iflg= strchr(opts, 'i') != NULL;
  223.     Regexp r(pat, iflg?Regexp::nocase:0);
  224.     return m(r);
  225. }
  226.  
  227. int SPString::m(Regexp& r, SPStringList& psl)
  228. {
  229.     if(!r.match(*this)) return 0;
  230.     psl.reset();    // clear it first
  231.     Range rng;
  232.     for (int i=0; i<r.groups(); i++){
  233.         rng= r.getgroup(i);
  234.     psl.push(substr(rng.start(), rng.length()));
  235.     }
  236.     return r.groups();
  237. }
  238.  
  239. int SPString::m(const char *pat, SPStringList& psl, const char *opts)
  240. {
  241. int iflg= strchr(opts, 'i') != NULL;
  242.     Regexp r(pat, iflg?Regexp::nocase:0);
  243.     return m(r, psl);
  244. }
  245.  
  246. //
  247. // I know! This is not fast, but it works!!
  248. //
  249. int SPString::tr(const char *sl, const char *rl, const char *opts)
  250. {
  251.     if(length() == 0 || strlen(sl) == 0) return 0;
  252.  
  253.     int cflg= strchr(opts, 'c') != NULL; // thanks Michael
  254.     int dflg= strchr(opts, 'd') != NULL;
  255.     int sflg= strchr(opts, 's') != NULL;
  256.  
  257.     int cnt= 0, flen= 0;
  258.     SPString t;
  259.     unsigned char lstc= '\0', fr[256];
  260.     
  261.     // build search array, which is a 256 byte array that stores the index+1
  262.     // in the search string for each character found, == 0 if not in search
  263.     memset(fr, 0, 256);
  264.     for(int i=0;i<strlen(sl);i++){
  265.     if(i && sl[i] == '-'){ // got a range
  266.         assert(i+1 < strlen(sl) && lstc <= sl[i+1]); // sanity check
  267.         for(unsigned char c=lstc+1;c<=sl[i+1];c++){
  268.         fr[c]= ++flen;
  269.         }
  270.         i++; lstc= '\0';
  271.     }else{
  272.         lstc= sl[i];
  273.         fr[sl[i]]= ++flen;
  274.     }
  275.     }
  276.  
  277.     int rlen;
  278.     // build replacement list
  279.     if((rlen=strlen(rl)) != 0){
  280.     for(i=0;i<rlen;i++){
  281.         if(i && rl[i] == '-'){ // got a range
  282.         assert(i+1 < rlen && t[t.length()-1] <= rl[i+1]); // sanity check
  283.         for(char c=t[i-1]+1;c<=rl[i+1];c++) t += c;
  284.         i++;
  285.         }else t += rl[i];
  286.     }
  287.     }
  288.  
  289.     // replacement string that is shorter uses last character for rest of string
  290.     // unless the delete option is in effect or it is empty
  291.     while(!dflg && rlen && flen > t.length()){
  292.     t += t[t.length()-1]; // duplicate last character
  293.     }
  294.  
  295.     rlen= t.length(); // length of translation string   
  296.    
  297.     // do translation, and deletion if dflg (actually falls out of length of t)
  298.     // also squeeze translated characters if sflg
  299.  
  300.     SPString tmp; // need this in case dflg, and string changes size
  301.     for(i=0;i<length();i++){
  302.     int off;
  303.     if(cflg){ // complement, ie if NOT in f
  304.         char rc= !dflg ? t[t.length()-1] : '\0'; // always use last character for replacement
  305.         if((off=fr[(*this)[i]]) == 0){ // not in map
  306.         cnt++;
  307.         if(!dflg && (!sflg || tmp.length() == 0 || tmp[tmp.length()-1] != rc))
  308.             tmp += rc;
  309.         }else tmp += (*this)[i]; // just stays the same
  310.     }else{ // in fr so substitute with t, if no equiv in t then delete
  311.         if((off=fr[(*this)[i]]) > 0){
  312.         off--; cnt++;
  313.         if(rlen==0 && !dflg && (!sflg || tmp.length() == 0 || tmp[tmp.length()-1] != (*this)[i])) tmp += (*this)[i]; // stays the same
  314.         else if(off < rlen && (!sflg || tmp.length() == 0 || tmp[tmp.length()-1] != t[off]))
  315.             tmp += t[off]; // substitute
  316.         }else tmp += (*this)[i]; // just stays the same
  317.     }
  318.     }
  319.  
  320.     *this= tmp;
  321.     return cnt;
  322. }
  323.  
  324. int SPString::s(const char *exp, const char *repl, const char *opts)
  325. {
  326. int gflg= strchr(opts, 'g') != NULL;
  327. int iflg= strchr(opts, 'i') != NULL;
  328. int cnt= 0;
  329. Regexp re(exp, iflg?Regexp::nocase:0);
  330. Range rg;
  331.  
  332.     if(re.match(*this)){
  333.     // OK I know, this is a horrible hack, but it seems to work
  334.     if(gflg){ // recursively call s() until applied to whole string
  335.         rg= re.getgroup(0);
  336.         if(rg.end()+1 < length()){
  337.         SPString st(substr(rg.end()+1));
  338. //        cout << "Substring: " << st << endl;
  339.         cnt += st.s(exp, repl, opts);
  340.         substr(rg.end()+1)= st;
  341. //        cout << "NewString: " << *this << endl;
  342.         }
  343.     }    
  344.  
  345.     if(!strchr(repl, '$')){ // straight, simple substitution
  346.         rg= re.getgroup(0);
  347.         substr(rg.start(), rg.length())= repl;
  348.         cnt++;    
  349.     }else{ // need to do subexpression substitution
  350.         char c;
  351.         const char *src= repl;
  352.         SPString dst;
  353.         int no;
  354.         while ((c = *src++) != '\0') {
  355.         if(c == '$' && *src == '&'){
  356.             no = 0; src++;
  357.         }else if(c == '$' && '0' <= *src && *src <= '9')
  358.             no = *src++ - '0';
  359.         else no = -1;
  360.  
  361.         if(no < 0){    /* Ordinary character. */
  362.             if(c == '\\' && (*src == '\\' || *src == '$'))
  363.             c = *src++;
  364.             dst += c;
  365.         }else{
  366.             rg= re.getgroup(no);
  367.             dst += substr(rg.start(), rg.length());
  368.         }
  369.         }
  370.         rg= re.getgroup(0);
  371.         substr(rg.start(), rg.length())= dst;
  372.         cnt++;
  373.     }
  374.  
  375.     return cnt;
  376.     }
  377.     return cnt;
  378. }
  379.  
  380. SPStringList SPString::split(const char *pat, int limit)
  381. {
  382. SPStringList l;
  383.  
  384.     l.split(*this, pat, limit);
  385.     return l;
  386. }
  387.  
  388. //************************************************************
  389. // SPStringList stuff
  390. //************************************************************
  391.  
  392. int SPStringList::split(const char *str, const char *pat, int limit)
  393. {
  394. Regexp re(pat);
  395. Range rng;
  396. SPString s(str);
  397. int cnt= 1;
  398.     
  399.     if(*pat == '\0'){ // special empty string case splits entire thing
  400.     while(*str){
  401.         s= *str++;
  402.         push(s);
  403.     }
  404.     return count();
  405.     }
  406.  
  407.     if(strcmp(pat, "' '") == 0){ // special awk case
  408.     char *p, *ws= " \t\n";
  409.     TempString t(str); // can't hack users data
  410.     p= strtok(t, ws);
  411.     while(p){
  412.         push(p);
  413.         p= strtok(NULL, ws);
  414.     }
  415.     return count();
  416.     }
  417.  
  418.     while(re.match(s) && (limit < 0 || cnt < limit)){ // find separator
  419.     rng= re.getgroup(0); // full matched string (entire separator)
  420.     push(s.substr(0, rng.start()));
  421.     for(int i=1;i<re.groups();i++){
  422.         push(s.substr(re.getgroup(i))); // add subexpression matches
  423.     }
  424.     
  425.     s= s.substr(rng.end()+1);
  426.     cnt++;
  427.     }
  428.     if(s.length()) push(s);
  429.  
  430.     if(limit < 0){ // strip trailing null entries
  431.     int off= count()-1;
  432.     while(off >= 0 && (*this)[off].length() == 0){
  433.         off--;
  434.     }
  435.     splice(off+1);
  436.     }
  437.     return count();
  438. }
  439.  
  440. SPString SPStringList::join(const char *pat)
  441. {
  442. SPString ts;
  443.  
  444.     for(int i=0;i<count();i++){
  445.     ts += (*this)[i];
  446.     if(i<count()-1) ts += pat;
  447.     }
  448.     return ts;
  449. }
  450.  
  451.  
  452. SPStringList::SPStringList(const SPStringList& n)
  453. {
  454.     for(int i=0;i<n.count();i++){
  455.     push(n[i]);
  456.     }
  457. }
  458.  
  459. SPStringList& SPStringList::operator=(const SPList<SPString>& n)
  460. {
  461.     if(this == &n) return *this;
  462.     // erase old one
  463.     reset();
  464.     
  465.     for(int i=0;i<n.count();i++){
  466.     push(n[i]);
  467.     }
  468.     return *this;
  469. }
  470.  
  471. int SPStringList::m(const char *rege, const char *targ, const char *opts)
  472. {
  473. int iflg= strchr(opts, 'i') != NULL;
  474. Regexp r(rege, iflg?Regexp::nocase:0);
  475.     if(!r.match(targ)) return 0;
  476.     Range rng;
  477.     for (int i=0; i<r.groups(); i++){
  478.         rng= r.getgroup(i);
  479.     push(SPString(targ).substr(rng.start(), rng.length()));
  480.     }
  481.     return r.groups();
  482. }
  483.  
  484. SPStringList SPStringList::grep(const char *rege, const char *opts)
  485. {
  486. SPStringList rt;
  487. int iflg= strchr(opts, 'i') != NULL;
  488.  
  489.     Regexp rexp(rege, iflg?Regexp::nocase:0);    // compile once
  490.     for(int i=0;i<count();i++){
  491.         if(rexp.match((*this)[i])){
  492.         rt.push((*this)[i]);
  493.     }
  494.     }
  495.     return rt;
  496. }
  497.  
  498. //************************************************************
  499. // streams stuff
  500. //************************************************************
  501.  
  502. istream& operator>>(istream& ifs, SPString& s)
  503. {
  504. char c;
  505. #if 0
  506. char buf[40];
  507. #else
  508. char buf[132];
  509. #endif
  510.  
  511.     s= ""; // empty string
  512.     ifs.get(buf, sizeof buf); 
  513.     // This is tricky because a line teminated by end of file that is not terminated
  514.     // with a '\n' first is considered an OK line, but ifs.good() will fail.
  515.     // This will correctly return the last line if it is terminated by eof with the
  516.     // stream still in a non-fail condition, but at eof, so next call will fail as
  517.     // expected
  518.     if(ifs){         // previous operation was ok
  519.         s += buf;     // append buffer to string
  520. //    cout << "<" << buf << ">" << endl;
  521.     // if its a long line continue appending to string
  522.     while(ifs.good() && (c=ifs.get()) != '\n'){
  523. //        cout << "eof1= " << ifs.eof() << endl;
  524.         ifs.putback(c);
  525. //          cout << "eof2= " << ifs.eof() << endl;
  526.         if(ifs.get(buf, sizeof buf)) s += buf; // append to line
  527.     }
  528.     }
  529.     return ifs;    
  530. }
  531.  
  532. istream& operator>>(istream& ifs, SPStringList& sl)
  533. {
  534. SPString s;
  535.  
  536.     // Should I reset sl first?
  537.     sl.reset(); // I think so, to be consistent
  538.     
  539.     while(ifs >> s){
  540.     sl.push(s);
  541. //    cout << "<" << s << ">" << endl;
  542.     };
  543.     return ifs;    
  544. }
  545.  
  546. ostream& operator<<(ostream& os,  const SPString& arr)
  547. {
  548. #ifdef TEST
  549.     os << "(" << arr.length() << ")" << "\"";
  550.     os << (const char *)arr;
  551.     os << "\"";
  552. #else
  553.     os << (const char *)arr;
  554. #endif
  555.     return os;   
  556. }
  557.  
  558. ostream& operator<<(ostream& os,  const SPStringList& arr)
  559. {
  560.  
  561.     for(int i=0;i<arr.count();i++)
  562. #ifdef TEST
  563.     os << "[" << i << "]" << arr[i] << endl;
  564. #else     
  565.     os << arr[i] << endl; 
  566. #endif     
  567.     return os;   
  568. }
  569.  
  570. //************************************************************
  571. // Slice stuff
  572. //************************************************************
  573.  
  574. // a..b is range a thru b
  575. // a-b is also range a thru b
  576. // a,b,c is a and b and c
  577. Slice::Slice(const char *s)
  578. {
  579. SPStringList sl;
  580. SPString sep;
  581. int i1, i2;
  582.  
  583.     rl= new SPList<Range>;
  584.     sl.split(s, "([-,]|\\.\\.)"); // split on separators and save them
  585. //    cout << sl << endl;
  586.     
  587.     while(sl){
  588.     i1= atoi(sl.shift()); // +++ should check it is a valid number            
  589.         if(sl){
  590.         sep= sl.shift(); // get separator
  591.             if(sep == "-" || sep == ".."){ // its a range
  592.         if(sl.isempty()){ // check there is more
  593.                     cerr << "\nError in Slice, bad range in string: " << s << endl;
  594.             return;                         
  595.                 }
  596.         i2= atoi(sl.shift()); // +++ get end of range
  597.                 rl->push(Range(i1, i2)); // +++ Should see if range is reversed, or contiguous
  598.                 if(sl && (sep=sl.shift()) != ","){
  599.                     cerr << "\nError in Slice, Range not terminated correctly in string: "
  600.                          << s << " by: " << sep << endl;
  601.                 }
  602.             }else if(sep == ","){ // its a single
  603.         add(i1);
  604.             }else{ // oops
  605.                 cerr << "\nError in Slice, bad separator in string: " << s
  606.              << "at: " << sep << endl;
  607.                 return;
  608.             }
  609.         }else add(i1); // last one must be a single
  610.     }
  611.     cout << *this << endl;
  612. }
  613.  
  614. void Slice::add(int i)
  615. {
  616. int n= rl->count()-1;
  617.     // if and only if this index is one greater than the previous on
  618.     if(n >= 0 && (*rl)[n].end() == i-1){ // extend end of range by 1
  619.     ((*rl)[n])++;
  620.     }else rl->push(Range(i, i));
  621. }
  622.  
  623. // a list of at least one indices, terminated by -1
  624. Slice::Slice(int n, ...)
  625. {
  626. va_list ap;
  627. int arg;
  628. va_start(ap, n);
  629.  
  630.     rl= new SPList<Range>;
  631.     add(n);
  632.     while((arg=va_arg(ap, int)) >= 0){
  633.     add(arg);
  634.     }
  635.     va_end(ap);
  636. }
  637.  
  638. ostream& operator<<(ostream& os, const Slice& sl)
  639. {
  640.     for(int i=0;i<sl.rl->count();i++){
  641.         os << (*sl.rl)[i] << endl;
  642.     }
  643.     return os;
  644. }
  645.