Exemple #1
0
/**
 * Print a single character in UTF-8 to a file by its descriptor
 * 
 * @param  fd  The file descriptor
 * @param  c   The character
 */
static void fdputucs(int fd, int32_t c)
{
  static char ucs_buffer[8] = {[7] = 0};
  if (c < 0)
    ; /* cannot, if it does, ignore it */
  else if (c < 0x80)
    {
      ucs_buffer[6] = (char)c;
      fdprint(fd, ucs_buffer + 6);
    }
  else
    {
      long off = 7;
      *ucs_buffer = (int8_t)0x80;
      while (c)
	{
	  *(ucs_buffer + --off) = (char)((c & 0x3F) | 0x80);
	  *ucs_buffer |= (*ucs_buffer) >> 1;
	  c >>= 6;
	}
      if ((*ucs_buffer) & (*(ucs_buffer + off) & 0x3F))
	*(ucs_buffer + --off) = (char)((*ucs_buffer) << 1);
      else
	*(ucs_buffer + off) |= (char)((*ucs_buffer) << 1);
      fdprint(fd, ucs_buffer + off);
    }
}
Exemple #2
0
static void check_xflags(char **argv) {
    char **argi, *arg;
    for (argi = argv + 1; (arg = *argi); ++argi) {
        /* TODO(pts): Copy some of these flags to clang_trampoline.cc */
        if ((arg[0] == '-' && arg[1] == 'B') ||
                is_argprefix(arg, "--sysroot") ||
                is_argprefix(arg, "--gcc-toolchain") ||
                /* TODO(pts): Accept a few targets (and don't pass the flag). */
                0 == strcmp(arg, "-target") ||  /* Clang-specific. */
                0 == strcmp(arg, "-m64") ||
                0 == strcmp(arg, "-sysld") ||
                0 == strcmp(arg, "--sysld") ||
                0 == strcmp(arg, "-p") ||  /* Needs Mcrt1.o (| gcrt1.o), clang crt1.o */
                0 == strcmp(arg, "-pg") ||  /* Needs gcrt1.o, clang uses crt1.o */
                0 == strcmp(arg, "-pie") ||
                0 == strcmp(arg, "-fpic") ||
                0 == strcmp(arg, "-fPIC") ||
                0 == strcmp(arg, "-fpie") ||
                0 == strcmp(arg, "-fPIE") ||  /* This would link Scrt1.o etc. */
                0 == strcmp(arg, "-shared") ||
                0 == strcmp(arg, "-shared-libgcc")) {
            fdprint(2, strdupcat(
                        "xstatic: error: flag not supported: ", arg, "\n"));
            exit(122);
        }
    }
}
Exemple #3
0
/* Check that the linker is not instructed to link *crt*.o files from the
 * wrong directory. Most of the time this is a configuration issue, e.g. the
 * wrong value for the -B compiler flag was specified, and now the compiler
 * has instructed the linker to look for the *crt*.o files a system-default
 * (e.g. /usr/lib/...) directory.
 */
static void check_ld_crtoarg(char **argv) {
    /* If a relevant *crt*.o file is missing from xstaticcld, then
     * gcc generates e.g.
     * /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../i386-linux-gnu/crtn.o , and
     * pts-static-clang generates crtn.o . We detect and fail on both.
     */
    char had_error = 0;
    char **argi, *supported_dir;
    const char *arg, *basename, *argend, *supbasename;
    size_t supdirlen;
    for (supbasename = argv[0] + strlen(argv[0]);
            supbasename != argv[0] && supbasename[-1] != '/'; --supbasename) {}
    supdirlen = supbasename - argv[0];
    for (argi = argv + 1; (arg = *argi); ++argi) {
        if (0 == strcmp(arg, "-o") && argi[1]) {
            ++argi;
            continue;
        }
        if (arg[0] == '-') continue;
        argend = arg + strlen(arg);
        for (basename = argend; basename != arg && basename[-1] != '/';
                --basename) {}
        if (basename[0] == '\0' || argend - arg < 2 ||
                argend[-1] != 'o' || argend[-2] != '.' ||
                ((0 != strncmp(basename, "crt", 3) ||
                  (0 != strcmp(basename, "crt0.o") &&
                   0 != strcmp(basename, "crt1.o") &&
                   0 != strcmp(basename, "crt2.o") &&
                   0 != strcmp(basename, "crti.o") &&
                   0 != strcmp(basename, "crtn.o") &&
                   0 != strcmp(basename, "crtbegin.o") &&
                   0 != strcmp(basename, "crtbeginS.o") &&
                   0 != strcmp(basename, "crtbeginT.o") &&
                   0 != strcmp(basename, "crtend.o") &&
                   0 != strcmp(basename, "crtendS.o") &&
                   0 != strcmp(basename, "crtfastmath.o"))) &&
                 (0 != strcmp(basename + 1, "crt1.o") ||
                  (0 != strcmp(basename, "Scrt1.o") &&
                   0 != strcmp(basename, "Mcrt1.o") &&
                   0 != strcmp(basename, "gcrt1.o"))))) continue;
        if (0 == strncmp(arg, argv[0], supdirlen)) continue;
        if (supdirlen == 0) {
            supported_dir = ".";
        } else {
            supported_dir = malloc(supdirlen);
            memcpy(supported_dir, argv[0], supdirlen - 1);
            supported_dir[supdirlen - 1] = '\0';
        }
        fdprint(2, strdupcat(
                    "xstatic-ld: error: *crt*.o file linked from unsupported location "
                    "(supported is ", strdupcat(supported_dir, "): ", arg), "\n"));
        had_error = 1;
    }
    if (had_error) exit(122);
}
Exemple #4
0
static void check_bflags(char **argv) {
    char **argi, *arg;
    for (argi = argv + 1; (arg = *argi); ++argi) {
        if ((arg[0] == '-' && arg[1] == 'B') ||
                is_argprefix(arg, "--sysroot") ||
                is_argprefix(arg, "--gcc-toolchain")) {
            fdprint(2, strdupcat(
                        "error: flag not supported in this ldmode: ", arg, "\n"));
            exit(122);
        }
    }
}
Exemple #5
0
static char *get_up_dir_alloc(const char *dir) {
    char *dirup = NULL, *p;
    if (0 == strcmp(dir, "..") || (dirup = readlink_alloc(dir))) {
        free(dirup);
        return strdupcat(dir, "/..", "");
    }
    dirup = strdupcat(dir, ".", "");
    for (p = dirup + strlen(dirup) - 1; p != dirup && p[-1] != '/'; --p) {}
    if (p == dirup) {
        *p++ = '.';
        if (0 == strcmp(dir, ".")) *p++ = '.';
        *p = '\0';
    } else if (dirup[0] == '/' && dirup[1] == '\0') {
        fdprint(2, strdupcat("xstatic: error: no parent dir for: ", dir, "\n"));
        exit(122);
    } else {
        p[-1] = '\0';  /* Remove basename of dirup. */
    }
    return dirup;
}
Exemple #6
0
int main(int argc, char **argv) {
    char *prog;
    char *dir, *dirup;
    char *p;
    char **args, **argp;
    char is_verbose = 0;
    char **argi, *arg;
    struct stat st;

    dir = find_on_path(argv[0]);
    if (!dir) {
        fdprint(2, "xstatic: error: could not find myself on $PATH\n");
        return 122;
    }
    dir = readlink_alloc_all(dir);
    for (p = dir + strlen(dir); p != dir && p[-1] != '/'; --p) {}
    if (p == dir) {
        strcpy(dir, ".");
    } else {
        for (; p != dir && p[-1] == '/'; --p) {}
        p[p == dir] = '\0';  /* Remove basename of dir. */
    }
    dirup = get_up_dir_alloc(dir);
    if (detect_linker(argv)) {  /* clang trampoline runs as as ld. */
        char is_static = 0;
        char **argli;  /* Where to add the -L flags. */

        /* All we do is exec()ing ld.bin with the following dropped from argv:
         *
         * -L/usr/lib...
         * -L/lib...
         * --hash-style=both (because not supported by old ld)
         * --build-id (because not supported by old ld)
         * -z relro (because it increases the binary size and it's useless for static)
         */
        check_ld_crtoarg(argv);
        argp = args = malloc(sizeof(*args) * (argc + 5));
        *argp++ = *argv;
        for (argi = argv + 1; (arg = *argi); ++argi) {
            if (0 == strcmp(arg, "--do-xstaticldv")) {
                is_verbose = 1;
            } else if (0 == strcmp(arg, "-static")) {
                is_static = 1;
            }
        }
        if (!is_static) {
            fdprint(2, "xstatic: error: missing -static\n");
            exit(1);
        }

        *argp++ = "-nostdlib";  /* No system directories to find .a files. */
        /* Find argli: in front of the last -L (which will be removed), but no
         * later than just before the first -l.
         */
        argli = NULL;
        for (argi = argv + 1; (arg = *argi); ++argi) {
            if (arg[0] == '-') {
                if (arg[1] == 'L') {  /* "-L". */
                    argli = argi;
                } else if (arg[1] == 'l') {  /* "-l". */
                    if (!argli) argli = argi;
                    break;
                }
            }
        }
        if (!argli) argli = argv + 1;

        for (argi = argv + 1; (arg = *argi); ++argi) {
            if (argi == argli) {
                char **argj;
                /* Add the user-specified link dirs first (just like non-xstatic). */
                for (argj = argv + 1; *argj; ++argj) {
                    if (0 == strncmp(*argj, "-=L", 3)) {
                        *argp++ = strdupcat("-L", "", *argj + 3);
                    }
                }
                /* We put xstaticcld with libgcc.a first, because clang puts
                 * /usr/lib/gcc/i486-linux-gnu/4.4 with libgcc.a before /usr/lib with
                 * libc.a .
                 *
                 * We add these -L flags even if compiler -nostdlib was specified,
                 * because non-xstatic compilers do the same.
                 */
                *argp++ = strdupcat("-L", dirup, "/xstaticcld");
                *argp++ = strdupcat("-L", dirup, "/xstaticusr/lib");
                argli = NULL;  /* Don't insert the -L flags again. */
            }
            if (0 == strcmp(arg, "-z") &&
                    argi[1] && 0 == strcmp(argi[1], "relro")) {
                /* Would increase size. */
                ++argi;  /* Omit both arguments: -z relro */
            } else if (arg[0] == '-' && arg[1] == 'L') {  /* "-L". */
                if (arg[2] == '\0' && argi[1]) ++argi;
                /* Omit -L... containing the system link dirs, the user-specified link
                 * dir flags were passed as -=L...
                 */
            } else if (0 == strncmp(arg, "-=L", 3)) {
                /* Omit -=L here, gets added at position argli. */
            } else if (
                0 == strcmp(arg, "-nostdlib") ||
                0 == strcmp(arg, "--do-xstaticcld") ||
                0 == strcmp(arg, "--do-xstaticldv") ||
                0 == strncmp(arg, "--hash-style=", 13) ||  /* Would increase size. */
                0 == strcmp(arg, "--build-id") ||
                0 == strncmp(arg, "--sysroot=", 10)) {
                /* Omit this argument. */
            } else {
                *argp++ = *argi;
            }
        }
        *argp = NULL;
        prog = strdupcat(argv[0], ".bin", "");  /* "ld.bin". */
        args[0] = prog;
        if (is_verbose) {
            fdprint(2, escape_argv("xstatic-ld: info: running ld:\n", args, "\n"));
        }
        execv(prog, args);
        fdprint(2, strdupcat("xstatic-ld: error: exec failed: ", prog, "\n"));
        return 120;
    }

    if (!argv[1] || 0 == strcmp(argv[1], "--help")) {
        fdprint(1, strdupcat(
                    "Usage: ", argv[0], " <gcc|g++|clang|clang++> [<compiler-arg>...]\n"
                    "Invokes the C/C++ compiler with -static against the xstatic uClibc.\n"
                ));
        return 1;
    }
    if (argv[1][0] == '-') {
        fdprint(2, "xstatic: error: please specify gcc|clang prog in 1st arg\n");
        return 1;
    }
    check_bflags(argv);
    check_xflags(argv);
    p = strdupcat(dirup, "/xstaticcld/ld.bin", "");
    if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) {
file_missing:
        fdprint(2, strdupcat(
                    "xstatic: error: file missing, please install: ", p, "\n"));
        return 123;
    }
    p = strdupcat(dirup, "/xstaticusr/lib/libc.a", "");
    if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing;
    p = strdupcat(dirup, "/xstaticusr/include/stdio.h", "");
    if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing;
    p = strdupcat(dirup, "/xstaticusr/lib/crt1.o", "");
    if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing;
    p = strdupcat(dirup, "/xstaticcld/crtbeginT.o", "");
    if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing;
    prog = find_on_path(argv[1]);
    if (!prog) {
        fdprint(2, strdupcat(
                    "xstatic: error: compiler not found: ", argv[1], "\n"));
        return 119;
    }
    argv[1] = argv[0];
    ++argv;
    --argc;
    argp = args = malloc(sizeof(*args) * (argc + 20));
    *argp++ = prog;  /* Set destination argv[0]. */
    /* No need to check for -m64, check_bflags has already done that. */
    if (!argv[1] ||
            (!argv[2] &&
             (0 == strcmp(argv[1], "-v") || 0 == strcmp(argv[1], "-m32"))) ||
            (argv[2] && !argv[3] &&
             0 == strcmp(argv[1], "-m32") &&
             0 == strcmp(argv[2], "-v"))) {
        /* This changes the `Target: ...' of Clang to i386, but of GCC. */
        if (!argv[1] || (!argv[2] && 0 != strcmp(argv[1], "-m32"))) {
            *argp++ = "-m32";
        }
        /* Don't add any flags, because the user wants some version info, and with
         * `-Wl,... -v' gcc and clang won't display version info.
         */
        memcpy(argp, argv + 1, argc * sizeof(*argp));
    } else {
        char need_linker;
        char has_nostdinc, has_nostdincxx;
        lang_t lang;
        need_linker = detect_need_linker(argv);
        detect_nostdinc(argv, &has_nostdinc, &has_nostdincxx);
        detect_lang(prog, argv, &lang);

        /* When adding more arguments here, increase the args malloc count. */
        /* We don't need get_autodetect_archflag(argv), we always send "-m32". */
        *argp++ = "-m32";
        *argp++ = "-static";
        /* The linker would be ../xstaticcld/ld, which is also a trampoline binary
         * of ours.
         */
        *argp++ = strdupcat("-B", dirup, "/xstaticcld");
        /* This puts xstaticusr/include to the top of the include path, and keeps
         * the gcc or clang headers below that. Specifying --nostdinc (when
         * lang.is_compiling) would remove these compiler-specific headers (e.g.
         * stdarg.h), which we don't want removed, because libc headers depend
         * on them.
         *
         * TODO(pts): Make /usr/local/bin/clang also work.
         */
        *argp++ = strdupcat("--sysroot=", dirup,
                            lang.is_clang && is_dirprefix(prog, "/usr/bin") ?
                            "/xstaticclangempty" : "/xstaticempty");
        if (lang.is_compiling) {
            /*
             * Without this we get the following error compiling binutils 2.20.1:
             * chew.c:(.text+0x233f): undefined reference to `__stack_chk_fail'
             * We can't implement this in a compatible way, glibc gcc generates %gs:20,
             * uClibc-0.9.33 has symbol __stack_chk_guard.
             */
            *argp++ = "-fno-stack-protector";
            if (lang.is_cxx) {
                /* Glitch: It's not possible to disable the gcc warning ``cc1: warning:
                 * command line option "-nostdinc++" is valid for C++/ObjC++ but not
                 * for C'', there is no such clang warning. Example invocation (with
                 * both .c and .cc files): ``xstatic gcc -c -W -Wall -O2 co.c
                 * hello.cc''.
                 */
                *argp++ = "-nostdinc++";
                if (!has_nostdincxx) {
                    /* Clang has -cxx-isystem, which is a no-op when compiling C code,
                     * but it adds the directory after the regular -isystem. But we need
                     * the C++ headers in front of the C headers (because of conflicting
                     * files, e.g. complex.h, tgmath.h, fenv.h).
                     *
                     * So we don't use -cxx-isystem, but we use -isystem uniformly for
                     * Clang and GCC.
                     *
                     * A small glitch: if there are both C and C++ source files
                     * specified (because C source files mustn't have C++ headers on
                     * their include path), and a non-C++ compiler is used (e.g.
                     * ``xstatic gcc -c -W -Wall -O2 helloco.c hello.cc''), then
                     * `#include <vector>' in the .c file would find the C++ vector
                     * header, and fail with a confusing error message when parsing it.
                     */
                    *argp++ = "-isystem";
                    *argp++ = strdupcat(dirup, "/xstaticusr/c++include", "");
                    /* Regular g++ libstdc++ has non-backward and then backward. */
                    *argp++ = "-isystem";
                    *argp++ = strdupcat(dirup, "/xstaticusr/c++include/backward", "");
                }
            }
            if (!has_nostdinc) {
                *argp++ = "-isystem";
                *argp++ = strdupcat(dirup, "/xstaticusr/include", "");
            }
        }

        for (argi = argv + 1; (arg = *argi); ++argi) {
            if (0 == strcmp(arg, "-v")) {
                is_verbose = 1;
                *argp++ = arg;
            } else if (0 == strcmp(arg, "-static") ||
                       0 == strcmp(arg, "-xstatic") ||
                       0 == strcmp(arg, "--xstatic") ||
                       0 == strcmp(arg, "-nostdinc") ||
                       0 == strcmp(arg, "-nostdinc++") ||
                       0 == strcmp(arg, "-m32")) {
            } else if (arg[0] == '-' && arg[1] == 'L') {  /* "-L". */
                /* Convert -L to -=L in the linker command-line on which our ld wrapper
                 * code can trigger.
                 */
                if (arg[2] == '\0' && argi[1]) {
                    *argp++ = strdupcat("-Wl,-=L", "", *++argi);
                } else {
                    *argp++ = strdupcat("-Wl,-=L", "", arg + 2);
                }
            } else {
                *argp++ = arg;
            }
        }

        if (need_linker) {
            *argp++ = "-Wl,--do-xstaticcld";
            if (is_verbose) *argp++ = "-Wl,--do-xstaticldv";
        }
        *argp++ = NULL;
    }

    if (is_verbose) {
        fdprint(2, escape_argv("xstatic: info: running compiler:\n", args, "\n"));
    }
    execv(prog, args);
    fdprint(2, strdupcat("xstatic: error: exec failed: ", prog, "\n"));
    return 120;
}
Exemple #7
0
/**
 * Read one line from the keyboard
 * 
 * @param  fd  File descriptor for the sink
 */
void readkbd(int fd)
{
  static int next_is_dead2 = 0;
  static int have_dead_key = 0;
  static int modifiers = 0;
  
  for (;;) /* Please fix or report any inconsistency with the Linux VT keyboard. */
    {
      int c = getchar();
      int released = !!(c & 0x80);
      
      if ((KTYP(key_maps[0][c & 0x7F]) & 0x0F) == KT_SHIFT)
	{
	  c = key_maps[0][c & 0x7F];
	  if (released)
	    modifiers &= ~(1 << KVAL(c));
	  else
	    modifiers |= 1 << KVAL(c);
	  continue;
	}
      
      if (key_maps[modifiers] == NULL)
	continue;
      c = key_maps[modifiers][c] & 0x0FFF;
      
      switch (KTYP(c))
	{
	case KT_LETTER: /* Symbols that are affected by the Royal Canterlot Voice key */
	case KT_LATIN:  /* Symbols that are not affected by the Royal Canterlot Voice key */
	  if (next_is_dead2)
	    {
	      next_is_dead2 = 0;
	      have_dead_key = KVAL(c) & 255;
	    }
	  else if (have_dead_key) /* TODO: how does multiple dead keys work? */
	    {
	      unsigned int i;
	      c = KVAL(c) & 255;
	      for (i = 0; i < accent_table_size; i++)
		if (accent_table[i].diacr == have_dead_key)
		  if (accent_table[i].base == c)
		    {
		      c = accent_table[i].result;
		      break;
		    }
	      if (i == accent_table_size)
		{
		  for (i = 0; fallback_accent_table[i].result; i++)
		    if (fallback_accent_table[i].diacr == have_dead_key)
		      if (fallback_accent_table[i].base == c)
			{
			  c = fallback_accent_table[i].result;
			  break;
			}
		  if (fallback_accent_table[i].result == 0)
		    {
		      if (c == ' ')
			c = have_dead_key;
		      else if (c != have_dead_key)
			fdputucs(fd, have_dead_key);
		    }
		}
	      fdputucs(fd, c);
	      have_dead_key = 0;
	    }
	  else
	    fdputucs(fd, KVAL(c) & 255);
	  break;
	  
	case KT_META:   /* Just like KT_LATIN, except with meta modifier */
	  fdputucs(fd, '\033'); /* We will assume this mode rather than set 8:th bit-mode */
	  fdputucs(fd, KVAL(c) & 255);
	  /* TODO how should `next_is_dead2` and `have_dead_key` behave here? */
	  break;
	  
	case KT_FN:     /* Customisable keys, usally for escape sequnces. Includes F-keys and some misc. keys */
	  if (func_table[KVAL(c)] != NULL)
	    fdprint(fd, func_table[KVAL(c)]);
	  break;
	  
	case KT_DEAD:   /* Dead key */
	  next_is_dead2 = 0;
	  have_dead_key = *(KVAL_MAP[KTYP(c)][KVAL(c)]) & 255;
	  break;
	  
	case KT_DEAD2:  /* Table-assisted customisable dead keys */
	  next_is_dead2 = 0;
	  have_dead_key = KVAL(c);
	  break;
	  
	case KT_SPEC:   /* Special keys*/
	case KT_PAD:    /* Keypad */
	case KT_CUR:    /* Arrows keys */
	case KT_ASCII:  /* This is what happens when somepony holds down Alternative whil using the keypad */
	  if (KVAL_MAP[KTYP(c)][KVAL(c)] != NULL)
	    {
	      const char* str = KVAL_MAP[KTYP(c)][KVAL(c)];
	      fdprint(fd, str);
	      if (!strcmp(str, "\n"))
		return;
	    }
	  else if (KTYP(c) == KT_SPEC)
	    switch (c)
	      {
	      case K_COMPOSE:     /* Compose key */
		next_is_dead2 = 1;
		break;
		
	      case K_NUM:         /* TODO: Num Lock */
	      case K_BARENUMLOCK: /* No difference as far as this program is consired.
				   * (See linux-howtos/Keyboard-and-Console-HOWTO for more information.)  */
		break;
		
	      default: /* Other keys do nothing */
		break;
	      }
	  break;
	  
	case KT_SHIFT:  /* A modifier is used, we took care about this before the switch, so this should not happen */
	case KT_CONS:   /* Somepony is trying to switch VT. Fat chance! */
	case KT_LOCK:   /* TODO: Is this sticky keys that toggle? */
	case KT_SLOCK:  /* TODO: Is this sticky keys that resemble dead keys? */
	case KT_BRL:    /* TODO: Braille, how does this work? */
	default:        /* What?! This should not happen! */
	  break;
	}
    }
}
Exemple #8
0
/*---------------------------------------------------------------------------
 Send a ramp and our ID to the other system, get their ramp and ID.
 print any differences in the ramp to both DPRINT and fdprint.
 If session ID's are the same, print warning to both DPRINT and fdprint.
---------------------------------------------------------------------------*/
void exchange_ramp(PORT *port, long sessionID)
{
	// See if data is getting through to the other side.
	unsigned char ibuf[256], obuf[256];
	char message[256];
	long theirID;
	int i;

	// Generate ramp and send it.
	for (i=0; i<sizeof(obuf); i++)
		obuf[i] = i;
	WriteBuffer(port, obuf, sizeof(obuf));
	// Send sessionID.
	WriteBuffer(port, (char *)&sessionID, sizeof(sessionID));
	// Let other side do the same, and let data percolate.
	sleep(1);
	if (SpaceUsedInTXBuffer(port) > 0) {
		sprintf(message, "Warning: %d bytes left in output buffer after 1 second\r\n",SpaceUsedInTXBuffer(port));
		fdprint(message);
		DPRINT((message));
	}

	// Skip newline if present
	// Read the other side's ramp.
	ReadBuffer(port, ibuf, 1);
	if (ibuf[0] == '\n') {
		fdprint("skipping newline\r\n");
		DPRINT(("skipping newline\n"));
		ReadBuffer(port, ibuf, sizeof(obuf));
	} else {
		ReadBuffer(port, ibuf+1, sizeof(obuf)-1);
		// kludge
		port->count++;
	}

	sprintf(message, "Sent %d byte ramp, read %d bytes.\r\n", sizeof(obuf), port->count);
	fdprint(message);
	DPRINT((message));
	if (port->count < 10) {
		fdprint("\r\nError: Ramp not received.  Check to make sure the cable is connected,\r\n"
		"that you have selected the right comm port on both computers,\r\n"
		"and that both players ran netmech with -Ktest.\r\n\r\n");
		return;
	}

	// Find first byte that differs, print rest out.
	for (i=0; (i<port->count) && (ibuf[i] == obuf[i]) && (i < sizeof(obuf)); i++)
		;
	if (i < port->count) {
		sprintf(message, "Error in received ramp starting at byte %d:\r\n", i);
		fdprint(message);
		DPRINT((message));
		while ((i < port->count) && (i < sizeof(ibuf))) {
			if (!(i % 16)) { fdprint("\r\n"); DPRINT(("\n")); }
			sprintf(message, "%2.2x ", ibuf[i]);
			fdprint(message);
			DPRINT((message));
			i++;
		}
		fdprint("\r\n");
		DPRINT(("\n"));
	}
	if (i < sizeof(obuf)) {
		fdprint("Input ramp ends early.\r\n");
		DPRINT(("Input ramp ends early.\n"));
	}
	// Get their ID
	ReadBuffer(port, (char *)&theirID, sizeof(theirID));
	sprintf(message, "Trying to read their sessionID; read %d bytes, theirID=%x.\r\n", port->count, theirID);
	fdprint(message);
	DPRINT((message));
	if (theirID == sessionID) {
		sprintf(message,
		"Error: Their sessionID same as ours!  Looks like there's no serial\r\n"
		"cable or modem attached!!  Please check your cables.\r\n");
		fdprint(message);
		DPRINT((message));
	}
}