1414#include < cstdio>
1515#include < cstring>
1616#include < fcntl.h>
17+ #include < map>
1718#include < memory>
1819#include < string>
1920#include < unistd.h>
@@ -62,19 +63,38 @@ using mdi_list = std::vector<std::string>; /* message data item (RFC 3501 §6.4.
6263namespace {
6364
6465struct dir_tree {
66+ struct cmp {
67+ inline bool operator ()(const std::string &a, const std::string &b) const { return strcasecmp (a.c_str (), b.c_str ()) < 0 ; }
68+ };
69+
6570 dir_tree () = default ;
66- ~dir_tree ();
67- NOMOVE (dir_tree);
6871
69- void load_from_memfile (const std::vector<enum_folder_t > &);
70- DIR_NODE *match (const char *path);
71- static DIR_NODE *get_child (DIR_NODE *);
72- bool has_children (DIR_NODE *x) const { return get_child (x) != nullptr ; }
72+ template <typename Vec> void load_from_memfile (Vec &&pfile) try
73+ {
74+ for (auto &&[id, orig] : std::forward<Vec>(pfile)) {
75+ auto curr = this ;
76+ constexpr bool inplace_edit = std::is_rvalue_reference<Vec>::value;
77+ using ref_or_copy = typename std::conditional<inplace_edit, std::string &, std::string>::type;
78+ ref_or_copy name{orig};
79+ char *saveptr = nullptr ;
80+
81+ for (auto token = strtok_r (name.data (), " /" , &saveptr);
82+ token != nullptr ;
83+ token = strtok_r (nullptr , " /" , &saveptr)) {
84+ auto [it, added] = curr->entries .try_emplace (token);
85+ curr = &it->second ;
86+ }
87+ }
88+ } catch (const std::bad_alloc &) {
89+ mlog (LV_ERR, " E-2903: ENOMEM" );
90+ }
91+
92+ const dir_tree *match (const char *path) const ;
93+ dir_tree *match (const char *path) { return const_cast <dir_tree *>(const_cast <const dir_tree *>(this )->match (path)); }
94+ bool has_children () const { return entries.size () > 0 ; }
7395
74- SIMPLE_TREE stree{} ;
96+ std::map<std::string, dir_tree, cmp> entries ;
7597};
76- using DIR_TREE = dir_tree;
77- using DIR_TREE_ENUM = void (*)(DIR_NODE *, void *);
7898
7999enum {
80100 TYPE_WILDS = 1 ,
@@ -98,121 +118,25 @@ static constexpr const builtin_folder g_folder_list[] = {
98118 {PRIVATE_FID_JUNK, " \\ Junk \\ Spam" },
99119};
100120
101- void dir_tree::load_from_memfile (const std::vector<enum_folder_t > &pfile) try
102- {
103- auto ptree = this ;
104- char *ptr1, *ptr2;
105- char temp_path[4096 + 1 ];
106- SIMPLE_TREE_NODE *pnode, *pnode_parent;
107-
108- auto proot = ptree->stree .get_root ();
109- if (NULL == proot) {
110- auto pdir = std::make_unique<DIR_NODE>();
111- pdir->stree .pdata = pdir.get ();
112- pdir->name [0 ] = ' \0 ' ;
113- pdir->b_loaded = TRUE ;
114- proot = &pdir->stree ;
115- ptree->stree .set_root (std::move (pdir));
116- }
117-
118- for (const auto &pfile_path : pfile) {
119- gx_strlcpy (temp_path, pfile_path.second .c_str (), std::size (temp_path));
120- auto len = strlen (temp_path);
121- pnode = proot;
122- if (len == 0 || temp_path[len-1 ] != ' /' ) {
123- temp_path[len++] = ' /' ;
124- temp_path[len] = ' \0 ' ;
125- }
126- ptr1 = temp_path;
127- while ((ptr2 = strchr (ptr1, ' /' )) != NULL ) {
128- *ptr2 = ' \0 ' ;
129- pnode_parent = pnode;
130- pnode = pnode->get_child ();
131- if (NULL != pnode) {
132- do {
133- auto pdir = static_cast <DIR_NODE *>(pnode->pdata );
134- if (strcasecmp (pdir->name , ptr1) == 0 )
135- break ;
136- } while ((pnode = pnode->get_sibling ()) != nullptr );
137- }
138-
139- if (NULL == pnode) {
140- auto pdir = std::make_unique<DIR_NODE>();
141- pdir->stree .pdata = pdir.get ();
142- gx_strlcpy (pdir->name , ptr1, std::size (pdir->name ));
143- pdir->b_loaded = FALSE ;
144- pnode = &pdir->stree ;
145- ptree->stree .add_child (pnode_parent,
146- std::move (pdir), SIMPLE_TREE_ADD_LAST);
147- }
148- ptr1 = ptr2 + 1 ;
149- }
150- static_cast <DIR_NODE *>(pnode->pdata )->b_loaded = TRUE ;
151- }
152- } catch (const std::bad_alloc &) {
153- mlog (LV_ERR, " E-2903: ENOMEM" );
154- }
155-
156- static void dir_tree_clear (DIR_TREE *ptree)
157- {
158- auto pnode = ptree->stree .get_root ();
159- if (pnode != nullptr )
160- ptree->stree .destroy_node (pnode, [](tree_node *p) { delete static_cast <DIR_NODE *>(p->pdata ); });
161- }
162-
163- DIR_NODE *dir_tree::match (const char *path)
121+ const dir_tree *dir_tree::match (const char *path) const
164122{
165- auto ptree = this ;
166- int len;
167- DIR_NODE *pdir = nullptr ;
168- char *ptr1, *ptr2;
169- char temp_path[4096 + 1 ];
170-
171- auto pnode = ptree->stree .get_root ();
172- if (pnode == nullptr )
173- return NULL ;
123+ auto curr = this ;
174124 if (*path == ' \0 ' )
175- return static_cast <DIR_NODE *>(pnode-> pdata ) ;
176- len = strlen (path);
125+ return curr ;
126+ auto len = strlen (path);
177127 if (len >= 4096 )
178128 return NULL ;
179- memcpy (temp_path, path, len);
180- if (temp_path[len-1 ] != ' /' )
181- temp_path[len++] = ' /' ;
182- temp_path[len] = ' \0 ' ;
183-
184- ptr1 = temp_path;
185- for (unsigned int level = 0 ; (ptr2 = strchr (ptr1, ' /' )) != nullptr ; ++level) {
186- *ptr2 = ' \0 ' ;
187- pnode = pnode->get_child ();
188- if (pnode == nullptr )
189- return NULL ;
190- do {
191- pdir = static_cast <DIR_NODE *>(pnode->pdata );
192- if (strcasecmp (pdir->name , ptr1) == 0 )
193- break ;
194- if (level == 0 && strcasecmp (pdir->name , " INBOX" ) == 0 &&
195- strcasecmp (ptr1, " inbox" ) == 0 )
196- break ;
197- } while ((pnode = pnode->get_sibling ()) != nullptr );
198- if (pnode == nullptr )
129+ std::string temp_path (path, len);
130+ char *saveptr = nullptr ;
131+ for (auto token = strtok_r (temp_path.data (), " /" , &saveptr);
132+ token != nullptr ;
133+ token = strtok_r (nullptr , " /" , &saveptr)) {
134+ auto it = curr->entries .find (token);
135+ if (it == curr->entries .cend ())
199136 return NULL ;
200- ptr1 = ptr2 + 1 ;
137+ curr = &it-> second ;
201138 }
202- return pdir;
203- }
204-
205- DIR_NODE *dir_tree::get_child (DIR_NODE* pdir)
206- {
207- auto pnode = pdir->stree .get_child ();
208- return pnode != nullptr ? static_cast <DIR_NODE *>(pnode->pdata ) : nullptr ;
209- }
210-
211- dir_tree::~dir_tree ()
212- {
213- auto ptree = this ;
214- dir_tree_clear (ptree);
215- ptree->stree .clear ();
139+ return curr;
216140}
217141
218142static const builtin_folder *special_folder (uint64_t fid)
@@ -1621,7 +1545,7 @@ int icp_delete(int argc, char **argv, imap_context &ctx)
16211545 auto dh = folder_tree.match (argv[2 ]);
16221546 if (dh == nullptr )
16231547 return 1925 ;
1624- if (folder_tree. has_children (dh ))
1548+ if (dh-> has_children ())
16251549 return 1924 ;
16261550 }
16271551
@@ -1763,7 +1687,7 @@ int icp_list(int argc, char **argv, imap_context &ctx) try
17631687 if (!icp_wildcard_match (sys_name.c_str (), search_pattern.c_str ()))
17641688 continue ;
17651689 auto pdir = folder_tree.match (sys_name.c_str ());
1766- auto have_cld = pdir != nullptr && folder_tree. has_children (pdir );
1690+ auto have_cld = pdir != nullptr && pdir-> has_children ();
17671691 auto buf = fmt::format (" * LIST (\\ Has{}Children{}{}) \" /\" {}\r\n " ,
17681692 have_cld ? " " : " No" ,
17691693 return_special && special != nullptr ? " " : " " ,
@@ -1816,7 +1740,7 @@ int icp_xlist(int argc, char **argv, imap_context &ctx) try
18161740 continue ;
18171741 auto special = special_folder (fentry.first );
18181742 auto pdir = folder_tree.match (sys_name.c_str ());
1819- auto have = pdir != nullptr && folder_tree. has_children (pdir );
1743+ auto have = pdir != nullptr && pdir-> has_children ();
18201744 auto buf = fmt::format (" * XLIST (\\ Has{}Children{}{}) \" /\" {}\r\n " ,
18211745 have ? " " : " No" ,
18221746 special != nullptr ? " " : " " ,
@@ -1880,7 +1804,7 @@ int icp_lsub(int argc, char **argv, imap_context &ctx) try
18801804 if (!icp_wildcard_match (sys_name.c_str (), search_pattern.c_str ()))
18811805 continue ;
18821806 auto pdir = folder_tree.match (sys_name.c_str ());
1883- auto have = pdir != nullptr && folder_tree. has_children (pdir );
1807+ auto have = pdir != nullptr && pdir-> has_children ();
18841808 auto buf = fmt::format (" * LSUB (\\ Has{}Children) \" /\" {}\r\n " ,
18851809 have ? " " : " No" , quote_encode (sys_name));
18861810 if (pcontext->stream .write (buf.c_str (), buf.size ()) != STREAM_WRITE_OK)
0 commit comments