ftp.nice.ch/pub/next/unix/communication/pcomm.NIHS.bs.tar.gz#/pcomm/Source/x_batch.c

This is x_batch.c in view mode; [Download] [Up]

/*
 * Routines to support the batch protocols.
 */

#include <stdio.h>
#include <ctype.h>
#include <curses.h>
#include "config.h"
#include "misc.h"
#include "xmodem.h"

/*
 * Send the file name for the modem7 batch.  Only uses 11 characters
 * of the filename.  Returns zero on success or the standard error codes.
 */

int
send_modem7(win, name)
WINDOW *win;
char *name;
{
	char *new_name;
	static char *fix_name();
	unsigned char sum, calc_sum();

					/* convert to 11 character name */
	new_name = fix_name(name);
	sum = calc_sum((unsigned char *) new_name, 12);

	putc_line(ACK);
					/* for each character in the name */
	while (*new_name != CTRLZ) {
		putc_line((unsigned char) *new_name);

		switch (getc_line(3)) {
			case -1:	/* timed out */
				clear_line(win, 12, 24, TRUE);
				waddstr(win, "NO RESPONSE");
				wrefresh(win);
				return(ERROR);
			case ACK:	/* got it! */
				break;
			case CAN:	/* cancel transmission */
				if (getc_line(2) == CAN) {
					beep();
					clear_line(win, 12, 24, TRUE);
					wattrstr(win, A_BOLD, "REMOTE ABORTED");
					wrefresh(win);
					return(CANCEL);
				}
				/* fall thru... */
			default:
				clear_line(win, 12, 24, TRUE);
				waddstr(win, "NAME FAILED");
				wrefresh(win);
				return(ERROR);
		}
		new_name++;
	}
	putc_line(CTRLZ);
					/* verify the checksum */
	if (getc_line(10) != sum) {
		putc_line('u');
		clear_line(win, 12, 24, TRUE);
		waddstr(win, "CHECKSUM FAILED");
		wrefresh(win);
		return(ERROR);
	}
	putc_line(ACK);
	return(0);
}

/*
 * Receive a modem7 file name.  Returns zero on success, the standard error
 * codes, or a -1 on the end-of-batch.  (Oddly enough, the end-of-batch code
 * is the same as the code for a user abort)
 */

int
rcv_modem7(win, default_err)
WINDOW *win;
int default_err;
{
	extern char file_name[15];
	int i, j, err_method, err_count, got_it;
	unsigned char sum, calc_sum();
	char temp_name[13];
	static void change_name(), unfix_name();

	err_method = default_err;
	if (default_err == CRC_CHECKSUM)
		err_method = CRC;

	err_count = 0;
	got_it = 0;
	while (err_count < MAX_ERRORS) {
					/* switch to checksum? */
		if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
			err_method = CHECKSUM;

		if (err_method == CRC)
			putc_line('C');
		else
			putc_line(NAK);
					/* what'd we get? */
		switch (getc_line(10)) {
			case -1:	/* timed out */
				clear_line(win, 12, 24, TRUE);
				wattrstr(win, A_BOLD, "NO RESPONSE");
				wrefresh(win);
				err_count++;
				break;
			case ACK:	/* ready to go... */
				got_it++;
				break;
			default:	/* huh? */
				clear_line(win, 12, 24, TRUE);
				wattrstr(win, A_BOLD, "BAD HEADER");
				wrefresh(win);
				err_count++;
		}
		if (got_it)
			break;
	}
	if (!got_it)
		return(ERROR);
					/* get the name */
	for (i=0; i<12; i++) {
		j = getc_line(3);

		switch (j) {
			case -1:	/* timed out */
				clear_line(win, 12, 24, TRUE);
				wattrstr(win, A_BOLD, "NO RESPONSE");
				wrefresh(win);
				return(ERROR);
			case EOT:	/* end of batch? */
				return(-1);
			case CAN:	/* cancel transmission */
				if (getc_line(2) == CAN) {
					beep();
					clear_line(win, 12, 24, TRUE);
					wattrstr(win, A_BOLD, "REMOTE ABORTED");
					wrefresh(win);
					return(CANCEL);
				}
				/* fall thru... */
			case 'u':	/* bad name character */
				beep();
				clear_line(win, 12, 24, TRUE);
				wattrstr(win, A_BOLD, "BAD NAME");
				wrefresh(win);
				return(ERROR);
			default:	/* the name... */
				temp_name[i] = j & 0xff;
				if (j != CTRLZ)
					putc_line(ACK);
				break;
		}
	}
	temp_name[12] = '\0';
					/* send our checksum */
	sum = calc_sum((unsigned char *) temp_name, 12);
	putc_line(sum);
					/* do they agree? */
	if (getc_line(10) != ACK) {
		beep();
		clear_line(win, 12, 24, TRUE);
		wattrstr(win, A_BOLD, "BAD NAME");
		wrefresh(win);
		return(ERROR);
	}
					/* load the file_name array */
	unfix_name(temp_name);
					/* any name collisions? */
	change_name(win, file_name);
	return(0);
}

/*
 * Send the block 0 information for a ymodem batch transfer.  Uses only
 * the name component of the path and the file size.
 */

int
send_ymodem(win, file, size)
WINDOW *win;
char *file;
long size;
{
	unsigned short crc, calc_crc();
	char *strcpy(), *memset();
	unsigned char buf[133];
					/* start with a clean block */
	memset((char *) buf, '\0', 133);
					/* the header */
	buf[0] = SOH;
	buf[1] = 0;
	buf[2] = 255;

	/*
	 * The block zero consists of the file name (no path component),
	 * a NULL, and the file length (as a string).  The end of batch
	 * marker is an empty block.
	 */
	if (*file != '\0') {
		strcpy((char *) &buf[3], file);
		sprintf((char *) &buf[strlen(file)+4], "%ld", size);
	}
					/* the crc */
	crc = calc_crc(&buf[3], 128);
	buf[131] = crc >> 8;
	buf[132] = crc;
					/* the block count */
	mvwaddstr(win, 7, 24, "0   ");

	return(send_block(win, buf, 133));
}

/*
 * Receive the block 0 information for a ymodem batch transfer.  We
 * only use the file name and the size (if present).  Currently doesn't
 * support full path names.
 */

int
rcv_ymodem(win)
WINDOW *win;
{
	extern unsigned char buf[1029];
	extern long file_length;
	extern char file_name[15];
	int code, length_is_at;
	long atol();
	static void change_name();

	file_length = 0L;
	file_name[0] = '\0';
					/* read the zero block */
	if (code = rcv_block(win, 1, 1024, 0))
		return(code);
					/* at end of batch */
	if (buf[3] == '\0')
		return(0);
					/* get the file name */
	change_name(win, (char *) &buf[3]);
					/* any trouble? */
	if (file_name[0] == '\0') {
		putc_line(CAN);
		return(0);
	}
	/*
	 * The file length is placed after the NULL of the file name
	 * and is terminated by another NULL.  If the length is missing,
	 * atol() will see a NULL and return 0.
	 */
	length_is_at = strlen((char *) &buf[3]) + 4;
	file_length = atol((char *) &buf[length_is_at]);
	return(0);
}

/*
 * Handle file name collisions.  Prepend an "X" to the name until you find
 * a name that doesn't already exist.  Creates a NULL name on error.
 * Loads the global character array "file_name".
 */

static void
change_name(win, str)
WINDOW *win;
char *str;
{
	extern char file_name[15];
	register int i;
	int modified;
	char temp[15], ans[15], *s, *strrchr(), *strcpy(), *strncat();
	char *strncpy();
	unsigned int sleep();
					/* dissect the name component */
	if ((s = strrchr(str, '/')))
		strncpy(temp, ++s, 15);
	else
		strncpy(temp, str, 15);
	temp[14] = '\0';

	strcpy(ans, temp);
	file_name[0] = '\0';
					/* write permission on directory? */
	if (access(".", 2)) {
		beep();
		clear_line(win, 12, 24, TRUE);
		wattrstr(win, A_BOLD, "NO WRITE ON DIRECTORY");
		wrefresh(win);
		return;
	}
					/* prepend up to 13 "X"s */
	modified = 0;
	for (i=1; i<14; i++) {
		if (access(ans, 0)) {
			if (modified) {
				beep();
				clear_line(win, 12, 24, TRUE);
				waddstr(win, "NAME COLLISION");
				wrefresh(win);
				sleep(1);
			}
			strcpy(file_name, ans);
			return;
		}

		modified++;
		strcpy(temp, "X");
		strncat(temp, ans, 13);
		temp[14] = '\0';
		strcpy(ans, temp);
	}
	beep();
	clear_line(win, 12, 24, TRUE);
	waddstr(win, "BAD NAME");
	wrefresh(win);
	return;
}

/*
 * Convert a perfectly good Unix file name to fit the CP/M file name
 * rules.  Used for the modem7 batch file transfer.  Returns a pointer
 * to a static area containing the new name.
 */

static char *
fix_name(path)
char *path;
{
	int i, dot;
	char *s, *name, temp[15], *ext, *strncpy(), *strrchr();
	static char ans[13];
					/* ignore the path component */
	if (s = strrchr(path, '/'))
		strncpy(temp, ++s, 15);
	else
		strncpy(temp, path, 15);
	temp[14] = '\0';
	name = temp;

	ext = "";
	dot = 0;
	for (i=strlen(temp)-1; i>=0; i--) {
		if (temp[i] == '.' && !dot) {
			dot = 1;
			temp[i] = '\0';
			ext = &temp[i+1];
		}
		if (islower(temp[i]))
			temp[i] = toupper(temp[i]);
	}
					/* if null name component */
	if (*name == '\0')
		name = "X";
					/* if name too long */
	if (strlen(name) > 8)
		*(name+8) = '\0';
					/* if extension too long */
	if (strlen(ext) > 3)
		*(ext+3) = '\0';

	sprintf(ans, "%-8.8s%-3.3s%c", temp, ext, CTRLZ);
	return(ans);
}

/*
 * Convert a CP/M style filename into a legal Unix file name.  Loads the
 * global character array "file_name".
 */

static void
unfix_name(cpm_name)
char *cpm_name;
{
	extern char file_name[15];
	register int i, n;
	int dot;
	char temp[15], *strcpy();

	file_name[0] = '\0';
	if (*cpm_name == '\0')
		return;

	strcpy(temp, cpm_name);
					/* 8 character of the name */
	n = 0;
	for (i=0; i<8; i++) {
		if (temp[i] != ' ') {
			if (isupper(temp[i]))
				file_name[n++] = tolower(temp[i]);
			else
				file_name[n++] = temp[i];
		}
	}
					/* 3 character extension */
	dot = 0;
	for (i=8; i<11; i++) {
		if (temp[i] != ' ') {
			if (!dot) {
				dot++;
				file_name[n++] = '.';
			}
			if (isupper(temp[i]))
				file_name[n++] = tolower(temp[i]);
			else
				file_name[n++] = temp[i];
		}
	}
	file_name[n] = '\0';
	return;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.