home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xfld085s.zip / helpers / stringh.c < prev    next >
C/C++ Source or Header  |  1999-03-15  |  19KB  |  609 lines

  1.  
  2. /*
  3.  *@@sourcefile stringh.c:
  4.  *      contains string/text helper functions that are independent
  5.  *      of a single application, i.e. you can use them in any
  6.  *      program. These are good for parsing/splitting strings and
  7.  *      other stuff used throughout XFolder.
  8.  *
  9.  *      Function prefixes (new with V0.81):
  10.  *      --  strh*       string helper functions.
  11.  *
  12.  *@@include #define INCL_DOSDATETIME
  13.  *@@include #include <os2.h>
  14.  *@@include #include "stringh.h"
  15.  */
  16.  
  17. /*
  18.  *      Copyright (C) 1997-99 Ulrich Möller.
  19.  *      This file is part of the XFolder source package.
  20.  *      XFolder is free software; you can redistribute it and/or modify
  21.  *      it under the terms of the GNU General Public License as published
  22.  *      by the Free Software Foundation, in version 2 as it comes in the
  23.  *      "COPYING" file of the XFolder main distribution.
  24.  *      This program is distributed in the hope that it will be useful,
  25.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *      GNU General Public License for more details.
  28.  */
  29.  
  30. #define INCL_WINSHELLDATA
  31. #include <os2.h>
  32.  
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <ctype.h>
  37. #include <math.h>
  38.  
  39. #include "stringh.h"
  40.  
  41. // #define _PMPRINTF_
  42. #include "pmprintf.h"
  43.  
  44. /*
  45.  *@@ strhReplace:
  46.  *      replace oldStr with newStr in str.
  47.  *
  48.  *      str should have enough allocated space for the replacement, no check
  49.  *      is made for this. str and OldStr/NewStr should not overlap.
  50.  *      The empty string ("") is found at the beginning of every string.
  51.  *
  52.  *      Returns: pointer to first location behind where NewStr was inserted
  53.  *      or NULL if OldStr was not found.
  54.  *      This is useful for multiple replacements also.
  55.  *      (be careful not to replace the empty string this way !)
  56.  *
  57.  *      Author:     Gilles Kohl
  58.  *      Started:    09.06.1992   12:16:47
  59.  *      Modified:   09.06.1992   12:41:41
  60.  *      Subject:    Replace one string by another in a given buffer.
  61.  *                  This code is public domain. Use freely.
  62.  */
  63.  
  64. char* strhReplace(char* str, char* oldStr, char* newStr)
  65. {
  66.       int OldLen, NewLen;
  67.       char *p, *q;
  68.  
  69.       if (NULL == (p = strstr(str, oldStr)))
  70.             return p;
  71.       OldLen = strlen(oldStr);
  72.       NewLen = strlen(newStr);
  73.       memmove(q = p+NewLen, p+OldLen, strlen(p+OldLen)+1);
  74.       memcpy(p, newStr, NewLen);
  75.       return (q);
  76. }
  77.  
  78. /*
  79.  *@@ strhInsert:
  80.  *      inserts to_insert into in_str at position place and
  81.  *      copies the result to out_str
  82.  *
  83.  *      Author:     Gilles Kohl
  84.  *      Started:    09.06.1992   12:16:47
  85.  *      Modified:   09.06.1992   12:41:41
  86.  *      Subject:    Replace one string by another in a given buffer.
  87.  *                  This code is public domain. Use freely.
  88.  */
  89.  
  90. char* strhInsert(char* out_str, char* in_str, char* to_insert, int place)
  91. {
  92.     char *out_ptr = out_str, buffer[255], *ptr = buffer;
  93.  
  94.     if (place <= 0)
  95.         return ptr;
  96.  
  97.     if ( in_str == out_str )
  98.     {
  99.         while( -- place )
  100.             if ( ! *out_str++ ) return(out_ptr);
  101.             while ( *ptr++ = *in_str++ )
  102.                 ; /* strcpy : used if in_str = out_str */
  103.             ptr = buffer;
  104.     }
  105.     else {
  106.         while( -- place )
  107.             if ( ! (*out_str++ = *in_str++) )
  108.                 return(out_ptr);
  109.  
  110.         ptr = in_str;
  111.     }
  112.  
  113.     while( *out_str++ = *to_insert++ )
  114.         ;
  115.     out_str --;
  116.  
  117.     while ( *out_str++ = *ptr++ )
  118.         ;
  119.  
  120.     return out_ptr;
  121. }
  122.  
  123. /*
  124.  *@@ strhistr:
  125.  *      like strstr, but case-insensitive.
  126.  */
  127.  
  128. PSZ strhistr(PSZ string1, PSZ string2)
  129. {
  130.     PSZ prc = NULL;
  131.  
  132.     if ((string1) && (string2)) {
  133.         PSZ pszSrchIn = strdup(string1);
  134.         PSZ pszSrchFor = strdup(string2);
  135.         strupr(pszSrchIn);
  136.         strupr(pszSrchFor);
  137.  
  138.         if ((pszSrchIn) && (pszSrchFor)) {
  139.             prc = strstr(pszSrchIn, pszSrchFor);
  140.             if (prc) {
  141.                 // prc now has the first occurence of the string,
  142.                 // but in pszSrchIn; we need to map this
  143.                 // return value to the original string
  144.                 prc = (prc-pszSrchIn) // offset in pszSrchIn
  145.                       + string1;
  146.             }
  147.         }
  148.         if (pszSrchFor)
  149.             free(pszSrchFor);
  150.         if (pszSrchIn)
  151.             free(pszSrchIn);
  152.     }
  153.     return (prc);
  154. }
  155.  
  156. /*
  157.  *@@ strhThousandsULong:
  158.  *      converts a ULONG into a decimal string, while
  159.  *      inserting thousands separators into it. Specify
  160.  *      the separator char in cThousands.
  161.  *      Returns pszTarget so you can use it directly
  162.  *      with sprintf and the "%s" flag.
  163.  *      For cThousands, you should use the data in
  164.  *      OS2.INI ("PM_National" application), which is
  165.  *      always set according to the "Country" object.
  166.  *      Use strhThousandsDouble for "double" values.
  167.  */
  168.  
  169. PSZ strhThousandsULong(PSZ pszTarget,       // out: decimal as string
  170.                        ULONG ul,            // in: decimal to convert
  171.                        CHAR cThousands)     // in: separator char (e.g. '.')
  172. {
  173.     USHORT ust, uss, usc;
  174.     CHAR   szTemp[40];
  175.     sprintf(szTemp, "%d", ul);
  176.  
  177.     ust = 0;
  178.     usc = strlen(szTemp);
  179.     for (uss = 0; uss < usc; uss++)
  180.     {
  181.         if (uss)
  182.             if (((usc - uss) % 3) == 0)
  183.             {
  184.                 pszTarget[ust] = cThousands;
  185.                 ust++;
  186.             }
  187.         pszTarget[ust] = szTemp[uss];
  188.         ust++;
  189.     }
  190.     pszTarget[ust] = '\0';
  191.  
  192.     return (pszTarget);
  193. }
  194.  
  195. /*
  196.  *@@ strhThousandsDouble:
  197.  *      like strhThousandsULong, but for a "double"
  198.  *      value. Note that after-comma values are truncated.
  199.  */
  200.  
  201. PSZ strhThousandsDouble(PSZ pszTarget, double dbl, CHAR cThousands)
  202. {
  203.     USHORT ust, uss, usc;
  204.     CHAR   szTemp[40];
  205.     sprintf(szTemp, "%.0f", floor(dbl));
  206.  
  207.     ust = 0;
  208.     usc = strlen(szTemp);
  209.     for (uss = 0; uss < usc; uss++)
  210.     {
  211.         if (uss)
  212.             if (((usc - uss) % 3) == 0)
  213.             {
  214.                 pszTarget[ust] = cThousands;
  215.                 ust++;
  216.             }
  217.         pszTarget[ust] = szTemp[uss];
  218.         ust++;
  219.     }
  220.     pszTarget[ust] = '\0';
  221.  
  222.     return (pszTarget);
  223. }
  224.  
  225. /*
  226.  *@@ strhFileDate:
  227.  *      converts file date data to a string (to pszBuf).
  228.  *      You can pass any FDATE structure to this function,
  229.  *      which are returned in those FILEFINDBUF* or
  230.  *      FILESTATUS* structs by the Dos* functions.
  231.  *      ulDateFormat can be queried using
  232.  +              PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
  233.  *
  234.  *      meaning:
  235.  *      --  0   mm.dd.yyyy  (English)
  236.  *      --  1   dd.mm.yyyy  (e.g. German)
  237.  *      --  2   yyyy.mm.dd  (Japanese)
  238.  *      --  3   yyyy.dd.mm
  239.  *
  240.  *      cDateSep is used as a date separator (e.g. '.').
  241.  *      This can be queried using:
  242.  +              PrfQueryProfileString(HINI_USER, "PM_National", "sDate", "/",
  243.  +                  szDateSep, sizeof(szDateSep)-1);
  244.  */
  245.  
  246. VOID strhFileDate(PSZ pszBuf,           // out: string returned
  247.                   FDATE* pfDate,        // in: date information
  248.                   ULONG ulDateFormat,   // in: date format (0-3)
  249.                   CHAR cDateSep)        // in: date separator (e.g. '.')
  250. {
  251.     switch (ulDateFormat) {
  252.         case 0:  // mm.dd.yyyy  (English)
  253.             sprintf(pszBuf, "%02d%c%02d%c%04d",
  254.                 pfDate->month,
  255.                     cDateSep,
  256.                 pfDate->day,
  257.                     cDateSep,
  258.                 ((pfDate->year)+1980));
  259.         break;
  260.  
  261.         case 1:  // dd.mm.yyyy  (e.g. German)
  262.             sprintf(pszBuf, "%02d%c%02d%c%04d",
  263.                 pfDate->day,
  264.                     cDateSep,
  265.                 pfDate->month,
  266.                     cDateSep,
  267.                 ((pfDate->year)+1980));
  268.         break;
  269.  
  270.         case 2: // yyyy.mm.dd  (Japanese)
  271.             sprintf(pszBuf, "%04d%c%02d%c%02d",
  272.                 ((pfDate->year)+1980),
  273.                     cDateSep,
  274.                 pfDate->month,
  275.                     cDateSep,
  276.                 pfDate->day);
  277.         break;
  278.  
  279.         default: // yyyy.dd.mm
  280.             sprintf(pszBuf, "%04d%c%02d%c%02d",
  281.                 ((pfDate->year)+1980), cDateSep,
  282.                 pfDate->day, cDateSep,
  283.                 pfDate->month);
  284.         break;
  285.     }
  286. }
  287.  
  288. /*
  289.  *@@ strhFileTime:
  290.  *      converts file time data to a string (to pszBuf).
  291.  *      You can pass any FTIME structure to this function,
  292.  *      which are returned in those FILEFINDBUF* or
  293.  *      FILESTATUS* structs by the Dos* functions.
  294.  *      ulTimeFormat can be queried using
  295.  +              PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
  296.  *      meaning:
  297.  *      --  0   12-hour clock
  298.  *      --  >0  24-hour clock
  299.  *
  300.  *      cDateSep is used as a time separator (e.g. ':').
  301.  *      This can be queried using:
  302.  +              PrfQueryProfileString(HINI_USER, "PM_National", "sTime", ":",
  303.  +                  szTimeSep, sizeof(szTimeSep)-1);
  304.  *
  305.  *@@changed 99-03-15 fixed 12-hour crash
  306.  */
  307.  
  308. VOID strhFileTime(PSZ pszBuf,           // out: string returned
  309.                   FTIME* pfTime,        // in: time information
  310.                   ULONG ulTimeFormat,   // in: 24-hour time format (0 or 1)
  311.                   CHAR cTimeSep)        // in: time separator (e.g. ':')
  312. {
  313.     if (ulTimeFormat == 0)
  314.     {
  315.         // for 12-hour clock, we need additional INI data
  316.         CHAR szAMPM[10] = "err";
  317.  
  318.         if (pfTime->hours > 12)
  319.         {
  320.             // > 12h: PM.
  321.  
  322.             // Note: 12:xx noon is 12 AM, not PM (even though
  323.             // AM stands for "ante meridiam", but English is just
  324.             // not logical), so that's handled below.
  325.  
  326.             PrfQueryProfileString(HINI_USER,
  327.                 "PM_National",
  328.                 "s2359",        // key
  329.                 "PM",           // default
  330.                 szAMPM, sizeof(szAMPM)-1);
  331.             sprintf(pszBuf, "%02d%c%02d%c%02d %s",
  332.                 // leave 12 == 12 (not 0)
  333.                 pfTime->hours % 12,
  334.                     cTimeSep,
  335.                 pfTime->minutes,
  336.                     cTimeSep,
  337.                 pfTime->twosecs*2,
  338.                 szAMPM);
  339.         } else
  340.         {
  341.             // <= 12h: AM
  342.             PrfQueryProfileString(HINI_USER,
  343.                 "PM_National",
  344.                 "s1159",        // key
  345.                 "AM",           // default
  346.                 szAMPM, sizeof(szAMPM)-1);
  347.             sprintf(pszBuf, "%02d%c%02d%c%02d %s",
  348.                 pfTime->hours,
  349.                     cTimeSep,
  350.                 pfTime->minutes,
  351.                     cTimeSep,
  352.                 pfTime->twosecs*2,
  353.                 szAMPM);
  354.         }
  355.     }
  356.     else
  357.         // 24-hour clock
  358.         sprintf(pszBuf, "%02d%c%02d%c%02d",
  359.             pfTime->hours, cTimeSep,
  360.             pfTime->minutes, cTimeSep,
  361.             pfTime->twosecs*2);
  362. }
  363.  
  364. /*
  365.  *@@ strhFindEOL:
  366.  *      returns a pointer to the next \r, \n or NULL character
  367.  *      following pszSearchIn. Stores the offset in *pulOffset.
  368.  */
  369.  
  370. PSZ strhFindEOL(PSZ pszSearchIn, PULONG pulOffset)
  371. {
  372.     PSZ p = pszSearchIn;
  373.     if (pulOffset)
  374.         *pulOffset = 0;
  375.     while (TRUE) {
  376.         if ( (*p == '\r') || (*p == '\n') || (*p == 0) )
  377.             return (p);
  378.         p++;
  379.         if (pulOffset)
  380.             (*pulOffset)++;
  381.     }
  382.  
  383. }
  384.  
  385. /*
  386.  *@@ strhFindKey:
  387.  *      finds pszKey in pszSearchIn; similar to strhistr,
  388.  *      but this one makes sure the key is at the beginning
  389.  *      of a line. Spaces before the key are tolerated.
  390.  *      Returns NULL if the key was not found.
  391.  *      Used by strhGetParameter/strhSetParameter; useful
  392.  *      for analyzing CONFIG.SYS settings.
  393.  */
  394.  
  395. PSZ strhFindKey(PSZ pszSearchIn,     // in: text buffer to search
  396.                PSZ pszKey,          // in: key to search for
  397.                PBOOL pfIsAllUpperCase) // out: TRUE if key is completely in upper case;
  398.                                        // can be NULL if not needed
  399. {
  400.     PSZ     p = NULL;
  401.     BOOL    fFound = FALSE;
  402.  
  403.     _Pmpf(("  strhFindKey %s", pszKey));
  404.     p = pszSearchIn;
  405.     do {
  406.         p = strhistr(p, pszKey);
  407.         _Pmpf(("    found @ ofs %d", (p-pszSearchIn) ));
  408.  
  409.         if ((p) && (p >= pszSearchIn)) {
  410.             // make sure the key is at the beginning of a line
  411.             // by going backwards until we find a char != " "
  412.             PSZ p2 = p;
  413.             while ( (*p2 == ' ') && (p2 > pszSearchIn) )
  414.                 p2--;
  415.  
  416.             // if previous char is an EOL sign, go on
  417.             if (    (*(p2-1) == '\r')
  418.                  || (*(p2-1) == '\n')
  419.                  || (p2 == pszSearchIn)
  420.                )
  421.             {
  422.                 // found:
  423.                 fFound = TRUE; // go on, p contains found key
  424.  
  425.                 if (pfIsAllUpperCase) {
  426.                     // test for all upper case
  427.                     ULONG cbKey = strlen(pszKey),
  428.                           ul = 0;
  429.                     *pfIsAllUpperCase = TRUE;
  430.                     for (ul = 0; ul < cbKey; ul++)
  431.                         if (islower(*(p+ul))) {
  432.                             _Pmpf(("  Lower case found: %c", *(p+ul)));
  433.                             *pfIsAllUpperCase = FALSE;
  434.                             break;
  435.                         }
  436.  
  437.                 }
  438.             } // else search next key
  439.             else
  440.                 p++; // search on after this key
  441.         }
  442.     } while ((!fFound) && (p != NULL) && (p != pszSearchIn));
  443.  
  444.     return (p);
  445. }
  446.  
  447.  
  448. /*
  449.  *@@ strhGetParameter:
  450.  *      searches pszSearchIn for the key pszKey; if found, it
  451.  *      returns a pointer to the following characters in pszSearchIn
  452.  *      and, if pszCopyTo != NULL, copies the rest of the line to
  453.  *      that buffer, of which cbCopyTo specified the size.
  454.  *      If the key is not found, NULL is returned.
  455.  *      This is useful for querying CONFIG.SYS settings.
  456.  *      <P><B>Example:</B> this would return "YES" if you searched
  457.  *      for "PAUSEONERROR", and "PAUSEONERROR=YES" existed in pszSearchIn.
  458.  */
  459.  
  460. PSZ strhGetParameter(PSZ pszSearchIn,   // in: text buffer to search
  461.                      PSZ pszKey,        // in: key to search for
  462.                      PSZ pszCopyTo,     // out: key value
  463.                      ULONG cbCopyTo)    // out: sizeof(*pszCopyTo)
  464. {
  465.     PSZ p = strhFindKey(pszSearchIn, pszKey, NULL),
  466.         prc = NULL;
  467.     // copy to pszCopyTo
  468.     if (p) {
  469.         prc = p + strlen(pszKey);
  470.         if (pszCopyTo) {
  471.             ULONG cb;
  472.             PSZ pEOL = strhFindEOL(prc, &cb);
  473.             if (pEOL) {
  474.                 _Pmpf(("%s: cb: %d, p2-p1: %d", pszKey, cb, pEOL-prc));
  475.                 if (cb > cbCopyTo)
  476.                     cb = cbCopyTo-1;
  477.                 strncpy(pszCopyTo, prc, cb);
  478.                 pszCopyTo[cbCopyTo-1] = '\0';
  479.             }
  480.         }
  481.     }
  482.  
  483.     return (prc);
  484. }
  485.  
  486. /*
  487.  *@@ strhSetParameter:
  488.  *      searches pszSearchIn for the key pszKey; if found, it
  489.  *      replaces the characters following this key up to the
  490.  *      end of the line by pszParam. If pszKey is not found in
  491.  *      pszSearchIn, it is appended to the file in a new line. You
  492.  *      must ensure that *pszSearchin is large enough for the
  493.  *      manipulation, i.e. it must be at least of the following
  494.  *      size:
  495.  *          strlen(pszSearchIn) + strlen(pszNewParam) + 1
  496.  *      in case the key was not found and will be appended.
  497.  *      This function searches w/out case sensitivity.
  498.  *      Returns a pointer to the new parameter.
  499.  */
  500.  
  501. PSZ strhSetParameter(PSZ pszSearchIn,    // in: text buffer to search
  502.                     PSZ pszKey,         // in: key to search for
  503.                     PSZ pszNewParam,    // in: new parameter to set for key
  504.                     BOOL fRespectCase)  // in: if TRUE, pszNewParam will
  505.                             // be converted to upper case if the found key is
  506.                             // in upper case also. pszNewParam should be in
  507.                             // lower case if you use this.
  508. {
  509.     BOOL fIsAllUpperCase = FALSE;
  510.     PSZ pKey = strhFindKey(pszSearchIn, pszKey, &fIsAllUpperCase),
  511.         prc = NULL;
  512.  
  513.     _Pmpf(("%s, upper case: %d", pszKey, fIsAllUpperCase));
  514.  
  515.     if (pKey) {
  516.         // key found in file:
  517.         // replace existing parameter
  518.         PSZ pOldParam = pKey + strlen(pszKey);
  519.         prc = pOldParam;
  520.         // pOldParam now has the old parameter, which we
  521.         // will overwrite now
  522.  
  523.         // printf("  found %s @ %d\n", pszKey, (pKey-pszSearchIn));
  524.         if (pOldParam) {
  525.             ULONG cbOldParam;
  526.             PSZ pEOL = strhFindEOL(pOldParam, &cbOldParam);
  527.             // pEOL now has first end-of-line after the
  528.             // parameter
  529.  
  530.             // printf("  old param is @ %d\n", pOldParam-pszSearchIn);
  531.             if (pEOL) {
  532.                 PSZ pszOldCopy = (PSZ)malloc(cbOldParam+1);
  533.                 strncpy(pszOldCopy, pOldParam, cbOldParam);
  534.                 pszOldCopy[cbOldParam] = '\0';
  535.                 // printf("  found EOL @ %d\n", pEOL-pszSearchIn);
  536.  
  537.                 if (fIsAllUpperCase)
  538.                     strupr(pszNewParam);
  539.                 strhReplace(pOldParam, pszOldCopy, pszNewParam);
  540.  
  541.                 free(pszOldCopy);
  542.             }
  543.         }
  544.     } else {
  545.         // key not found: append to end of file
  546.         strcat(pszSearchIn, "\r\n");
  547.         strcat(pszSearchIn, pszKey);
  548.         strcat(pszSearchIn, strupr(pszNewParam));
  549.         strcat(pszSearchIn, "\r\n");
  550.         // printf("  Appended %s%s to end of file\n", pszKey, pszNewParam);
  551.     }
  552.  
  553.     return (prc);
  554. }
  555.  
  556. /*
  557.  *@@ strhDeleteLine:
  558.  *      this deletes the line in pszSearchIn which starts with
  559.  *      the key pszKey. Returns TRUE if the line was found and
  560.  *      deleted.
  561.  */
  562.  
  563. BOOL strhDeleteLine(PSZ pszSearchIn, PSZ pszKey)
  564. {
  565.     BOOL fIsAllUpperCase = FALSE;
  566.     PSZ pKey = strhFindKey(pszSearchIn, pszKey, &fIsAllUpperCase);
  567.     BOOL brc = FALSE;
  568.  
  569.     if (pKey) {
  570.         PSZ pEOL = strhFindEOL(pKey, NULL);
  571.         // pEOL now has first end-of-line after the key
  572.         if (pEOL) {
  573.             // delete line by overwriting it with
  574.             // the next line
  575.             strcpy(pKey, pEOL+2);
  576.         } else {
  577.             // EOL not found: we must be at the end of the file
  578.             *pKey = '\0';
  579.         }
  580.         brc = TRUE;
  581.     }
  582.  
  583.     return (brc);
  584. }
  585.  
  586. /*
  587.  *@@ strhBeautifyTitle:
  588.  *      replaces all line breaks (0xd, 0xa) with spaces.
  589.  */
  590.  
  591. BOOL strhBeautifyTitle(PSZ psz)
  592. {
  593.     BOOL rc = FALSE;
  594.     CHAR *p;
  595.     while (p = strchr(psz, 0xa))
  596.     {
  597.         *p = ' ';
  598.         rc = TRUE;
  599.     }
  600.     while (p = strchr(psz, 0xd))
  601.     {
  602.         *p = ' ';
  603.         rc = TRUE;
  604.     }
  605.     return (rc);
  606. }
  607.  
  608.  
  609.