patch 8.2.0861: cannot easily get all the current marks

Problem:    Cannot easily get all the current marks.
Solution:   Add getmarklist(). (Yegappan Lakshmanan, closes #6032)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index fc8b044..f40a2ee 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -579,6 +579,7 @@
     {"getjumplist",	0, 2, FEARG_1,	  ret_list_any,	f_getjumplist},
     {"getline",		1, 2, FEARG_1,	  ret_f_getline, f_getline},
     {"getloclist",	1, 2, 0,	  ret_list_dict_any, f_getloclist},
+    {"getmarklist",	0, 1, 0,	  ret_list_dict_any,  f_getmarklist},
     {"getmatches",	0, 1, 0,	  ret_list_dict_any, f_getmatches},
     {"getmousepos",	0, 0, 0,	  ret_dict_number, f_getmousepos},
     {"getpid",		0, 0, 0,	  ret_number,	f_getpid},
diff --git a/src/mark.c b/src/mark.c
index e1dbe3a..ca64c90 100644
--- a/src/mark.c
+++ b/src/mark.c
@@ -1412,3 +1412,124 @@
 {
     return namedfm;
 }
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Add information about mark 'mname' to list 'l'
+ */
+    static int
+add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
+{
+    dict_T	*d;
+    list_T	*lpos;
+
+    if (pos->lnum <= 0)
+	return OK;
+
+    d = dict_alloc();
+    if (d == NULL)
+	return FAIL;
+
+    if (list_append_dict(l, d) == FAIL)
+    {
+	dict_unref(d);
+	return FAIL;
+    }
+
+    lpos = list_alloc();
+    if (lpos == NULL)
+	return FAIL;
+
+    list_append_number(lpos, bufnr);
+    list_append_number(lpos, pos->lnum);
+    list_append_number(lpos, pos->col);
+    list_append_number(lpos, pos->coladd);
+
+    if (dict_add_string(d, "mark", mname) == FAIL
+	    || dict_add_list(d, "pos", lpos) == FAIL
+	    || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
+	return FAIL;
+
+    return OK;
+}
+
+/*
+ * Get information about marks local to a buffer.
+ */
+    static void
+get_buf_local_marks(buf_T *buf, list_T *l)
+{
+    char_u	mname[3] = "' ";
+    int		i;
+
+    // Marks 'a' to 'z'
+    for (i = 0; i < NMARKS; ++i)
+    {
+	mname[1] = 'a' + i;
+	add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
+    }
+
+    // Mark '' is a window local mark and not a buffer local mark
+    add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
+
+    add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
+}
+
+/*
+ * Get information about global marks ('A' to 'Z' and '0' to '9')
+ */
+    static void
+get_global_marks(list_T *l)
+{
+    char_u	mname[3] = "' ";
+    int		i;
+    char_u	*name;
+
+    // Marks 'A' to 'Z' and '0' to '9'
+    for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
+    {
+	if (namedfm[i].fmark.fnum != 0)
+	    name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
+	else
+	    name = namedfm[i].fname;
+	if (name != NULL)
+	{
+	    mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
+	    add_mark(l, mname, &namedfm[i].fmark.mark,
+		    namedfm[i].fmark.fnum, name);
+	    if (namedfm[i].fmark.fnum != 0)
+		vim_free(name);
+	}
+    }
+}
+
+/*
+ * getmarklist() function
+ */
+    void
+f_getmarklist(typval_T *argvars, typval_T *rettv)
+{
+    buf_T	*buf = NULL;
+
+    if (rettv_list_alloc(rettv) != OK)
+	return;
+
+    if (argvars[0].v_type == VAR_UNKNOWN)
+    {
+	get_global_marks(rettv->vval.v_list);
+	return;
+    }
+
+    buf = tv_get_buf(&argvars[0], FALSE);
+    if (buf == NULL)
+	return;
+
+    get_buf_local_marks(buf, rettv->vval.v_list);
+}
+#endif
diff --git a/src/proto/mark.pro b/src/proto/mark.pro
index a28fa9a..cc45f0d 100644
--- a/src/proto/mark.pro
+++ b/src/proto/mark.pro
@@ -27,4 +27,5 @@
 void set_last_cursor(win_T *win);
 void free_all_marks(void);
 xfmark_T *get_namedfm(void);
+void f_getmarklist(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_marks.vim b/src/testdir/test_marks.vim
index fcae2ac..ffd50a1 100644
--- a/src/testdir/test_marks.vim
+++ b/src/testdir/test_marks.vim
@@ -259,4 +259,29 @@
   call delete('Xtwo')
 endfunc
 
+" Test for the getmarklist() function
+func Test_getmarklist()
+  new
+  " global marks
+  delmarks A-Z 0-9 \" ^.[]
+  call assert_equal([], getmarklist())
+  call setline(1, ['one', 'two', 'three'])
+  mark A
+  call cursor(3, 5)
+  normal mN
+  call assert_equal([{'file' : '', 'mark' : "'A", 'pos' : [bufnr(), 1, 0, 0]},
+        \ {'file' : '', 'mark' : "'N", 'pos' : [bufnr(), 3, 4, 0]}],
+        \ getmarklist())
+  " buffer local marks
+  delmarks!
+  call assert_equal([{'mark' : "''", 'pos' : [bufnr(), 1, 0, 0]},
+        \ {'mark' : "'\"", 'pos' : [bufnr(), 1, 0, 0]}], getmarklist(bufnr()))
+  call cursor(2, 2)
+  normal mr
+  call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 1, 0]},
+        \ getmarklist(bufnr())[0])
+  call assert_equal([], getmarklist({}))
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index f63cb0c..d1ebe5b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    861,
+/**/
     860,
 /**/
     859,