+
+/**
+ * @ingroup HashListMset
+ * @brief parses an MSet string into a list for later use
+ * @param MSetList List to be read from MSetStr
+ * @param MSetStr String containing the list
+ */
+int ParseMSet(MSet **MSetList, StrBuf *MSetStr)
+{
+ const char *POS = NULL, *SetPOS = NULL;
+ StrBuf *OneSet;
+ HashList *ThisMSet;
+ long StartSet, EndSet;
+ long *pEndSet;
+
+ *MSetList = NULL;
+ if ((MSetStr == NULL) || (StrLength(MSetStr) == 0))
+ return 0;
+
+ OneSet = NewStrBufPlain(NULL, StrLength(MSetStr));
+ if (OneSet == NULL)
+ return 0;
+
+ ThisMSet = NewHash(0, lFlathash);
+ if (ThisMSet == NULL)
+ {
+ FreeStrBuf(&OneSet);
+ return 0;
+ }
+
+ *MSetList = (MSet*) ThisMSet;
+
+ /* an MSet is a coma separated value list. */
+ StrBufExtract_NextToken(OneSet, MSetStr, &POS, ',');
+ do {
+ SetPOS = NULL;
+
+ /* One set may consist of two Numbers: Start + optional End */
+ StartSet = StrBufExtractNext_long(OneSet, &SetPOS, ':');
+ EndSet = 0; /* no range is our default. */
+ /* do we have an end (aka range?) */
+ if ((SetPOS != NULL) && (SetPOS != StrBufNOTNULL))
+ {
+ if (*(SetPOS) == '*')
+ EndSet = LONG_MAX; /* ranges with '*' go until infinity */
+ else
+ /* in other cases, get the EndPoint */
+ EndSet = StrBufExtractNext_long(OneSet, &SetPOS, ':');
+ }
+
+ pEndSet = (long*) malloc (sizeof(long));
+ if (pEndSet == NULL)
+ {
+ FreeStrBuf(&OneSet);
+ DeleteHash(&ThisMSet);
+ return 0;
+ }
+ *pEndSet = EndSet;
+
+ Put(ThisMSet, LKEY(StartSet), pEndSet, NULL);
+ /* if we don't have another, we're done. */
+ if (POS == StrBufNOTNULL)
+ break;
+ StrBufExtract_NextToken(OneSet, MSetStr, &POS, ',');
+ } while (1);
+ FreeStrBuf(&OneSet);
+
+ return 1;
+}
+
+/**
+ * @ingroup HashListMset
+ * @brief checks whether a message is inside a mset
+ * @param MSetList List to search for MsgNo
+ * @param MsgNo number to search in mset
+ */
+int IsInMSetList(MSet *MSetList, long MsgNo)
+{
+ /* basicaly we are a ... */
+ long MemberPosition;
+ HashList *Hash = (HashList*) MSetList;
+ long HashAt;
+ long EndAt;
+ long StartAt;
+
+ if (Hash == NULL)
+ return 0;
+ if (Hash->MemberSize == 0)
+ return 0;
+ /** first, find out were we could fit in... */
+ HashAt = FindInHash(Hash, MsgNo);
+
+ /* we're below the first entry, so not found. */
+ if (HashAt < 0)
+ return 0;
+ /* upper edge? move to last item */
+ if (HashAt >= Hash->nMembersUsed)
+ HashAt = Hash->nMembersUsed - 1;
+ /* Match? then we got it. */
+ else if (Hash->LookupTable[HashAt]->Key == MsgNo)
+ return 1;
+ /* One above possible range start? we need to move to the lower one. */
+ else if ((HashAt > 0) &&
+ (Hash->LookupTable[HashAt]->Key > MsgNo))
+ HashAt -=1;
+
+ /* Fetch the actual data */
+ StartAt = Hash->LookupTable[HashAt]->Key;
+ MemberPosition = Hash->LookupTable[HashAt]->Position;
+ EndAt = *(long*) Hash->Members[MemberPosition]->Data;
+ if ((MsgNo >= StartAt) && (EndAt == LONG_MAX))
+ return 1;
+ /* no range? */
+ if (EndAt == 0)
+ return 0;
+ /* inside of range? */
+ if ((StartAt <= MsgNo) && (EndAt >= MsgNo))
+ return 1;
+ return 0;
+}
+
+
+/**
+ * @ingroup HashListMset
+ * @brief frees a mset [redirects to @ref DeleteHash
+ * @param FreeMe to be free'd
+ */
+void DeleteMSet(MSet **FreeMe)
+{
+ DeleteHash((HashList**) FreeMe);
+}