/******************************************************************************
    (c) 2000-2008 Christine Caulfield                 christine.caulfield@googlemail.com
    (c) 2003 Dmitri Popov

    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 of the License, or
    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.
******************************************************************************/

// LAT Control Program (latcp)


#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>
#include <ctype.h>
#include <regex.h>
#include <stdlib.h>
#include <utmp.h>
#include <pwd.h>
#include <signal.h>
#include <limits.h>
#include <assert.h>

#include <list>
#include <queue>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>
#include <iostream>

#include "lat.h"
#include "latcp.h"
#include "utils.h"
#include "dn_endian.h"

static int latcp_socket;

static void make_upper(char *str);

int  read_reply(int fd, int &cmd, unsigned char *&cmdbuf, int &len);
bool send_msg(int fd, int cmd, char *buf, int len);
bool open_socket(bool);

// Command processing routines
void display(int argc, char *argv[]);
void add_service(int argc, char *argv[]);
void del_service(int argc, char *argv[]);
void set_multicast(int);
void set_retransmit(int);
void set_keepalive(int);
void set_responder(int onoff);
void shutdown();
void purge_services();
void start_latd(int argc, char *argv[]);
void set_rating(int argc, char *argv[]);
void set_ident(int argc, char *argv[]);
void set_server_groups(int argc, char *argv[]);
void set_user_groups(int argc, char *argv[]);
void set_node (char *);


// Misc utility routines
static void make_bitmap(char *bitmap, char *cmdline);


int usage(char *cmd)
{
    printf ("Usage: latcp      {option}\n");
    printf ("     where option is one of the following:\n");
    printf ("       -s [<latd args>]\n");
    printf ("       -h\n");
    printf ("       -A -a service [-i description] [-r rating] [-s] [-C command] [-u user] [-m max conn]\n");
    printf ("       -A -p tty -V learned_service [-R rem_port] [-H rem_node] [-Q] [-8]\n");
    printf ("       -D {-a service | -p tty}\n");
    printf ("       -C service command}\n");
    printf ("       -i description -a service\n");
    printf ("       -g list\n");
    printf ("       -G list\n");
    printf ("       -u list\n");
    printf ("       -U list\n");
    printf ("       -j\n");
    printf ("       -J\n");
    printf ("       -Y\n");
    printf ("       -x rating [-s] -a service\n");
    printf ("       -n node\n");
    printf ("       -r retransmit limit\n");
    printf ("       -m multicast timer (100ths/sec)\n");
    printf ("       -k keepalive timer (seconds)\n");
    printf ("       -d [ [-l [-v] [-n] ] ]\n");
    printf ("       -z \n");

    return 2;
}

int main(int argc, char *argv[])
{

// Parse the command.
// because the args vary so much for each command I just check argv[1]
// for the command switch and the command processors call getopt themselves

    if (argc == 1)
    {
	exit(usage(argv[0]));
    }

    if (argv[1][0] != '-')
    {
	exit(usage(argv[0]));
    }

    switch (argv[1][1])
    {
    case 'A':
	add_service(argc-1, &argv[1]);
	break;
    case 'D':
	del_service(argc-1, &argv[1]);
	break;
    case 'j':
	set_responder(1);
	break;
    case 'J':
	set_responder(0);
	break;
    case 'Y':
	purge_services();
	break;
    case 'n':
	set_node(argv[2]);
	break;
    case 'm':
	if (argv[2])
	    set_multicast(atoi(argv[2]));
	else
	    exit(usage(argv[0]));
	break;
    case 'r':
        if (argv[2])
            set_retransmit(atoi(argv[2]));
        else
            exit(usage(argv[0]));
        break;
    case 'k':
        if (argv[2])
            set_keepalive(atoi(argv[2]));
        else
            exit(usage(argv[0]));
        break;
    case 'd':
	display(argc-1, &argv[1]);
	break;
    case 'h':
	shutdown();
	break;
    case 's':
	start_latd(argc, argv);
	break;
    case 'x':
	set_rating(argc, argv);
	break;
    case 'i':
	set_ident(argc, argv);
	break;
    case 'z':
	printf("counters not yet done\n");
	break;
    case 'U':
	set_user_groups(argc, argv);
	break;
    case 'u':
	set_user_groups(argc, argv);
	break;
    case 'g':
	set_server_groups(argc, argv);
	break;
    case 'G':
	set_server_groups(argc, argv);
	break;
    default:
	exit(usage(argv[0]));
	break;
    }
}


// Display latd characteristics or learned services
void display(int argc, char *argv[])
{
    char verboseflag[1] = {'\0'};
    signed char opt;
    bool show_services = false;
    bool show_nodes = false;

    if (!open_socket(false)) return;

    while ((opt=getopt(argc,argv,"lvn")) != EOF)
    {
	switch(opt)
	{
	case 'l':
	    show_services=true;
	    break;

	case 'v':
	    verboseflag[0] = 1;
	    break;

	case 'n':
	    show_nodes = true;
	    break;

	default:
	    fprintf(stderr, "only -v, -n or -l valid with -d flag\n");
	    exit(2);
	}
    }

     if (show_nodes)
     {
     	send_msg(latcp_socket, LATCP_SHOWNODES, verboseflag, 1);
         unsigned char *result;
         int len;
         int cmd;
         if (!read_reply(latcp_socket, cmd, result, len))
	 {
		 std::cout << result;

		 delete[] result;
	 }
         return;
     }

    if (show_services)
    {
	send_msg(latcp_socket, LATCP_SHOWSERVICE, verboseflag, 1);
    }
    else
    {
	send_msg(latcp_socket, LATCP_SHOWCHAR, verboseflag, 1);
    }

    unsigned char *result = NULL;
    int len;
    int cmd;

    if (!read_reply(latcp_socket, cmd, result, len))
    {
	    std::cout << result;

	    delete[] result;
    }
}


// Change the rating of a service
void set_rating(int argc, char *argv[])
{
    int new_rating = 0;
    bool static_rating = false;
    char service[256];
    signed char opt;

    if (!open_socket(false)) return;

    while ((opt=getopt(argc,argv,"x:sa:")) != EOF)
    {
	switch(opt)
	{
	case 'x':
	    new_rating = atoi(optarg);
	    break;

	case 's':
	    static_rating = true;
	    break;

	case 'a':
	    strcpy(service, optarg);
	    make_upper(service);
	    break;

	default:
	    fprintf(stderr, "only valid with -a and -x flags\n");
	    exit(2);
	}
    }

    if (!new_rating)
    {
	fprintf(stderr, "Invalid rating\n");
	exit(2);
    }

    make_upper(service);

    char message[520];
    int ptr = 2;
    message[0] = (int)static_rating;
    message[1] = new_rating;
    add_string((unsigned char*)message, &ptr, (unsigned char*)service);
    send_msg(latcp_socket, LATCP_SETRATING, message, ptr);

    // Wait for ACK or error
    unsigned char *result = NULL;
    int len;
    int cmd;

    exit (read_reply(latcp_socket, cmd, result, len));

}


// Change the ident of a service
void set_ident(int argc, char *argv[])
{
    char service[256];
    char ident[256];
    signed char opt;

    if (!open_socket(false)) return;

    while ((opt=getopt(argc,argv,"i:a:")) != EOF)
    {
	switch(opt)
	{
	case 'a':
	    strcpy(service, optarg);
	    make_upper(service);
	    break;
	case 'i':
	    strcpy(ident, optarg);
	    break;

	default:
	    fprintf(stderr, "must have both -i and -a flags\n");
	    exit(2);
	}
    }

    make_upper(service);

    char message[1024];
    int ptr = 0;
    add_string((unsigned char*)message, &ptr, (unsigned char*)service);
    add_string((unsigned char*)message, &ptr, (unsigned char*)ident);
    send_msg(latcp_socket, LATCP_SETIDENT, message, ptr);

    // Wait for ACK or error
    unsigned char *result = NULL;
    int len;
    int cmd;
    exit(read_reply(latcp_socket, cmd, result, len));
}


void set_server_groups(int argc, char *argv[])
{
    signed char opt;
    int  cmd;
    char groups[256];

    while ((opt=getopt(argc,argv,"G:g:")) != EOF)
    {
	switch(opt)
	{
	case 'G':
	    cmd = LATCP_SETSERVERGROUPS;
	    strcpy(groups, optarg);
	    break;

	case 'g':
	    cmd = LATCP_UNSETSERVERGROUPS;
	    strcpy(groups, optarg);
	    break;

	default:
	    fprintf(stderr, "Just G or g please\n");
	    exit(2);
	}
    }

    if (!open_socket(false)) return;

    char bitmap[32]; // 256 bits
    make_bitmap(bitmap, groups);

    send_msg(latcp_socket, cmd, bitmap, 32);

    // Wait for ACK or error
    unsigned char *result = NULL;
    int len;
    exit(read_reply(latcp_socket, cmd, result, len));

}

void set_user_groups(int argc, char *argv[])
{
    signed char opt;
    int  cmd;
    char groups[256];

    while ((opt=getopt(argc,argv,"u:U:")) != EOF)
    {
	switch(opt)
	{
	case 'U':
	    cmd = LATCP_SETUSERGROUPS;
	    strcpy(groups, optarg);
	    break;

	case 'u':
	    cmd = LATCP_UNSETUSERGROUPS;
	    strcpy(groups, optarg);
	    break;

	default:
	    fprintf(stderr, "Just U or u please\n");
	    exit(2);
	}
    }

    if (!open_socket(false)) return;

    char bitmap[32]; // 256 bits
    make_bitmap(bitmap, groups);

    send_msg(latcp_socket, cmd, bitmap, 32);

    // Wait for ACK or error
    unsigned char *result = NULL;
    int len;
    exit(read_reply(latcp_socket, cmd, result, len));

}

// Enable/Disable the service responder
void set_responder(int onoff)
{
    if (!open_socket(false)) return;

    char flag[1];
    flag[0] = onoff;
    send_msg(latcp_socket, LATCP_SETRESPONDER, flag, 1);
}

// Shutdown latd
void shutdown()
{
    if (!open_socket(false)) return;

    char dummy[1];
    send_msg(latcp_socket, LATCP_SHUTDOWN, dummy, 0);
}

// Purge learned services
void purge_services()
{
    if (!open_socket(false)) return;

    char dummy[1];
    send_msg(latcp_socket, LATCP_PURGE, dummy, 0);
}

void set_multicast(int newtime)
{
    if (newtime < 10 || newtime > 180)
    {
	fprintf(stderr, "invalid multicast time\n");
	return;
    }
    if (!open_socket(false)) return;

    send_msg(latcp_socket, LATCP_SETMULTICAST, (char *)&newtime, sizeof(int));
}

void set_retransmit(int newlim)
{
    if (newlim < 4 || newlim > 120)
    {
        fprintf(stderr, "invalid retransmit limit\n");
        return;
    }
    if (!open_socket(false)) return;

    send_msg(latcp_socket, LATCP_SETRETRANS, (char *)&newlim, sizeof(int));
}

void set_keepalive(int newtime)
{
    if (newtime < 10 || newtime > 180)
    {
	fprintf(stderr, "invalid keepalive timer\n");
	return;
    }
    if (!open_socket(false)) return;

    send_msg(latcp_socket, LATCP_SETKEEPALIVE, (char *)&newtime, sizeof(int));
}

void set_node(char *name)
{
    if (!open_socket(false)) return;

    make_upper(name);

    char message[520];
    int ptr = 0;
    add_string((unsigned char*)message, &ptr, (unsigned char*)name);

    send_msg(latcp_socket, LATCP_SETNODENAME, message, ptr);
    return;
}

void add_service(int argc, char *argv[])
{
    char name[255] = {'\0'};
    char ident[255] = {'\0'};
    char remport[255] = {'\0'};
    char localport[255] = {'\0'};
    char remnode[255] = {'\0'};
    char remservice[255] = {'\0'};
    char password[255] = {'\0'};
    char command[1024] = {'\0'};
    signed char opt;
    bool got_service=false;
    bool got_port=false;
    bool static_rating=false;
    int  rating = 0;
    int  queued = 0;
    int  clean = 0;
    int  max_connections = 0;
    uid_t target_uid = 0;
    gid_t target_gid = 0;
    struct passwd *target_user;

    opterr = 0;
    while ((opt=getopt(argc,argv,"a:i:p:H:R:V:r:w:sQ8C:m:u:")) != EOF)
    {
	switch(opt)
	{
	case 'a':
	    got_service=true;
	    strcpy(name, optarg);
	    make_upper(name);
	    break;

	case 'i':
	    got_service=true;
	    strcpy(ident, optarg);
	    break;

	case 'w':
	    strcpy(password, optarg);
	    break;

	case 'p':
	    got_port=true;
	    strcpy(localport, optarg);
	    if (strncmp(localport, LAT_DIRECTORY, strlen(LAT_DIRECTORY)) != 0)
	    {
		fprintf(stderr, "Local port name must start " LAT_DIRECTORY "\n");
		return;
	    }
	    break;

	case 'H':
	    got_port=true;
	    strcpy(remnode, optarg);
	    make_upper(remnode);
	    break;

	case 'R':
	    got_port=true;
	    strcpy(remport, optarg);
	    make_upper(remport);
	    break;

	case 'V':
	    got_port=true;
	    strcpy(remservice, optarg);
	    make_upper(remservice);
	    break;

	case 'Q':
	    got_port=true;
	    queued = 1;
	    break;

	case 's':
	    static_rating = true;
	    break;

	case '8':
	    clean = true;
	    break;

	case 'C':
	    strcpy(command, optarg);
	    break;

	case 'u':
	    target_user = getpwnam(optarg);
	    if (target_user == NULL)
	    {
		fprintf(stderr, "Unknown username '%s'\n", optarg);
		exit(99);
	    }
	    target_uid = target_user->pw_uid;
	    target_gid = target_user->pw_gid;
	    break;

	case 'm':
	    max_connections = atoi(optarg);
	    break;

	case 'r':
	    rating = atoi(optarg);
	    break;

	default:
	    fprintf(stderr, "No more service switches defined yet\n");
	    exit(2);
	}
    }

    if (!open_socket(false)) return;

    // Variables for ACK message
    unsigned char *result = NULL;
    int len;
    int cmd;

    if (got_service)
    {
	if (name[0] == '\0')
	{
	    fprintf(stderr, "No name for new service\n");
	    exit(2);
	}

	char message[520];
	int ptr = 2;
	message[0] = (int)static_rating;
	message[1] = rating;
	add_string((unsigned char*)message, &ptr, (unsigned char*)name);
	add_string((unsigned char*)message, &ptr, (unsigned char*)ident);
	message[ptr++] = max_connections;
	memcpy(message+ptr, &target_uid, sizeof(uid_t)); ptr += sizeof(uid_t);
	memcpy(message+ptr, &target_gid, sizeof(uid_t)); ptr += sizeof(gid_t);
	add_string((unsigned char*)message, &ptr, (unsigned char*)command);

	send_msg(latcp_socket, LATCP_ADDSERVICE, message, ptr);

	// Wait for ACK or error
	exit(read_reply(latcp_socket, cmd, result, len));
    }

    if (got_port)
    {
	char message[520];
	int ptr = 0;
	add_string((unsigned char*)message, &ptr, (unsigned char*)remservice);
	add_string((unsigned char*)message, &ptr, (unsigned char*)remport);
	add_string((unsigned char*)message, &ptr, (unsigned char*)localport);
	add_string((unsigned char*)message, &ptr, (unsigned char*)remnode);
	message[ptr++] = queued;
	message[ptr++] = clean;
	add_string((unsigned char*)message, &ptr, (unsigned char*)password);
	send_msg(latcp_socket, LATCP_ADDPORT, message, ptr);

	// Wait for ACK or error
	exit(read_reply(latcp_socket, cmd, result, len));
    }

    fprintf(stderr, "Sorry, did you want me to do something??\n");
}

void del_service(int argc, char *argv[])
{
    char name[255] = {'\0'};
    signed char opt;
    bool got_service = false;
    bool got_port = false;
    opterr = 0;

    while ((opt=getopt(argc,argv,"a:p:")) != EOF)
    {
	switch(opt)
	{
	case 'a':
	    got_service = true;
	    strcpy(name, optarg);
	    make_upper(name);
	    break;

	case 'p':
	    got_port = true;
	    strcpy(name, optarg);
	    break;

	default:
	    fprintf(stderr, "No more service switches defined yet\n");
	    exit(2);
	}
    }

    if ((got_port && got_service) ||
	(!got_port && !got_service))
    {
	fprintf(stderr, "Either -a or -p can be specified but not both\n");
	return;
    }


    if (!open_socket(false)) return;

    char message[520];
    int ptr = 0;
    add_string((unsigned char*)message, &ptr, (unsigned char*)name);
    if (got_service)
	send_msg(latcp_socket, LATCP_REMSERVICE, message, ptr);
    else
	send_msg(latcp_socket, LATCP_REMPORT, message, ptr);

    unsigned char *result = NULL;
    int len;
    int cmd;
    exit(read_reply(latcp_socket, cmd, result, len));
}

// Start latd & run startup script.
void start_latd(int argc, char *argv[])
{
    if (getuid() != 0)
    {
	fprintf(stderr, "You must be root to start latd\n");
	return;
    }

    // If we can connect to LATD than it's already running
    if (open_socket(true))
    {
	fprintf(stderr, "LAT is already running\n");
	return;
    }

    // Look for latd in well-known places
    struct stat st;
    char *latd_bin = NULL;
    char *latd_path = NULL;

    // Look for latd in the same place as latcp
    char *name = (char *)malloc(strlen(argv[0])+1);
    char *path = (char *)malloc(strlen(argv[0])+1);
    strcpy(name, argv[0]);

    char *slash = rindex(name, '/');
    if (slash)
    {
	*slash='\0';
	strcpy(path, name);
	strcat(name, "/latd");
	if (!stat(name, &st))
	{
	    latd_bin = name;
	    latd_path = path;
	}
    }
    // Otherwise look in some well-known places
    else if (!stat("/usr/sbin/latd", &st))
    {
	    latd_bin = (char *)"/usr/sbin/latd";
	    latd_path = (char *)"/usr/sbin";
    }
    else if (!stat("/usr/local/sbin/latd", &st))
    {
	    latd_bin = (char *)"/usr/local/sbin/latd";
	    latd_path = (char *)"/usr/local/sbin";
    }

    // Did we find it?
    if (latd_bin)
    {
        char *newargv[argc+1];
	char *newenv[4];
	int   i;
	char  latcp_bin[PATH_MAX];
	char  latcp_env[PATH_MAX+7];

// This is VERY Linux specific and needs /proc mounted.
// we get the full path of the current executable by doing a readlink.
// /proc/<pid>/exe

#ifdef __linux__
	char  latcp_proc[PATH_MAX];

	sprintf(latcp_proc, "/proc/%d/exe", getpid());
	if ( (i=readlink(latcp_proc, latcp_bin, sizeof(latcp_bin))) == -1)
	{
	    fprintf(stderr, "readlink in /proc failed. Make sure the the proc filesystem is mounted on /proc\n");
	    exit(2);
	}
#endif
	sprintf(latcp_env, "LATCP=%s", latcp_bin);

	newargv[0] = latd_bin;
	newargv[1] = NULL;
	newenv[0] = (char *)"PATH=/bin:/usr/bin:/sbin:/usr/sbin";
	newenv[1] = (char*)"LATCP_STARTED=true"; // Tell latd it was started by us.
	newenv[2] = latcp_env;
	newenv[3] = NULL;

	switch(fork())
	{
	case 1: //Error
	    perror("fork failed");
	    return;
	case 0: // Child
	    // Start latd with our args (after the "-s")
	    for (i=2; i<argc; i++)
		newargv[i-1] = argv[i];
	    newargv[i-1] = NULL;

	    execve(latd_bin, newargv, newenv);
	    perror("exec of latd failed");
	    break;

	default: //Parent
	    {
		// Wait for latd to start up
		int count = 0;
		while (!open_socket(true) && count < 10)
		{
		    sleep(1);
		    count++;
		}
		if (count >= 10)
		{
		    fprintf(stderr, "latd did not start\n");
		    exit(2);
		}


		// Run startup script if there is one.
		if (!stat("/etc/latd.conf", &st))
		{
		    pid_t shell_pid;
		    switch ( (shell_pid=fork()) )
		    {
		    case 0: // Child
			newargv[0] = (char *)"/bin/sh";
			newargv[1] = (char *)"/etc/latd.conf";
			newargv[2] = NULL;
			execve("/bin/sh", newargv, newenv);
			perror("exec of /bin/sh failed");
			exit(0);

		    case -1:
			perror("Fork failed");
			exit(0);

		    default: // Parent. Wait for child to finish
			waitpid(shell_pid, NULL, 0);
		    }
		}
		// OK, latd has started and we have run the startup script.
		// Now "unlock" latd. ie tell it we have finished initialisation
		char dummy[1];
		send_msg(latcp_socket, LATCP_UNLOCK, dummy, 0);
	    }
	    break;
	}
    }
    else
    {
	fprintf(stderr, "cannot find latd\n");
	return;
    }
}


bool send_msg(int fd, int cmd, char *buf, int len)
{
    unsigned char outhead[3];

    outhead[0] = cmd;
    outhead[1] = len/256;
    outhead[2] = len%256;
    if (write(fd, outhead, 3) != 3) return false;
    if (write(fd, buf, len) != len) return false;

    return true;
}


// Return 0 for success and -1 for failure
int read_reply(int fd, int &cmd, unsigned char *&cmdbuf, int &len)
{
    unsigned char head[3];

    // Get the message header (cmd & length)
    if (read(fd, head, sizeof(head)) != 3)
	return -1; // Bad header

    len = head[1] * 256 + head[2];
    cmd = head[0];
    cmdbuf = new unsigned char[len];
    if (cmdbuf == NULL)
	return -1;

    // Read the message buffer
    if (read(fd, cmdbuf, len) != len)
    {
	return -1; // Bad message
    }

    if (cmd == LATCP_ERRORMSG)
    {
	fprintf(stderr, "%s\n", cmdbuf);
	return -1;
    }

    return 0;
}

bool open_socket(bool quiet)
{
    struct sockaddr_un sockaddr;

    latcp_socket = socket(PF_UNIX, SOCK_STREAM, 0);
    if (latcp_socket == -1)
    {
	if (!quiet) perror("Can't create socket");
	return false; /* arggh ! */
    }

    strcpy(sockaddr.sun_path, LATCP_SOCKNAME);
    sockaddr.sun_family = AF_UNIX;
    if (connect(latcp_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)))
    {
	if (!quiet) perror("Can't connect to latd");
	close(latcp_socket);
	return false;
    }

    unsigned char *result = NULL;
    int len;
    int cmd;

    // Send our version
    send_msg(latcp_socket, LATCP_VERSION, (char*)VERSION, strlen(VERSION)+1);

    if (!read_reply(latcp_socket, cmd, result, len)) // Read version number back
	delete[] result;

    return true;
}

static void make_upper(char *str)
{
    unsigned int i;

    for (i=0; i<strlen(str); i++)
    {
	str[i] = toupper(str[i]);
    }
}

static inline void set_in_bitmap(char *bits, int entry)
{

    if (entry < 256)
    {
	unsigned int  intnum;
	unsigned int  bitnum;

	intnum = entry / 8;
	bitnum = entry % 8;
	bits[intnum] |= 1<<bitnum;
    }
}

static void make_bitmap(char *bitmap, char *cmdline)
{
    int   firstnum;
    int   secondnum;
    int   i;
    bool  finished = false;
    char  delimchar;
    char* delimiter;

    memset(bitmap, 0, 32);
    delimiter = strpbrk(cmdline, ",-");

    do
    {
	if (delimiter == NULL)
	{
	    delimchar = ',';
	}
	else
	{
	    delimchar = delimiter[0];
	    delimiter[0] = '\0';
	}
	firstnum = atoi(cmdline);
	if (delimiter != NULL) delimiter[0] = delimchar;

	/* Found a comma -- mark the number preceding it as read */
	if ((delimchar == ',') || (delimiter == NULL))
	{
	    set_in_bitmap(bitmap, firstnum);
	}

	/* Found a hyphen -- mark the range as read */
	if (delimchar == '-')
	{
	    cmdline = delimiter+1;
	    delimiter = strpbrk(cmdline, ",");

	    if (delimiter != NULL)
	    {
		delimchar = delimiter[0];
		delimiter[0] = '\0';
	    }
	    secondnum = atoi(cmdline);
	    if (delimiter != NULL) delimiter[0] = delimchar;

	    for (i=firstnum; i<=secondnum; i++)
	    {
		set_in_bitmap(bitmap, i);
	    }
	}
	if (delimiter == NULL) finished = true;

	cmdline = delimiter+1;
	if (delimiter != NULL) delimiter = strpbrk(cmdline, ",-");
    } while (!finished);
}
