// $Id: discover.c,v 1.39 2011/12/14 23:35:09 lpoulsen Exp $
//
// Copyright (c) 2003 by AFAR Communications Inc, Santa Barbara, CA 93117, USA
//
// Node discovery phase of ECON utility program for AFAR and Wi-LAN radios
//
// $Log: discover.c,v $
// Revision 1.39  2011/12/14 23:35:09  lpoulsen
// At then end of DISCOVER, say "Use CONNECT"
//
// Revision 1.38  2011/08/29 22:29:27  lpoulsen
// Clean compilation on FC3 linux.
//
// Revision 1.37  2011/07/08 21:50:30  lpoulsen
// Eliminate (almost all) use of debug_level, use debug_flags instead.
//
// Revision 1.36  2011/05/09 19:32:27  lpoulsen
// Reject device range (1-7) where a single radio is required
//
// Revision 1.35  2011/05/06 22:15:14  lpoulsen
// Preparing for ECON 04.07 release
//
// Revision 1.34  2011/03/22 00:41:56  lpoulsen
// Better feedback on "sel all" and "des all"
//
// Revision 1.33  2011/02/22 01:46:45  lpoulsen
// Discovery of PC sessions.
//
// Revision 1.32  2011/02/22 01:31:42  lpoulsen
// Do proper byteswaps on HW_TYPE_PC
//
// Revision 1.31  2011/02/22 01:18:16  lpoulsen
// Sepate device table for PCs
//
// Revision 1.30  2011/02/22 00:02:23  lpoulsen
// Show device table after F4 disconnect
// Clear device table before new discovery
// Use device type = 0x8086 for PCs
//
// Revision 1.29  2011/02/21 22:26:36  lpoulsen
// "adapter" command works on Linux
//
// Revision 1.28  2011/02/18 22:31:09  lpoulsen
// Add "adapter" command to switch to another ethernet port
//
// Revision 1.27  2011/02/08 22:07:02  lpoulsen
// evived the LINUX build of ECON - now ECON-PLUS
//
// Revision 1.26  2011/02/04 22:29:06  lpoulsen
// Many little fixes.
//
// Revision 1.25  2011/01/22 01:29:05  lpoulsen
// Release candidate:
// - allow "show first=1 max=45" to support large systems
// - allow "sort by=distance" to support "connect all reboot"
// - improve download efficiency
//
// Revision 1.24  2011/01/11 00:51:13  lpoulsen
// When operating in batch mode,
// - print progress after each session (2 of 5)
// - save session result code to device table to be printed with "show"
//
// Revision 1.23  2010/12/22 17:36:33  lpoulsen
// Select/Deselect list of ranges
// Expand to up to 511 radios
// Protocol fixes
//
// Revision 1.22  2008/08/06 22:48:16  lpoulsen
// "Nuno mode" - F4 prompts for reselect instead of Econsole>
//
// Revision 1.21  2008/08/05 23:58:37  lpoulsen
// User interface updates.
//
// Revision 1.20  2008/07/30 07:11:42  lpoulsen
// New option "Connect keyboard=yes"
// Fixes for timeout probelm at end of file.
//
// Revision 1.19  2008/07/26 02:00:48  lpoulsen
// Econ protocol version 4 - prompt bit
//
// Revision 1.18  2008/07/25 00:43:27  lpoulsen
// Added "select all" and "edit" commands.
// Optional OEM_COMPANY personalization
//
// Revision 1.17  2008/07/17 00:14:37  lpoulsen
// Exit gracefully on EOF on stdin.
//
// Revision 1.16  2008/07/16 00:00:28  lpoulsen
// Add deselect command.
//
// Revision 1.15  2008/07/11 20:14:10  lpoulsen
// Resolve many compiler warnings on Linux
//
// Revision 1.14  2008/07/11 16:28:46  lpoulsen
// Econ 4.00 release candidate supports UPDATE and SELECT commands.
//
// Revision 1.13  2008/06/10 00:06:10  lpoulsen
// New -X option is now working
// Updated -I and -O implementation
//
// Revision 1.12  2008/05/31 00:35:09  lpoulsen
// Econ 4.x preview - now with command shell
//
// Revision 1.11  2008/05/10 01:17:37  lpoulsen
// Making econ -x filename work (not yet complete)
//
// Revision 1.10  2006/06/06 02:03:29  lpoulsen
// First attempt at compiling on MAC OS X
//
// Revision 1.9  2006/02/17 21:48:58  lpoulsen
// Enter only IDENTIFY_RESPONSE packets in peer table.
//
// Revision 1.8  2005/05/11 17:04:33  lpoulsen
// Solve problems on WinXP by moving to WinPCap 3.1 beta 4
// Use IP helper API to get MAC addresses (no longer works on Win95)
// Change references to "radios" to say "devices"
//
// Revision 1.7  2003/12/23 21:38:05  lpoulsen
// Display firmware version if available
//
// Revision 1.6  2003/12/23 04:35:42  lpoulsen
// EConsole protocol version 2 support.
// Respond to IDENTIFY_REQUEST (version 1 and version 2 format) with
//      version 2 format IDENTIFY_RESPONSE.
//
// Revision 1.5  2003/12/17 18:19:08  lpoulsen
// Respond to IDENTIFY_REQUEST (to support "show econ" on radio/mux)
//
// Revision 1.4  2003/08/25 20:19:16  lpoulsen
// Clean up compile warning.
//
// Revision 1.3  2003/08/25 20:15:30  lpoulsen
// Do not send a probe just before exiting from discover.
//
// Revision 1.2  2003/08/22 01:46:32  lpoulsen
// Make econ compile on Linux again.
//
// Revision 1.1  2003/08/22 00:01:17  lpoulsen
// New module carved out from epacket.c and util.c contains almost all of
//     the node discovery process
//
#include <time.h>
#include <stdio.h>
#include <assert.h>
#ifdef LINUX
#include <sys/time.h> // struct timeval
#else
#include <windows.h>
#endif
#include "afartype.h"
#include "afarutil.h"
#include "epacket.h"
#include "eprotocol.h"
#include "util.h"
#include "ucon.h"
#include "adapter.h"            // my_mac_address
#include "parser.h"
#include "uciosess.h"

#ifndef __APPLE__
#define ntohs(a) swap_uint16(a)
#define ntohl(a) swap_uint32(a)
#endif

extern char *fmt_session_result(int);
extern void  clear_device_table(void);

// -- Local procedures

int     process_id_resp(void *packet, int packet_size, int *sessions,
                        boolean show_progress, uint32 distance_msec);
void    process_pc_discovery(epkt_header *epkt, econ_header *eh);
void    print_radio(int device_index, uint16 format);
boolean isRange(char *str);
void    select_range(char *tmp, boolean on_off);

// -- Global data

macAddr bcastMacAddress =
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

#define MAX_PCS 5

struct device_entry afar_device[MAX_DEVICES] = {{0}};
struct device_entry pc_device[MAX_PCS] = {{0}};
int                 numOfPcsFound = 0;

// -- Local data

extern char *format_ip_addr(ip_addr *ipa);

// ** radio discovery  ............................................

cmd_ret_t
econ_discover(Argument_List *a, ucio_session *s)
{
    clear_device_table();
    numOfPcsFound = 0;
    numOfRadiosFound = discover_radios(false, 2);
    if (numOfRadiosFound) {
        sort_device_table(0);
        show_radios(false, 0, 1, 45);
        printf(
            "\n%d devices were found. Type \"CONNECT xx\" to start session.\n",
            numOfRadiosFound);
        if (numOfPcsFound) {
            printf("There is an open econ session from %s",
                   pc_device[0].radio_name);
            if (numOfPcsFound > 1)
                printf(" - and other PCs as well");
            printf(".\n");
        }
    } else {
        printf("No devices were found.\n");
    }
    return CMD_SUCCESS;
}

void
clear_device_table(void)
{
    memset(&afar_device[0], 0, MAX_DEVICES*sizeof(struct device_entry));
    numOfRadiosFound = 0;
}

int
discover_radios(boolean show_progress, int seconds)
{
    int          i;
    static byte *econ_pkt = NULL;
    time_t       startime, probetime, curtime;
    int          res;
    struct ether_session tmpSession;
    macAddr      destination_address;
    int          radios_found = 0;
    int          prev_rtt = 0;

    econ_phase = ECON_DISCOVER;

    afar_pfc_setup();

    curtime = time(NULL);

    if (econ_pkt == NULL)
    {
        econ_pkt = malloc(1640);
    }

    if (show_progress) print_discovery_header();

    // set up a fake ECON session control block to allow us to
    //  send an "Identify Request" message, in our local network (broadcast)

    // broadcast message
    memset(destination_address, 0xFF, 6);

    memcpy(tmpSession.destination_address, destination_address, 6);
    tmpSession.PDU = (IDENTIFY_REQUEST << 8);
    tmpSession.ConnectionID = getRandomID();
    tmpSession.TSEQ = 0;
    tmpSession.RSEQ = 0;
    tmpSession.NBytes = 0;
    tmpSession.state = SESSION_INIT;

    startime = time(NULL);


    for (i = 0; i < seconds; i++)
    {
        uint32   t_poll, t_resp;
        uint32   ms_delay;

        ether_send(&tmpSession, NULL, 0);
        probetime = time(NULL);
        t_poll = afar_pfc_read();

        while ((curtime = time(NULL)) == probetime)
            while ((res = adapter_receive(econ_pkt)) != 0)
            {
                t_resp = afar_pfc_read();
                ms_delay = afar_usec_delta(t_poll, t_resp) / 1000;
                if (ms_delay == prev_rtt) ms_delay += 1;
                if (debug_flags & DBFLG_ADAPTER)
                    printf("discovery received pkt - size = %4d\n", res);
                if (res > 1514)
                    break;
                if (res < 0)
                    break;
                if (res >= MIN_PACKET_SIZE) {
                    process_id_resp(econ_pkt, res, &radios_found,
                                                   show_progress, ms_delay);
                    prev_rtt = ms_delay;
                }
                if (time(NULL) != curtime)
                    break;
            }
    }
    econ_phase = ECON_STARTING;

    return radios_found;     
}

int
process_id_resp(void *packet, int packet_size, int *sessions,
                boolean show_progress, uint32 distance_msec)
{
    uint16 PDU;
    uint32 NBytes;
    uint8 *payload;
    econ_packet *econ_pkt = packet;
    econ_packet_vlan *vp = packet;
    int next_device = *sessions + 1;
    uint16 vlan = 0;
    econ_header *eh;
    int wqa;
    int rtt_ms;

    assert(next_device < MAX_DEVICES);

    if (ntohs(econ_pkt->econ_ehdr.epkt_proto) == 0x8100)
    {
        vlan = ntohs(vp->epkt_VLAN_tci);
        printf("Saw VLAN tag %d\n", vlan);
        if (ntohs(vp->epkt_proto) > 1500)
            return INVALID_PACKET;
        eh = &(vp->econ_chdr);
    }
    else
    {
        if (ntohs(econ_pkt->econ_ehdr.epkt_proto) > 1500)
            return INVALID_PACKET;
        eh = &(econ_pkt->econ_chdr);
    }
    if (memcmp(eh->econ_snapid, econ_snap_prefix, 8))
        return INVALID_PACKET;
    if (debug_flags & DBFLG_ADAPTER)
            printf(" Saw ECON packet sent to %s from %s\n",
                   fmt_mac_addr(econ_pkt->econ_ehdr.epkt_dest),
                   fmt_mac_addr(econ_pkt->econ_ehdr.epkt_src) );

    if (memcmp(econ_pkt->econ_ehdr.epkt_src, my_mac_address, 6) == 0)
    {
        if (debug_flags & DBFLG_ADAPTER) {
            printf(" Rejecting ECON packet sent by myself\n");
        }
        return INVALID_PACKET;
    }
    // If it is not addressed to me, it is not a response to my probe
    if (memcmp(econ_pkt->econ_ehdr.epkt_dest, my_mac_address, 6))
    {
        if (debug_flags & DBFLG_ADAPTER) {
            printf(" Rejecting ECON packet sent to %s rather than %s\n",
                   fmt_mac_addr(econ_pkt->econ_ehdr.epkt_dest),
                   fmt_mac_addr(my_mac_address) );
        }
        return INVALID_PACKET;
    }


    // extract protocol parameters
    PDU = ntohs(eh->econ_pdu);
    NBytes = ntohl(eh->econ_nbytes);
    payload = (void *) eh->econ_data;

    // Ignore data packets
    if (PDU != IDENTIFY_RESPONSE)
        return 0;

    if (NBytes < PROTO_POSITION)
        eh->econ_version = 0;

    if (eh->econ_version < 2)
        eh->econ_hw_type = 0;

    if (debug_flags & DBFLG_ADAPTER)
        printf("    hw_type = %04X (%u)\n",
               (unsigned int)ntohl(eh->econ_hw_type),
               (unsigned int)ntohl(eh->econ_hw_type));

    if (ntohl(eh->econ_hw_type) == HW_TYPE_PC) {
        process_pc_discovery(&econ_pkt->econ_ehdr, eh);
        return 0;
    }

    // create new device_entry ...

    // upon duplicates, update the information

    next_device = numOfRadiosFound + *sessions + 1;

    for (wqa = 1; wqa < next_device; wqa++)
    {
        if (afar_device[wqa].datestamp)
        {
            if (memcmp(afar_device[wqa].mac_address,
                       econ_pkt->econ_ehdr.epkt_src, 6) == 0)
                break;          // we have already received a discovery response

        }
    }
    if (wqa >= next_device)
    {
        wqa = next_device++;
        (*sessions)++;
	if (debug_flags & DBFLG_ADAPTER) printf("New device in slot no %d\n", wqa);
    }
    else if (debug_flags & DBFLG_ADAPTER) printf("Updating device in slot no %d\n", wqa);

    memcpy(afar_device[wqa].mac_address,
           econ_pkt->econ_ehdr.epkt_src, 6);
    strncpy(afar_device[wqa].radio_name,
            (NBytes ? (char *) payload : "Unknown name"), 64);

    if (NBytes < PROTO_POSITION)
        afar_device[wqa].proto_version = 0;
    else
    {
        afar_device[wqa].proto_version = eh->econ_version;
    }
    if (afar_device[wqa].proto_version >= 2)
    {
        strncpy(afar_device[wqa].firmware, eh->econ_sw_type, 16);
        afar_device[wqa].ip_address    = eh->econ_ip_addr;
        afar_device[wqa].device_uptime = ntohl(eh->econ_uptime);
    }
    else
    {
        memset(afar_device[wqa].firmware, 0, 16);
        afar_device[wqa].ip_address    = 0;
        afar_device[wqa].device_uptime = 0;
    }
    if (afar_device[wqa].proto_version >= 3)
        afar_device[wqa].device_type = (uint16)ntohl(eh->econ_hw_type);
    afar_device[wqa].datestamp = time(NULL);
    
    rtt_ms = (afar_device[wqa].rtt_ms * afar_device[wqa].rtt_cnt)
                                     + distance_msec;
    afar_device[wqa].rtt_cnt++;
    afar_device[wqa].rtt_ms = rtt_ms / afar_device[wqa].rtt_cnt;


    if (show_progress && (wqa == next_device))
        print_radio(next_device, 0);

    return *sessions;
}

void
process_pc_discovery(epkt_header *epkt, econ_header *eh)
{
    int i;

    for (i = 0; i < numOfPcsFound; i++) {
        if (memcmp(pc_device[i]. mac_address, epkt->epkt_src, 6) == 0)
                break;          // we have already received a discovery response
    }
    if (numOfPcsFound >= MAX_PCS)
        return;
    if (i >= numOfPcsFound)
        numOfPcsFound++;

    memcpy(pc_device[i].mac_address, epkt->epkt_src, 6);
    strncpy(pc_device[i].radio_name, eh->econ_data, ntohl(eh->econ_nbytes));
    pc_device[i].device_type = (uint16)ntohl(eh->econ_hw_type);
    pc_device[i].datestamp = time(NULL);
    if (debug_flags & DBFLG_ADAPTER) printf("Saw PC %s\n", pc_device[i].radio_name);
    return;
}

void
print_discovery_header()
{
    int i;
    char horstring[80];
#ifdef LINUX
    char upleft = 32, vertical = 32, middown = 95, horizontal = 95;
#else
    char upleft = (char) 218, vertical = (char) 179, middown = (char) 194, horizontal = (char) 196;
#endif

    for (i = 0; i < 79; i++)
        horstring[i] = horizontal;
    horstring[0] = upleft;
    horstring[7] = middown;
    horstring[27] = middown;
    horstring[47] = middown;
    horstring[55] = middown;
    horstring[75] = 0;

    printf("\r%s\n", horstring);
    printf
        ("%c Node %c %-17s %c %-17s %c %-5s %c %-17s\n",
      vertical, vertical, "Name", vertical, "Firmware", vertical, "S/N", vertical, "Last Connect");
}

void 
print_radio(int device_index, uint16 format)
{
#ifdef LINUX
    // char bullet_char = 46;
    char vertical    = 32;
#else
    char bullet_char = (char) 254;
    char vertical    = (char) 179;
#endif
    char radioserial[32];

    if (device_index == 0) {
        printf("%c %3d  %c %-17s %c %-17s %c %5s %c %-16.16s\n",
           vertical, 0, vertical, "Econ prompt", vertical,
           "", vertical, "", vertical, "");
        return;
    }

    extractSerialNumber(afar_device[device_index].mac_address, radioserial);

    printf("%c %3d%c %c %-17s %c %-16.16s  %c %s %c %-16.16s\n",
           vertical, device_index,
        (afar_device[device_index].device_flags & DEVFLAG_SELECTED ? '*' :' '),
           vertical, afar_device[device_index].radio_name,
           vertical, afar_device[device_index].firmware,
           vertical, radioserial,
           vertical, fmt_session_result(afar_device[device_index].connect_result));
    if (format) 
        printf("%c      %c   %-15s %c %-17s %c pr=%2d %c dev=%4d rtt=%d ms \n",
               vertical, vertical,
               format_ip_addr((void *)&afar_device[device_index].ip_address),
               vertical, fmt_mac_addr(afar_device[device_index].mac_address),
               vertical, afar_device[device_index].proto_version,
               vertical, afar_device[device_index].device_type,
                         afar_device[device_index].rtt_ms);
}

void
show_radios(boolean line_0, uint16 format, int first, int max)
{
    int i, last;

    print_discovery_header();

    if (numOfRadiosFound == 0)
    {
        printf("No devices found.\n");
        return;
    }

    if (line_0) print_radio(0, format);

    last = first + max;
    if (last > (numOfRadiosFound + 1))
        last = numOfRadiosFound + 1;

    for (i = first; i < last; i++)
    {
        if (afar_device[i].datestamp)
        {
            print_radio(i, format);
        }
    }
    if (numOfRadiosFound >= last)
        printf("          [%d more]\n", numOfRadiosFound - last + 1);
}

// Look for a radio
//   The argument may be:
//   - a device name
//   - a device MAC address
//   - a device serial number
//   - a numerical index into the device list
//   Returns:
//   - 0 if no match
//   - index into device table for a match

int
lookup_radio(char *device_name)
{
    int i;
    macAddr Madr;

    for (i = 1; i <= numOfRadiosFound; i++)
        if (strcmp(afar_device[i].radio_name, device_name) == 0)
            return i;

    if (isValidMAC(device_name, Madr))
    {
        for (i = 1; i <= numOfRadiosFound; i++)
            if (memcmp(afar_device[i].mac_address, Madr,
                       sizeof(macAddr)) == 0)
                return i;
    }
    if (isnumeric(device_name))
    {
        int serial = atoi(device_name);

        for (i = 1; i <= numOfRadiosFound; i++)
        {
            unsigned char *rmac =
            (void *) afar_device[i].mac_address;
            int sn2 = (rmac[3] << 16) + (rmac[4] << 8) + rmac[5];

            if (serial == sn2)
                return i;
        }

        i = atoi(device_name);
        if ((i > 0) && (i <= numOfRadiosFound))
            return i;
    }
    return 0;
}

int
select_radio()
{
    int i;
    macAddr Madr;
    char input[52];

    memset(input, 0, sizeof(input));

    assert(numOfRadiosFound < MAX_DEVICES);

    if (option_R) {
        int i = lookup_radio(option_R);
        if (i)
            return i;
    }

retry:
    memset(input, 0, sizeof(input));

    fflush(stdin);
    printf("Select device: ");
    fgets(input, 50, stdin);
    if (feof(stdin))
    {
        printf("\n");
        exit(EXIT_SUCCESS);
    }

    // Trim off trailing blanks (and control characters)
    input[sizeof(input) - 1] = 0;
    for (i = strlen(input) - 1; (i >= 0) && (input[i] <= ' '); i--)
        input[i] = 0;

    if (strlen(input) == 0)
        return 0;
    if (strcmp("0", input) == 0)
        return 0;

    for (i = 1; i <= numOfRadiosFound; i++)
        if (strcmp(afar_device[i].radio_name, input) == 0)
            return i;

    if (isValidMAC(input, Madr))
    {
        for (i = 1; i <= numOfRadiosFound; i++)
            if (memcmp(afar_device[i].mac_address, Madr,
                       sizeof(macAddr)) == 0)
                return i;
    }
    else
    {
        int serial = atoi(input);

        for (i = 1; i <= numOfRadiosFound; i++)
        {
            unsigned char *rmac = (void *) afar_device[i].mac_address;
            int sn2 = (rmac[3] << 16) + (rmac[4] << 8) + rmac[5];
            if (serial == sn2)
                return i;
        }
        i = atoi(input);
        if ((i > 0) && (i <= numOfRadiosFound))
            return i;
    }
    fprintf(stderr, "Invalid selection, try again\n");
    goto retry;
}

// ---------------------------------------------------------------------------

Select device_type_sel[] = {
    { "2",        0 },
    { "3",        0 },
    { "11",       0 },
    { "12",       0 },
    { "2411",     0 },
    { "2423",     0 },
    { "2424",     0 },
    { "2560",     0 },
    { "2561",     0 },
    { "2562",     0 },
    { NULL,       0 }
};

Arg select_args[] = {
    { "device",   STRING,                  0 },
    { "type",     SELECT(device_type_sel), PARSER_DISABLED },
    { "name",     STRING,                  0 },
    { "firmware", STRING,                  0 },
    { NULL,      {UNDEF_ARG},              0 }
};

cmd_ret_t
econ_select(Argument_List *a, ucio_session *s)
{
    char *tmp;
    int   i, type;
    int   selected_before = 0,
          selected_total = 0,
          selected_new = 0;

    for (i = 1; i <= numOfRadiosFound; i++) {
        if (afar_device[i].device_flags & DEVFLAG_SELECTED)
            selected_before += 1;
    }

    tmp = parser_get_value(a, "device");
    if (tmp) {
        if (strcmpi(tmp, "all") == 0)
        {
            for (i = 1; i <= numOfRadiosFound; i++)
                afar_device[i].device_flags |= DEVFLAG_SELECTED;
            goto confirm;
        }
        if (isRange(tmp)) {
            select_range(tmp, true);
            goto do_type;
        }
        if (strchr(tmp, ',')) {
            char *c;
          cl1:  
            c = strchr(tmp, ',');
            if (c) {
                *c = 0; c++;
            }
            if (isRange(tmp)) {
                select_range(tmp, true);
            } else {
                i = lookup_radio(tmp);
                afar_device[i].device_flags |= DEVFLAG_SELECTED;
            }
            tmp = c;
            if (tmp) goto cl1;
            goto do_type;
        } // end if comma-list

        i = lookup_radio(tmp);
        if (i == 0) {
            fprintf(stderr, "Cannot identify device \"%s\"\n", tmp);
            return CMD_FAILED;
        }
        if ((afar_device[i].device_flags & DEVFLAG_SELECTED) == 0)
            selected_new++;
        afar_device[i].device_flags |= DEVFLAG_SELECTED;
    }
    do_type:
    tmp = parser_get_value(a, "type");
    if (tmp) {
        type = atoi(tmp);
        for (i = 1; i <= numOfRadiosFound; i++) {
            if (afar_device[i].device_type == type)
            {
                if ((afar_device[i].device_flags & DEVFLAG_SELECTED) == 0)
                    selected_new++;
                afar_device[i].device_flags |= DEVFLAG_SELECTED;
            }
        }
    }
    tmp = parser_get_value(a, "firmware");
    if (tmp) {
        for (i = 1; i <= numOfRadiosFound; i++) {
            if (strnicmp(afar_device[i].firmware, tmp, strlen(tmp)) == 0)
            {
                if ((afar_device[i].device_flags & DEVFLAG_SELECTED) == 0)
                    selected_new++;
                afar_device[i].device_flags |= DEVFLAG_SELECTED;
            }
        }
    }
    tmp = parser_get_value(a, "name");
    if (tmp) {
        for (i = 1; i <= numOfRadiosFound; i++) {
            if (strnicmp(afar_device[i].radio_name, tmp, strlen(tmp)) == 0)
            {
                if ((afar_device[i].device_flags & DEVFLAG_SELECTED) == 0)
                    selected_new++;
                afar_device[i].device_flags |= DEVFLAG_SELECTED;
            }
        }
    }
confirm:
    for (i = 1; i <= numOfRadiosFound; i++) {
        if (afar_device[i].device_flags & DEVFLAG_SELECTED)
            selected_total++;
    }
    selected_new = selected_total - selected_before;
    if (selected_total == 0)
        printf("No devices selected\n");
    else if (selected_new)
    {
        if (selected_before == 0)
            printf("%d units selected\n", selected_new);
        else
            printf("%d additional units selected - %d selected now\n",
                   selected_new, selected_total);
    }
    else
        printf("No additional devices selected - %d selected previously\n",
                selected_total);
    return CMD_SUCCESS;
}

void
select_range(char *str, boolean on_off)
{
    int   i, first, last;
    char *d;
    char  tmp[20];

    strncpy(tmp, str, 20);
    tmp[19] = 0;

    d = strchr(tmp, '-');
    if (d == NULL) return;
    *d = 0;
    first = atoi(tmp);
    last = atoi(d+1);

    if (last > numOfRadiosFound) last = numOfRadiosFound;

    for (i = first; i <= last; i++)
        if (on_off) afar_device[i].device_flags |=  DEVFLAG_SELECTED;
        else        afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
}

cmd_ret_t
econ_deselect(Argument_List *a, ucio_session *s)
{
    char *tmp;
    int   i, type;
    int   selected_before = 0,
          selected_total = 0,
          desel_new = 0;

    for (i = 1; i <= numOfRadiosFound; i++) {
        if (afar_device[i].device_flags & DEVFLAG_SELECTED)
            selected_before += 1;
    }

    tmp = parser_get_value(a, "device");
    if (tmp) {
        if (strcmpi(tmp, "all") == 0)
        {
            for (i = 1; i <= numOfRadiosFound; i++)
                afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
            goto confirm;
        }
        if (isRange(tmp)) {
            select_range(tmp, false);
            goto do_type;
        }
        if (strchr(tmp, ',')) {
            char *c;
          cl1:  
            c = strchr(tmp, ',');
            if (c) {
                *c = 0; c++;
            }
            if (isRange(tmp)) {
                select_range(tmp, false);
            } else {
                i = lookup_radio(tmp);
                afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
            }
            tmp = c;
            if (tmp) goto cl1;
            goto do_type;
        } // end if comma-list

        i = lookup_radio(tmp);
        if (i == 0) {
            fprintf(stderr, "Cannot identify device \"%s\"\n", tmp);
            return CMD_FAILED;
        }
        if (afar_device[i].device_flags & DEVFLAG_SELECTED) 
            desel_new++;
        afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
    }
    do_type:
    tmp = parser_get_value(a, "type");
    if (tmp) {
        type = atoi(tmp);
        for (i = 1; i < numOfRadiosFound; i++) {
            if ((afar_device[i].device_type == type)
             && (afar_device[i].device_flags & DEVFLAG_SELECTED))
            {
                afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
                desel_new++;
            }
        }
    }
    tmp = parser_get_value(a, "firmware");
    if (tmp) {
        for (i = 1; i < numOfRadiosFound; i++) {
            if ((strnicmp(afar_device[i].firmware, tmp, strlen(tmp)) == 0)
             && (afar_device[i].device_flags & DEVFLAG_SELECTED))
            {
                afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
                desel_new++;
            }
        }
    }
    tmp = parser_get_value(a, "name");
    if (tmp) {
        for (i = 1; i < numOfRadiosFound; i++) {
            if ((strnicmp(afar_device[i].radio_name, tmp, strlen(tmp)) == 0)
             && (afar_device[i].device_flags & DEVFLAG_SELECTED))
            {
                afar_device[i].device_flags &= ~DEVFLAG_SELECTED;
                desel_new++;
            }
        }
    }
confirm:
    for (i = 1; i <= numOfRadiosFound; i++) {
        if (afar_device[i].device_flags & DEVFLAG_SELECTED)
            selected_total++;
    }
    desel_new = selected_before - selected_total;
    if (selected_total == 0)
        printf("No devices are now selected\n");
    else if (desel_new)
        printf("Deselected %d devices, leaving %d still selected\n",
                desel_new, selected_total);
    else
        printf("%d devices are still selected (no change)\n",
                selected_total);
    return CMD_SUCCESS;
}

boolean
isRange(char *str)
{
    char *a, *b;
    char tmp[20];

    strncpy(tmp, str, 20);
    tmp[19] = 0;

    a = strchr(tmp, '-');
    if (!a) return false;
    *a = 0;
    if (strlen(tmp) == 0) return false;

    for (b = tmp; *b; b++)
        if ((*b < '0') || (*b > '9')) return false;

    a++;
    if (strlen(a) == 0) return false;

    for (b = a; *b; b++)
        if ((*b < '0') || (*b > '9')) return false;

    return true;
}

// End
