devsh.c 8.36 KB
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "ultrahost.h"
#include "PR/bbfs.h"
#include "PR/R4300.h"
#include "PR/bcp.h"

#include <readline/readline.h>
#include <readline/history.h>

#include "devmon.h"

static char devsh_version[] = VERSION;

/*
 * send commands to remote monitor and receive responses
 * basic flow is:
 *     send 2-word request <command, length of data>
 *     send data
 *     receive 2-word response <response is 255-cmd, length of data>
 *     receive data if any
 */

static int fd = 255;

/*XXXblythe round up transfers to a multiple of 4 for irix */
#define RND(x)		(((x)+3)&~3)

static const char*
basename(const char* s) {
    char* p;
    if ((p = strrchr(s, '/')) && p[1]) return p+1;
    return s;; 
}

static int
send_cmd(const void* buf, int len) {
    int rv, i;

    for(i = 0; i < len/4; i++)
	*((unsigned int*)buf+i) = htonl(*((unsigned int*)buf+i));
    if ((rv = uhWriteGame(fd, (void*)buf, len)) == -1) {
        perror("uhWriteGame");
	exit(1);
    }
    return rv;
}

static int
read_rsp(void* buf, int len) {
    int rv, i;
    if ((rv = uhReadGame(fd, buf, len)) == -1) {
        perror("uhReadGame");
	exit(1);
    }
    for(i = 0; i < len/4; i++)
	*((unsigned int*)buf+i) = ntohl(*((unsigned int*)buf+i));
    return rv;
}

static int
send_data(const void* buf, int len) {
    int rv;
    if ((rv = uhWriteGame(fd, (void*)buf, len)) == -1) {
        perror("uhWriteGame");
	exit(1);
    }
    return rv;
}

static int
read_data(void* buf, int len) {
    int rv;
    if ((rv = uhReadGame(fd, buf, len)) == -1) {
        perror("uhReadGame");
	exit(1);
    }
    return rv;
}

static int
delete_file(const char* file, int report) {
    unsigned int hbuf[2];
    hbuf[0] = REQ_DELETE_FILE;
    hbuf[1] = strlen(file)+1;
    send_cmd(hbuf, 8);
    send_data(file, RND(strlen(file)+1));
    read_rsp(hbuf, 8);
    if (hbuf[0] != 255-REQ_DELETE_FILE || (report && hbuf[1] != 0)) {
	fprintf(stderr, "delete %s failed\n", file);
    }
    return 0;
}

static int
move_file(const char* old, const char* new) {
    unsigned int hbuf[2];
    hbuf[0] = REQ_RENAME_FILE;
    hbuf[1] = strlen(old)+1;
    send_cmd(hbuf, 8);
    send_data(old, RND(strlen(old)+1));
    hbuf[0] = htonl(strlen(new)+1);
    send_data(hbuf, 4);
    send_data(new, RND(strlen(new)+1));
    read_rsp(hbuf, 8);
    if (hbuf[0] != 255-REQ_RENAME_FILE || hbuf[1] != 0) {
	fprintf(stderr, "move %s %s failed %d\n", new, old, hbuf[1]);
    }
    return 0;
}

static int
write_file(const char* in, const char* out) {
    FILE* fp;
    unsigned char* b;
    unsigned int hbuf[2];
    struct stat sb;
    unsigned int x, n, i;
    struct timeval tv1, tv2;
    float t;
    if ((fp = fopen(in, "r")) == NULL) {
	perror(in);
	return -1;
    }
    fstat(fileno(fp), &sb);
    n = RND(sb.st_size);
    b = malloc(n);
    fread(b, sb.st_size, 1, fp);
    fclose(fp);
    gettimeofday(&tv1, NULL);
    hbuf[0] = REQ_WRITE_FILE;
    hbuf[1] = sb.st_size;
    send_cmd(hbuf, 8);
    x = htonl(strlen(out)+1);
    send_data(&x, 4);
    send_data(out, RND(ntohl(x)));
    /* read first response */
    read_rsp(hbuf, 8);
    if (hbuf[0] != 255-REQ_WRITE_FILE || hbuf[1] != 0) {
	fprintf(stderr, "file write of %s failed\n", in);
	free(b);
	return -1;
    }
    for(i = 0; i < n; i += BB_FL_BLOCK_SIZE)
	send_data(b+i, n-i < BB_FL_BLOCK_SIZE ? n-i : BB_FL_BLOCK_SIZE);
    free(b);
    /* read second response */
    read_rsp(hbuf, 8);
    if (hbuf[0] != 255-REQ_WRITE_FILE || hbuf[1] != 0) {
	fprintf(stderr, "file write of %s failed\n", in);
	return -1;
    }
    gettimeofday(&tv2, NULL);
    t = (float)(tv2.tv_sec-tv1.tv_sec) + (tv2.tv_usec-tv1.tv_usec)/1000000.f;
    printf("%.1f KB %.1f sec %.1f KB/s\n", sb.st_size/1024.f, t, sb.st_size/1000.f/t);
    return 0;
}

static int
read_file(const char* in, const char* out) {
    FILE* fp;
    unsigned char* b;
    unsigned int hbuf[2];
    int len, n;
    if ((fp = fopen(out, "w")) == NULL) {
	perror(out);
	return -1;
    }
    hbuf[0] = REQ_READ_FILE;
    hbuf[1] = strlen(in)+1;
    send_cmd(hbuf, 8);
    send_data(in, RND(strlen(in)+1));
    read_rsp(hbuf, 8);
    if (hbuf[0] != 255-REQ_READ_FILE || (hbuf[1]&0x80000000)) {
	fprintf(stderr, "file read of %s failed\n", in);
	fclose(fp);
	return -1;
    }
    b = malloc(n = RND(hbuf[1]));
    for(len = 0; len < n; len += BB_FL_BLOCK_SIZE) {
	read_data(b+len, n-len < BB_FL_BLOCK_SIZE ? n-len : BB_FL_BLOCK_SIZE);
    }
    fwrite(b, hbuf[1], 1, fp);
    fclose(fp);
    free(b);
    return 0;
}

int
main(int argc, char **argv) {
    char *p, file[256], file2[256];
    char *line=NULL, *expansion = NULL; 
    int result;
    char history_file[256];
    unsigned int hbuf[2];
    int n, i, rv;

    if ((fd = uhOpenGame("/tmp/u64_data")) == -1) {
	perror("uhOpenGame");
    	exit(1);
    }

    if (getenv("HOME")) {
        strcpy(history_file, getenv("HOME"));
    } else {
        history_file[0] = '.';
        history_file[1] = 0;
    }
    strcat(history_file, "/.bb_history");
    read_history(history_file);

    for(;;) {
next:
        if (line) free(line);
        if ((line = readline("> ")) == NULL) break;
        expansion = NULL;
        result = history_expand(line, &expansion);
        if (expansion) {
            free(line);
            line = expansion;
        }
        if (result != 0) {
            printf("%s\n", line);
            if (result != 1)
                continue;
        }
	if ((p = strrchr(line, '\n'))) *p = '\0';
	for(p = line; *p && isspace(*p); ++p) ;
        if (*p) add_history(p);
	memset(hbuf, 0, sizeof hbuf);
	memset(file, 0, sizeof file);
	memset(file2, 0, sizeof file2);
	if (*p == 'h' || *p == '?') { /* help */
	    printf("\th             - help\n"
		   "\ta file [name] - add file [name] to BBCard\n"
		   "\tc file [name] - copy file from BBCard to host\n"
		   "\td file        - delete file from BBCard\n"
		   "\tl             - list files\n"
		   "\tm old new     - rename old to new\n"
		   "\tp             - ping BB\n"
		   "\tv             - print version information\n"
		   "\tx file        - load and execute rom file from BBCard\n"
		   "\tq             - quit\n");
	} else if (*p == 'q') { /* quit */
	    break;
	} else if (*p == 'v') { /* version */
	    printf("%s built %s\n", argv[0], devsh_version);
	} else if (*p == 'p') { /* ping */
	    hbuf[0] = REQ_PING;
	    send_cmd(hbuf, 8);
            read_rsp(hbuf, 8);
	    if (hbuf[0] != 255-REQ_PING || hbuf[1] != 0) {
		fprintf(stderr, "sync loss\n");
		return 1;
	    }
	} else if (*p == 'a') { /* add file */
	    if ((rv = sscanf(p+1, "%s %s", file, file2)) < 1) {
		fprintf(stderr, "usage: a file [name]\n");
		goto next;
	    }
	    write_file(file, rv == 1 ? basename(file) : file2);
	} else if (*p == 'c') { /* copy file */
	    if ((rv = sscanf(p+1, "%s %s", file, file2)) < 1) {
		fprintf(stderr, "usage: c file [name]\n");
		goto next;
	    }
	    read_file(file, rv == 1 ? file : file2);
	} else if (*p == 'd') { /* delete file */
	    if (sscanf(p+1, "%s", file) != 1) {
		fprintf(stderr, "usage: d file\n");
		goto next;
	    }
	    delete_file(file, 1);
	} else if (*p == 'm') { /* move file */
	    if ((rv = sscanf(p+1, "%s %s", file, file2)) < 2) {
		fprintf(stderr, "usage: m old new\n");
		goto next;
	    }
	    move_file(file, file2);
	} else if (*p == 'l') { /* list directory */
	    hbuf[0] = REQ_READ_DIR;
	    send_cmd(hbuf, 8);
	    read_rsp(hbuf, 8);
printf("read dir %d\n", hbuf[1]);
	    if (hbuf[0] != 255-REQ_READ_DIR || hbuf[1] & 0x80000000) {
		fprintf(stderr, "read directory failed %d\n", hbuf[1]);
	    } else {
		int size;
		for(i = 0; i < hbuf[1]; i++) {
		    read_data(file, 16);
		    read_data(&size, 4); size = ntohl(size);
		    printf("%.12s %d\n", file, size);
		}
	    }
	} else if (*p == 'x') { /* execute BBCard file */
	    if (sscanf(p+1, "%s%n", file, &n) != 1) {
		fprintf(stderr, "usage: x file\n");
		goto next;
	    }
	    hbuf[0] = REQ_EXEC_FILE;
	    hbuf[1] = strlen(file)+1;
	    send_cmd(hbuf, 8);
	    send_data(file, RND(strlen(file)+1));
	    read_rsp(hbuf, 8);
	    if (hbuf[0] != 255-REQ_EXEC_FILE || hbuf[1] != 0) {
		fprintf(stderr, "exec %s failed\n", file);
	    }
	} else {
	    fprintf(stderr, "unknown command '%s'\n", p);
	}
    }

    write_history(history_file);
    history_truncate_file(history_file, 100);

    if (uhCloseGame(fd) != 0) {
	perror("uhCloseGame");
	exit(1);
    }
    return 0;
}