Пример #1
0
static int notsane_extended(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    uint32_t end_ebr;

    dp = ((struct disk_dos_mbr *)iter->data)->table;

    if (!dp[1].ostype)
	return 0;

    if (!ost_is_nondata(dp[1].ostype)) {
	error("2nd EBR entry must be extended or empty.\n");
	return -1;
    }

    end_ebr = dp[1].start_lba + dp[1].length;

    if (!dp[1].start_lba ||
	!dp[1].length ||
	!sane(dp[1].start_lba, dp[1].length) ||
	end_ebr > iter->sub.dos.bebr_size) {

	error("Insane extended partition.\n");
	return -1;
    }

    return 0;
}
Пример #2
0
static int notsane_extended(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    uint32_t end_ebr;

    dp = ((struct disk_dos_mbr *)iter->data)->table;

    if (!dp[1].ostype)
	return 0;

    if (!ost_is_nondata(dp[1].ostype)) {
	error("The 2nd EBR entry must be extended or empty.");
	return -1;
    }

    if (iter->flags & PIF_RELAX)
	return 0;

    end_ebr = dp[1].start_lba + dp[1].length;

    if (!dp[1].start_lba ||
	!dp[1].length ||
	!sane(dp[1].start_lba, dp[1].length) ||
	end_ebr > iter->dos.bebr_siz) {

	error("Extended partition (EBR) with invalid offset and/or length.");
	return -1;
    }

    return 0;
}
Пример #3
0
static int notsane_logical(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    uint32_t end_log;

    dp = ((struct disk_dos_mbr *)iter->data)->table;

    if (!dp[0].ostype)
	return 0;

    if (ost_is_ext(dp[0].ostype)) {
	error("1st EBR entry must be data or empty.\n");
	return -1;
    }

    end_log = dp[0].start_lba + dp[0].length;

    if (!dp[0].start_lba ||
	!dp[0].length ||
	!sane(dp[0].start_lba, dp[0].length) ||
	end_log > iter->sub.dos.ebr_size) {

	error("Insane logical partition.\n");
	return -1;
    }

    return 0;
}
Пример #4
0
static int notsane_logical(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    uint32_t end_log;

    dp = ((struct disk_dos_mbr *)iter->data)->table;

    if (!dp[0].ostype)
	return 0;

    if (ost_is_ext(dp[0].ostype)) {
	error("The 1st EBR entry must be data or empty.");
	return -1;
    }

    if (iter->flags & PIF_RELAX)
	return 0;

    end_log = dp[0].start_lba + dp[0].length;

    if (!dp[0].start_lba ||
	!dp[0].length ||
	!sane(dp[0].start_lba, dp[0].length) ||
	end_log > iter->dos.nebr_siz) {

	error("Logical partition (in EBR) with invalid offset and/or length.");
	return -1;
    }

    return 0;
}
Пример #5
0
static int notsane_gpt_hdr(const struct disk_info *di, const struct disk_gpt_header *gpth, int flags)
{
    uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
    uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
    uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
    uint64_t gpt_sec;	    /* secondary gpt header */

    if (!(flags & PIF_STRICT))
	return 0;

    if (gpth->lba_alt < gpth->lba_cur)
	gpt_sec = gpth->lba_cur;
    else
	gpt_sec = gpth->lba_alt;
    gpt_loff = gpth->lba_table;
    gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
    gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;

    /*
     * disk_read_sectors allows reading of max 255 sectors, so we use
     * it as a sanity check base. EFI doesn't specify max (AFAIK).
     */
    if (gpt_loff < 2 || !gpt_lsiz || gpt_lcnt > 255u ||
	    gpth->lba_first_usable > gpth->lba_last_usable ||
	    !sane(gpt_loff, gpt_lcnt) ||
	    (gpt_loff + gpt_lcnt > gpth->lba_first_usable && gpt_loff <= gpth->lba_last_usable) ||
	     gpt_loff + gpt_lcnt > gpt_sec ||
	    ((flags & PIF_STRICTER) && (gpt_sec >= di->lbacnt)) ||
	    gpth->part_size < sizeof(struct disk_gpt_part_entry))
	return -1;

    return 0;
}
Пример #6
0
static int notsane_primary(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;

    if (!dp->ostype)
	return 0;

    if (!dp->start_lba ||
	!dp->length ||
	!sane(dp->start_lba, dp->length) ||
	dp->start_lba + dp->length > iter->di.lbacnt) {
	error("Insane primary (MBR) partition.\n");
	return -1;
    }

    return 0;
}
Пример #7
0
static int notsane_primary(const struct part_iter *iter)
{
    const struct disk_dos_part_entry *dp;
    dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;

    if (!dp->ostype)
	return 0;

    if (iter->flags & PIF_RELAX)
	return 0;

    if (!dp->start_lba ||
	!dp->length ||
	!sane(dp->start_lba, dp->length) ||
	dp->start_lba + dp->length > iter->di.lbacnt) {
	error("Primary partition (in MBR) with invalid offset and/or length.");
	return -1;
    }

    return 0;
}
Пример #8
0
/**
 * pi_begin() - check disk, validate, and get proper iterator
 * @di:	    diskinfo struct pointer
 *
 * This function checks the disk for GPT or legacy partition table and allocates
 * an appropriate iterator.
 **/
struct part_iter *pi_begin(const struct disk_info *di, int stepall)
{
    int setraw = 0;
    struct part_iter *iter = NULL;
    struct disk_dos_mbr *mbr = NULL;
    struct disk_gpt_header *gpth = NULL;
    struct disk_gpt_part_entry *gptl = NULL;

    /* Read MBR */
    if (!(mbr = disk_read_sectors(di, 0, 1))) {
	error("Couldn't read first disk sector.\n");
	goto bail;
    }

    setraw = -1;

    /* Check for MBR magic*/
    if (mbr->sig != disk_mbr_sig_magic) {
	error("No MBR magic.\n");
	goto bail;
    }

    /* Check for GPT protective MBR */
    if (mbr->table[0].ostype == 0xEE) {
	if (!(gpth = disk_read_sectors(di, 1, 1))) {
	    error("Couldn't read potential GPT header.\n");
	    goto bail;
	}
    }

    if (gpth && gpth->rev.uint32 == 0x00010000 &&
	    !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
	/* looks like GPT v1.0 */
	uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
	uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
	uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
#ifdef DEBUG
	puts("Looks like a GPT v1.0 disk.");
	disk_gpt_header_dump(gpth);
#endif
	/* Verify checksum, fallback to backup, then bail if invalid */
	if (gpt_check_hdr_crc(di, &gpth))
	    goto bail;

	gpt_loff = gpth->lba_table;
	gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
	gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;

	/*
	 * disk_read_sectors allows reading of max 255 sectors, so we use
	 * it as a sanity check base. EFI doesn't specify max (AFAIK).
	 * Apart from that, some extensive sanity checks.
	 */
	if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
		gpth->lba_first_usable > gpth->lba_last_usable ||
		!sane(gpt_loff, gpt_lcnt) ||
		gpt_loff + gpt_lcnt > gpth->lba_first_usable ||
		!sane(gpth->lba_last_usable, gpt_lcnt) ||
		gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt ||
		gpth->lba_alt >= di->lbacnt ||
		gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
	    error("Invalid GPT header's values.\n");
	    goto bail;
	}
	if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) {
	    error("Couldn't read GPT partition list.\n");
	    goto bail;
	}
	/* Check array checksum(s). */
	if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
	    error("WARNING: GPT partition list checksum invalid, trying backup.\n");
	    free(gptl);
	    /* secondary array directly precedes secondary header */
	    if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) {
		error("Couldn't read backup GPT partition list.\n");
		goto bail;
	    }
	    if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
		error("Backup GPT partition list checksum invalid.\n");
		goto bail;
	    }
	}
	/* allocate iterator and exit */
	iter = pi_new(typegpt, di, stepall, gpth, gptl);
    } else {
	/* looks like MBR */
	iter = pi_new(typedos, di, stepall, mbr);
    }

    setraw = 0;
bail:
    if (setraw) {
	error("WARNING: treating disk as raw.\n");
	iter = pi_new(typeraw, di, stepall);
    }
    free(mbr);
    free(gpth);
    free(gptl);

    return iter;
}
Пример #9
0
int lpd_main(int argc UNUSED_PARAM, char *argv[])
{
	int spooling = spooling; // for compiler
	char *s, *queue;
	char *filenames[2];

	// goto spool directory
	if (*++argv)
		xchdir(*argv++);

	// error messages of xfuncs will be sent over network
	xdup2(STDOUT_FILENO, STDERR_FILENO);

	// nullify ctrl/data filenames
	memset(filenames, 0, sizeof(filenames));

	// read command
	s = queue = xmalloc_read_stdin();
	// we understand only "receive job" command
	if (2 != *queue) {
 unsupported_cmd:
		printf("Command %02x %s\n",
			(unsigned char)s[0], "is not supported");
		goto err_exit;
	}

	// parse command: "2 | QUEUE_NAME | '\n'"
	queue++;
	// protect against "/../" attacks
	// *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do
	if (!*sane(queue))
		return EXIT_FAILURE;

	// queue is a directory -> chdir to it and enter spooling mode
	spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done
	// we don't free(s), we might need "queue" var later

	while (1) {
		char *fname;
		int fd;
		// int is easier than ssize_t: can use xatoi_u,
		// and can correctly display error returns (-1)
		int expected_len, real_len;

		// signal OK
		safe_write(STDOUT_FILENO, "", 1);

		// get subcommand
		// valid s must be of form: "SUBCMD | LEN | space | FNAME"
		// N.B. we bail out on any error
		s = xmalloc_read_stdin();
		if (!s) { // (probably) EOF
			char *p, *q, var[2];

			// non-spooling mode or no spool helper specified
			if (!spooling || !*argv)
				return EXIT_SUCCESS; // the only non-error exit
			// spooling mode but we didn't see both ctrlfile & datafile
			if (spooling != 7)
				goto err_exit; // reject job

			// spooling mode and spool helper specified -> exec spool helper
			// (we exit 127 if helper cannot be executed)
			var[1] = '\0';
			// read and delete ctrlfile
			q = xmalloc_xopen_read_close(filenames[0], NULL);
			unlink(filenames[0]);
			// provide datafile name
			// we can use leaky setenv since we are about to exec or exit
			xsetenv("DATAFILE", filenames[1]);
			// parse control file by "\n"
			while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) {
				*p++ = '\0';
				// q is a line of <SYM><VALUE>,
				// we are setting environment string <SYM>=<VALUE>.
				// Ignoring "l<datafile>", exporting others:
				if (*q != 'l') {
					var[0] = *q++;
					xsetenv(var, q);
				}
				q = p; // next line
			}
			// helper should not talk over network.
			// this call reopens stdio fds to "/dev/null"
			// (no daemonization is done)
			bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
			BB_EXECVP(*argv, argv);
			exit(127);
		}

		// validate input.
		// we understand only "control file" or "data file" cmds
		if (2 != s[0] && 3 != s[0])
			goto unsupported_cmd;
		if (spooling & (1 << (s[0]-1))) {
			printf("Duplicated subcommand\n");
			goto err_exit;
		}
		// get filename
		*strchrnul(s, '\n') = '\0';
		fname = strchr(s, ' ');
		if (!fname) {
// bad_fname:
			printf("No or bad filename\n");
			goto err_exit;
		}
		*fname++ = '\0';
//		// s[0]==2: ctrlfile, must start with 'c'
//		// s[0]==3: datafile, must start with 'd'
//		if (fname[0] != s[0] + ('c'-2))
//			goto bad_fname;
		// get length
		expected_len = bb_strtou(s + 1, NULL, 10);
		if (errno || expected_len < 0) {
			printf("Bad length\n");
			goto err_exit;
		}
		if (2 == s[0] && expected_len > 16 * 1024) {
			// SECURITY:
			// ctrlfile can't be big (we want to read it back later!)
			printf("File is too big\n");
			goto err_exit;
		}

		// open the file
		if (spooling) {
			// spooling mode: dump both files
			// job in flight has mode 0200 "only writable"
			sane(fname);
			fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
			if (fd < 0)
				goto err_exit;
			filenames[s[0] - 2] = xstrdup(fname);
		} else {
			// non-spooling mode:
			// 2: control file (ignoring), 3: data file
			fd = -1;
			if (3 == s[0])
				fd = xopen(queue, O_RDWR | O_APPEND);
		}

		// signal OK
		safe_write(STDOUT_FILENO, "", 1);

		// copy the file
		real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len);
		if (real_len != expected_len) {
			printf("Expected %d but got %d bytes\n",
				expected_len, real_len);
			goto err_exit;
		}
		// get EOF indicator, see whether it is NUL (ok)
		// (and don't trash s[0]!)
		if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) {
			// don't send error msg to peer - it obviously
			// doesn't follow the protocol, so probably
			// it can't understand us either
			goto err_exit;
		}

		if (spooling) {
			// chmod completely downloaded file as "readable+writable"
			fchmod(fd, 0600);
			// accumulate dump state
			// N.B. after all files are dumped spooling should be 1+2+4==7
			spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile
		}

		free(s);
		close(fd); // NB: can do close(-1). Who cares?

		// NB: don't do "signal OK" write here, it will be done
		// at the top of the loop
	} // while (1)

 err_exit:
	// don't keep corrupted files
	if (spooling) {
#define i spooling
		for (i = 2; --i >= 0; )
			if (filenames[i])
				unlink(filenames[i]);
	}
	return EXIT_FAILURE;
}
Пример #10
0
/* pi_begin() - validate and and get proper iterator for a disk described by di */
struct part_iter *pi_begin(const struct disk_info *di, int flags)
{
    int gptprot, ret = -1;
    struct part_iter *iter;
    struct disk_dos_mbr *mbr = NULL;
    struct disk_gpt_header *gpth = NULL;
    struct disk_gpt_part_entry *gptl = NULL;

    /* Preallocate iterator */
    if (!(iter = pi_alloc()))
	goto bail;

    /* Read MBR */
    if (!(mbr = disk_read_sectors(di, 0, 1))) {
	error("Couldn't read the first disk sector.");
	goto bail;
    }

    /* Check for MBR magic */
    if (mbr->sig != disk_mbr_sig_magic) {
	warn("No MBR magic, treating disk as raw.");
	/* looks like RAW */
	ret = pi_ctor(iter, di, flags);
	goto bail;
    }

    /* Check for GPT protective MBR */
    gptprot = 0;
    for (size_t i = 0; i < 4; i++)
	gptprot |= (mbr->table[i].ostype == 0xEE);
    if (gptprot && !(flags & PIF_PREFMBR)) {
	if (!(gpth = disk_read_sectors(di, 1, 1))) {
	    error("Couldn't read potential GPT header.");
	    goto bail;
	}
    }

    if (gpth && gpth->rev.uint32 == 0x00010000 &&
	    !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) {
	/* looks like GPT v1.0 */
	uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
	uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
	uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
#ifdef DEBUG
	dprintf("Looks like a GPT v1.0 disk.\n");
	disk_gpt_header_dump(gpth);
#endif
	/* Verify checksum, fallback to backup, then bail if invalid */
	if (gpt_check_hdr_crc(di, &gpth))
	    goto bail;

	gpt_loff = gpth->lba_table;
	gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
	gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;

	/*
	 * disk_read_sectors allows reading of max 255 sectors, so we use
	 * it as a sanity check base. EFI doesn't specify max (AFAIK).
	 * Apart from that, some extensive sanity checks.
	 */
	if (!(flags & PIF_RELAX) && (
		!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
		gpth->lba_first_usable > gpth->lba_last_usable ||
		!sane(gpt_loff, gpt_lcnt) ||
		gpt_loff + gpt_lcnt > gpth->lba_first_usable ||
		!sane(gpth->lba_last_usable, gpt_lcnt) ||
		gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt ||
		gpth->lba_alt >= di->lbacnt ||
		gpth->part_size < sizeof *gptl)) {
	    error("Invalid GPT header's values.");
	    goto bail;
	}
	if (!(gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt))) {
	    error("Couldn't read GPT partition list.");
	    goto bail;
	}
	/* Check array checksum(s). */
	if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
	    warn("Checksum of the main GPT partition list is invalid, trying backup.");
	    free(gptl);
	    /* secondary array directly precedes secondary header */
	    if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, gpt_lcnt))) {
		error("Couldn't read backup GPT partition list.");
		goto bail;
	    }
	    if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, gpt_lsiz)) {
		error("Checksum of the backup GPT partition list is invalid, giving up.");
		goto bail;
	    }
	}
	/* looks like GPT */
	ret = pi_gpt_ctor(iter, di, flags, gpth, gptl);
    } else {
	/* looks like MBR */
	ret = pi_dos_ctor(iter, di, flags, mbr);
    }
bail:
    if (ret < 0)
	free(iter);
    free(mbr);
    free(gpth);
    free(gptl);

    return iter;
}
Пример #11
0
static void set(char *argv[], struct termios *sp)
{
	const Tty_t *tp;
	register int c,off;
	char *cp;
	char *ep;
	while(cp = *argv++)
	{
		off = 0;
		if(*cp=='-')
		{
			cp++;
			off=1;
		}
		if(!(tp=lookup(cp)) || (off && (tp->type!=BIT) && (tp->type!=TABS)))
			error(ERROR_exit(1),"%s: unknown mode",cp);
		switch(tp->type)
		{
		    case CHAR:
			if(off)
				error(ERROR_exit(1),"%s: unknown mode",cp);
			if(!*argv)
				error(ERROR_exit(1),"missing argument to %s",cp);
			c = gettchar(*argv++);
			if(c>=0)
				sp->c_cc[tp->mask] = c;
			else
				sp->c_cc[tp->mask] = _POSIX_VDISABLE;
			break;
		    case BIT: case BITS:
			switch(tp->field)
			{
			    case C_FLAG:
				if(off)
					sp->c_cflag &= ~tp->mask;
				else
					sp->c_cflag |= tp->mask;
				break;
			    case I_FLAG:
				if(off)
					sp->c_iflag &= ~tp->mask;
				else
					sp->c_iflag |= tp->mask;
				break;
			    case O_FLAG:
				sp->c_oflag &= ~tp->mask;
				sp->c_oflag |= tp->val;
				break;
			    case L_FLAG:
				if(off)
					sp->c_lflag &= ~tp->mask;
				else
					sp->c_lflag |= tp->mask;
				break;
			}
			break;
		    case TABS:
			sp->c_oflag &= ~tp->mask;
			if(off)
				sp->c_oflag |= tp->val;
			break;
#ifdef TIOCSWINSZ
		    case WIND:
		    {
			struct winsize win;
			int n;
			if(ioctl(0,TIOCGWINSZ,&win)<0)
				error(ERROR_system(1),"cannot set %s",tp->name);
			if(!(cp= *argv))
			{
				sfprintf(sfstdout,"%d\n",tp->mask?win.ws_col:win.ws_row);
				break;
			}
			argv++;
			n=strtol(cp,&cp,10);
			if(*cp)
				error(ERROR_system(1),"%d: invalid number of %s",argv[-1],tp->name);
			if(tp->mask)
				win.ws_col = n;
			else
				win.ws_row = n;
			if(ioctl(0,TIOCSWINSZ,&win)<0)
				error(ERROR_system(1),"cannot set %s",tp->name);
			break;
		    }
#endif
		    case NUM:
			cp = *argv;
			if (!cp)
			{
				if (tp->field == C_SPEED)
				{
					if (tp = getspeed(*tp->name == 'i' ? cfgetispeed(sp) : cfgetospeed(sp)))
						sfprintf(sfstdout, "%s\n", tp->name);
					break;
				}
				error(ERROR_exit(1), "%s: missing numeric argument", tp->name);
			}
			argv++;
			c = (int)strtol(cp, &ep, 10);
			if (*ep)
				error(ERROR_exit(1), "%s: %s: numeric argument expected", tp->name, cp);
			switch (tp->field)
			{
#if _mem_c_line_termios
			case C_LINE:
				sp->c_line = c;
				break;
#endif
			case C_SPEED:
				if(getspeed(c))
				{
					if (*tp->name != 'o')
						cfsetispeed(sp, c);
					if (*tp->name != 'i')
						cfsetospeed(sp, c);
				}
				else
					error(ERROR_exit(1), "%s: %s: invalid speed", tp->name, cp);
				break;
			case T_CHAR:
				sp->c_cc[tp->mask] = c;
				break;
			}
			break;
		    case SPEED:
			cfsetospeed(sp, tp->mask);
			cfsetispeed(sp, tp->mask);
			break;
		    case SIZE:
			sp->c_cflag &= ~CSIZE;
			sp->c_cflag |= tp->mask;
			break;
		    case SANE:
			sane(sp);
			break;
#if defined(OLCUC) && defined(IUCLC)
		    case CASE:
			if(off)
			{
				sp->c_iflag |= IUCLC;
				sp->c_oflag |= OLCUC;
			}
			else
			{
				sp->c_iflag &= ~IUCLC;
				sp->c_oflag &= ~OLCUC;
			}
			break;
#endif /* OLCUC && IUCLC */
		}
	}
}
Пример #12
0
static void output(struct termios *sp, int flags)
{
	const Tty_t *tp;
	struct termios tty;
	register int delim = ' ';
	register int i,off,off2;
	char schar[2];
	unsigned int ispeed = cfgetispeed(sp);
	unsigned int ospeed = cfgetospeed(sp);
	if(flags&G_FLAG)
	{
		gout(sp);
		return;
	}
	tty = *sp;
	sane(&tty);
	for(i=0; i < elementsof(Ttable); i++)
	{
		tp= &Ttable[i];
		if(tp->flags&IG)
		{
			if(tp->flags&NL)
				sfputc(sfstdout,'\n');
			continue;
		}
		switch(tp->type)
		{
		    case BIT:
		    case BITS:
			off = off2 = 1;
			switch(tp->field)
			{
			    case C_FLAG:
				if(sp->c_cflag&tp->mask)
					off = 0;
				if(tty.c_cflag&tp->mask)
					off2 = 0;
				break;
			    case I_FLAG:
				if(sp->c_iflag&tp->mask)
					off = 0;
				if(tty.c_iflag&tp->mask)
					off2 = 0;
				break;
			    case O_FLAG:
				if((sp->c_oflag&tp->mask)==tp->val)
					off = 0;
				if(tty.c_oflag&tp->mask)
					off2 = 0;
				break;
			    case L_FLAG:
				if(sp->c_lflag&tp->mask)
					off = 0;
				if(tty.c_lflag&tp->mask)
					off2 = 0;
			}
			if(tp->flags&NL)
				delim = '\n';
			if(!flags && off==off2)
				continue;
			if(!off)
				sfprintf(sfstdout,"%s%c",tp->name,delim);
			else if(tp->type==BIT)
				sfprintf(sfstdout,"-%s%c",tp->name,delim);
			delim = ' ';
			break;

		    case CHAR:
			off = sp->c_cc[tp->mask];
			if(tp->flags&NL)
				delim = '\n';
			if(!flags && off==(unsigned char)tty.c_cc[tp->mask])
				continue;
			if(off==_POSIX_VDISABLE)
				sfprintf(sfstdout,"%s = <undef>;%c",tp->name,delim);
			else if(isprint(off&0xff))
				sfprintf(sfstdout,"%s = %c;%c",tp->name,off,delim);
			else
#if CC_NATIVE == CC_ASCII
			sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':(off^0100),delim);
#else
			{
				off = ccmapc(off, CC_NATIVE, CC_ASCII);
				sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':ccmapc(off^0100,CC_ASCII,CC_NATIVE),delim);
			}
#endif
			delim = ' ';
			break;
		    case SIZE:
			if((sp->c_cflag&CSIZE)!=tp->mask)
				continue;
			if(flags || (sp->c_cflag&CSIZE) != (tty.c_cflag&CSIZE))
				sfprintf(sfstdout,"%s ",tp->name);
			break;
		    case SPEED:
			if(tp->mask==ispeed)
			{
				if(ispeed!=ospeed)
					schar[0]='i';
				else
					schar[0]=0;
			}
			else if(tp->mask==ospeed)
				schar[0]='o';
			else
				continue;
			schar[1] = 0;
#ifdef TIOCSWINSZ
			{
				struct winsize win;
				off = ioctl(0,TIOCGWINSZ,&win);
				if(off>=0)
					sfprintf(sfstdout,"%sspeed %s baud; rows %d; columns %d;\n",schar,tp->name,win.ws_row,win.ws_col);
			}
			if(off<0)
#endif
				sfprintf(sfstdout,"%sspeed %s baud;\n",schar,tp->name);
		}
	}
	if(delim=='\n')
		sfputc(sfstdout,'\n');
}