//strTools.c -- some utility functions about string processing
/// @CopyLeft by tsaiwn@csie.nctu.edu.tw 
////// 更多關於字串處理的函數 -- more string processing functions
///字串(string)在 C 語言其實就是用 0 ('\0')結束的一串字符(char)!
/// 了解這觀念後, 要對字串處理應該就能得心應手了!
///   之前我們看過了 isalpha, isdigit, tolower, toupper 等,
//也看過了 strlen,strcmp, strcpy,strncpy,strcat 等程式庫都很簡單!
/// *本範例中共寫了 10 個很常用的字串處理 functions (不在程式庫),
///   (有些之前已給過大家, 重新整理在這方便大家收藏:)
/// 以後若你的工作是寫程式, 這些 functions 將會很有用 :-)
/// 自己想一下, 如何寫 int startWith(char*s, char*pat); ?
/// === === ===
#include <ctype.h>
#include <string.h>
#include <stdlib.h> 
/// 用 macro 定義一些常用看起來像"函數", 但其實是 Macro(巨集)
#define max(x, y) ((x)>(y)) ? (x) : (y)
#define min(x, y) ((x)<(y)) ? (x) : (y) 
/// 模仿 Pascal Language 的  repeat ... until 
#define repeat do {
#define until(x)  } while(!(x)); 
/// Declare  先宣告 
char* chop(char*);    // 咬掉尾巴的 NewLine '\n' if any
char* squeeze(char*);   // 擠掉所有的空白 (white space)
char* toLower(char*p), *toUpper(char*);
char* trim(char*p), *trimLeft(char*), *trimRight(char*);
int sIndex(char*s, char*pat);  // position of pat in s
char* substr(char*s, int start, int n); // MID$(s, start, n)
char* sqz22(char*); // 去掉頭尾空白, 中間則把多空白變只留一空格
///  
char* chop(char*p) {   // remove tail '\n' ; by tsaiwn@csie.nctu
    char* pOLD = p;
    if(p[0] == 0) return p; // NULL string
    while(p[0] != 0) ++p;   // 到字串結束的 NULL char; p[0]就是 *p
    --p; // move back to the last char
    if(*p == '\n') *p = 0; // remove the newLine if it is
    return pOLD;
}// chop(  
char* toLower(char*p){
   int i;
   for(i=0; i< strlen(p); i++) p[i]= tolower(p[i]);
   return p;
} // toLower  
char* toUpper(char*p){
   char* pOLD = p;
   while(*p) {  // *p != 0
      *p = toupper(p[0]);   // p[0] 就是 *p
      ++p;
   }
   return pOLD;
} // toUpper  
char* squeeze(char*p) {  // 擠掉所有空白
   char*pOLD = p;
   char*p2 = p;
   while(*p) {  // p[0] != 0
      if( isspace(*p) ) { p++; continue; }
      *p2 = *p;
      ++p2; p++;
   } // while
   *p2 = 0;  // NULL terminated
   return pOLD;
} // squeeze(  
char* trimLeft(char*p) {   // 去掉 Leading Blanks
   char*pOLD = p;
   char*p2 = p;
   while(*p  &&  isspace(*p) ) ++p;  // till nonSpace  
   while(*p) {   // till end of the string
      *p2 = *p;
      p2++; ++p;
   }//while
   *p2 = 0;  // NULL terminated
   return pOLD;
} // trimLeft  
char* trimRight(char*p) {   // 只去掉尾巴的空白
   char*p2 = p;
   if(*p == 0) return p;  // EMPTY string
   while(*p2) ++p2;  // found the end of string p
   --p2;  // back one char
   while( p2 != p) {
      if( !isspace(*p2) ) break;  // 不是空白, Done !
      *p2 = 0;  // 去掉尾巴 white space
      --p2; 
   }//while
   return p;   // 正確 OK
} // trimRight  
char* trim(char*p) {   // 去掉頭部(Leading)空白和尾巴的空白
   trimRight(p);
   return trimLeft(p);   // 注意 trim 不去管中間的空白喔 !
} // trim(  
int sIndex(char*s, char*pat) {  // find position of pat in s
    char* p = strstr(s, pat);  // find pat in s
    if(p == 0) return -1;  // not found
    return p - s;   // position 0..n-1
} // kIndex(  
char* substr(char*s, int start, int len) {
    // sub string 從 s[start] 開始的 len 個 char
    char* p = malloc(len+1);     // 宣告在 <stdlib.h> 內
    p[len] = 0;  // NULL terminated, 萬一真的 n 字還沒結束 :-)
    strncpy(p, s+start, len); // copy最多len字從 s+start 到 p
    return p;    // MID$(s, start, len);
    // Note that this might cause memory leaking !
} // substr  注意與 Java 的 String 的 substring 用法不同喔!
char* sqz22(char*s) {
    int hasSpace;
    char*pOLD=s, * p = s;   // 注意這寫法, 不然寫成兩句 !
    char* p2 = s;  // p2 points to s[0] too
    trim(s);  // remove leading blanks, and trailing blanks
    hasSpace = 0;  // no space now
    while(*p) {
       if( isspace(*p) ) {
           if(hasSpace) { ++p; continue; } // skip it
           *p2 = ' ';  // 換成真的空格, 不管原來是啥空白符號
           hasSpace = 1;  // FLAG on
       }else{
           hasSpace = 0; // reset the FLAG, 不必管它原來怎樣!
           *p2 = *p;   // 這 char 當然要囉 
       } // if
       ++p2; p++;
    }// while
    *p2 = 0;  // NULL terminated 
    return pOLD;
} // sqz22(   
#include <stdio.h>
int main( ) {
    static char ss[ ]="   a b   c123   4    5 678   ";
    static char tmps[99];  // temporary string
    char gg[ ] = "abcde1234567";  // array of char == string
    char *yy = "HaHeeABC135";   // yy points to a string
    printf("gg=%s\nyy=%s\n", gg, yy);
    printf("substr(gg, 3, 5)=%s\n", substr(gg, 3, 5) );
    printf("substr(gg, 9, 5)=%s\n", substr(gg, 9, 5) );
    printf("substr(yy, 2, 3)=%s\n", substr(yy, 2, 3) );
    printf("substr(yy, 6, 5)=%s\n", substr(yy, 6, 5) );
    printf("substr(yy, 9, 5)=%s\n", substr(yy, 9, 5) );
    strcpy(tmps, ss); // 注意不要把常數字串傳給 trim, squeeze 等
    printf(" String tmps is \"%s\"\n", tmps);
///
    printf("Test squeeze( tmps )=%s==\n", squeeze(tmps) );
    strcpy(tmps, ss); // 重新 copy ss 到 tmps for Test
    printf("Test sqz22( tmps )=%s==\n", sqz22(tmps) );
    strcpy(tmps, ss); // 重新 copy ss 到 tmps
    printf("Test trimLeft( tmps )=%s==\n", trimLeft(tmps) );
    strcpy(tmps, ss); // 重新 copy 到 tmps 
    printf("Test trimRight( tmps )=%s==\n", trimRight(tmps) );
    strcpy(tmps, ss); // 重新 copy 
    printf("Test trim( tmps )=%s==去頭尾\n", trim(tmps) );
///
    printf("Test sIndex, should be 5 ? == %d\n", 
           sIndex("abcHeHelloHaha", "Hello") );
    printf("Test sIndex, should be -1 ? == %d\n", 
           sIndex("abcHeHelloHaha", "ggHello") );
    printf("Hit ENTER key...");
    getchar( ); return 0;
} // main( 
/*******   
D:\Test> path C:\Dev-Cpp\bin;%path%
D:\Test> gcc strTools.c
D:\Test> a.exe
gg=abcde1234567
yy=HaHeeABC135
substr(gg, 3, 5)=de123
substr(gg, 9, 5)=567
substr(yy, 2, 3)=Hee
substr(yy, 6, 5)=BC135
substr(yy, 9, 5)=35
 String tmps is "   a b   c123   4    5 678   "
Test squeeze( tmps )=abc12345678==
Test sqz22( tmps )=a b c123 4 5 678==
Test trimLeft( tmps )=a b   c123   4    5 678   ==
Test trimRight( tmps )=   a b   c123   4    5 678==
Test trim( tmps )=a b   c123   4    5 678==去頭尾
Test sIndex, should be 5 ? == 5
Test sIndex, should be -1 ? == -1
Hit ENTER key...
D:\test>  
********************************/