/* Implementation for "cvs watch add", "cvs watchers", and related commands

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program 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 General Public License for more details.  */

#include "cvs.h"
#include "edit.h"
#include "fileattr.h"
#include "watch.h"

const char *const watch_usage[] =
{
    "Usage: %s %s [on|off|ro|rw|add|remove] [-lR] [-a action] [files...]\n",
    "on/off/readonly/readwrite: turn on/off read-only checkouts of files\n",
    "add/remove: add or remove notification on actions\n",
    "-l (on/off/ro/rw/add/remove): Local directory only, not recursive\n",
    "-R (on/off/ro/rw/add/remove): Process directories recursively\n",
    "-a (add/remove): Specify what actions, one of\n",
    "    edit,unedit,commit,all,none\n",
    "(Specify the --help global option for a list of other help options)\n",
    NULL
};

static struct addremove_args the_args;

void watch_modify_watchers (const char *file, const char *who, struct addremove_args *what)
{
	XmlHandle_t filehandle;
	
	TRACE(3,"watch_modify_watchers(%s,%s)",PATCH_NULL(file),PATCH_NULL(who));
	if(file)
		filehandle = fileattr_find(NULL,"file[@name=F'%s']/watcher[@name=U'%s']",file,who);
	else
		filehandle = fileattr_find(NULL,"directory/default/watcher[@name=U'%s']",who);

	if(!filehandle && !what->adding)
		return;
	if(!filehandle)
	{
		if(file)
			filehandle= fileattr_create(NULL,"file[@name=F'%s']/watcher[@name=U'%s']",file,who);
		else
			filehandle= fileattr_create(NULL,"directory/default/watcher[@name=U'%s']",who);
	}
	if(!filehandle)
		error(0,0,"Couldn't create node in modify_watchers");
	
	if(!what->adding)
	{
		if(what->edit)
			fileattr_delete(filehandle,"edit");
		if(what->commit)
			fileattr_delete(filehandle,"commit");
		if(what->unedit)
			fileattr_delete(filehandle,"unedit");
		if(what->remove_temp)
		{
			fileattr_delete(filehandle,"temp_edit");
			fileattr_delete(filehandle,"temp_commit");
			fileattr_delete(filehandle,"temp_unedit");
		}
		fileattr_prune(filehandle);
	}
	else
	{
		if(what->edit)
			fileattr_create(filehandle,"edit");
		if(what->commit)
			fileattr_create(filehandle,"commit");
		if(what->unedit)
			fileattr_create(filehandle,"unedit");
		if(what->add_tedit)
			fileattr_create(filehandle,"temp_edit");
		if(what->add_tcommit)
			fileattr_create(filehandle,"temp_commit");
		if(what->add_tunedit)
			fileattr_create(filehandle,"temp_unedit");
	}
}

static int addremove_fileproc(void *callerdat,
				      struct file_info *finfo)
{
    watch_modify_watchers (finfo->file, CVS_Username, &the_args);
    return 0;
}

static int addremove_filesdoneproc (void *callerdat, int err, char *repository,
    char *update_dir, List *entries)
{
    if (the_args.setting_default)
		watch_modify_watchers (NULL, CVS_Username, &the_args);
    return err;
}

static int watch_addremove(int argc, char **argv)
{
    int c;
    int local = 0;
    int err;
    int a_omitted;

    a_omitted = 1;
    the_args.commit = 0;
    the_args.edit = 0;
    the_args.unedit = 0;
    optind = 0;
    while ((c = getopt (argc, argv, "+lRa:")) != -1)
    {
	switch (c)
	{
	    case 'l':
		local = 1;
		break;
	    case 'R':
		local = 0;
		break;
	    case 'a':
		a_omitted = 0;
		if (strcmp (optarg, "edit") == 0)
		    the_args.edit = 1;
		else if (strcmp (optarg, "unedit") == 0)
		    the_args.unedit = 1;
		else if (strcmp (optarg, "commit") == 0)
		    the_args.commit = 1;
		else if (strcmp (optarg, "all") == 0)
		{
		    the_args.edit = 1;
		    the_args.unedit = 1;
		    the_args.commit = 1;
		}
		else if (strcmp (optarg, "none") == 0)
		{
		    the_args.edit = 0;
		    the_args.unedit = 0;
		    the_args.commit = 0;
		}
		else
		    usage (watch_usage);
		break;
	    case '?':
	    default:
		usage (watch_usage);
		break;
	}
    }
    argc -= optind;
    argv += optind;

    if (a_omitted)
    {
	the_args.edit = 1;
	the_args.unedit = 1;
	the_args.commit = 1;
    }

    if (current_parsed_root->isremote)
    {
	if (local)
	    send_arg ("-l");
	/* FIXME: copes poorly with "all" if server is extended to have
	   new watch types and client is still running an old version.  */
	if (the_args.edit)
	{
	    send_arg ("-a");
	    send_arg ("edit");
	}
	if (the_args.unedit)
	{
	    send_arg ("-a");
	    send_arg ("unedit");
	}
	if (the_args.commit)
	{
	    send_arg ("-a");
	    send_arg ("commit");
	}
	if (!the_args.edit && !the_args.unedit && !the_args.commit)
	{
	    send_arg ("-a");
	    send_arg ("none");
	}
	send_arg("--");
	send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
	send_file_names (argc, argv, SEND_EXPAND_WILD);
	send_to_server (the_args.adding ?
                        "watch-add\n" : "watch-remove\n",
                        0);
	return get_responses_and_close ();
    }

    the_args.setting_default = (argc <= 0);

    lock_tree_for_write (argc, argv, local, W_LOCAL, 0);

    err = start_recursion (addremove_fileproc, addremove_filesdoneproc,
			   (PREDIRENTPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
			   argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, NULL,
			   1, verify_write);

    Lock_Cleanup ();
    return err;
}

int watch_add(int argc, char **argv)
{
    the_args.adding = 1;
    return watch_addremove (argc, argv);
}

int watch_remove (int argc, char **argv)
{
    the_args.adding = 0;
    return watch_addremove (argc, argv);
}

int watch (int argc, char **argv)
{
    if (argc <= 1)
	usage (watch_usage);
    if (!strcmp(argv[1], "on") || !strcmp(argv[1],"ro"))
    {
	--argc;
	++argv;
	return watch_on (argc, argv);
    }
    else if(!strcmp(argv[1], "off") || !strcmp(argv[1],"rw"))
    {
	--argc;
	++argv;
	return watch_off (argc, argv);
    }
    else if (strcmp (argv[1], "add") == 0)
    {
	--argc;
	++argv;
	return watch_add (argc, argv);
    }
    else if (strcmp (argv[1], "remove") == 0)
    {
	--argc;
	++argv;
	return watch_remove (argc, argv);
    }
    else
	usage (watch_usage);
    return 0;
}

static const char *const watchers_usage[] =
{
    "Usage: %s %s [-lR] [files...]\n",
    "\t-l\tProcess this directory only (not recursive).\n",
    "\t-R\tProcess directories recursively.\n",
    "(Specify the --help global option for a list of other help options)\n",
    NULL
};

static int watchers_fileproc (void *callerdat, struct file_info *finfo)
{
	XmlHandle_t handle;
	const char *name;

	handle = fileattr_find(NULL,"file[@name=F'%s']/watcher",finfo->file);

	if(!handle)
		return 0;

    cvs_output (fn_root(finfo->fullname), 0);

    while (handle)
    {
		cvs_output ("\t", 1);
		name=fileattr_getvalue(handle,"@name");
		cvs_output(name,0);
		if(fileattr_find(handle,"edit"))
			cvs_output("\tedit",0);
		if(fileattr_find(handle,"unedit"))
			cvs_output("\tunedit",0);
		if(fileattr_find(handle,"commit"))
			cvs_output("\tcommit",0);
		if(fileattr_find(handle,"temp_edit"))
			cvs_output("\ttedit",0);
		if(fileattr_find(handle,"temp_unedit"))
			cvs_output("\ttunedit",0);
		if(fileattr_find(handle,"temp_commit"))
			cvs_output("\ttcommit",0);
	    cvs_output ("\n", 1);
		handle = fileattr_next(handle);
	}

    return 0;
}

int watchers (int argc, char **argv)
{
    int local = 0;
    int c;

    if (argc == -1)
	usage (watchers_usage);

    optind = 0;
    while ((c = getopt (argc, argv, "+lR")) != -1)
    {
	switch (c)
	{
	    case 'l':
		local = 1;
		break;
	    case 'R':
		local = 0;
		break;
	    case '?':
	    default:
		usage (watchers_usage);
		break;
	}
    }
    argc -= optind;
    argv += optind;

    if (current_parsed_root->isremote)
    {
	if (local)
	    send_arg ("-l");
	send_arg("--");
	send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
	send_file_names (argc, argv, SEND_EXPAND_WILD);
	send_to_server ("watchers\n", 0);
	return get_responses_and_close ();
    }

    return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL,
			    (PREDIRENTPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
			    argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, NULL,
			    1, verify_write);
}


syntax highlighted by Code2HTML, v. 0.9.1