/* This file is part of the Project Athena Zephyr Notification System.
 * It contains code for the "zctl" command.
 *
 *	Created by:	Robert French
 *
 *	Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h". 
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sysdep.h>
#include <zephyr/zephyr.h>
#include <ss/ss.h>
#include <com_err.h>
#include <pwd.h>
#include <netdb.h>
#include <errno.h>

RCSID("$Id: zctl.c,v 1.14 2000/09/27 18:27:50 assar Exp $");

#include "zutils.h"

#define USERS_SUBS "/.zephyr.subs"
#define OLD_SUBS "/.subscriptions"

#define	ERR		(-1)
#define	NOT_REMOVED	0
#define	REMOVED		1

int sci_idx;
char subsname[BUFSIZ];

extern ss_request_table zctl_cmds;

#define SS_FUNCTION(x) void (x) (int argc, char *argv[])
SS_FUNCTION(set_file);
SS_FUNCTION(flush_locations);
SS_FUNCTION(wgc_control);
SS_FUNCTION(hm_control);
SS_FUNCTION(show_var);
SS_FUNCTION(set_var);
SS_FUNCTION(unset_var);
SS_FUNCTION(do_hide);
SS_FUNCTION(cancel_subs);
SS_FUNCTION(show_realm);
SS_FUNCTION(subscribe);
SS_FUNCTION(sub_file);
SS_FUNCTION(load_subs);
SS_FUNCTION(loadall);
SS_FUNCTION(current);

#undef SS_FUNCTION

void add_file (char *fn, char *realm, short wgport, 
	       ZSubscription_t *subs, int mode);
void del_file (char *fn, char *realm, short wgport,
	       ZSubscription_t *subs, int mode);
int make_exist(char *filename);
char *filename;
int purge_subs(char *fn, ZSubscription_t *subs, int mode);;




int
main(int argc, char **argv)
{
    struct passwd *pwd;
    char ssline[BUFSIZ],oldsubsname[BUFSIZ],*envptr,*tty = NULL;
    int retval, code, i;
    
    if ((retval = ZInitialize()) != ZERR_NONE) {
	com_err(argv[0],retval,"while initializing");
	exit (1);
    }
    
    /* Set hostname and tty for locations.  If we support X, use the
     * DISPLAY environment variable for the tty name. */
#ifndef X_DISPLAY_MISSING
    tty = getenv("DISPLAY");
#endif	
    if ((retval = ZInitLocationInfo(NULL, tty)) != ZERR_NONE)
	com_err(argv[0], retval, "initializing location information");
    
    envptr = getenv("ZEPHYR_SUBS");
    if (envptr)
	strcpy(subsname,envptr);
    else {
	envptr = getenv("HOME");
	if (envptr)
	    strcpy(subsname,envptr);
	else {
	    if (!(pwd = getpwuid((int) getuid()))) {
		fprintf(stderr,"Who are you?\n");
		exit (1);
	    }
	    
	    strcpy(subsname,pwd->pw_dir);
	}
	strcpy(oldsubsname,subsname);
	strcat(oldsubsname,OLD_SUBS);
	strcat(subsname,USERS_SUBS);
	if (!access(oldsubsname,F_OK) && access(subsname, F_OK)) {
	    /* only if old one exists and new one does not exist */
	    printf("The .subscriptions file in your home directory "
		   "is now being used as\n.zephyr.subs . I will rename "
		   "it to .zephyr.subs for you.\n");
	    if (rename(oldsubsname,subsname))
		com_err(argv[0], errno, "renaming .subscriptions");
	}
    }
    
    zutil_init("zctl");
    
    sci_idx = ss_create_invocation("zctl","",0,&zctl_cmds,&code);
    if (code) {
	ss_perror(sci_idx,code,"while creating invocation");
	exit(1);
    }
    
    if (argc > 1) {
	*ssline = '\0';
	for (i=1;i<argc;i++)
	    (void) sprintf(ssline+strlen(ssline),"%s ",argv[i]);
	ssline[strlen(ssline)-1] = '\0';
	code = ss_execute_line(sci_idx,ssline);
	if (code)
	    fprintf (stderr, "%s: %s: %s\n",
		     argv[0], error_message (code), ssline);
	exit((code != 0));
    } 
    
    printf("ZCTL $Revision: 1.14 $ (Protocol %s%d.%d) - "
	   "Type '?' for a list of commands.\n\n",
	   ZVERSIONHDR,
	   ZVERSIONMAJOR,ZVERSIONMINOR_REALM);
    
    ss_listen(sci_idx);
    exit(0);
}

void
set_file(int argc, char **argv)
{
    if (argc > 2) {
	fprintf(stderr,"Usage: %s filename\n",argv[0]);
	return;
    }
    
    if (argc == 1)
	printf("Current file: %s\n",subsname);
    else
	(void) strcpy(subsname,argv[1]);
}

void
flush_locations(int argc, char **argv)
{
    int retval;
    char *realm;
    int cnt, i;
	
    if (argc > 2) {
	fprintf(stderr,"Usage: %s [realm]\n",argv[0]);
	return;
    }

    realm = (argc > 1)?argv[1]:NULL;
    
    if (realm && strcmp(realm, "*") == 0) {
	retval = ZGetRealmCount(&cnt);
	if (retval) {
	    ss_perror(sci_idx, retval, "while getting realm count");
	    return;
	}
	
	for (i=0; i<cnt; i++) {
	    retval = ZGetRealmName(i, &realm);
	    if (retval) {
		ss_perror(sci_idx, retval, "while getting realm name");
		return;
	    }
	    
	    retval = ZFlushMyLocations(realm);
	    if (retval != ZERR_NONE) {
		ss_perror(sci_idx, retval, "while flushing locations");
		return;
	    }
	}
    } else {
	if ((retval = ZFlushMyLocations(realm)) != ZERR_NONE) {
	    ss_perror(sci_idx, retval, "while flushing locations");
	    return;
	}
    }
}

void
wgc_control(int argc, char **argv)
{
    Code_t retval;
    short newport;
    struct sockaddr_in newsin;
    ZNotice_t notice;

    if (argc > 1) {
	fprintf(stderr,"Usage: %s\n",argv[0]);
	return;
    }

    if ((newport = ZGetWGPort()) == -1) {
	ss_perror(sci_idx,errno,"while getting WindowGram port");
	return;
    }

    newsin.sin_port = newport;
    retval = ZSetDestAddr(&newsin);
    if (retval != ZERR_NONE) {
	ss_perror(sci_idx,retval,"while setting destination address");
	return;
    }

    (void) memset((char *)&notice, 0, sizeof(notice));
    notice.z_kind = UNSAFE;
    notice.z_port = 0;
    notice.z_class = WG_CTL_CLASS;
    notice.z_class_inst = WG_CTL_USER;

    if (!strcmp(argv[0],"wg_read"))
	notice.z_opcode = USER_REREAD;
    if (!strcmp(argv[0],"wg_shutdown"))
	notice.z_opcode = USER_SHUTDOWN;
    if (!strcmp(argv[0],"wg_startup"))
	notice.z_opcode = USER_STARTUP;
    if (!strcmp(argv[0],"wg_exit"))
	notice.z_opcode = USER_EXIT;
    if (!notice.z_opcode) {
	fprintf(stderr,
		"unknown WindowGram client control command %s\n",
		argv[0]);
	return;
    }

    if (retval)
	ss_perror(sci_idx, retval,
		  "while sending WindowGram control message");
}

void
hm_control(int argc,char **argv)
{
    int retval;
    ZNotice_t notice;
    char *realm;
    int cnt, i;

    if (argc > 2) {
	fprintf(stderr,"Usage: %s [realm]\n",argv[0]);
	return;
    }
	
    realm = (argc > 1)?argv[1]:NULL;

    (void) memset((char *)&notice, 0, sizeof(notice));
    notice.z_kind = HMCTL;
    notice.z_port = 0;
    notice.z_class = HM_CTL_CLASS;
    notice.z_class_inst = HM_CTL_CLIENT;

    if (!strcmp(argv[0],"hm_flush")) {
#ifdef HM_FLUSH_RESTRICT
	if (getuid() != 0) {
            char s[2];
            
            if (!isatty(0)) {
		fprintf(stderr, "hm_flush is intended to be run interactively.\n");
		return;
	    }
	    printf("hm_flush is intended for use only on single-user machines.\n"
		   "Please confirm this is a single-user machine? [n] ");
	    fgets(s, 2, stdin);
	    if (strncasecmp(s,"y",1) != 0)
		return;
	}
#endif /* HM_FLUSH_RESTRICT */
	notice.z_opcode = CLIENT_FLUSH;
    }

    if (!strcmp(argv[0],"new_server"))
	notice.z_opcode = CLIENT_NEW_SERVER;
    if (!notice.z_opcode) {
	fprintf(stderr, "unknown HostManager control command %s\n",
		argv[0]);
	return;
    }
    notice.z_sender = 0;
    notice.z_recipient = "";
    notice.z_default_format = "";
    notice.z_message_len = 0;

    if (realm && strcmp(realm, "*") == 0) {
	retval = ZGetRealmCount(&cnt);
	if (retval) {
	    ss_perror(sci_idx, retval, "while getting realm count");
	    return;
	}

	for (i=0; i<cnt; i++) {
	    retval = ZGetRealmName(i, &realm);
	    if (retval) {
		ss_perror(sci_idx, retval, "while getting realm name");
		return;
	    }

	    notice.z_dest_realm = realm;

	    retval = ZSendNotice(&notice,ZNOAUTH);
	    if (retval != ZERR_NONE) {
		ss_perror(sci_idx,retval,"while sending notice");
		return;
	    }
	}
    } else {
	retval = ZSendNotice(&notice,ZNOAUTH);
	if (retval != ZERR_NONE) {
	    ss_perror(sci_idx,retval,"while sending notice");
	    return;
	}
    }
} 

void
show_var(int argc, char **argv)
{
    int i;
    char *value;
	
    if (argc < 2) {
	fprintf(stderr,"Usage: %s <varname> <varname> ...\n",argv[0]);
	return;
    }

    for (i=1;i<argc;i++) {
	value = ZGetVariable(argv[i]);
	if (value)
	    printf("%s: %s\n",argv[i],value);
	else
	    printf("%s: not defined\n",argv[i]);
    }
}

void
show_realm (int argc, char **argv)
{
    if (argc != 1)
	fprintf (stderr, "ignoring extra arguments\n");

    printf ("Default realm is: `%s'.\n", ZGetDefaultRealm());
}

void
set_var(int argc,char **argv)
{
    int retval,setting_exp,i;
    char *realm;
    char varcat[BUFSIZ];
	
    if (argc < 2) {
	fprintf(stderr,"Usage: %s <varname> [value]\n",
		argv[0]);
	return;
    }

    setting_exp = 0;

    if (strncasecmp(argv[1],"exposure",8) == 0) {
	setting_exp = 1;
	if (argc != 3) {
	    fprintf(stderr, "An exposure setting must be specified.\n");
	    return;
	}
	if (strlen(argv[1]) == 8) {
	    realm = NULL; /* the default */
	} else if ((strlen(argv[1]) == 9) ||
		   (ZGetRhs(argv[1]+9) == NULL)) {
	    fprintf(stderr, "The exposure variable's realm name was "
		    "empty or invalid.\nUse a variable of the "
		    "form exposure-REALMNAME.\n");
	    return;
	} else {
	    realm = argv[1]+9;
	}
    } 

    if (argc == 2)
	retval = ZSetVariable(argv[1],"");
    else {
	(void) strcpy(varcat,argv[2]);
	for (i=3;i<argc;i++) {
	    (void) strcat(varcat," ");
	    (void) strcat(varcat,argv[i]);
	} 
	retval = ZSetVariable(argv[1],varcat);
    } 

    if (retval != ZERR_NONE) {
	ss_perror(sci_idx,retval,"while setting variable value");
	return;
    }

    /* Side-effects?  Naw, us? */
	
    if (setting_exp)
	set_exposure(realm?realm:"*", argv[2]);
}

void
do_hide(int argc, char **argv)
{
    char *exp;
    char *realm;
    Code_t code;

    if (argc > 2) {
	fprintf(stderr, "Usage: %s [realm]\n",argv[0]);
	return;
    }

    if (strcmp(argv[0], "unhide") == 0)
	exp = ZGetVariable("exposure");
    else
	exp = EXPOSE_OPSTAFF;

    realm = (argc > 1) ? argv[1] : "*";

    code = set_exposure(realm, exp);
    if (code) {
	ss_perror(sci_idx, code, "while setting exposures");
	return;
    }
}

void
unset_var(int argc, char **argv)
{
    int retval,i;
    
    if (argc < 2) {
	fprintf(stderr,"Usage: %s <varname> [<varname> ... ]\n",
		argv[0]);
	return;
    }
    
    for (i=1;i<argc;i++) {
	retval = ZUnsetVariable(argv[i]);
	if (retval != ZERR_NONE) 
	    ss_perror(sci_idx,retval, "while unsetting variable value");
    }
}

void
cancel_subs(int argc, char **argv)
{
    int retval;
    short wgport;
    int i, cnt;
    char *realm;

    if (argc > 2) {
	fprintf(stderr,"Usage: %s [realm]\n",argv[0]);
	return;
    }

    if ((wgport = ZGetWGPort()) == -1) {
	ss_perror(sci_idx,errno,"while finding WindowGram port");
	return;
    } 

    realm = (argc > 1)?argv[1]:NULL;

    if (realm && strcmp(realm, "*") == 0) {
	retval = ZGetRealmCount(&cnt);
	if (retval) {
	    ss_perror(sci_idx, retval, "while getting realm count");
	    return;
	}

	for (i=0; i<cnt; i++) {
	    retval = ZGetRealmName(i, &realm);
	    if (retval) {
		ss_perror(sci_idx, retval, "while getting realm name");
		return;
	    }

	    retval = ZCancelSubscriptions(realm, (u_short)wgport);
	    if (retval != ZERR_NONE) {
		ss_perror(sci_idx,retval,"while cancelling subscriptions");
		return;
	    }
	}
    } else {
	retval = ZCancelSubscriptions(realm, (u_short)wgport);
	if (retval != ZERR_NONE) {
	    ss_perror(sci_idx,retval,"while cancelling subscriptions");
	    return;
	}
    }
}

void
subscribe(int argc, char **argv)
{
    int retval;
    short wgport;
    ZSubscription_t sub,sub2;
    char *realm;
    int mode;
    
    if (argc > 5 || argc < 3) {
	fprintf(stderr,"Usage: %s class instance [* [realm]]\n",
		argv[0]);
	return;
    }
    
    if (strncmp(argv[0], "sub", 3) == 0) {
	mode = SUB;
    } else if (strncmp(argv[0], "unsub", 5) == 0) {
	mode = UNSUB;
    } else if ((strncmp(argv[0], "punt", 4) == 0) ||
	       (strncmp(argv[0], "sup", 3) == 0)) {
	mode = PUNT;
    } else if ((strncmp(argv[0], "unpunt", 6) == 0) ||
	       (strncmp(argv[0], "unsup", 5) == 0)) {
	mode = UNPUNT;
    } else {
	ss_perror(sci_idx, 0, "internal error in subscribe");
	exit(1);
    }
    
    sub.zsub_class = argv[1];
    sub.zsub_classinst = argv[2];
    sub.zsub_recipient = (argc > 3)?argv[3]:
	(((mode == PUNT) || (mode == UNPUNT))?"":ZGetSender());
    realm = (argc > 4)?argv[4]:NULL;
    
    zutil_fix_macros(&sub,&sub2,1);
    
    if ((wgport = ZGetWGPort()) == -1) {
	ss_perror(sci_idx,errno,"while finding WindowGram port");
	return;
    } 
    
    switch (mode) {
    case SUB:
	retval = ZSubscribeTo(realm, &sub2, 1, (u_short) wgport);
	if (retval)
	    ss_perror(sci_idx, retval, "while subscribing");
	break;
    case UNSUB:
	retval = ZUnsubscribeTo(realm, &sub2, 1, (u_short) wgport);
	if (retval)
	    ss_perror(sci_idx, retval, "while unsubscribing");
	break;
    case PUNT:
	retval = xpunt(sub2.zsub_class, sub2.zsub_classinst,
		       sub2.zsub_recipient, PUNT);
	if (retval)
	    ss_perror(sci_idx, retval, "while suppressing");
	break;
    case UNPUNT:
	retval = xpunt(sub2.zsub_class, sub2.zsub_classinst,
		       sub2.zsub_recipient, UNPUNT);
	if (retval)
	    ss_perror(sci_idx, retval, "while unsuppressing");
	break;
    }
} 

void
sub_file(int argc, char **argv)
{
    ZSubscription_t sub;
    short wgport;
    char fn[MAXPATHLEN];
    char *argrealm, *realm;
    int cnt, i;
    Code_t retval;
    int del, mode;

    if (argc > 5 || argc < 3) {
	fprintf(stderr,"Usage: %s class instance [* [realm]]\n",
		argv[0]);
	return;
    }

    if (!strcmp(argv[0],"add")) {
	del = 0;
	mode = SUB;
    } else if (!strcmp(argv[0],"add_unsubscription") ||
	       !strcmp(argv[0],"add_un")) {
	del = 0;
	mode = UNSUB;
    } else if (!strcmp(argv[0],"add_suppression") ||
	       !strcmp(argv[0],"add_punt")) {
	del = 0;
	mode = PUNT;
    } else if (!strcmp(argv[0],"delete") ||
	       !strcmp(argv[0],"del") ||
	       !strcmp(argv[0],"dl")) {
	del = 1;
	mode = SUB;
    } else if (!strcmp(argv[0],"delete_unsubscription") ||
	       !strcmp(argv[0],"del_un")) {
	del = 1;
	mode = UNSUB;
    } else if (!strcmp(argv[0],"delete_suppression") ||
	       !strcmp(argv[0],"del_punt")) {
	del = 1;
	mode = PUNT;
    } else {
	ss_perror(sci_idx,0,"unknown command name");
    }

    if (argv[1][0] == '!') {
	ss_perror(sci_idx,0, (mode == UNSUB)?
		  "Do not use `!' as the first character of a class.\n"
		  "\tIt is automatically added before modifying the "
		  "subscription file." :
		  "Do not use `!' as the first character of a class.\n\t"
		  "It is reserved for internal use with un-subscriptions.");
	return;
    } else if (argv[1][0] == '-') {
	ss_perror(sci_idx,0, (mode == PUNT)?
		  "Do not use `-' as the first character of a class.\n\t"
		  "It is automatically added before modifying "
		  "the subscription file." :
		  "Do not use `-' as the first character of a class.\n\t"
		  "It is reserved for internal use "
		  "with suppressions.");
	return;
    }

    sub.zsub_class = argv[1];
    sub.zsub_classinst = argv[2];
    sub.zsub_recipient = (argc > 3)?argv[3]:
	(((mode == PUNT) || (mode == UNPUNT))?"":TOKEN_ME);
    argrealm = (argc > 4)?argv[4]:NULL;

    if (argrealm) {
	retval = ZGetRealmCount(&cnt);
	if (retval) {
	    ss_perror(sci_idx, retval, "while getting realm count");
	    return;
	}

	for (i=0; i<cnt; i++) {
	    retval = ZGetRealmName(i, &realm);
	    if (retval) {
		ss_perror(sci_idx, retval, "while getting realm name");
		return;
	    }

	    if (strcasecmp(realm, argrealm) == 0)
		break;
	}

	/*
	 * XXX verify correctness
	 */

	sub.zsub_class = argv[1];
	sub.zsub_classinst = argv[2];
	if ((argc > 3) && (argv[3][0] == '*') && (argv[3][1] == '@')) {
	    char srealm[REALM_SZ], *realm;

	    realm = ZExpandRealm(argv[3]+2, srealm, sizeof(srealm));
	    if (!strcmp(ZGetDefaultRealm(), realm)) 
		sub.zsub_recipient = TOKEN_WILD;
	    else {
		sub.zsub_recipient = malloc(strlen(realm)+2);
		strcpy(sub.zsub_recipient, "*@");
		strcpy(sub.zsub_recipient+2, realm);
	    }
	} else 
	    sub.zsub_recipient = (argc == 3)?TOKEN_ME:argv[3];

	if (i == cnt) {
	    ss_perror(sci_idx, 0,
		      "unknown realm specified while modifying "
		      "subscripion file");
	    return;
	}
    } else {
	realm = ZGetDefaultRealm();
    }

    if ((wgport = ZGetWGPort()) == -1) {
	ss_perror(sci_idx,errno,"while finding WindowGram port");
	return;
    } 

    strcpy(fn, subsname);
    strcat(fn, "-");
    strcat(fn, realm);

    if (!strcmp(argv[0],"add")) {
	add_file(fn, realm, wgport, &sub, SUB);
    } else if (!strcmp(argv[0],"add_unsubscription") ||
	       !strcmp(argv[0],"add_un")) {
	add_file(fn, realm, wgport, &sub, UNSUB);
    } else if (!strcmp(argv[0],"add_suppression") ||
	       !strcmp(argv[0],"add_punt")) {
	add_file(fn, realm, wgport, &sub, PUNT);
    } else if (!strcmp(argv[0],"delete") ||
	       !strcmp(argv[0],"del") ||
	       !strcmp(argv[0],"dl")) {
	del_file(fn, realm, wgport, &sub, SUB);
    } else if (!strcmp(argv[0],"delete_unsubscription") ||
	       !strcmp(argv[0],"del_un")) {
	del_file(fn, realm, wgport, &sub, UNSUB);
    } else if (!strcmp(argv[0],"delete_suppression") ||
	       !strcmp(argv[0],"del_punt")) {
	del_file(fn, realm, wgport, &sub, PUNT);
    } else {
	ss_perror(sci_idx,0,"unknown command name");
    }
    if ((sub.zsub_recipient != TOKEN_ME) && 
	(sub.zsub_recipient != TOKEN_WILD) &&
	(sub.zsub_recipient != argv[3]))
	free(sub.zsub_recipient);
    return;
}

void
add_file(char *fn, char *realm,short wgport, ZSubscription_t *subs, int mode)
{
    FILE *fp;
    char errbuf[BUFSIZ];
    ZSubscription_t sub2;
    Code_t retval;
    
    (void) purge_subs(fn,subs,ALL);	/* remove copies in the subs file */
    if (!(fp = fopen(fn,"a"))) {
	(void) sprintf(errbuf,"while opening %s for append", fn);
	ss_perror(sci_idx,errno,errbuf);
	return;
    } 
    fprintf(fp,"%s%s,%s,%s\n",
	    ((mode == UNSUB) ? "!" : 
	     ((mode == PUNT) ? "*" :
	      "")),
	    subs->zsub_class, subs->zsub_classinst, subs->zsub_recipient);
    if (fclose(fp) == EOF) {
	(void) sprintf(errbuf, "while closing %s", subsname);
	ss_perror(sci_idx, errno, errbuf);
	return;
    }
    zutil_fix_macros(subs,&sub2,1);
    if (mode == UNSUB) {
	retval = ZUnsubscribeTo(realm, &sub2,1,(u_short)wgport);
	if (retval)
	    ss_perror(sci_idx, retval, "while subscribing");
    } else if (mode == PUNT) {
	retval = xpunt(sub2.zsub_class, sub2.zsub_classinst,
		       sub2.zsub_recipient, PUNT);
	if (retval)
	    ss_perror(sci_idx, retval, "while unsubscribing");
    } else {
	retval = ZSubscribeTo(realm, &sub2,1,(u_short)wgport);
	if (retval)
	    ss_perror(sci_idx, retval, "while suppressing");
    }
    
    return;
}

void
del_file(char *fn, char *realm, short wgport,ZSubscription_t * subs, int mode)
{
    ZSubscription_t sub2;
    int retval;
    
    retval = purge_subs(fn, subs, mode);
    if (retval == ERR)
	return;
    if (retval == NOT_REMOVED)
	fprintf(stderr,
		"Couldn't find %sclass %s instance %s "
		"recipient %s in\n\tfile %s\n",
		((mode == UNSUB) ? "un-subscription " : 
		 ((mode == PUNT) ? "suppression " :
		  "")),
		subs->zsub_class, subs->zsub_classinst,
		subs->zsub_recipient, subsname);
    zutil_fix_macros(subs,&sub2,1);
    if (mode == PUNT) {
	retval = xpunt(sub2.zsub_class, sub2.zsub_classinst,
		       sub2.zsub_recipient, UNPUNT);
	if (retval)
	    ss_perror(sci_idx,retval,"while unsuppressing");
    } else {
	retval = ZUnsubscribeTo(realm, &sub2,1,(u_short)wgport);
	if (retval != ZERR_NONE)
	    ss_perror(sci_idx,retval,"while unsubscribing");
    }
}

int
purge_subs(char *fn, ZSubscription_t *subs, int mode)
{
    FILE *fp,*fpout;
    char errbuf[BUFSIZ],subline[BUFSIZ];
    char backup[BUFSIZ],ourline[BUFSIZ];
    int delflag = NOT_REMOVED;
    int purge;
    
    switch (mode) {
    case SUB:
    case UNSUB:
    case PUNT:
    case ALL:
	break;
    default:
	ss_perror(sci_idx,0,"internal error in purge_subs");
	return(ERR);
    }
    
    (void) sprintf(ourline,"%s,%s,%s",
		   subs->zsub_class,
		   subs->zsub_classinst,
		   subs->zsub_recipient);
    
    if (!(fp = fopen(fn,"r"))) {
	if (errno == ENOENT)
	    /* if the filw doesn't exist, then the sub
	       is clearly purged */
	    return(delflag);
	(void) sprintf(errbuf,"while opening %s for read", fn);
	ss_perror(sci_idx,errno,errbuf);
	return(ERR);
    } 
    (void) strcpy(backup, fn);
    (void) strcat(backup, ".temp");
    (void) unlink(backup);
    if (!(fpout = fopen(backup,"w"))) {
	(void) sprintf(errbuf,"while opening %s for writing",backup);
	ss_perror(sci_idx,errno,errbuf);
	(void) fclose(fp);
	return(ERR);
    } 
    for (;;) {
	if (!fgets(subline,sizeof subline,fp))
	    break;
	if (*subline)
	    subline[strlen(subline)-1] = '\0'; /* nuke newline */
	switch (mode) {
	case SUB:
	    purge = (strcmp(subline,ourline) == 0);
	    break;
	case UNSUB:
	    purge = (*subline == '!' &&
		     (strcmp(subline+1,ourline) == 0));
	    break;
	case PUNT:
	    purge = (*subline == '-' &&
		     (strcmp(subline+1,ourline) == 0));
	    break;
	case ALL:
	    purge = ((strcmp(subline,ourline) == 0) ||
		     (((*subline == '!') || (*subline == '-')) &&
		      (strcmp(subline+1, ourline) == 0)));
	    break;
	}
	if (purge) {
	    delflag = REMOVED;
	} else {
	    fputs(subline, fpout);
	    if (ferror(fpout) || (fputc('\n', fpout) == EOF)) {
		(void) sprintf(errbuf, "while writing to %s",
			       backup);
		ss_perror(sci_idx, errno, errbuf);
	    }
	}
    }
    (void) fclose(fp);		/* open read-only, ignore errs */
    if (fclose(fpout) == EOF) {
	(void) sprintf(errbuf, "while closing %s",backup);
	ss_perror(sci_idx, errno, errbuf);
	return(ERR);
    }
    if (rename(backup, fn) == -1) {
	(void) sprintf(errbuf,"while renaming %s to %s\n",
		       backup, fn);
	ss_perror(sci_idx,errno,errbuf);
	return(ERR);
    }
    return(delflag);
}

void
load_subs(int argc, char **argv)
{
    int type, cnt, i;
    char *file;
    char *argrealm, *realm;
    Code_t code;

    if (argc > 3) {
	fprintf(stderr,"Usage: %s [file [realm]]\n",argv[0]);
	return;
    }

    if (*argv[0] == 'u')
	type = UNSUB;
    else if (!strcmp(argv[0],"list") || !strcmp(argv[0],"ls"))
	type = LIST;
    else
	type = SUB;

    file = (argc > 1)?argv[1]:subsname;
    argrealm = (argc > 2)?argv[2]:NULL;

    if (argrealm) {
	code = ZGetRealmCount(&cnt);
	if (code) {
	    ss_perror(sci_idx, code, "while getting realm count");
	    return;
	}

	for (i=0; i<cnt; i++) {
	    code = ZGetRealmName(i, &realm);
	    if (code) {
		ss_perror(sci_idx, code, "while getting realm name");
		return;
	    }

	    if (strcasecmp(realm, argrealm) == 0)
		break;
	}

	if (i == cnt) {
	    ss_perror(sci_idx, 0,
		      "unknown realm specified while loading subscription file");
	    return;
	}
    } else {
	realm = NULL;
    }

    code = load_sub_file(type, file, realm);
    if (code)
	ss_perror(sci_idx, code,
		  "while loading subscription file");

}

void
loadall(int argc, char **argv)
{	
    int type;
    int retval;
	
    if (argc > 1) {
	fprintf(stderr,"Usage: %s\n",argv[0]);
	return;
    }

    if (!strcmp(argv[0],"list") || !strcmp(argv[0],"ls"))
	type = LIST;
    else
	type = SUB;

    retval = load_all_sub_files(type, subsname);
    if (retval)
	ss_perror(sci_idx, retval, "initializing location information");
}

void
current(int argc, char **argv)
{
    FILE *fp;
    char errbuf[BUFSIZ];
    ZSubscription_t subs;
    int i,nsubs,retval,save,one,defs;
    short wgport;
    char *realm, *file, backup[BUFSIZ];
	
    save = 0;
    defs = 0;

    if (!strcmp(argv[0],"save"))
	save = 1;
    else if (!strcmp(argv[0], "defaults") || !strcmp(argv[0], "defs"))
	defs = 1;

    if (save) {
	if (argc > 3) {
	    fprintf(stderr,"Usage: %s [filename [realm]]\n",
		    argv[0]);
	    return;
	} else {
	    file = (argc > 1)?argv[1]:subsname;
	    realm = (argc > 2)?argv[2]:NULL;
	}
    } else {
	if (argc > 2) {
	    fprintf(stderr,"Usage: %s [realm]\n",argv[0]);
	    return;
	} else {
	    realm = (argc > 1)?argv[1]:NULL;
	}
    }

    if (!defs)
	if ((wgport = ZGetWGPort()) == -1) {
	    ss_perror(sci_idx,errno,
		      "while finding WindowGram port");
	    return;
	} 

    if (defs)
	retval = ZRetrieveDefaultSubscriptions(realm, &nsubs);
    else
	retval = ZRetrieveSubscriptions(realm, (u_short)wgport,&nsubs);

    if (retval == ZERR_TOOMANYSUBS) {
	fprintf(stderr,"Too many subscriptions -- some have not been returned.\n");
	if (save) {
	    fprintf(stderr,"Save aborted.\n");
	    return;
	}
    }
    else
	if (retval != ZERR_NONE) {
	    ss_perror(sci_idx,retval,"retrieving subscriptions");
	    return;
	}

    if (save) {
	(void) strcpy(backup,file);
	(void) strcat(backup,".temp");
	if (!(fp = fopen(backup,"w"))) {
	    (void) sprintf(errbuf,"while opening %s for write",
			   backup);
	    ss_perror(sci_idx,errno,errbuf);
	    return;
	}
    }
	
    for (i=0;i<nsubs;i++) {
	one = 1;
	if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
	    ss_perror(sci_idx,retval,"while getting subscription");
	    if (save) {
		fprintf(stderr,"Subscriptions file not modified\n");
		(void) fclose(fp);
		(void) unlink(backup);
	    }
	    return;
	} 
	if (save)
	    fprintf(fp,"%s,%s,%s\n",subs.zsub_class,
		    subs.zsub_classinst, subs.zsub_recipient);
	else
	    printf("Class %s Instance %s Recipient %s\n",
		   subs.zsub_class, subs.zsub_classinst,
		   subs.zsub_recipient);
    }

    if (save) {
	if (fclose(fp) == EOF) {
	    (void) sprintf(errbuf, "while closing %s", backup);
	    ss_perror(sci_idx, errno, errbuf);
	    return;
	}
	if (rename(backup,file) == -1) {
	    (void) sprintf(errbuf,"while renaming %s to %s",
			   backup,file);
	    ss_perror(sci_idx,retval,errbuf);
	    (void) unlink(backup);
	}
    }
}

int
make_exist(char *filename)
{
    char errbuf[BUFSIZ];
    FILE *fpout;
	
    if (!access(filename,F_OK))
	return (0);

    if (!(fpout = fopen(filename,"w"))) {
	(void) sprintf(errbuf,"while opening %s for write",filename);
	ss_perror(sci_idx,errno,errbuf);
	return (1);
    }

    if (fclose(fpout) == EOF) {
	(void) sprintf(errbuf, "while closing %s", filename);
	ss_perror(sci_idx, errno, errbuf);
	return(1);
    }
    return (0);
}
