/* * strlist.h: Implementation of the StringList class. * * This file is a part of NSIS. * * Copyright (C) 1999-2009 Nullsoft and Contributors * * Licensed under the zlib/libpng license (the "License"); * you may not use this file except in compliance with the License. * * Licence details can be found in the file COPYING. * * This software is provided 'as-is', without any express or implied * warranty. * * Unicode support and Doxygen comments by Jim Park -- 07/27/2007 */ #ifndef _STRLIST_H_ #define _STRLIST_H_ #include "Platform.h" #include #include #include "tchar.h" #include "growbuf.h" /** * Implements a list of strings mapped into a straight buffer. It is * virtually always O(n) to do any basic operations on this thing. Very * memory efficient but very slow performance. */ class StringList { private: // don't copy instances StringList(const StringList&); void operator=(const StringList&); public: StringList() {} ~StringList() {} /** * Adds a string to the StringList. If the string already exists in the * list, then it just returns the index of the found string. * * @param str String to add. * * @param case_sensitive If 0, search for whole string case insensitively. * If 1, then search for whole string case sensitively. If 2, then search * for just the end of the string match case sensitively. Basically it's * like searching for regexp('str$') in the list of string. If -1, then it * adds the string regardless of whether there is a match or not! * * @return the index to the string in TCHARs. */ int add(const TCHAR *str, int case_sensitive); /** * Search the StrinList for a string. If the string already exists in the * list then it returns the index of the found string. Returns -1 on * failure. * * @param str String to search for. * * @param case_sensitive If 0, search for whole string case insensitively.If * 1, then search for whole string case sensitively. If 2, then * search for just the end of the string match, case sensitively. * Basically it's like searching for regexp('str$') in the list of string. * * @param idx If not NULL, the *idx is set to the cardinal number of the * string. In other words, it tells you which nth string it is in the * list. * * @return the TCHAR index to the string (not necessarily the byte positional * offset). -1 if not found. */ int find(const TCHAR *str, int case_sensitive, int *idx=NULL) const; // returns -1 if not found /** * Delete the string at the positional index. * * @param pos The number of TCHARS to count from the beginning of the buffer * before the start of the string. */ void delbypos(int pos); /** * Converts the string index to the positional TCHAR index. For example, * it gives the answer to which TCHAR position is the beginning of the * nth string in the list? * * @param idx The string index. */ int idx2pos(int idx) const; /** * Get the count of the number of strings in the list. * @return the number of string in the list. */ int getnum() const; /** * Get the buffer straight as a const TCHAR pointer. Very unwise to use. * @return gr.m_s cast as a TCHAR*. */ const TCHAR *get() const; /** * Get the buffer size in bytes. * @return The buffer size in bytes. */ int getlen() const; private: GrowBuf gr; }; /** * This class maintains a list of T types in a GrowBuf sorted by T.name which * is assumed to be a string (TCHAR*). So it's really sort of a * map where X is whatever else is defined in T. But T must define * a TCHAR* name. * * The T struct should have the 'name' as the first element in its list of * members. Otherwise, all kinds of bad things will happen. */ template class SortedStringList { public: /** * Jim Park: Note that SortedStringList actually owns T.name. * Yes, this violates all kinds of encapsulation ideas. */ virtual ~SortedStringList() { T *s=(T*)gr.get(); size_t num=gr.getlen()/sizeof(T); for (size_t i=0; i ll) { int res; if (case_sensitive) res=_tcscmp(str, data[nextpos].name); else res=_tcsicmp(str, data[nextpos].name); if (res==0) return returnbestpos ? -1 : nextpos; if (res<0) ul=nextpos; else ll=nextpos+1; nextpos=(ul+ll)/2; } return returnbestpos ? nextpos : -1; } /** * This function looks for str in T.name and deletes the T in the * buffer. * * @return Returns 0 on success, 1 on failure. */ int del(const TCHAR *str, int case_sensitive=0) { int pos=find(str, case_sensitive); if (pos==-1) return 1; delbypos(pos); return 0; } /** * Given a T position, it deletes it from the buffer. It will * move the rest of the Ts to fill its position. * * @param pos The position of the target for deletion in T* offsets. */ void delbypos(int pos) { T *db=(T *)gr.get(); free(db[pos].name); memmove(db+pos,db+pos+1,gr.getlen()-(pos*sizeof(T))-sizeof(T)); gr.resize(gr.getlen()-sizeof(T)); } protected: TinyGrowBuf gr; }; #define mymin(x, y) ((x < y) ? x : y) /** * This class maintains a list of T types in a GrowBuf sorted by T.name which * is assumed to be an index into m_strings. So it's really sort of a * map where X is whatever else is defined in T. But T must define * a int name. * * The T struct should have the 'name' as the first element in its list of * members. Otherwise, all kinds of bad things will happen. * * This version does not have a delete function, hence the ND designation. * Also, because nothing is malloc'ed and free'd, this structure can be * placed in a single flat buffer. (Of course, T itself can be holding * pointers to things on the heap, which this structure does not * disallow explicitly.) */ template class SortedStringListND // no delete - can be placed in GrowBuf { public: SortedStringListND() { } virtual ~SortedStringListND() { } /** * Adds name into the list of sorted strings. * * @param name String to store. * @param case_sensitive Look for string case sensitively. Default is 0. * @param alwaysreturnpos Always return the position regardless of whether * name was inserted into the list or not. The default is 0. * * @return Returns -1 when name already exists, otherwise the T offset * into which the struct was stored in m_gr. If alwaysreturnpos * is true, then it will return the byte offset regardless of whether * the string was found. */ int add(const TCHAR *name, int case_sensitive=0, int alwaysreturnpos=0) { int where=0; T newstruct={0,}; // Find the best position to insert. int pos=find(name,-1,case_sensitive,1,&where); if (pos==-1) return alwaysreturnpos ? where : -1; // Note that .name is set with the TCHAR* offset into m_strings. newstruct.name=strings.add(name,(_tcsclen(name)+1)*sizeof(TCHAR))/sizeof(TCHAR); gr.add(&newstruct,sizeof(T)); T *s=(T*)gr.get(); memmove(s+pos+1,s+pos,gr.getlen()-((pos+1)*sizeof(T))); memcpy(s+pos,&newstruct,sizeof(T)); return pos; } /** * This function looks for the string str, in T.name in the buffer m_gr. * If it finds it, it returns the position found. Otherwise, it returns * -1. * * This behavior changes when returnbestpos == 1. In this case, * it will do the reverse. It will return -1 when it is found, noting * that there is no good place to put this duplicate string. If it * is not found, it returns the position where it ought to be * placed. * * When case_sensitive == -1 and returnbestpos == 1, then when the string * is found, it returns * the position of the string so that one can overwrite it. Very strange * special case behavior that I'm not sure if anyone actually uses. * * @param str The key string to search for. * * @param n_chars The number of characters to compare. Use -1 to match * the entire string. * * @param case_sensitive 1 = case sensitive, 0 = case insensitive, * -1 is a special case where it is case sensitive and overrides the * returnbestpos behavior when the string is found. * * @param returnbestpos If 1, then look for the best position to add the * string. If found in the list, return -1. * * @param where When str is found, returns the position of the string. * * @return The position of T where T.name == str. If returnbestpos != 0 * then return the best position to add T if not found, otherwise, -1. */ int find ( const TCHAR* str, /* key to search for */ int n_chars=-1, /* if -1, test the entire string, otherwise just n characters */ int case_sensitive=0, int returnbestpos=0, /* if not found, return best pos */ int* where=0 /* */ ) { T *data=(T *)gr.get(); int ul=gr.getlen()/sizeof(T); int ll=0; int nextpos=(ul+ll)/2; // Do binary search on m_gr which is sorted. m_strings is NOT sorted. while (ul > ll) { int res; const TCHAR *pCurr = (TCHAR*)strings.get() + data[nextpos].name; if (n_chars < 0) { if (case_sensitive) res = _tcscmp(str, pCurr); else res = _tcsicmp(str, pCurr); } else { unsigned int pCurr_len = _tcslen(pCurr); if (case_sensitive) res = _tcsncmp(str, pCurr, mymin((unsigned int) n_chars, pCurr_len)); else res = _tcsncicmp(str, pCurr, mymin((unsigned int) n_chars, pCurr_len)); // If there is a match and we are looking for a partial match and // n_chars is NOT the length of the current string, then the // comparison result is determined by the length comparison. if (res == 0 && n_chars != -1 && (unsigned int) n_chars != pCurr_len) res = n_chars - pCurr_len; } // Found! if (res==0) { // Return where we found it in *where. if (where) *where = nextpos; // If returnbestpos, then we should return -1, otherwise where // we found it. But if (returnbestpos && case_sensitive == -1) // returns nextpos. return returnbestpos ? (case_sensitive!=-1 ? -1 : nextpos) : nextpos; } if (res<0) ul=nextpos; else ll=nextpos+1; nextpos=(ul+ll)/2; } return returnbestpos ? nextpos : -1; } protected: TinyGrowBuf gr; GrowBuf strings; }; /** * Structure stored by DefineList. */ struct define { TCHAR *name; // key TCHAR *value; // value stored }; /** * DefineList is a specialized version of a SortedStringList * which is like a string to string mapping class. */ class DefineList : public SortedStringList { private: // don't copy instances DefineList(const DefineList&); void operator=(const DefineList&); public: /* Empty default constructor */ DefineList() {} // VC6 complains otherwise virtual ~DefineList(); /** * Add a name-value pair, case insensitively. * * @param name The name of the variable or key to search by. In a * std::map, it would be the .first of the pair. * * @param value The value to store. In a std::map, it would be the.second * of the pair. * * @return Returns 0 if successful, 1 if already exists. Errors cause * general program exit with error logging. */ int add(const TCHAR *name, const TCHAR *value=_T("")); /** * This function returns the pointer to the .value TCHAR* that corresponds * to the name key. * * @param name The key to search with. * * @return The TCHAR* to the value portion of the define struct. If not * found, returns NULL. */ TCHAR *find(const TCHAR *name); /** * This function deletes the define struct corresponding to the key 'str'. * * @return Returns 0 on success, 1 otherwise */ int del(const TCHAR *str); /** * This function returns the number of define structs in the sorted array. */ int getnum(); /** * Get the .name string of the (num)th define struct in the sorted array. * * @return Returns 0 if not found, otherwise the pointer to the .name. */ TCHAR *getname(int num); /** * Get the .value string of the (num)th define struct in the sorted array. * * @return Returns 0 if not found, otherwise the pointer to the .value. */ TCHAR *getvalue(int num); }; /** * Storage unit for FastStringList. Contains the byte offset into m_strings. */ struct string_t { int name; }; /** * This class uses SortedStringListND to implement a "faster" storage of * strings. It sort of implements a std::set that allows you * to add to the set and check for existence of the set. * * It's only faster in the sense that memory moves now only need to occur * on the array of string_t structs (or pointers) rather than entire * strings. A truly faster implementation would be using a hash table. */ class FastStringList : public SortedStringListND { private: // don't copy instances FastStringList(const FastStringList&); void operator=(const FastStringList&); public: /* Empty constructor */ FastStringList() {} // VC6 complains otherwise /* Empty virtual destructor */ virtual ~FastStringList() {} /** * Adds name to sorted array and returns the TCHAR* offset of m_strings * where it is stored. * * @param name The string to store. * * @param case_sensitive Should we store this case sensitively or not? * Setting case_sensitive to -1 will cause it to be case sensitive and * always overwrite. (Weird bad behavior). * * @return The TCHAR* offset of name in m_string as an int. */ int add(const TCHAR *name, int case_sensitive=0); /** * Get the buffer that contains the list of the strings in the order * in which they were added. * * @return The pointer to m_strings as a TCHAR*. */ TCHAR *get() const; /** * The size of the collection of m_strings as bytes. * * @return The size of m_strings in bytes. */ int getlen() const; /** * The number of strings stored in the sorted array. * * @return The number of strings stored. */ int getnum() const; }; #endif//_STRLIST_H_