Commit e4a76054 e4a76054ba29cd5af1bc80a96089580d3515279a by Sergey Poznyakoff

Implement list mapping.

* include/mailutils/list.h (MU_LIST_MAP_OK)
(MU_LIST_MAP_SKIP,MU_LIST_MAP_STOP): New constants.
(mu_list_mapper_t): New typedef.
(mu_list_map): New proto.
* libmailutils/list/gmap.c: New file.
* libmailutils/list/map.c: New file.
* libmailutils/list/Makefile.am: Add gmap.c and map.c
* libmailutils/tests/listop.c: New command "map".
* libmailutils/tests/list.at: Test the list mapping.
1 parent db7e58b4
......@@ -73,6 +73,50 @@ extern int mu_list_insert_list (mu_list_t list, void *item, mu_list_t new_list,
extern void mu_list_append_list (mu_list_t list, mu_list_t new_list);
extern void mu_list_prepend_list (mu_list_t list, mu_list_t new_list);
/* List mapper functions */
typedef int (*mu_list_mapper_t) (void **itmv, size_t itmc, void *call_data);
/* A generalized list mapping function.
Mu_list_gmap iterates over the LIST, gathering its elements in an
array of type void **. When NELEM elements has been collected, it
calls the MAP function, passing it as arguments the constructed array,
number of elements in it (can be less than NELEM on the last call),
and call-specific DATA. Iteration continues while MAP returns 0 and
until all elements from the array have been visited.
Mu_list_gmap returns 0 on success and a non-zero error code on failure.
If MAP returns !0, its return value is propagated to the caller.
*/
int mu_list_gmap (mu_list_t list, mu_list_mapper_t map, size_t nelem,
void *data);
#define MU_LIST_MAP_OK 0x00
#define MU_LIST_MAP_SKIP 0x01
#define MU_LIST_MAP_STOP 0x02
/* List-to-list mapping.
Apply the list mapping function MAP to each NELEM elements from the source
LIST and use its return values to form a new list, which will be returned
in RES.
MAP gets pointers to the collected elements in its first argument (ITMV).
Its second argument (ITMC) gives the number of elements filled in ARR.
It can be less than NELEM on the last call to MAP. The DATA pointer is
passed as the 3rd argument.
Return value from MAP governs the mapping process. Unless it has the
MU_LIST_MAP_SKIP bit set, the itmv[0] element is appended to the new
list. If it has MU_LIST_MAP_STOP bit set, iteration is stopped
immediately and any remaining elements in LIST are ignored.
*/
int mu_list_map (mu_list_t list, mu_list_mapper_t map,
void *data, size_t nelem,
mu_list_t *res);
#ifdef __cplusplus
}
#endif
......
......@@ -28,11 +28,13 @@ liblist_la_SOURCES = \
empty.c\
get.c\
getcomp.c\
gmap.c\
insert.c\
intersect.c\
iterator.c\
listlist.c\
locate.c\
map.c\
prepend.c\
remove.c\
replace.c\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010, 2011
Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <mailutils/errno.h>
#include <mailutils/sys/list.h>
int
mu_list_gmap (mu_list_t list, mu_list_mapper_t map, size_t nelem, void *data)
{
int rc;
int status;
mu_iterator_t itr;
void **buf;
size_t i;
if (!list || !map || nelem == 0)
return EINVAL;
rc = mu_list_get_iterator (list, &itr);
if (rc)
return status;
buf = calloc (nelem, sizeof (buf[0]));
if (!buf)
{
mu_iterator_destroy (&itr);
return ENOMEM;
}
for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
void *item;
mu_iterator_current (itr, &item);
buf[i++] = item;
if (i == nelem)
{
i = 0;
rc = map (buf, nelem, data);
if (rc)
break;
}
}
if (rc == 0 && i > 0 && i < nelem)
rc = map (buf, i, data);
mu_iterator_destroy (&itr);
free (buf);
return rc;
}
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010, 2011
Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <mailutils/errno.h>
#include <mailutils/sys/list.h>
struct map_closure
{
mu_list_mapper_t map;
void *data;
mu_list_t list;
int status;
};
static int
_list_mapper (void **itmv, size_t itmc, void *data)
{
struct map_closure *clos = data;
int rc, status;
if (!clos->list)
{
status = mu_list_create (&clos->list);
if (status)
{
clos->status = status;
return MU_ERR_FAILURE;
}
}
rc = clos->map (itmv, itmc, clos->data);
if (!(rc & MU_LIST_MAP_SKIP))
{
status = mu_list_append (clos->list, itmv[0]);
if (status)
{
clos->status = status;
return MU_ERR_FAILURE;
}
}
if (rc & MU_LIST_MAP_STOP)
return MU_ERR_CANCELED;
return 0;
}
int
mu_list_map (mu_list_t list, mu_list_mapper_t map, void *data, size_t nelem,
mu_list_t *res)
{
int rc;
struct map_closure cl;
if (!res)
return MU_ERR_OUT_PTR_NULL;
cl.map = map;
cl.data = data;
cl.list = NULL;
cl.status = 0;
rc = mu_list_gmap (list, _list_mapper, nelem, &cl);
if (cl.list)
*res = cl.list;
if (rc == MU_ERR_FAILURE)
return cl.status;
return 0;
}
......@@ -109,7 +109,7 @@ TESTLIST([get],[],
# ------------------------------------------------------------
m4_define([MU_TEST_GROUP],[Iterator])
m4_define([MU_TEST_KEYWORDS],MU_TEST_KEYWORDS[ iterator itr])
m4_pushdef([MU_TEST_KEYWORDS],MU_TEST_KEYWORDS[ iterator itr])
TESTLIST([forward],[],
[add en to tre fire fem
......@@ -246,6 +246,84 @@ cur
0:2:fire
])
m4_popdef([MU_TEST_KEYWORDS])
# ------------------------------------------------------------
# Maps
# ------------------------------------------------------------
m4_define([MU_TEST_GROUP],[Map])
m4_pushdef([MU_TEST_KEYWORDS],MU_TEST_KEYWORDS[ map])
TESTLIST([even],[],
[add null en to tre fire fem
map even
],
[# items: 3
null
to
fire
])
TESTLIST([odd],[],
[add null en to tre fire fem
map odd
],
[# items: 3
en
tre
fem
])
TESTLIST([odd0],[],
[add null
map odd
],
[# items: 0
])
TESTLIST([concat],[],
[add en to tre fire fem
map concat 2 -
],
[# items: 3
en-to
tre-fire
fem
])
TESTLIST([concat3],[],
[add en to tre fire fem
map concat 3 -
],
[# items: 2
en-to-tre
fire-fem
])
TESTLIST([skip],[],
[add null en to tre fire fem
map skip 3
],
[# items: 3
tre
fire
fem
])
TESTLIST([trim],[],
[add null en to tre fire fem
map trim 3
],
[# items: 3
null
en
to
])
m4_popdef([MU_TEST_KEYWORDS])
dnl ------------------------------------------------------------
dnl Cleanup
m4_popdef([TESTLIST])
......
......@@ -428,6 +428,197 @@ cur (int num, mu_iterator_t itr)
printf ("%s\n", text);
}
static int
map_even (void **itmv, size_t itmc, void *call_data)
{
int *num = call_data, n = *num;
*num = !*num;
if ((n % 2) == 0)
{
itmv[0] = strdup (itmv[0]);
return MU_LIST_MAP_OK;
}
return MU_LIST_MAP_SKIP;
}
static int
map_odd (void **itmv, size_t itmc, void *call_data)
{
int *num = call_data, n = *num;
*num = !*num;
if (n % 2)
{
itmv[0] = strdup (itmv[0]);
return MU_LIST_MAP_OK;
}
return MU_LIST_MAP_SKIP;
}
static int
map_concat (void **itmv, size_t itmc, void *call_data)
{
char *delim = call_data;
size_t dlen = strlen (delim);
size_t i;
size_t len = 0;
char *res, *p;
for (i = 0; i < itmc; i++)
len += strlen (itmv[i]);
len += (itmc - 1) * dlen + 1;
res = malloc (len);
if (!res)
abort ();
p = res;
for (i = 0; ; )
{
p = mu_stpcpy (p, itmv[i++]);
if (i == itmc)
break;
p = mu_stpcpy (p, delim);
}
itmv[0] = res;
return MU_LIST_MAP_OK;
}
struct trim_data
{
size_t n;
size_t lim;
};
static int
map_skip (void **itmv, size_t itmc, void *call_data)
{
struct trim_data *td = call_data;
if (td->n++ < td->lim)
return MU_LIST_MAP_SKIP;
itmv[0] = strdup (itmv[0]);
return MU_LIST_MAP_OK;
}
static int
map_trim (void **itmv, size_t itmc, void *call_data)
{
struct trim_data *td = call_data;
if (td->n++ < td->lim)
{
itmv[0] = strdup (itmv[0]);
return MU_LIST_MAP_OK;
}
return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP;
}
int
map (mu_list_t *plist, int argc, char **argv)
{
mu_list_t list = *plist;
mu_list_t result;
int rc;
int replace = 0;
if (argc > 1 && strcmp (argv[1], "-replace") == 0)
{
replace = 1;
argc--;
argv++;
}
if (argc < 2)
{
fprintf (stderr, "map [-replace] even|odd|concat|keep|trim\n");
return 0;
}
if (strcmp (argv[1], "even") == 0)
{
int n = 0;
rc = mu_list_map (list, map_even, &n, 1, &result);
}
else if (strcmp (argv[1], "odd") == 0)
{
int n = 0;
rc = mu_list_map (list, map_odd, &n, 1, &result);
}
else if (strcmp (argv[1], "concat") == 0)
{
size_t num;
char *delim = "";
if (argc < 3 || argc > 4)
{
fprintf (stderr, "map concat NUM [DELIM]?\n");
return 0;
}
num = atoi (argv[2]);
if (argc == 4)
delim = argv[3];
rc = mu_list_map (list, map_concat, delim, num, &result);
}
else if (strcmp (argv[1], "skip") == 0)
{
struct trim_data td;
if (argc < 3 || argc > 4)
{
fprintf (stderr, "map skip NUM?\n");
return 0;
}
td.n = 0;
td.lim = atoi (argv[2]);
rc = mu_list_map (list, map_skip, &td, 1, &result);
}
else if (strcmp (argv[1], "trim") == 0)
{
struct trim_data td;
if (argc < 3 || argc > 4)
{
fprintf (stderr, "map trim NUM?\n");
return 0;
}
td.n = 0;
td.lim = atoi (argv[2]);
rc = mu_list_map (list, map_trim, &td, 1, &result);
}
else
{
mu_error ("unknown map name\n");
return 0;
}
if (rc)
{
mu_error ("map failed: %s", mu_strerror (rc));
return 0;
}
mu_list_set_destroy_item (result, mu_list_free_item);
if (replace)
{
size_t count[2];
mu_list_count (list, &count[0]);
mu_list_count (result, &count[1]);
printf ("%lu in, %lu out\n", (unsigned long) count[0],
(unsigned long) count[1]);
mu_list_destroy (&list);
*plist = result;
return 1;
}
else
{
print (result);
mu_list_destroy (&result);
}
return 0;
}
void
help ()
{
......@@ -446,6 +637,7 @@ help ()
printf ("ictl repl item\n");
printf ("ictl ins item [item*]\n");
printf ("ictl dir [backwards|forwards]\n");
printf ("map NAME [ARGS]\n");
printf ("print\n");
printf ("quit\n");
printf ("iter num\n");
......@@ -517,6 +709,14 @@ shell (mu_list_t list)
print (list);
else if (strcmp (ws.ws_wordv[0], "cur") == 0)
cur (num, itr[num]);
else if (strcmp (ws.ws_wordv[0], "map") == 0)
{
int i;
if (map (&list, ws.ws_wordc, ws.ws_wordv))
for (i = 0; i < NITR; i++)
mu_iterator_destroy (&itr[i]);
}
else if (strcmp (ws.ws_wordv[0], "quit") == 0)
return;
else if (strcmp (ws.ws_wordv[0], "iter") == 0)
......@@ -591,7 +791,7 @@ main (int argc, char **argv)
while (argc--)
{
rc = mu_list_append (list, *argv++);
rc = mu_list_append (list, strdup (*argv++));
if (rc)
lperror ("mu_list_append", rc);
}
......