/*
 *      bridge.c from Access Point SNMP Utils for Linux
 *
 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
 * Copyright (c) 2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 from
 * June 1991 as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "ap-utils.h"

#define IPADDR _("[I] IP: ")
#define NETMASK _("[N] Netmask: ")
#define GATEWAY _("[G] Gateway: ")
#define IP_FILTER _("[F] Filter non-IP traffic: ")
#define PR_PORT _("[P] Primary port: ")
#define SB_ATTMAC _("Attached station MAC: ")
#define DHCP _("[D] DHCP client: ")
#define OPER _("[O] Operational mode: ")
#define REMOTE_MAC _("[M] Preferred BSSID (remote MAC addr.): ")
#define CF_PORT _("[C] Configuration-enabled port(s): ")
#define TRAP_PORT _("[T] Trap-sending port: ")
#define FW_BCAST _("[R] Forward broadcast traffic: ")
#define SB_BCAST _("[B] Isolate wireless clients (broadcast traffic): ")
#define SB_UCAST _("[U] Isolate wireless clients (unicast traffic): ")
#define HELP _("INGFPDOMSCRBUT - set; W - write conf; Q - quit to menu")

extern short ap_type, ap_vendorext;


void bridging()
{

char *bridge_modes[6] = {
    _("Wireless Bridge Point to MultiPoint"),
    _("Access Point"),
    _("Access Point client"),
    _("Wireless Bridge Point to Point"),
    _("Repeater"),
    _("unknown")
};

    char sysTrapSwitch[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x01, 0x03, 0x00
    };

    char operIPAddress[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x01, 0x00
    };
    char operIPMask[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x02, 0x00
    };
    char operEthernetAddress[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x03, 0x00
    };
    char operGateway[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x04, 0x00
    };
    char operDHCP[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x05, 0x00
    };
    char operPrimaryPort[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x06, 0x00
    };

    /* This one is ATMEL410 SBRIDGES MIB and ATMEL12350 all MIBs specific. */
    char operConfigurationPort[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x02, 0x07, 0x00
    };

    /* This one is ATMEL12350 GEMTEK MIB and ATMEL12350 EZYNET MIB specific. */
    char operTrapPort[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x01, 0x02, 0x08, 0x00
    };

    char operIPFilter[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x03, 0x01, 0x00
    };

    /* These 3 are ATMEL410 SBRIDGES MIB and ATMEL12350 all MIBs specific. */
    char operForwarbBcast[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x03, 0x02, 0x00
    };
    char operSendBackBcast[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x03, 0x03, 0x00
    };
    char operSendBackUnicast[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x03, 0x04, 0x00
    };

    char bridgeOperationalMode[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x01, 0x00
    };
    char bridgeRemoteBridgeBSSID[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x02, 0x00
    };

    extern WINDOW *main_sub;
    varbind varbinds[15];
    struct in_addr ip, mask, gw;
    unsigned char message[1024], filter, primary_port, dhcp, RemoteBSSID[6],
	bridge_mode, traps, config_port, fw_bcast, sb_bcast,
	sb_ucast;
    char m_filter = 0, m_bridge_mode = 0, m_primary_port = 0, m_traps = 0,
	m_dhcp = 0, m_ip = 0, m_mask = 0, m_gw = 0, m_remote_bssid = 0,
	m_config_port = 0, m_trap_port = 0, m_fw_bcast = 0, m_sb_bcast = 0,
	m_sb_ucast = 0;
    char *pr_ports[2] = {
	_("Ethernet"),
	_("Wireless")
    }, *cf_ports[3] = {
	_("Ethernet"),
	_("Wireless (can be risky)"),
	_("Both")
    };
    int i;
    unsigned int trap_port = 0;

    if (ap_type == ATMEL12350) {
	sysTrapSwitch[5] = 0xE0;
	sysTrapSwitch[6] = 0x3E;
    	operIPAddress[5] = 0xE0;
	operIPAddress[6] = 0x3E;
	operIPMask[5] = 0xE0;
	operIPMask[6] = 0x3E;
	operEthernetAddress[5] = 0xE0;
	operEthernetAddress[6] = 0x3E;
	operGateway[5] = 0xE0;
	operGateway[6] = 0x3E;
	operDHCP[5] = 0xE0;
    	operDHCP[6] = 0x3E;
	operPrimaryPort[5] = 0xE0;
	operPrimaryPort[6] = 0x3E;
	operConfigurationPort[5] = 0xE0;
	operConfigurationPort[6] = 0x3E;
     	operIPFilter[5] = 0xE0;
 	operIPFilter[6] = 0x3E;
	operForwarbBcast[5] = 0xE0;
	operForwarbBcast[6] = 0x3E;
	operSendBackBcast[5] = 0xE0;
	operSendBackBcast[6] = 0x3E;
	operSendBackUnicast[5] = 0xE0;
	operSendBackUnicast[6] = 0x3E;
	bridgeOperationalMode[5] = 0xE0;
	bridgeOperationalMode[6] = 0x3E;
	bridgeRemoteBridgeBSSID[5] = 0xE0;
	bridgeRemoteBridgeBSSID[6] = 0x3E;
    }

    for (i = 0; i < 15; i++) {
	varbinds[i].type = NULL_VALUE;
	varbinds[i].len_val = 0;
	varbinds[i].len_oid = sizeof(sysTrapSwitch);
    }

    i = 0;

    varbinds[i++].oid = sysTrapSwitch;
    varbinds[i++].oid = operIPAddress;
    varbinds[i++].oid = operIPMask;
    varbinds[i++].oid = operEthernetAddress;
    varbinds[i++].oid = operGateway;
    varbinds[i++].oid = operDHCP;
    varbinds[i++].oid = operPrimaryPort;
    varbinds[i++].oid = operIPFilter;
    varbinds[i++].oid = bridgeOperationalMode;
    varbinds[i++].oid = bridgeRemoteBridgeBSSID;
    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
	(ap_type == ATMEL12350)) {
	varbinds[i++].oid = operConfigurationPort;
	varbinds[i++].oid = operForwarbBcast;
	varbinds[i++].oid = operSendBackBcast;
	varbinds[i++].oid = operSendBackUnicast;
    }
    if (ap_type == ATMEL12350 &&
	(ap_vendorext == GEMTEK || ap_vendorext == EZYNET)) {
	    varbinds[i++].oid = operTrapPort;
    }

    print_help(WAIT_RET);
    if (snmp(varbinds, i, GET) < i) {
	print_helperr(ERR_RET);
	goto exit;
    }

    print_top(NULL, _("Bridging"));

    sprintf(message, "%s%02X%02X%02X%02X%02X%02X", MAC,
	    varbinds[3].value[0] & 0xFF,
	    varbinds[3].value[1] & 0xFF,
	    varbinds[3].value[2] & 0xFF,
	    varbinds[3].value[3] & 0xFF,
	    varbinds[3].value[4] & 0xFF,
	    varbinds[3].value[5] & 0xFF);
    mvwaddstr(main_sub, 0, 0, message);

    memcpy(&ip.s_addr, varbinds[1].value, 4);
    sprintf(message, "%s%s", IPADDR, inet_ntoa(ip));
    mvwaddstr(main_sub, 1, 0, message);

    memcpy(&mask.s_addr, varbinds[2].value, 4);
    sprintf(message, "%s%s", NETMASK, inet_ntoa(mask));
    mvwaddstr(main_sub, 1, 24, message);

    memcpy(&gw.s_addr, varbinds[4].value, 4);
    sprintf(message, "%s%s", GATEWAY, inet_ntoa(gw));
    mvwaddstr(main_sub, 2, 0, message);

    filter = *(varbinds[7].value);
    sprintf(message, "%s%s", IP_FILTER, (filter == 1) ? ON : OFF);
    mvwaddstr(main_sub, 3, 0, message);

/*
    if (ap_type == ATMEL410 && ap_vendorext == SBRIDGES) {
	sprintf(message, "%s%02X%02X%02X%02X%02X%02X", SB_ATTMAC,
		*(varbinds[6].value +0) & 0xFF, *(varbinds[6].value +1) & 0xFF,
		*(varbinds[6].value +2) & 0xFF, *(varbinds[6].value +3) & 0xFF,
		*(varbinds[6].value +4) & 0xFF, *(varbinds[6].value +5) & 0xFF);
    } else {
*/
	primary_port = *(varbinds[6].value);
	if (primary_port < 1 || primary_port > 2) {
	    primary_port = 1;
	}
	sprintf(message, "%s%s", PR_PORT, pr_ports[primary_port - 1]);
/*
    }
*/
    mvwaddstr(main_sub, 4, 0, message);

    dhcp = *(varbinds[5].value);
    sprintf(message, "%s%s", DHCP, (dhcp == 1) ? ON : OFF);
    mvwaddstr(main_sub, 5, 0, message);

    memcpy(RemoteBSSID, varbinds[9].value, 6);
    if ((bridge_mode = *(varbinds[8].value)) != 2) {
	sprintf(message, "%s%02X%02X%02X%02X%02X%02X", REMOTE_MAC,
		*(RemoteBSSID + 0) & 0xFF, *(RemoteBSSID + 1) & 0xFF,
		*(RemoteBSSID + 2) & 0xFF, *(RemoteBSSID + 3) & 0xFF,
		*(RemoteBSSID + 4) & 0xFF, *(RemoteBSSID + 5) & 0xFF);
	mvwaddstr(main_sub, 7, 0, message);
    }
    if (bridge_mode > 5)
	bridge_mode  = 6;
    sprintf(message, "%s%s", OPER, bridge_modes[bridge_mode - 1]);
    mvwaddstr(main_sub, 6, 0, message);

    traps = *(varbinds[0].value);
    sprintf(message, "%s%s", TRAPS, (traps == 1) ? ON : OFF);
    mvwaddstr(main_sub, 8, 0, message);

    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
	ap_type == ATMEL12350) {
	config_port = *(varbinds[10].value);
	if (config_port == 0) /* happens with ATMEL12350 EZYNET firmware only */
	    config_port = 3;
	sprintf(message, "%s%s", CF_PORT, cf_ports[config_port - 1]);
	mvwaddstr(main_sub, 9, 0, message);

	fw_bcast = *(varbinds[11].value);
	sprintf(message, "%s%s", FW_BCAST, (fw_bcast == 1) ? ON : OFF);
	mvwaddstr(main_sub, 10, 0, message);

	sb_bcast = *(varbinds[12].value);
	sprintf(message, "%s%s", SB_BCAST, (sb_bcast == 1) ? ON : OFF);
	mvwaddstr(main_sub, 11, 0, message);

	sb_ucast = *(varbinds[13].value);
	sprintf(message, "%s%s", SB_UCAST, (sb_ucast == 1) ? ON : OFF);
	mvwaddstr(main_sub, 12, 0, message);
    }

    if (ap_type == ATMEL12350 &&
	(ap_vendorext == GEMTEK || ap_vendorext == EZYNET)) {
	for (i = 0; i < varbinds[14].len_val;
	    trap_port += varbinds[14].value[i] *
	    (1 << ((varbinds[14].len_val - i - 1) * 8)), i++);
	sprintf(message, "%s%u", TRAP_PORT, trap_port);
	mvwaddstr(main_sub, 13, 0, message);
    }

    wrefresh(main_sub);
    noecho();

    print_help(HELP);
    while (1) {
	switch (getch()) {
	case 'I':
	case 'i':
	    get_ip(&ip, 1, strlen(IPADDR), HELP);
	    m_ip = 1;
	    continue;
	case 'N':
	case 'n':
	    get_mask(&mask, 1, strlen(IPADDR) + 16 + strlen(NETMASK), HELP);
	    m_mask = 1;
	    continue;
	case 'G':
	case 'g':
	    get_ip(&gw, 2, strlen(GATEWAY), HELP);
	    m_gw = 1;
	    continue;
	case 'F':
	case 'f':
	    filter = on_off(3, strlen(IP_FILTER));
	    clear_main_new(3, 4);
	    print_menusel(3, 0, IP_FILTER, (filter == 1) ? ON : OFF);
	    m_filter = 1;
	    continue;
	case 'P':
	case 'p':
/*
	    if (ap_type == ATMEL410 && ap_vendorext == SBRIDGES)
		continue;
*/
	    primary_port = menu_choose(4, strlen(PR_PORT), pr_ports, 2) + 1;
	    clear_main_new(4, 5);
	    print_menusel(4, 0, PR_PORT, pr_ports[primary_port - 1]);
	    m_primary_port = 1;
	    continue;
	case 'D':
	case 'd':
	    dhcp = on_off(5, strlen(DHCP));
	    clear_main_new(5, 6);
	    print_menusel(5, 0, DHCP, (dhcp == 1) ? ON : OFF);
	    m_dhcp = 1;
	    continue;
	case 'O':
	case 'o':
	    bridge_mode = menu_choose(6, strlen(OPER), bridge_modes, 5) + 1;
	    clear_main_new(6, 8);
	    print_menusel(6, 0, OPER, bridge_modes[bridge_mode - 1]);
	    if (bridge_mode != 2) {
		sprintf(message, "%02X%02X%02X%02X%02X%02X",
			*(RemoteBSSID + 0) & 0xFF,
			*(RemoteBSSID + 1) & 0xFF,
			*(RemoteBSSID + 2) & 0xFF,
			*(RemoteBSSID + 3) & 0xFF,
			*(RemoteBSSID + 4) & 0xFF,
			*(RemoteBSSID + 5) & 0xFF);
		print_menusel(7, 0, REMOTE_MAC, message);
	    }
	    m_bridge_mode = 1;
	    continue;
	case 'M':
	case 'm':
	    if (bridge_mode == 2)
		continue;

	    get_mac(RemoteBSSID, 7, strlen(REMOTE_MAC));
/*	    mvwaddstr(main_sub, 7, 21, "  :  :  :  :  :  ");
	    for (i = 0; i < 6; i++) {
		get_value(message, 7, 21 + i * 3, 3, ANY_STRING, 0, 0, NULL);
		RemoteBSSID[i] = strtol(message, NULL, 16);
	    }
	    sprintf(message, "%s%02X%02X%02X%02X%02X%02X", REMOTE_MAC,
		*(RemoteBSSID + 0) & 0xFF, *(RemoteBSSID + 1) & 0xFF,
		*(RemoteBSSID + 2) & 0xFF, *(RemoteBSSID + 3) & 0xFF,
		*(RemoteBSSID + 4) & 0xFF, *(RemoteBSSID + 5) & 0xFF);
*/
	    m_remote_bssid = 1;
	    continue;
	case 'S':
	case 's':
	    traps = on_off(8, strlen(TRAPS));
	    clear_main_new(8, 9);
	    print_menusel(8, 0, TRAPS, (traps == 1) ? ON : OFF);
	    m_traps = 1;
	    continue;
	case 'C':
	case 'c':
	    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
		ap_type == ATMEL12350) {
		config_port = menu_choose(9, strlen(CF_PORT), cf_ports, 3) + 1;
		clear_main_new(9, 10);
		print_menusel(9, 0, CF_PORT, cf_ports[config_port - 1]);
		m_config_port = 1;
            }
	    continue;
	case 'R':
	case 'r':
	    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
		ap_type == ATMEL12350) {
		fw_bcast = on_off(10, strlen(FW_BCAST));
		clear_main_new(10, 11);
		print_menusel(10, 0, FW_BCAST, (fw_bcast == 1) ? ON : OFF);
		m_fw_bcast = 1;
	    }
	    continue;
	case 'B':
	case 'b':
	    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
		ap_type == ATMEL12350) {
		sb_bcast = on_off(11, strlen(SB_BCAST));
		clear_main_new(11, 12);
		print_menusel(11, 0, SB_BCAST, (sb_bcast == 1) ? ON : OFF);
		m_sb_bcast = 1;
	    }
	    continue;
	case 'U':
	case 'u':
	    if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
		ap_type == ATMEL12350) {
		sb_ucast = on_off(12, strlen(SB_UCAST));
		clear_main_new(12, 13);
		print_menusel(12, 0, SB_UCAST, (sb_ucast == 1) ? ON : OFF);
		m_sb_ucast = 1;
	    }
	    continue;
	case 'T':
	case 't':
	    if (ap_type == ATMEL12350 &&
		(ap_vendorext == GEMTEK || ap_vendorext == EZYNET)) {
		get_value(message, 13, strlen(TRAP_PORT), 6, INT_STRING,
		    0, 65535, HELP);
		trap_port = atoi(message);
        	m_trap_port = 1;
	    }
	    continue;
	case 'w':
	case 'W':
	    i = 0;
	    if (m_filter) {
		varbinds[i].oid = operIPFilter;
		varbinds[i].len_oid = sizeof(operIPFilter);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &filter;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_primary_port) {
		varbinds[i].oid = operPrimaryPort;
		varbinds[i].len_oid = sizeof(operPrimaryPort);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &primary_port;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_dhcp) {
		varbinds[i].oid = operDHCP;
		varbinds[i].len_oid = sizeof(operDHCP);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &dhcp;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_ip) {
		varbinds[i].oid = operIPAddress;
		varbinds[i].len_oid = sizeof(operIPAddress);
		ip.s_addr = htonl(ip.s_addr);
		ip.s_addr = swap4(ip.s_addr);
		varbinds[i].value = (char *) &ip.s_addr;
		varbinds[i].len_val = 4;
		varbinds[i].type = INT_VALUE;
		i++;
	    }
	    if (m_mask) {
		varbinds[i].oid = operIPMask;
		varbinds[i].len_oid = sizeof(operIPMask);
		mask.s_addr = htonl(mask.s_addr);
		mask.s_addr = swap4(mask.s_addr);
		varbinds[i].value = (char *) &mask.s_addr;
		varbinds[i].len_val = 4;
		varbinds[i].type = INT_VALUE;
		i++;
	    }
	    if (m_gw) {
		varbinds[i].oid = operGateway;
		gw.s_addr = htonl(gw.s_addr);
		gw.s_addr = swap4(gw.s_addr);
		varbinds[i].len_oid = sizeof(operGateway);
		varbinds[i].value = (char *) &gw.s_addr;
		varbinds[i].len_val = 4;
		varbinds[i].type = INT_VALUE;
		i++;
	    }
	    if (m_traps) {
		varbinds[i].oid = sysTrapSwitch;
		varbinds[i].len_oid = sizeof(sysTrapSwitch);
		varbinds[i].value = &traps;
		varbinds[i].len_val = 1;
		varbinds[i].type = INT_VALUE;
		i++;
	    }
	    if (m_bridge_mode) {
		varbinds[i].oid = bridgeOperationalMode;
		varbinds[i].len_oid = sizeof(bridgeOperationalMode);
		varbinds[i].value = (char *) &bridge_mode;
		varbinds[i].len_val = 1;
		varbinds[i].type = INT_VALUE;
		i++;
	    }
	    if (m_remote_bssid) {
		varbinds[i].oid = bridgeRemoteBridgeBSSID;
		varbinds[i].len_oid = sizeof(bridgeRemoteBridgeBSSID);
		varbinds[i].value = RemoteBSSID;
		varbinds[i].len_val = 6;
		varbinds[i].type = STRING_VALUE;
		i++;
	    }
	    if (m_config_port) {
		varbinds[i].oid = operConfigurationPort;
		varbinds[i].len_oid = sizeof(operConfigurationPort);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &config_port;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_fw_bcast) {
		varbinds[i].oid = operForwarbBcast;
		varbinds[i].len_oid = sizeof(operForwarbBcast);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &fw_bcast;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_sb_bcast) {
		varbinds[i].oid = operSendBackBcast;
		varbinds[i].len_oid = sizeof(operSendBackBcast);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &sb_bcast;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_sb_ucast) {
		varbinds[i].oid = operSendBackUnicast;
		varbinds[i].len_oid = sizeof(operSendBackUnicast);
		varbinds[i].type = INT_VALUE;
		varbinds[i].value = (char *) &sb_ucast;
		varbinds[i].len_val = 1;
		i++;
	    }
	    if (m_trap_port) {
		int len_val;

		len_val = (trap_port > 0x7fff) ? 3 : (trap_port > 0x7f) ? 2 : 1;
		varbinds[i].oid = operTrapPort;
		varbinds[i].len_oid = sizeof(operTrapPort);
		varbinds[i].type = STRING_VALUE;
		varbinds[i].value = (char *) &trap_port;
		varbinds[i].len_val = len_val;
		i++;
	    }

	    print_help(WAIT_SET);
	    if (snmp(varbinds, i, SET) <= 0) {
		print_helperr(ERR_SET);
	    } else {
		wbkgd(main_sub, A_NORMAL);
		wrefresh(main_sub);
           	print_help(DONE_SET);
	    }
    	    goto exit;
	case 'Q':
	case 'q':
	    goto quit;
	}
	continue;
    }
  exit:
    getch();
  quit:
    print_top(NULL, NULL);
    clear_main(0);
}
