home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 v2.4 Fix / W95-v2.4fix.iso / ACADWIN / ADS / WIN / WINUTIL.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-14  |  27.5 KB  |  1,140 lines

  1. /*
  2.  
  3.         WinUtil
  4.  
  5.         General Purpose Functions for Windows DDE
  6.  
  7.           by Phil Ford
  8.      ________________________________________________________________________
  9.  
  10.       (C) Copyright 1990-1995 by Autodesk, Inc.
  11.  
  12.       Permission to use, copy, modify, and distribute this software and its
  13.       documentation for any purpose and without fee is hereby granted.  
  14.  
  15.       THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 
  16.       ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF 
  17.       MERCHANTABILITY ARE HEREBY DISCLAIMED.                                
  18.      ________________________________________________________________________
  19. */
  20.  
  21. #include "options.h"
  22. #include <string.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <ctype.h>
  26. #include "winutil.h"
  27.  
  28. /* Uses RTL's assert() which just exits the app */
  29. #ifndef ASSERT
  30. #define ASSERT  assert
  31. #endif
  32.  
  33. /* "+"  so spreadsheet knows it's a number
  34.    ".6" means 6 decimal places
  35.    "lf" means double floating point number 
  36.    This can be changed by C application for 
  37.    more or less precision. */
  38. char RealFmt[10] = "%+.6lf";
  39. const char RealScan[] = "%lf";
  40. int FpPrecision = 6;
  41.  
  42. char FldSep[30] = "\t";
  43. char *FldSepStr = FldSep;
  44. char FldSepChr = TAB;
  45.  
  46. char RecSep[30] = "\r\n";
  47. char *RecSepStr = RecSep;
  48. char RecSepChr1 = CR;
  49. char RecSepChr2 = LF;
  50.  
  51. const char NullStr[] = "";
  52. const char CrLf[] = "\r\n";
  53.  
  54. /*
  55.     The clib_xxxseparators are what C RTL use in functions like
  56.     sscanf(), sprintf() etc.  '.' is typically the decimal separator.
  57. */
  58. static const char clib_decseparator[] = ".";   /* decimal separator used by RTL */
  59. static const char clib_tseparator[] = ",";     /* thousand separator used by RTL */
  60. /*
  61.     The win_xxxseparators are what Windows use, they can be
  62.     changed from the control panel/international dialog box.
  63. */
  64. static char win_decseparator[ 3 ];     /* decimal separator used by Windows */
  65. static char win_tseparator[ 3 ];       /* thousand separator used by Windows */
  66. BOOL separator_inited = FALSE;
  67.  
  68. static void WinDecimalChars( char *strbuf );
  69. static void ClibDecimalChars( char *strbuf );
  70. static BOOL InitDecimalChars();
  71.  
  72. /* 
  73.  
  74.                      General Purpose Utilities
  75. */
  76.  
  77.  
  78.  
  79.  
  80. /*
  81.  
  82.                 Text Linked List Functions
  83.  
  84. */
  85.  
  86.  
  87.  
  88. /* Save some text in a linked list of text strings. 
  89.    Text links are a simple special case of generic LINK
  90.    structure.  Send ADDRESS of the text list starting node,
  91.    so it can be updated if necessary, instead of the value
  92.    (TextList).  E.g., 
  93.                 void *TextList = NULL;
  94.                 AddText(&TextList, "New text"); 
  95.                 AddText(&Textlist, "Next text");  
  96. */
  97. void *AddText(void **TextList, char *Str)
  98. {
  99.     Tlink *NewLink;
  100.  
  101.     if (!Str || !TextList)
  102.         return NULL;
  103.     NewLink = AddLink(TextList, sizeof(Tlink));
  104.     if (!NewLink)
  105.         return NULL;
  106.     NewLink->text = _strdup(Str);
  107.     return NewLink;
  108. }
  109.  
  110.  
  111. /* Is a certain string in a string list?  Not case sensitive. 
  112. */
  113. void *InTextList(void *TextList, char *Str)
  114. {
  115.     Tlink *Link;
  116.  
  117.     for (Link = TextList; Link != NULL; Link = Link->next) {
  118.         if (!_stricmp(Str, Link->text))
  119.             return Link;
  120.     }
  121.     return NULL;
  122. }
  123.  
  124.  
  125. /* Remove a string from a list, or use NULL Str to remove
  126.    entire list.   Send addr of first node of list. 
  127. */
  128. void *RemoveText(void **TextList, char *Str)
  129. {
  130.     Tlink *link;
  131.     Tlink *Next;
  132.     Tlink *Prev = NULL;
  133.  
  134.     for (link = *TextList; link != NULL; link = Next) {
  135.         Next = link->next;
  136.         if (!Str) {
  137.             free(link->text);
  138.             free(link);
  139.         }
  140.  
  141.         /* We're looking for topic, but
  142.            this isn't it. */
  143.         else if (!_stricmp(Str, link->text)) {
  144.             Prev = FreeText(TextList, link);
  145.             return Prev;
  146.         }
  147.     }
  148.     if (!Str) 
  149.         *TextList = NULL;             /* NULL list now--all links deleted. */   
  150.     return Prev;
  151. }
  152.  
  153.  
  154. /* Free a text link from list starting with TextList
  155.    (ADDR of first link). 
  156. */
  157. void *FreeText(void **TextList, void *TextLink)
  158. {
  159.     Tlink *Prev, *tlink;
  160.  
  161.     if (!TextLink)
  162.         return NULL;
  163.     tlink = TextLink;
  164.     free(tlink->text);
  165.     Prev = FreeLink(TextList, tlink, sizeof(Tlink));
  166.     return Prev;
  167. }
  168.  
  169.  
  170. /*
  171.  
  172.                     Generic Link List Functions
  173.  
  174.                       (First item in structure must 
  175.                       be pointer to next.)
  176.  
  177. */
  178.  
  179.  
  180. /* Allocate an object of LinkSize and add to list
  181.    starting with List.  Send address of first link in
  182.    list, e.g.,
  183.                 void *List = NULL;
  184.                 void *Item;
  185.                 Item = AddLink(&List, 30);
  186. */
  187. void *AddLink(void **List, int LinkSize)
  188. {
  189.     Tlink *link;
  190.     Tlink *Last;
  191.  
  192.     link = calloc(1, LinkSize);
  193.     if (!link)
  194.         return NULL;
  195.     if (!*List)
  196.         *List = link;
  197.     else {
  198.         Last = GetLastLink(*List);
  199.         Last->next = link;
  200.     }
  201.     return link;
  202. }
  203.  
  204.  
  205.  
  206. /* Remove link (fix previous->next) and then
  207.    free it.  Send address of first link, as List. 
  208. */
  209. void *FreeLink(void **List, void *link, int LinkSize)
  210. {
  211.     Tlink *Prev;
  212.  
  213.     Prev = RemoveLink(List, link);
  214.     free(link);
  215.     return Prev;
  216. }
  217.  
  218.  
  219. /* Get link after LastLink.  Set LastLink to NULL
  220.    to get first link.  Can use in a loop to cycle
  221.    through list:
  222.         Link = NULL;    
  223.         while (TRUE)
  224.             {
  225.             link = GetNextLink(List, link);
  226.             if (!link)
  227.                 link = GetNextLink(List, link);
  228.             ...
  229.             }
  230. */
  231. void *GetNextLink(void *List, void *LastLink)
  232. {
  233.     Tlink *link;
  234.  
  235.     if (!List)
  236.         return NULL;
  237.     if (!LastLink)
  238.         return List;
  239.     link = LastLink;
  240.     return link->next;
  241. }
  242.  
  243.  
  244. /* Remove a link, Target, from a generic linked list whose 
  245.    starting link is List.  List is updated if necessary.
  246.    The previous link is returned, or next if no previous. 
  247.    The link is NOT freed, just unlinked.  Use FreeLink to remove
  248.    and then free, if the link doesn't contain pointers that 
  249.    need to be freed individually.  
  250.    E.g.,
  251.         void *List = NULL;
  252.         void *link;
  253.         link = AddLink(&List, ITEMSIZE);
  254.         RemoveLink(&List, link); 
  255. */
  256. void *RemoveLink(void **List, void *Target)
  257. {
  258.     Tlink *link;
  259.     Tlink *Prev = NULL;
  260.     int FoundIt = FALSE;
  261.  
  262.     /* Make sure the list exists. */
  263.     if (*List == NULL || Target == NULL)
  264.         return NULL;
  265.     /* Scan list looking for the link to remove. */
  266.     for (link = *List; link != NULL; link = link->next) {
  267.         if (link == Target) {
  268.             FoundIt = TRUE;
  269.             break;
  270.         }
  271.         else 
  272.             Prev = link;
  273.     }
  274.     if (!FoundIt)
  275.         return NULL;
  276.     if (Prev == NULL) {               /* first link in list */
  277.         if (link->next == NULL)       /* only link in list */
  278.             *List = NULL;             /* no more list */
  279.         else
  280.             *List = link->next;       /* list starts with 2nd link, now */      
  281.         return *List;                 /* return neighbor after current
  282.                                          link */
  283.     }
  284.     Prev->next = link->next;          /* bypass removed link */
  285.     return Prev;                      /* return neighbor before current link */ 
  286. }
  287.  
  288.  
  289. /* Free links in list, starting with link "List".  E.g.,
  290.    void *List = NULL;
  291.    AddLink(&List, ITEMSIZE);
  292.    FreeList(&List, ITEMSIZE); 
  293. */
  294. void FreeList(void **List, int NodeSize)
  295. {
  296.     Tlink *link;
  297.     Tlink *Next;
  298.  
  299.     for (link = *List; link != NULL; link = Next) {
  300.         Next = link->next;
  301.         free(link);
  302.     }
  303.     *List = NULL;
  304. }
  305.  
  306.  
  307. /* Find last link in list starting with List link. 
  308. */
  309. void *GetLastLink(void *ListStart)
  310. {
  311.     Tlink *link, *Prev;
  312.  
  313.     if (!ListStart)
  314.         return NULL;
  315.     for (link = ListStart; link != NULL; link = link->next)
  316.         Prev = link;
  317.     return Prev;
  318. }
  319.  
  320.  
  321.  
  322. /* Get the length of a linked list.  First item in
  323.    structure must be pointer to next. 
  324. */
  325. int GetListLen(void *List)
  326. {
  327.     int Len = 0;
  328.     Tlink *link;
  329.  
  330.     for (link = List; link != NULL; link = link->next)
  331.         ++Len;
  332.     return Len;
  333. }
  334.  
  335.  
  336.  
  337. /*              
  338.                 Bit Field Functions
  339.  
  340.    Set, find, and clear empty bits in an array of int's.
  341.  
  342. */
  343.  
  344.  
  345. /* Find the first empty bit in the bit map and set it.  
  346.    MapLen is length in int'S of your bit map.
  347.    E.g.
  348.            #define NUMOBJECTS 1024      must be multiple of 16
  349.            #define MAPLEN (NUMOBJECTS / BITWORDLEN)
  350.  
  351.            USHORT BitMap[MAPLEN] = {0};       
  352.            Num = SetNextBit(BitMap, MAPLEN);
  353.            CheckBit(BitMap, 3);
  354.    etc.
  355. */
  356. int SetNextBit(USHORT *BitMap, UINT MapLen)
  357. {
  358.     UINT Bit;
  359.  
  360.     Bit = FindEmptyBit(BitMap, MapLen);
  361.     if (Bit != BITMAPFULL)
  362.         SetBit(BitMap, Bit);
  363.     return Bit;
  364. }
  365.  
  366.  
  367.  
  368. /* Is bit, Number, set.  Starts with 1.  
  369. */
  370. int CheckBit(USHORT *BitMap, UINT Number)
  371. {
  372.     USHORT BitMapWord;
  373.     USHORT Bit = 1;
  374.  
  375.     if (!Number)
  376.         return FALSE;
  377.     --Number;                         /* make 0 based */
  378.     Bit <<= (Number % BITWORDLEN);
  379.     BitMapWord = Number / BITWORDLEN;
  380.     if (BitMap[BitMapWord] & Bit) 
  381.         return 1;
  382.     else
  383.         return 0;
  384. }
  385.  
  386. /* Clear the bit, Number.  Starts with 1.  
  387. */
  388. void ClearBit(USHORT *BitMap, UINT Number)
  389. {
  390.     USHORT BitMapWord;
  391.     USHORT Bit = 1;
  392.  
  393.     if (!Number)
  394.         return;
  395.     --Number;                         /* make 0 based */
  396.     Bit <<= (Number % BITWORDLEN);
  397.     BitMapWord = Number / BITWORDLEN;
  398.     BitMap[BitMapWord] &= ~Bit; 
  399. }
  400.  
  401.  
  402. /* Set bit corresponding to Number from 
  403.    1 to MAPLEN. 
  404. */
  405. int SetBit(USHORT *BitMap, UINT Number)
  406. {
  407.     USHORT BitMapWord;
  408.     USHORT Bit = 1;
  409.  
  410.     if (!Number)
  411.         return FALSE;
  412.     --Number;                         /* make 0 based */
  413.     Bit <<= (Number % BITWORDLEN);
  414.     BitMapWord = Number / BITWORDLEN;
  415.     BitMap[BitMapWord] |= Bit; 
  416.     return TRUE;
  417. }
  418.  
  419.  
  420. /* Find an empty bit in the map.  Returns -1 if none.
  421.    The MapLen is the number of bits. 
  422. */
  423. int FindEmptyBit(USHORT *BitMap, UINT MapLen)
  424. {
  425.     USHORT Bit;
  426.     USHORT BitMask;
  427.     USHORT Word;
  428.     UINT FoundEmpty = 0;
  429.  
  430.     for (Word = 0; Word < MapLen; ++Word) {
  431.         if (BitMap[Word] != 0xffff) {
  432.             FoundEmpty = 1;     
  433.             break;
  434.         }
  435.     }
  436.     if (!FoundEmpty)
  437.         return BITMAPFULL;
  438.  
  439.     BitMask = 1;
  440.     for (Bit = 1; Bit <= BITWORDLEN; ++Bit) {
  441.         if ((BitMap[Word] & BitMask) == 0)
  442.             return ((Word * BITWORDLEN) + Bit);
  443.         else
  444.             BitMask <<= 1; 
  445.     }
  446. }
  447.  
  448. /* Save string up to a TAB, CR, etc.  Use free to
  449.    free it.  Mainly for getting floating point string from
  450.    text stream with TABS and CRLFs. 
  451. */
  452. char *FieldSave(char *Word)
  453. {
  454.     char *src, *dest;
  455.     char buf[120];
  456.  
  457.     src = Word;
  458.     dest = buf;
  459.     while ( *( ( unsigned char* )src ) >= ( unsigned char )SP )
  460.     {
  461.         *dest++ = *src++;
  462.     }
  463.     *dest = EOS;
  464.     return _strdup(buf);
  465. }
  466.  
  467. /* Limit string copy and put a 0 at the end of the destination
  468.    string, even if the source exceeds the max (unlike standard strncpy).  
  469.    Protects against a string that's too long for the destination.  
  470.    "maxstr" is maximum string length (max storage - 1).  The source
  471.    may be a NULL pointer, but not the destination.  Returns length of 
  472.    string. */
  473.  
  474. int
  475. /*FCN*/strzcpy(dest, source, maxstr)
  476.   char *dest;
  477.   char *source;
  478.   int maxstr;
  479. {
  480.     int idx;
  481.  
  482.     if (source == dest)
  483.         return 0;
  484.     if (source == NULL) {
  485.         dest[0] = EOS;
  486.         return 0;
  487.     }
  488.     for (idx = 0; idx < maxstr; ++idx) {
  489.         if ((dest[idx] = source[idx]) == EOS)
  490.             return idx;
  491.     }
  492.     dest[maxstr] = EOS;
  493.     return idx;
  494. }
  495.  
  496.  
  497.  
  498.  
  499. /*
  500.  
  501.                      Memory Allocation
  502.  
  503. */
  504.  
  505. /* Allocate a far pointer with GlobalAlloc to data over 64k.  Set ShareFlag
  506.    TRUE to make it sharable memory.  Use FreeFar to free it. 
  507. */
  508. char huge *AllocHuge(long DataLen, int ShareFlag)
  509. {
  510.     return (char huge *)AllocFar(DataLen, ShareFlag);
  511. }
  512.  
  513. /* Flexible alloc: try to alloc Len, if fail, half Len, up 
  514.    to 16 times.  Return pointer, return actual len in ActualLen.   
  515.    Use FreeFar to free it. 
  516. */
  517. void huge *AllocFlex(long Len, long *ActualLen)
  518. {
  519.     void huge *ptr;
  520.     long ActLen;
  521.     int cnt = 0;
  522.  
  523.     ActLen = Len;
  524.     while ((ptr = AllocFar(ActLen, FALSE)) == NULL) {
  525.         ++cnt;
  526.         if (cnt > 16)
  527.             break;
  528.         ActLen /= 2;
  529.     }
  530.     *ActualLen = ActLen;
  531.     return ptr;
  532. }
  533.  
  534. /* Allocate a far pointer with GlobalAlloc.  Set ShareFlag
  535.    TRUE to make it sharable memory.  Use FreeFar to free it. 
  536. */
  537. char far *AllocFar(long DataLen, int ShareFlag)
  538. {
  539. #if USE_GLOBAL
  540.     WORD wFlag;
  541.     HANDLE hmem;
  542.  
  543.     wFlag = GMEM_MOVEABLE;
  544.     if (ShareFlag)
  545.         wFlag |= GMEM_DDESHARE;
  546.  
  547.     hmem = GlobalAlloc(wFlag, DataLen);
  548.     if (!hmem) 
  549.         return NULL;
  550.     return GlobalLock(hmem);
  551. #else
  552.     return malloc((int)DataLen);
  553. #endif
  554. }
  555.  
  556.  
  557.  
  558.  
  559. /* Free far or huge pointer, acquired using AllocFar or AllocHuge.  
  560.    Returns 0 if no error. 
  561. */
  562. int FreeFar(char *ptr)
  563. {
  564. #if USE_GLOBAL
  565.     WORD wmem;
  566.     DWORD hmem;
  567.  
  568.     wmem = HIWORD((LONG)ptr);
  569.     if (wmem == (WORD)0)
  570.         return 0;
  571.     hmem = GlobalHandle(wmem);
  572.     if (hmem == (DWORD)0)
  573.         return 0;
  574.     GlobalUnlock((HANDLE)hmem);
  575.     return GlobalFree((HANDLE)hmem);
  576. #else
  577.     free(ptr);
  578.     return 0;
  579. #endif
  580. }
  581.  
  582.  
  583. /*
  584.  
  585.                         Pop Up Message Box
  586.  
  587. */
  588.  
  589. /* Display a message box when a Windows system request fails */
  590.  
  591. void resource_msg(void)
  592. {
  593.     MsgBox("AutoCAD ADS", 
  594.             "System request failed.\nTry closing an application.");
  595. }
  596.  
  597.  
  598. /* Pop up a message box with 2 message strings, the
  599.    first of which will be in the title bar.  Waits for
  600.    Enter key, space bar, or click on OK. 
  601. */
  602. int MsgBox(char *Msg1, char *Msg2)
  603. {
  604.     return MessageBox(GetActiveWindow(), Msg2, Msg1, MB_ICONHAND | MB_OK);
  605. }
  606.  
  607.  
  608. /* Check for break character on key down: 
  609.    ctrl-break or ctrl-C.   Call from WM_CHAR msg.  
  610. */
  611. BOOL CheckBreakKey(WPARAM wParam, LPARAM lParam)
  612. {
  613.     if (wParam == 3)
  614.         return TRUE;
  615.     return FALSE;
  616. }
  617.  
  618.  
  619.  
  620.  
  621.  
  622. /* 
  623.         Floating Point Conversion for DDE/Clipboard Text format
  624.  
  625. */
  626.  
  627. /* Fill an array of doubles from the DDE data returned from 
  628.    a range (e.g. of spreadsheet cells).  For example,
  629.  
  630.    double RealAry[10][2];
  631.         (or   "ads_point PtAry[10];")
  632.  
  633.    StrToReals(pdde->pData, RealAry, 10, 2);
  634.    pRealAry[0][0] has data from the first cell, etc. 
  635. */
  636.  
  637. short StrToReals(PHDATA DataStrm, double *RealAry,
  638.                  int nRow, int nCol)
  639. {
  640.     double *pReal;
  641.     PHDATA pStr;
  642.     short Sep = 1;
  643.     short cnt = 0;
  644.     char chr;
  645.     short CellCnt;
  646.     short col = 0;
  647.     short idx;
  648.  
  649.     CellCnt = nRow * nCol;
  650.     pStr = DataStrm;
  651.     pReal = RealAry;
  652.     Sep = *DataStrm;
  653.     do {
  654.         chr = *pStr;
  655.         /* separator before cell data.  DDE text format is 
  656.            null terminated */
  657.         if (Sep == 0)
  658.             break;
  659.         else if (Sep == RecSepChr1) { /* CR */
  660.             if (col < nCol) {         /* fill out missing 
  661.                                       fields in this line 
  662.                                       with 0.0's */
  663.                 for (idx = col; idx < nCol; ++idx) {
  664.                     *pReal++ = 0.0;
  665.                     ++cnt;
  666.                     if (cnt >= CellCnt)
  667.                         break;
  668.                 }
  669.             }
  670.             col = 0;
  671.         }
  672.         *pReal = StrToReal(pStr);
  673.         ++pReal;
  674.         ++cnt;
  675.         ++col;
  676.         if (cnt >= CellCnt)
  677.             break;
  678.     }
  679.     while (pStr = GetNextFld(pStr, &Sep));
  680.     return cnt;
  681. }
  682.  
  683.  
  684.  
  685.  
  686.  
  687.  
  688. /* From an array of doubles, build cell data nRow by nCol.
  689.    Returns pointer to data string.  Use FreeFar to free it. 
  690.    DataSize doesn't include the NULL, so add 1 to it, for strings. 
  691. */
  692. PHDATA RealsToStr(double *RealAry, int nRow, int nCol,
  693.                  ULONG *DataSize)
  694. {
  695.     double *pReal;
  696.     PHDATA pStr;
  697.     PHDATA pDataStrm;
  698.     short FirstRow = TRUE;
  699.     char RealStr[FPSTR+1];
  700.     ULONG DataLen;
  701.     int Row, Col;
  702.     int RealSize;
  703.  
  704.     RealSize = sizeof (double);
  705.  
  706.     /* allow for CRLF or TAB separator, NULL at end */
  707.     DataLen = nRow * nCol * (FPSTR + 1) + 4;
  708.     pDataStrm = pStr = AllocHuge(DataLen, FALSE);
  709.     if (!pStr)
  710.         return NULL;
  711.     pReal = RealAry;
  712.     for (Row = 0; Row < nRow; ++Row) {
  713.         for (Col = 0; Col < nCol; ++Col) {
  714.             RealToStr(*pReal++, RealStr);
  715.             strcpy(pStr, RealStr);
  716.             pStr += strlen(RealStr);
  717.             if (Col != (nCol-1))
  718.                 strcpy(pStr++, FldSepStr);
  719.         }
  720.         strcpy(pStr, RecSepStr);
  721.         pStr += 2;
  722.     }
  723.     *pStr = 0;
  724.  
  725.     *DataSize = pStr - pDataStrm;     /* get actual length */
  726.     return pDataStrm;
  727. }
  728.  
  729.  
  730. /* From a point in a text stream (number<TAB>number...),
  731.    get the next floating point value.  See StrToReals, also. 
  732. */
  733. double StrToReal(char *stream)
  734. {
  735.     double RealVal;
  736.     char strbuff[61];
  737.     short idx, idxb;
  738.     char chr;
  739.  
  740.     if (stream[0] < SP)
  741.         return 0.0;
  742.  
  743.     idxb = 0;
  744.  
  745.     for (idx = 0; idx < 60; ++idx) {
  746.         if ((chr = stream[idx]) >= SP) {
  747.         if ( chr != *win_tseparator )              /* ignore comma used as 1000's  */
  748.                 strbuff[idxb++] = chr; /* separator in spreadsheet values. */
  749.         }
  750.     else
  751.             break;
  752.     }
  753.     strbuff[idxb] = EOS;
  754.  
  755.     /*
  756.         Convert string to C library format so sscanf()
  757.         can read it correctly.
  758.     */
  759.     ClibDecimalChars( strbuff );
  760.     sscanf( strbuff, RealScan, &RealVal);
  761.  
  762.     return RealVal;
  763. }
  764.  
  765.  
  766. /* Convert double fvalue to a string in your buffer,
  767.    strbuff, with decimal precision FpPrecision.  
  768. */
  769. void RealToStr(double fvalue, char *strbuff)
  770. {
  771.     char *temp_char = NULL;
  772.     char *dec_char = NULL;
  773.     sprintf(strbuff, RealFmt, fvalue);
  774.     /*
  775.         Convert the string to be Windows compatible.
  776.         This ensures other Windows app can read the
  777.         format correctly.
  778.     */
  779.     WinDecimalChars( strbuff );
  780. }
  781.  
  782. /* get a pointer to the beginning of the next field
  783.    in a cell data list, such as -1<TAB>60000012<TAB>3.4<CR><LF>
  784.                     0<TAB>ARC<CR><LF>   
  785.    Returns separator, TAB, CR, or NULL 
  786. */
  787. char *GetNextFld(PHDATA pData, short *separator)
  788. {
  789.     char *ptr;
  790.     char chr;
  791.  
  792.     ptr = pData;
  793.     while (chr = *ptr) {
  794.         if (chr < SP)                 /* End of word.  SP is part 
  795.                                       of the word, here. */
  796.             break;
  797.         ++ptr;
  798.     }
  799.  
  800.     *separator = chr;                 /* TAB, CRLF, or NULL */
  801.     if (chr != EOS) {
  802.         if (chr == CR && (*(ptr+1) == RecSepChr2))
  803.             ptr += 2;
  804.         else
  805.             ++ptr;
  806.     }
  807.     return ptr;
  808. }
  809.  
  810.  
  811. /* STORE_STRING -- Store a string at a pointer that already points
  812.                    to a malloc'ed string or is NULL.  Check if
  813.                    new string is larger--free and malloc if so.
  814.                    Copy the new string.  The new pointer (or the
  815.                    old pointer) is returned in "poldstr".  E.g.
  816.                    (where value is a (char *)),
  817.                         store_string(&tile->value, value);
  818.                    NOTE: make sure tile->value is initialized to NULL. */
  819.  
  820. int
  821. /*FCN*/store_string(char **poldstr, char *newstr)
  822. {
  823.     int newlen, oldlen;
  824.     char *oldstr, *pstore;
  825.  
  826.     if (newstr == NULL)
  827.         return FALSE;
  828.     oldstr = *poldstr;
  829.     newlen = strlen(newstr) + 1;
  830.     if (oldstr != NULL) {
  831.         /* Replacing previous string.  See if new one is larger. */
  832.         oldlen = strlen(oldstr) + 1;
  833.         if (oldlen < newlen) {
  834.             free(oldstr);
  835.             oldstr = NULL;
  836.         }
  837.     }
  838.     if (oldstr == NULL)
  839.         pstore = malloc(newlen);
  840.     else
  841.         pstore = oldstr;
  842.     *poldstr = pstore;
  843.     if (pstore != NULL) {
  844.         strcpy(pstore, newstr);
  845.         return TRUE;
  846.     }
  847.     return FALSE;
  848. }
  849.  
  850. /* separate the path and name from a full path\file name.  Enter with
  851.    fullname--fills in path and fname.  Path has no trailing '\' unless
  852.    it is just "\", the root path.
  853. */
  854. void getpath(char *fullname, char *path, char *fname)
  855. {
  856.     int idx, maxidx, namestart;
  857.     char *tmpptr;
  858.     char chr;
  859.  
  860.     maxidx = strlen(fullname)-1;
  861.     namestart = -1;
  862.     for (idx = maxidx; idx >= 0; --idx) {
  863.         chr = fullname[idx];
  864.         if (chr == '\\' || chr == ':') {
  865.             namestart = idx+1;
  866.             break;
  867.         }
  868.     }
  869.     if (namestart == -1) {
  870.         strcpy(fname, fullname);
  871.         path[0] = 0;
  872.     }
  873.     else {
  874.         tmpptr = fullname;
  875.         tmpptr += namestart;
  876.         strcpy(fname, tmpptr);
  877.         strzcpy(path, fullname, namestart);
  878.     }
  879.     /* "\" */
  880.     if (special_path(path))
  881.         return;
  882.     delslash(path);
  883. }
  884.  
  885. /* Remove file separator from end of name, if it exists.  If the path
  886.    is "\", it does nothing. */
  887. void
  888. delslash(char *filename)
  889. {
  890.     char *plastchr;
  891.     
  892.     if (special_path(filename))
  893.         return;
  894.     plastchr = &filename[strlen(filename) - 1];
  895.     if (*plastchr == '\\')
  896.         *plastchr = EOS;
  897. }
  898.  
  899.  
  900.  
  901. /* Is this a special path: ""  "\"?"  "f:"  "f:\"? */
  902.  
  903. int
  904. special_path(char *path)
  905. {
  906.     /* "" or "\"? */
  907.     if ((*path == EOS) || (*path == '\\' && *(path + 1) == EOS))
  908.         return TRUE;
  909.  
  910.     /* "f:" or "f:\"? */
  911.     ++path;
  912.     if (*path == ':') {
  913.         ++path;
  914.         if ((*path == EOS) || (*path == '\\' && *(path + 1) == EOS))
  915.             return TRUE;
  916.     }
  917.     return FALSE;
  918. }
  919.  
  920.  
  921. /* Delete dellen chars from the beginning of a string.
  922.    Returns new string length. */
  923.  
  924. int
  925. str_delete(char *str, int dellen)
  926. {
  927.     int movlen;
  928.  
  929.     /* Include EOS in move */
  930.     movlen = strlen(str) + 1 - dellen;
  931.     if (movlen <= 0) {
  932.         *str = EOS;
  933.         return 0;
  934.     }
  935.     /* Overlapping memory OK for Microsoft C memmove. */
  936.     memmove(str, str + dellen, movlen);
  937.     return movlen - 1;
  938. }
  939.  
  940. /*
  941.     Change the decimal presentations from Windows to C library
  942. */
  943. void ClibDecimalChars( char *strbuf )
  944. {
  945.     char *dec_char, *tdec_char;
  946.  
  947.     if ( !separator_inited )
  948.     {
  949.         if ( InitDecimalChars() != TRUE )
  950.         {
  951.             return;
  952.         }
  953.     }
  954.  
  955.     /*
  956.         Check to see if the decimal points are different
  957.         from the win_decseparator which is what Windows uses.
  958.         if they are different, mark where it is and
  959.         replace it with clib_decseparator later, after replacing 
  960.         thousand decimal chars.
  961.     */
  962.     if ( strcmp( clib_decseparator, win_decseparator ) == 0 )
  963.     {
  964.         dec_char = NULL;
  965.     }
  966.     else
  967.     {
  968.         dec_char = strstr( strbuf, win_decseparator );
  969.     }
  970.  
  971.     /*
  972.         Check to see if thousand decimal is different from 
  973.         the one from clib.  Replace it if it is.
  974.     */
  975.     if ( strcmp( win_tseparator, clib_tseparator ) != 0 )
  976.     {
  977.         while ( ( tdec_char = strstr( strbuf, win_tseparator ) ) )
  978.         {
  979.             /*
  980.                 This may not work if the thousand decimal separator is
  981.                 2 or more chars for example the kanji character set.
  982.             */
  983.             *tdec_char = *clib_tseparator;
  984.             strbuf = tdec_char;
  985.         }
  986.     }
  987.  
  988.     /*
  989.         Replace the decimal the last.
  990.     */
  991.     if ( dec_char )
  992.     {
  993.         /*
  994.             This may not work if the decimal separator is
  995.             2 or more chars for example the kanji char set.
  996.         */
  997.         *dec_char = *clib_decseparator;
  998.     }
  999. }
  1000.  
  1001. /*
  1002.     Change the decimal presentation from clib to Windows
  1003. */
  1004. void    WinDecimalChars( char *strbuf )
  1005. {
  1006.     char *dec_char, *tdec_char;
  1007.  
  1008.     if ( !separator_inited )
  1009.     {
  1010.         if ( InitDecimalChars() != TRUE )
  1011.         {
  1012.             return;
  1013.         }
  1014.     }
  1015.  
  1016.     /*
  1017.         Check to see if the decimal points are different
  1018.         from the clib_decseparator which sprintf() uses.
  1019.         if they are different, mark where it is and
  1020.         replace it after replacing the thousand decimal chars.
  1021.     */
  1022.     if ( strcmp( win_decseparator, clib_decseparator ) == 0 )
  1023.     {
  1024.         dec_char = NULL;
  1025.     }
  1026.     else
  1027.     {
  1028.         dec_char = strstr( strbuf, clib_decseparator );
  1029.     }
  1030.  
  1031.     /*
  1032.         Check to see if the thousand decimal is different from 
  1033.         the one from clib.  Replace it if it is.
  1034.     */
  1035.     if ( strcmp( win_tseparator, clib_tseparator ) != 0 )
  1036.     {
  1037.         while ( ( tdec_char = strstr( strbuf, clib_tseparator ) ) )
  1038.         {
  1039.             /*
  1040.                 This may not work if the thousand decimal separator is
  1041.                 2 or more chars for example the kanji character set.
  1042.             */
  1043.             *tdec_char = *win_tseparator;
  1044.             strbuf = tdec_char;
  1045.         }
  1046.     }
  1047.  
  1048.     /*
  1049.         Replace the decimal the last.
  1050.     */
  1051.     if ( dec_char )
  1052.     {
  1053.         /*
  1054.            This may not work if the thousand decimal separator is
  1055.            2 or more chars; for example the kanji character set.
  1056.         */
  1057.         *dec_char = *win_decseparator;
  1058.     }
  1059. }
  1060.  
  1061. /*
  1062.     This routine query Windows for what it uses to represent
  1063.     decimal and thousand decimal.
  1064. */
  1065. BOOL InitDecimalChars()
  1066. {
  1067.     HKEY            hkey;
  1068.     DWORD           valuetype;
  1069.     unsigned long   len;
  1070.     char            temp_buf[ 256 ];
  1071.     BOOL            IsWin32s;
  1072.  
  1073.     IsWin32s = GetVersion() & 0x80000000;
  1074.  
  1075.     if ( IsWin32s )
  1076.     {
  1077.         len = GetProfileString( "intl"
  1078.                                 , "sThousand"
  1079.                                 , ","
  1080.                                 , win_tseparator
  1081.                                 , sizeof( win_tseparator ) );
  1082.         if ( GetProfileString( "intl"
  1083.                             , "sDecimal"
  1084.                             , "."
  1085.                             , win_decseparator
  1086.                             , sizeof( win_decseparator ) ) == 0
  1087.             || len == 0 )
  1088.         {
  1089.             separator_inited = FALSE;
  1090.         }
  1091.         separator_inited = TRUE;
  1092.     }
  1093.     else /* NT! */
  1094.     {
  1095.     
  1096.         if ( RegOpenKey( HKEY_CURRENT_USER
  1097.                         , "Control Panel\\International"
  1098.                         , &hkey ) == ERROR_SUCCESS )
  1099.     
  1100.         {
  1101.             ASSERT( hkey != 0 );
  1102.             len = sizeof( temp_buf ) - 1;
  1103.             RegQueryValueEx( hkey
  1104.                             , "sDecimal"
  1105.                             , NULL
  1106.                             , &valuetype
  1107.                             , temp_buf
  1108.                             , &len );
  1109.             ASSERT( temp_buf[ 0 ] != 0 );
  1110.             temp_buf[ len ] = 0;
  1111.             strncpy( win_decseparator, temp_buf, sizeof( win_decseparator ) - 1 );
  1112.     
  1113.             len = sizeof( temp_buf ) - 1;
  1114.             RegQueryValueEx( hkey
  1115.                             , "sThousand"
  1116.                             , NULL
  1117.                             , &valuetype
  1118.                             , temp_buf
  1119.                             , &len );
  1120.             ASSERT( temp_buf[ 0 ] != 0 );
  1121.             temp_buf[ len ] = 0;
  1122.             strncpy( win_tseparator, temp_buf, sizeof( win_tseparator ) - 1 );
  1123.     
  1124.             RegCloseKey( hkey );
  1125.             separator_inited = TRUE;
  1126.         }
  1127.         else
  1128.         {
  1129.             ASSERT( 0 );
  1130.         }
  1131.  
  1132.     }
  1133.     /*
  1134.         Check the length of Windows separators length to be 1 char
  1135.         only since we don't support multiple chars in the code.
  1136.     */
  1137.     ASSERT( win_decseparator[ 1 ] == EOS && win_tseparator[ 1 ] == EOS );
  1138.     return separator_inited;
  1139. }
  1140.