As I've mentioned before, I remain using Snownews in lieu of more modern maintained options because it actually works for me where the others don't; I do however check every now and again that is still the case.

One thing I don't like about Snownews is how easy it is for me to accidentally mark EVERYTHING as read and have no way to undo this. So for awhile I've had a "someday" action on my todo list to look at adding an undo functionality - it was "someday" because I presumed it would be difficult for me to do and would face me with the uncomfortable realisation that I'm not actually very good at programming whereas if I ignored it I could continue along in my happy ignorance.

As it turned out, it was remarkably easy to do and I even managed to get all the required changes right first time: Not even a compilation error! So now not only can I carry on in my ignorance - I've actually managed to reinforce it! Admittedly I've gone for a very simplistic "works ok for me" solution, but afterall that is all I need.

There are two relevant bits of code, depending on where in the UI you are. There's markread for within individual feeds:

if (uiinput == keybindings.markread) {
    /* Mark everything read. */
    for (markasread = current_feed->items; markasread != NULL; markasread = markasread->next_ptr) {
        markasread->data->readstatus = 1;
    }
}

And markallread for within the main index of all feeds:

if (uiinput == keybindings.markallread) {
    /* This function is safe for using in filter mode, because it only
       changes int values. It automatically marks the correct ones read
       if a filter is applied since we are using a copy of the main data. */
    for (cur_ptr = first_ptr; cur_ptr != NULL; cur_ptr = cur_ptr->next_ptr) {
        for (markasread = cur_ptr->items; markasread != NULL; markasread = markasread->next_ptr) {
            markasread->data->readstatus = 1;
        }
    }
}

So my cunning simple plan was to add another variable to the newsdata structure and backup the previous value in there. Shown here for the markread section, but applies similarly for markallread:

if (uiinput == keybindings.markread) {
    /* Mark everything read. */
    for (markasread = current_feed->items; markasread != NULL; markasread = markasread->next_ptr) {
        /* backup previous value */
        markasread->data->prevreadstatus = markasread->data->readstatus;
        markasread->data->readstatus = 1;
    }
}

Then restoring is as easy as adding a keybinding and reading the previous value:

if (uiinput == keybindings.undoread) {
    for (markasread = current_feed->items; markasread != NULL; markasread = markasread->next_ptr) {
        /* markasread is a bit of a naff name for the pointer here, but see no need to create new one */
        markasread->data->readstatus = markasread->data->prevreadstatus;
    }
}

The only downside is if I, for some idiotic reason, accidentally mark all as read twice in succession, but I happy with the compromise. I've put the patches in a gist on Github (with some unrelated minor keybinding changes muddled in - sorry).