diff options
Diffstat (limited to 'private/unimodem/new/match/mt.c')
-rw-r--r-- | private/unimodem/new/match/mt.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/private/unimodem/new/match/mt.c b/private/unimodem/new/match/mt.c new file mode 100644 index 000000000..c5950272a --- /dev/null +++ b/private/unimodem/new/match/mt.c @@ -0,0 +1,424 @@ +#include "unimdm.h" +#include "mcxp.h" + +#if 0 +#include <windows.h> +#endif + +#include <stdio.h> +#include <string.h> +#include "mt.h" + +// #define SILENT + +#ifdef SILENT +# define PRINTF0(_a) (0) +# define PRINTF1(_a,_b) (0) +# define PRINTF2(_a,_b,_c) (0) +# define PRINTF3(_a,_b,_c,_d) (0) +#else +#ifdef CONSOLE +# define PRINTF0(_a) printf(_a) +# define PRINTF1(_a,_b) printf((_a),(_b)) +# define PRINTF2(_a,_b,_c) printf((_a),(_b),(_c)) +# define PRINTF3(_a,_b,_c,_d) printf((_a),(_b),(_c),(_d)) +#else +# define PRINTF0(_a) DPRINTF(_a) +# define PRINTF1(_a,_b) DPRINTF1((_a),(_b)) +# define PRINTF2(_a,_b,_c) DPRINTF2((_a),(_b),(_c)) +# define PRINTF3(_a,_b,_c,_d) DPRINTF3((_a),(_b),(_c),(_d)) +#endif //!CONSOLE +#endif // !SILENT + +// ======================== PRIVATES =================================== + +#define ASSERT(_c) \ + ((_c) ? 0: printf("Assertion failed in %s:%d\n", __FILE__, __LINE__)) + +typedef struct _MNODE +{ + MATCHREC mr; + struct _MNODE *pmnNext; + struct _MNODE *pmnCh; + +} MNODE, *PMNODE; + +PMNODE mn_create_raw_tree(PMATCHREC pmr, DWORD dwcmr); +BOOL mn_cook_tree (PMNODE pmn, DWORD dwMin); +void mn_free_tree (PMNODE pmn); +PMNODE mn_alloc (PMATCHREC pmr, PMNODE pmnNext); +void mn_free (PMNODE pmn); +void mn_dump_tree (PMNODE pmn, UINT uDepth, UINT uWidth); +DWORD mn_find_smallest(PMNODE pmn); +DWORD mn_find_match(PMNODE pmn, PMATCHREC pmr); +// ======================== PRIVATES (end) =================================== + +HMATCHTREE mtCreateTree(PMATCHREC pmr, DWORD dwcmr) +{ + PMNODE pmn = mn_create_raw_tree(pmr, dwcmr); + + if (!mn_cook_tree(pmn, MAXDWORD)) + { + mn_free_tree(pmn); + pmn=NULL; + } + + return (DWORD) pmn; +} + +void mtFreeTree(HMATCHTREE hmt) +{ + PMNODE pmn = (PMNODE) hmt; + + mn_free_tree(pmn); // NULL is a valid tree +} + + +// Returns one or more fMATCH_ flags. +// Searches through all its siblings as well... +// Recursive function, so keep locals to a minimum. +DWORD mtFindMatch(HMATCHTREE hmt, PMATCHREC pmr) +{ + MATCHREC mr; + DWORD dwRet = 0; + + if (!pmr) goto end; + + mr = *pmr; // Structure copy. + dwRet = mn_find_match((PMNODE)hmt, &mr); + // mn_find_match trashes dwLen and cb fields of pmr, only pv field is valid + pmr->pv = mr.pv; + +end: + return dwRet; +} + + +#ifdef DEBUG +void mtDumpTree(HMATCHTREE hmt) +{ + PMNODE pmn = (PMNODE) hmt; + + mn_dump_tree(pmn, 0, 0); +} +#endif // DEBUG + + +PMNODE mn_create_raw_tree(PMATCHREC pmr, DWORD dwcmr) +{ + PMNODE pmnFirst=NULL; + + for (;dwcmr--;pmr++) + { + PMNODE pmnTmp = mn_alloc(pmr, pmnFirst); + if (!pmnTmp) goto failure; + pmnFirst=pmnTmp; + } + goto end; + +failure: + mn_free_tree(pmnFirst); // NULL is a valid tree to free + pmnFirst=NULL; + // FALL THROUGH + +end: + return pmnFirst; +} + + +PMNODE mn_alloc(PMATCHREC pmr, PMNODE pmnNext) +{ + PMNODE pmn = (pmr && pmr->pb && pmr->dwLen) + ? LocalAlloc(LPTR, sizeof(MNODE)) : NULL; + + if (pmn) + { + pmn->mr = *pmr; // structure copy. + pmn->pmnNext=pmnNext; + pmn->pmnCh=NULL; + } + + return pmn; +} + +void mn_free(PMNODE pmn) +{ + if (pmn) + { + ASSERT(!pmn->pmnCh && !pmn->pmnNext); + LocalFree(pmn); + } +} + + +void mn_free_tree(PMNODE pmn) +{ + if (pmn) + { + mn_free_tree(pmn->pmnCh); // recurse down + mn_free_tree(pmn->pmnNext); // recurse left + pmn->pmnNext=pmn->pmnCh=NULL; + mn_free(pmn); + } +} + +BOOL mn_cook_tree(PMNODE pmn, DWORD dwMin) +{ + // Find smallest string of me and siblings + if (dwMin==MAXDWORD) dwMin = mn_find_smallest(pmn); + + if (!pmn) goto success; + + // These are all serious problems -- so we assert on failure: + if (!dwMin || pmn->pmnCh || !pmn->mr.pb || (pmn->mr.dwLen<dwMin)) + goto failure; + + // Start my children's list by creating one if pmn->pb is longer + // than the minimum, in which case we also NULL pv, because pv should + // be NULL for internal (non-leaf) nodes. + if (pmn->mr.dwLen>dwMin) + { + MATCHREC mr; + mr.pb=pmn->mr.pb+dwMin; + mr.dwLen=pmn->mr.dwLen-dwMin; + mr.pv=pmn->mr.pv; + pmn->pmnCh = mn_alloc(&mr, NULL); + if (!pmn->pmnCh) goto failure; + pmn->mr.pv=NULL; + pmn->mr.dwLen=dwMin; + } + + // Add to my children's list by converting those siblings which + // share my dwMin-sized prefix into my children. (Obviously) remove + // these siblings from the sibling list. + { + PMNODE pmnTmp = pmn; + while(pmnTmp->pmnNext) + { + PMNODE pmnTmp1 = pmnTmp->pmnNext; + + if (pmnTmp1->mr.dwLen<dwMin || !pmnTmp1->mr.pb) goto failure; + + if (!_strnicmp(pmn->mr.pb, pmnTmp1->mr.pb, dwMin)) + { + // Found a prefix match -- remove from sibling list + // and add to child list if non-null + pmnTmp->pmnNext = pmnTmp1->pmnNext; + pmnTmp1->mr.dwLen-=dwMin; + pmnTmp1->mr.pb+=dwMin; + pmnTmp1->pmnNext=NULL; + // If nothing left to add -- we're at the end of pb -- + // (leaf node) so we make pmn->pv non-NULL, and free pmnTmp1 + // Otherwise we add it to pmn's child's list. + if (!pmnTmp1->mr.dwLen) + { + if (pmn->mr.pv) {PRINTF0("Warning: overriding pv!\n");} + pmn->mr.pv = pmnTmp1->mr.pv; + ASSERT(!pmnTmp1->pmnCh && !pmnTmp1->pmnNext); + mn_free(pmnTmp1); + } + else + { + pmnTmp1->pmnNext = pmn->pmnCh; + pmn->pmnCh=pmnTmp1; + } + } + else + { + pmnTmp = pmnTmp->pmnNext; + } + if (!pmnTmp) break; + } + } + + // recurse down + if (!mn_cook_tree(pmn->pmnCh, MAXDWORD)) goto failure; + + // recurse left + if (!mn_cook_tree(pmn->pmnNext, dwMin)) goto failure; + + // FALL THROUGH + +success: + return TRUE; + +failure: + ASSERT(FALSE); + return FALSE; +} + +DWORD mn_find_smallest(PMNODE pmn) +{ + DWORD dw = MAXDWORD; + + if (pmn) + { + dw = mn_find_smallest(pmn->pmnNext); + if (dw>pmn->mr.dwLen) dw=pmn->mr.dwLen; + } + return dw; +} + +void mn_dump_tree(PMNODE pmn, UINT uDepth, UINT uWidth) +{ + static char rgchFill[]="----------------------------------------"; + LONG lOff = sizeof(rgchFill) - (uDepth+1); + if (lOff<0) lOff=0; + // if(!pmn) return; + PRINTF2("%02lu%s", (unsigned long) uDepth, rgchFill+lOff); + if (!pmn) + { + PRINTF1("NULL(w=%lu)\n", (unsigned long) uWidth); + } + else + { + CHAR *pb = (pmn->mr.pb)?pmn->mr.pb:"\"\""; + CHAR c = pb[pmn->mr.dwLen]; + CHAR *pc2 = (CHAR *) pmn->mr.pv; + if (!pc2) pc2=""; + pb[pmn->mr.dwLen]=0; + PRINTF3("[%s]:%02lu %s\n", pc2, + (unsigned long) pmn->mr.dwLen, pb); + pb[pmn->mr.dwLen]=c; + mn_dump_tree(pmn->pmnCh, uDepth+1,0); // recurse down + mn_dump_tree(pmn->pmnNext, uDepth, uWidth+1); // recurse left + } +} + + +// Returns one or more fMATCH_ flags. +// Searches through all its siblings as well... +// Recursive function, so keep locals to a minimum. +// WARNING: Trashes dwLen and cb fields of pmr. +DWORD mn_find_match(PMNODE pmn, PMATCHREC pmr) +{ + DWORD dwRet=0; + DWORD dwcbMin; + CHAR *pc; + +#ifdef DEBUG + DWORD dwcb; + CHAR rgchTmp[256]; + DWORD dwcbTmp; +#endif // DEBUG + + if (!pmn || !pmr) goto end; + +#ifdef DEBUG +# define DWCB dwcb +# define DBGPSZ rgchTmp + DWCB = pmr->dwLen; + dwcbTmp = sizeof(DBGPSZ)-1; + if (dwcbTmp>DWCB) dwcbTmp=DWCB; + // We do this because pb is not null terminated. + CopyMemory(DBGPSZ, pmr->pb, dwcbTmp); + DBGPSZ[dwcbTmp]=0; +#else // !DEBUG +# define DBGPSZ "" +# define DWCB 0 +#endif // !DEBUG + + PRINTF1("Entering mn_find_match(-, [%s], -)\n", DBGPSZ); + + + + // Iterate through siblings, looking for matches and partial matches. + // We stop when we find the first match or partial match, because all + // the substrings at the same level are unique and have the same length: + // so if we found a match we can't find a partial match among the siblings, + // and vice versa. Furthermore, if we find a partial match, we don't really + // care which (could be more than one) sibling it is a partial match for. + // If in the future we we *do* return the node for efficiency purposes + // (so that the next call can start where we left off), + // we would return the node for the *first* partial match we found. + pc = pmr->pb; + dwcbMin = pmn->mr.dwLen; + if (pmr->dwLen<dwcbMin) + { + dwcbMin=pmr->dwLen; + dwRet = fMATCH_PARTIAL; + } + else if (pmr->dwLen==dwcbMin) + { + dwRet = fMATCH_COMPLETE; + } + else + { + // pmr->dwLen>pmn->mr.dwU, dwRet==0, so do nothing + } + + // WARNING: we trash pmr here, except for pmr->pv + pmr->dwLen-=dwcbMin; + pmr->pb+=dwcbMin; + + while(pmn) + { + // Fundamental assumption is that all dwLens of siblings are equal. + ASSERT((pmn->pmnNext)? pmn->mr.dwLen==pmn->pmnNext->mr.dwLen:TRUE); + + if (!_strnicmp(pc, pmn->mr.pb, dwcbMin)) + { + PRINTF3("Match: dwcb=%lu; pmn->mr.dwLen=%lu; pc=[%s]\n", + (unsigned long) DWCB, + (unsigned long) pmn->mr.dwLen, + DBGPSZ); + + // Note: dwRet is computed just once, out of the while loop. + // we were really overloading dwRet then. Now we compute the + // true return value. + switch(dwRet) + { + case 0: // i.e., (DWCB > pmn->mr.dwLen) + ASSERT(DWCB>pmn->mr.dwLen); + // Recurse down. WARNING: pmr is trashed, except for pmr->pv + dwRet = mn_find_match(pmn->pmnCh, pmr); + break; + + case fMATCH_PARTIAL: // i.e., (DWCB < pmn->mr.dwLen) + ASSERT(DWCB<pmn->mr.dwLen); + break; + + case fMATCH_COMPLETE: // i.e., (DWCB == pmn->mr.dwLen) + ASSERT(DWCB==pmn->mr.dwLen); + // Now let's see if we're truly a perfect macth: non-null + // pv indicates an actual response terminates in this node. + dwRet=0; + if (pmn->mr.pv) + { + pmr->pv = pmn->mr.pv; + dwRet = fMATCH_COMPLETE; + } + // Non-null pmnCh implies there are postfixes, i.e., longer + // responses + if (pmn->pmnCh) + { + dwRet |= fMATCH_PARTIAL; + } + break; + + default: + ASSERT(FALSE); + dwRet=0; + goto end; + } + if (dwRet) break; // break out of while loop if we got something + } + pmn=pmn->pmnNext; + + } + + if (!pmn) + { + dwRet=0; + } + else + { + ASSERT(dwRet && !_strnicmp(pc, pmn->mr.pb, dwcbMin)); + } + + PRINTF2("Exiting mn_find_match(-, [%s], -) returing 0x%lx\n", DBGPSZ, + (unsigned long) dwRet); + +end: + return dwRet; +} |