autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete)
{
    list_T	*aucmd_list;
    listitem_T	*li;
    dict_T	*event_dict;
    dictitem_T	*di;
    char_u	*event_name = NULL;
    list_T	*event_list;
    listitem_T	*eli;
    event_T	event;
    char_u	*group_name = NULL;
    int		group;
    char_u	*pat = NULL;
    list_T	*pat_list;
    listitem_T	*pli;
    char_u	*cmd = NULL;
    char_u	*end;
    int		once;
    int		nested;
    int		replace;		// replace the cmd for a group/event
    int		retval = VVAL_TRUE;
    int		save_augroup = current_augroup;

    rettv->v_type = VAR_BOOL;
    rettv->vval.v_number = VVAL_FALSE;

    if (check_for_list_arg(argvars, 0) == FAIL)
	return;

    aucmd_list = argvars[0].vval.v_list;
    if (aucmd_list == NULL)
	return;

    FOR_ALL_LIST_ITEMS(aucmd_list, li)
    {
	VIM_CLEAR(group_name);
	VIM_CLEAR(cmd);
	event_name = NULL;
	event_list = NULL;
	pat = NULL;
	pat_list = NULL;

	if (li->li_tv.v_type != VAR_DICT)
	    continue;

	event_dict = li->li_tv.vval.v_dict;
	if (event_dict == NULL)
	    continue;

	di = dict_find(event_dict, (char_u *)"event", -1);
	if (di != NULL)
	{
	    if (di->di_tv.v_type == VAR_STRING)
	    {
		event_name = di->di_tv.vval.v_string;
		if (event_name == NULL)
		{
		    emsg(_(e_string_required));
		    continue;
		}
	    }
	    else if (di->di_tv.v_type == VAR_LIST)
	    {
		event_list = di->di_tv.vval.v_list;
		if (event_list == NULL)
		{
		    emsg(_(e_list_required));
		    continue;
		}
	    }
	    else
	    {
		emsg(_(e_string_or_list_expected));
		continue;
	    }
	}

	group_name = dict_get_string(event_dict, (char_u *)"group", TRUE);
	if (group_name == NULL || *group_name == NUL)
	    // if the autocmd group name is not specified, then use the current
	    // autocmd group
	    group = current_augroup;
	else
	{
	    group = au_find_group(group_name);
	    if (group == AUGROUP_ERROR)
	    {
		if (delete)
		{
		    semsg(_(e_no_such_group_str), group_name);
		    retval = VVAL_FALSE;
		    break;
		}
		// group is not found, create it now
		group = au_new_group(group_name);
		if (group == AUGROUP_ERROR)
		{
		    semsg(_(e_no_such_group_str), group_name);
		    retval = VVAL_FALSE;
		    break;
		}

		current_augroup = group;
	    }
	}

	// if a buffer number is specified, then generate a pattern of the form
	// "<buffer=n>. Otherwise, use the pattern supplied by the user.
	if (dict_has_key(event_dict, "bufnr"))
	{
	    varnumber_T	bnum;

	    bnum = dict_get_number_def(event_dict, (char_u *)"bufnr", -1);
	    if (bnum == -1)
		continue;

	    vim_snprintf((char *)IObuff, IOSIZE, "<buffer=%d>", (int)bnum);
	    pat = IObuff;
	}
	else
	{
	    di = dict_find(event_dict, (char_u *)"pattern", -1);
	    if (di != NULL)
	    {
		if (di->di_tv.v_type == VAR_STRING)
		{
		    pat = di->di_tv.vval.v_string;
		    if (pat == NULL)
		    {
			emsg(_(e_string_required));
			continue;
		    }
		}
		else if (di->di_tv.v_type == VAR_LIST)
		{
		    pat_list = di->di_tv.vval.v_list;
		    if (pat_list == NULL)
		    {
			emsg(_(e_list_required));
			continue;
		    }
		}
		else
		{
		    emsg(_(e_string_or_list_expected));
		    continue;
		}
	    }
	    else if (delete)
		pat = (char_u *)"";
	}

	once = dict_get_bool(event_dict, (char_u *)"once", FALSE);
	nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE);
	// if 'replace' is true, then remove all the commands associated with
	// this autocmd event/group and add the new command.
	replace = dict_get_bool(event_dict, (char_u *)"replace", FALSE);

	cmd = dict_get_string(event_dict, (char_u *)"cmd", TRUE);
	if (cmd == NULL)
	{
	    if (delete)
		cmd = vim_strsave((char_u *)"");
	    else
		continue;
	}

	if (delete && (event_name == NULL
		    || (event_name[0] == '*' && event_name[1] == NUL)))
	{
	    // if the event name is not specified or '*', delete all the events
	    for (event = (event_T)0; (int)event < NUM_EVENTS;
		    event = (event_T)((int)event + 1))
	    {
		if (do_autocmd_event(event, pat, once, nested, cmd, delete,
							group, 0) == FAIL)
		{
		    retval = VVAL_FALSE;
		    break;
		}
	    }
	}
	else
	{
	    char_u *p = NULL;

	    eli = NULL;
	    end = NULL;
	    while (TRUE)
	    {
		if (event_list != NULL)
		{
		    if (eli == NULL)
			eli = event_list->lv_first;
		    else
			eli = eli->li_next;
		    if (eli == NULL)
			break;
		    if (eli->li_tv.v_type != VAR_STRING
			    || (p = eli->li_tv.vval.v_string) == NULL)
		    {
			emsg(_(e_string_required));
			break;
		    }
		}
		else
		{
		    if (p == NULL)
			p = event_name;
		    if (p == NULL || *p == NUL)
			break;
		}

		event = event_name2nr(p, &end);
		if (event == NUM_EVENTS || *end != NUL)
		{
		    // this also catches something following a valid event name
		    semsg(_(e_no_such_event_str), p);
		    retval = VVAL_FALSE;
		    break;
		}
		if (pat != NULL)
		{
		    if (do_autocmd_event(event, pat, once, nested, cmd,
				delete | replace, group, 0) == FAIL)
		    {
			retval = VVAL_FALSE;
			break;
		    }
		}
		else if (pat_list != NULL)
		{
		    FOR_ALL_LIST_ITEMS(pat_list, pli)
		    {
			if (pli->li_tv.v_type != VAR_STRING
				|| pli->li_tv.vval.v_string == NULL)
			{
			    emsg(_(e_string_required));
			    continue;
			}
			if (do_autocmd_event(event,
				    pli->li_tv.vval.v_string, once, nested,
				    cmd, delete | replace, group, 0) ==
				FAIL)
			{
			    retval = VVAL_FALSE;
			    break;
			}
		    }
		    if (retval == VVAL_FALSE)
			break;
		}
		if (event_name != NULL)
		    p = end;
	    }
	}

	// if only the autocmd group name is specified for delete and the
	// autocmd event, pattern and cmd are not specified, then delete the
	// autocmd group.
	if (delete && group_name != NULL &&
		(event_name == NULL || event_name[0] == NUL)
		&& (pat == NULL || pat[0] == NUL)
		&& (cmd == NULL || cmd[0] == NUL))
	    au_del_group(group_name);
    }

    VIM_CLEAR(group_name);
    VIM_CLEAR(cmd);

    current_augroup = save_augroup;
    rettv->vval.v_number = retval;
}