haiku-website/static/legacy-docs/benewsletter/Issue4-16.html

825 lines
60 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Be Newsletters - Volume 4: 1999</title><link rel="stylesheet" href="be_newsletter.css" type="text/css" media="all" /><link rel="shortcut icon" type="image/vnd.microsoft.icon" href="./images/favicon.ico" /><!--[if IE]>
<link rel="stylesheet" type="text/css" href="be_newsletter_ie.css" />
<![endif]--><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Be Newsletters" /><link rel="up" href="volume4.html" title="Volume 4: 1999" /><link rel="prev" href="Issue4-15.html" title="Issue 4-15, April 14, 1999" /><link rel="next" href="Issue4-17.html" title="Issue 4-17, April 28, 1999" /></head><body><div id="header"><div id="headerT"><div id="headerTL"><a accesskey="p" href="Issue4-15.html" title="Issue 4-15, April 14, 1999"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a accesskey="u" href="volume4.html" title="Volume 4: 1999"><img src="./images/navigation/up.png" alt="Up" /></a> <a accesskey="n" href="Issue4-17.html" title="Issue 4-17, April 28, 1999"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="headerTR"><div id="navigpeople"><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div><div class="navigboxed" id="naviglang" title="English">en</div></div><div id="headerTC">Be Newsletters - Volume 4: 1999</div></div><div id="headerB">Prev: <a href="Issue4-15.html">Issue 4-15, April 14, 1999</a>  Up: <a href="volume4.html">Volume 4: 1999</a>  Next: <a href="Issue4-17.html">Issue 4-17, April 28, 1999</a></div><hr /></div><div class="article"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Issue4-16"></a>Issue 4-16, April 21, 1999</h2></div></div></div><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Marketing4-16"></a>Here Comes the First Big Marketing Wave</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Roy</span> <span class="surname">Graham</span></span></div></div></div><p>
I'm nearing the end of my first month at Be and it has been the most
exciting and inspiring start that I have had at any company. I've been
particularly inspired by the passion shown by you, our Developers. Never
before have I seen such commitment to help a company succeed. Your
suggestions and comments come in thick and fast—some negatively
worded, most positive—but all with a passion that tells me you're
trying to help us all be even more successful. I love that.
</p><p>
Be has, you tell me, delivered great technology. Now it's time to
complement this with aggressive marketing, so we can rapidly expand the
user base to which you can market your applications. We're planning a
major marketing campaign for the second half of this year, with the
express goal of raising the market awareness of Be significantly, and
also of achieving a major expansion of the user base. Make plans to
attend PCExpo and be a part of our campaign kickoff.
</p><p>
In order to start our campaign with a bang, we needed a great product
platform. When I started at Be I spent a lot of time with Be developers,
learning the 4.1 release and where we were in the release cycle. It
quickly became apparent that it would not be ready for prime time until
June. You've come to expect very high quality from us and we're not about
to change that. So, late June is when to look for our next release.
</p><p>
Next we needed to decide what to call the release. Unfortunately, we'd
backed ourselves into a corner by calling it 4.1. Not any more. From now
on, all future releases will have a project name that is unrelated to
release numbers. Accordingly, 4.1 is now called Genki (Japanese for "all
is well"!!).
</p><p>
Closer to the release date we'll decide the exact version number (OK,
it's marketing, but give me some slack—I'm trying to help you by
expanding the user base for your applications!!). The good news is that
Genki (like 4.1) will remain an update, so it's free to all BeOS Release
4.0 users.
</p><p>
Other news that I announced at the BeDC included our expanded commitment
to the Japanese market, as attested to by the hiring of our first
employee in Japan, Takeaki Akahoshi, as Channel Sales Manager. Also, I've
initiated a crash program to get BeDepot into reasonable shape for now,
with a view to replacing it later this year with a longer term solution.
</p><p>
Finally, this quarter you'll see changes on the Be web site. We are
adding a commercial front end to give it a more polished look and feel,
with a view toward appealing to a wider audience.
</p><p>
So, I'm delighted to be on board and I look forward to working with you
as we ride the first big marketing wave.
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering4-16"></a>Be Engineering Insights: Son of Teletype Meets Multimedia</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Rico</span> <span class="surname">Tudor</span></span></div></div></div><p>
I write this article a few days after the BeDC devoted to the Media Kit.
There, the air was abuzz with MP3, AVI, EXIF, and other esoteric
encodings. And yet, the lowly data format called ASCII lives on in the
waning light of the second millennium. Long after everyone has forgotten
how to interpret the data flavors of this month, there will still be a
way to read those old e-mail messages. If you can imagine the BeOS
Terminal app as the MediaPlayer for ASCII, then I will discuss part of
the ASCII kit cryptically named the pseudo-tty driver.
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id780032"></a>History</h3></div></div></div><p>
The term "tty" is a contraction of Teletype, maker of such famous icons
as the Model 37 terminal. Printing noisily on paper, with margin bell and
optional paper tape punch, it was a multimedia device in its own right.
Historians can review its output in the early service of UNIX at
<a class="ulink" href="http://cm.bell-labs.com/cm/cs/who/dmr/1stEdman.html">http://cm.bell-labs.com/cm/cs/who/dmr/1stEdman.html</a>
</p><p>
The standard configuration in those days was
</p><pre class="screen">
shell—tty driver == tty
</pre><p>
where "--" indicates a user/kernel space interface, and "==" is a serial
cable. Hard copy terminals were replaced in time by "glass" ttys, and
ttys were replaced altogether by the PC (running a terminal emulator
program):
</p><pre class="screen">
shell—tty driver == tty driver—tty emulator
</pre><p>
As the PC gained CPU power and OS sophistication, there was no need for
the command line (shell) to reside on a distant, and expensive,
mini-computer or mainframe. This has resulted in entirely local plumbing
like so:
</p><pre class="screen">
shell—pty driver—tty emulator
</pre><p>
In essence, the pseudo-tty, or pty, driver acts as a tty driver with
serial line looped back. This concept was pioneered by the Berkeley
version of Unix, and is now universally available on UNIX and UNIX-like
systems.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id780092"></a>Purpose</h3></div></div></div><p>
Two distinct, but related, reasons for using the pty driver are to direct
interactive programs, and to provide tty services for them. These reasons
apply to command line programs only; BeOS apps that function graphically
are not going to be interested.
</p><p>
An interactive program tends to write <code class="filename">stdout</code> unbuffered or line-by-line.
This is less efficient, but operates sensibly with alternating prompts
and user input. The user can better see realtime progress, too. Posix
libraries determine whether the output is a "terminal" and, if not,
buffer output fully. Unfortunately, this means that running an
interactive program through a pipe doesn't work well:
</p><pre class="screen">
(sleep 1; echo bc; echo 1; echo '2^99999/2^99998') | sh
(sleep 1; echo bc; echo 1; echo '2^99999/2^99998') | sh | cat
</pre><p>
In the first case, the shell is instructed to start the desk calculator
<code class="command">bc</code>, which evaluates arithmetic expressions. Since the output is a
terminal, we see each result as it's ready. In the second case, <code class="command">bc</code> is
writing to a pipe, and all results are buffered until it exits.
</p><p>
Some programs behave even more radically when the output is a terminal.
Compare
</p><pre class="screen">
ls /bin
ls /bin | cat
</pre><p>
The general desire in all these cases is to control the shell and its
subprocesses with our own program, while appearing to be a terminal at
the far end of a serial line.
</p><p>
The second reason for using the pty driver is to provide tty services,
which includes input echoing, character erase
(<span class="keysym">ctrl</span>+<span class="keycap">H</span>),
signals
(<span class="keysym">ctrl</span>+<span class="keycap">C</span>),
etc. The services in BeOS are a subset of the Posix "termios" standard.
As an example, the Backspace key
(<span class="keysym">ctrl</span>+<span class="keycap">H</span>) erases the last character on
the current line of input; a program like <code class="command">bc</code> will see the fully formed
line only when you hit Return.
</p><p>
Current BeOS clients of the pty driver include "telnetd" (incoming telnet
connections), <span class="application">/bin/getty</span> (shell session on serial port), Terminal, and
Pavel's Eddie (C++ development environment).
</p><p>
The remainder of this article documents the code for a useful app called
"script." Modeled after the similarly named UNIX utility, it starts an
interactive subshell such that all I/O activity is recorded verbatim to a
log file, called "typescript." The code consists of two parts: first, a
reusable class called <code class="classname">PTY</code> and second, the particular use of this code for
"script."
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id780219"></a>The Code</h3></div></div></div><p>
Here is the declaration for the <code class="classname">PTY</code> class, which conveniently wraps the
pty driver. A program like "script" has quite a few chores, but these are
handled almost entirely by <code class="classname">PTY</code>. To use, you must derive your own class
from <code class="classname">PTY</code>, and define the hook functions
<code class="methodname">GetData()</code>, <code class="methodname">PutData()</code>,
and <code class="methodname">Done()</code>. Their
purpose is described later.
</p><pre class="programlisting cpp">
#include &lt;Application.h&gt;
#include &lt;signal.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;termios.h&gt;
#include &lt;dirent.h&gt;
#define <code class="function">ctrl</code>( <code class="parameter">c</code>) ((<code class="parameter">c</code>) - 0100)
struct <span class="type">PTY</span> {
<code class="methodname">PTY</code>( );
<code class="methodname">~PTY</code>( );
<span class="type">bool</span> <code class="methodname">Init</code>( <span class="type">char *</span>[]);
virtual <span class="type">int</span> <code class="methodname">GetData</code>( <span class="type">uchar</span> [], <span class="type">uint</span>) = 0;
virtual <span class="type">int</span> <code class="methodname">PutData</code>( <span class="type">uchar</span> [], <span class="type">uint</span>) = 0;
virtual <span class="type">void</span> <code class="methodname">Done</code>( ) = 0;
private:
<span class="type">bool</span> <code class="methodname">ptyinit</code>( );
static <span class="type">int32</span> <code class="methodname">feeder</code>( <span class="type">PTY *</span>),
<code class="methodname">drainer</code>( <span class="type">PTY *</span>);
<span class="type">struct termios</span> <code class="varname">tsaved</code>;
<span class="type">int</span> <code class="varname">ptyfd</code>,
<code class="varname">ttyfd</code>;
<span class="type">thread_id</span> <code class="varname">fid</code>,
<code class="varname">did</code>;
<span class="type">char</span> <code class="varname">tty</code>[40];
};
</pre><p>
A pair of devices are employed in a pty connection: master and slave. If
the master is <code class="filename">/dev/pt/p0</code>, then
the slave will be <code class="filename">/dev/tt/p0</code>. The shell
will inherit file descriptors to the slave, while "script" directs
through the master side. Finding an available pair uses the traditional
(ok, baroque) method: try to open each master device until one succeeds.
That master device is now yours exclusively. Here is the code:
</p><pre class="programlisting cpp">
<span class="type">bool</span> <code class="classname">PTY</code>::<code class="methodname">ptyinit</code>( )
{
<span class="type">DIR *</span><code class="varname">dd</code> = <code class="function">opendir</code>( "/dev/pt");
while (<span class="type">dirent *</span><code class="varname">d</code> = <code class="function">readdir</code>( <code class="varname">dd</code>))
if (<code class="varname">d</code>-&gt;<code class="varname">d_name</code>[0] != '.') {
<code class="function">sprintf</code>( <code class="varname">tty</code>, "/dev/pt/%s", <code class="varname">d</code>-&gt;<code class="varname">d_name</code>);
<code class="varname">ptyfd</code> = <code class="function">open</code>( <code class="varname">tty</code>, <code class="constant">O_RDWR</code>);
if (<code class="varname">ptyfd</code> &gt; 0) {
<code class="function">sprintf</code>( <code class="varname">tty</code>, "/dev/tt/%s", <code class="varname">d</code>-&gt;<code class="varname">d_name</code>);
<code class="varname">ttyfd</code> = <code class="function">open</code>( <code class="varname">tty</code>, <code class="constant">O_RDWR</code>);
if (<code class="varname">ttyfd</code> &gt; 0) {
<code class="function">closedir</code>( <code class="varname">dd</code>);
return (<code class="constant">TRUE</code>);
}
<code class="function">close</code>( <code class="varname">ptyfd</code>);
}
}
<code class="function">closedir</code>( <code class="varname">dd</code>);
return (<code class="constant">FALSE</code>);
}
</pre><p>
The "." and ".." directory entries need to be skipped.
</p><p>
<span class="application">script</span> must maintain two data paths.
The first comes from the <code class="filename">stdin</code> of <span class="application">script</span>,
goes through the pty driver from master side to slave side, and
finally to the shell's <code class="filename">stdin</code>. The second path originates from the shell's
<code class="filename">stdout</code>, goes through the pty driver from slave side to master side, and
finally to the <code class="filename">stdout</code> of <span class="application">script</span>. It is that
<code class="filename">stdout</code> which is cloned off to the log file, <code class="filename">typescript</code>.
Each data path has a dedicated BeOS thread, using the following code:
</p><pre class="programlisting cpp">
<span class="type">int32</span> <code class="classname">PTY</code>::<code class="methodname">feeder</code>( <span class="type">PTY *</span><code class="parameter">pty</code>)
{
<span class="type">uchar</span> <code class="varname">data</code>[1000];
<span class="type">int</span> <code class="varname">i</code>;
while ((<code class="varname">i</code> = <code class="parameter">pty</code>-&gt;<code class="methodname">GetData</code>( <code class="varname">data</code>, sizeof <code class="varname">data</code>)) &gt; 0)
<code class="function">write</code>( <code class="parameter">pty</code>-&gt;<code class="varname">ptyfd</code>, <code class="varname">data</code>, <code class="varname">i</code>);
<code class="parameter">pty</code>-&gt;<code class="methodname">Done</code>( );
}
<span class="type">int32</span> <code class="classname">PTY</code>::<code class="methodname">drainer</code>( <span class="type">PTY *</span><code class="parameter">pty</code>)
{
<span class="type">uchar</span> <code class="varname">data</code>[1000];
<span class="type">int</span> <code class="varname">i</code>;
while ((<code class="varname">i</code> = <code class="function">read</code>( <code class="parameter">pty</code>-&gt;<code class="varname">ptyfd</code>, <code class="varname">data</code>, sizeof <code class="varname">data</code>)) &gt; 0)
<code class="parameter">pty</code>-&gt;<code class="methodname">PutData</code>( <code class="varname">data</code>, <code class="varname">i</code>);
<code class="parameter">pty</code>-&gt;<code class="methodname">Done</code>( );
}
</pre><p>
The read and write system calls typically block until data is available,
which is why we need the dedicated threads. This also ensures the desired
data flow control. In the idle state of <span class="application">script</span>,
the feeder thread is
blocked in a read of <code class="filename">stdin</code>, while the drainer thread is blocked in a read
of the master side pty. Meanwhile, the team's main thread can service
<code class="classname">BApplication</code>.
</p><p>
The constructor/destructor code saves and restores the tty mode of
<span class="application">script</span> stdio. The destructor must kill
the feeder and drainer threads,
since they are most likely blocked where polite methods, like semaphores,
cannot help.
</p><pre class="programlisting cpp">
<code class="classname">PTY</code>::<code class="methodname">PTY</code>( )
{
<code class="varname">fid</code> = <code class="varname">did</code> = -1;
<code class="function">tcgetattr</code>( 0, &amp;<code class="varname">tsaved</code>);
}
<code class="classname">PTY</code>::<code class="methodname">~PTY</code>( )
{
<code class="function">tcsetattr</code>( 0, <code class="constant">TCSANOW</code>, &amp;<code class="varname">tsaved</code>);
if (<code class="varname">did</code> &gt; 0)
<code class="function">kill_thread</code>( <code class="varname">did</code>);
if (<code class="varname">fid</code> &gt; 0)
<code class="function">kill_thread</code>( <code class="varname">fid</code>);
}
</pre><p>
Take a deep breath! This code exercises both BeOS and Posix functions, and
is where the bureaucratic details are hiding. Our pty connection consists
of slave side file descriptor "ttyfd" and master side
"ptyfd". The slave side tty mode is set to something human
friendly. Between the <code class="function">fork()</code> and the
<code class="function">execv()</code>, we have a chance to customize the execution
environment of the subshell. This includes its <code class="filename">stdin</code>,
<code class="filename">stdout</code>, <code class="filename">stderr</code>, name of
controlling tty device, and its own process group for signals. The subshell
will have no knowledge of "ptyfd", or the execution environment
of "script." If the subshell exits, "script" will get
master side i/o errors; if <span class="application">script</span> exits, the
entire subshell session will get a hangup signal
(<code class="constant">SIGHUP</code>).
</p><pre class="programlisting cpp">
<span class="type">bool</span> <code class="classname">PTY</code>::<code class="methodname">Init</code>( <span class="type">char *</span><code class="parameter">com</code>[])
{
if ( ! <code class="function">ptyinit</code>( )) {
<code class="function">fprintf</code>( <code class="varname">stderr</code>, "no available pty\n");
return (<code class="constant">FALSE</code>);
}
<span class="type">struct termios</span> <code class="varname">t</code>;
<span class="type">struct winsize</span> <code class="varname">w</code>;
<code class="function">memset</code>( &amp;<code class="varname">t</code>, 0, <code class="function">sizeof</code>(<code class="varname">t</code>));
<code class="varname">t</code>.<code class="varname">c_iflag</code> = <code class="constant">ICRNL</code>;
<code class="varname">t</code>.<code class="varname">c_oflag</code> = <code class="constant">OPOST</code> | <code class="constant">ONLCR</code>;
<code class="varname">t</code>.<code class="varname">c_cflag</code> = <code class="constant">B19200</code> | <code class="constant">CS8</code> | <code class="constant">CREAD</code> | <code class="constant">HUPCL</code>;
<code class="varname">t</code>.<code class="varname">c_lflag</code> = <code class="constant">ISIG</code> | <code class="constant">ICANON</code> | <code class="constant">ECHO</code> | <code class="constant">ECHOE</code> | <code class="constant">ECHONL</code>;
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VINTR</code>] = <code class="function">ctrl</code>( 'C');
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VQUIT</code>] = <code class="function">ctrl</code>( '\\');
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VERASE</code>] = <code class="function">ctrl</code>( 'H');
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VKILL</code>] = <code class="function">ctrl</code>( 'U');
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VEOF</code>] = <code class="function">ctrl</code>( 'D');
<code class="function">tcsetattr</code>( <code class="varname">ptyfd</code>, <code class="constant">TCSANOW</code>, &amp;<code class="varname">t</code>);
if (<code class="function">ioctl</code>( 0, <code class="constant">TIOCGWINSZ</code>, &amp;<code class="varname">w</code>) == 0)
<code class="function">ioctl</code>( <code class="varname">ptyfd</code>, <code class="constant">TIOCSWINSZ</code>, &amp;<code class="varname">w</code>);
switch (<span class="type">pid_t</span> <code class="varname">pid</code> = <code class="function">fork</code>( )) {
case -1:
<code class="function">fprintf</code>( <code class="varname">stderr</code>, "system too busy\n");
return (<code class="constant">FALSE</code>);
case 0:
<span class="type">char</span> <code class="varname">te</code>[30];
<code class="varname">pid</code> = <code class="function">getpid</code>( );
<code class="function">setpgid</code>( <code class="varname">pid</code>, <code class="varname">pid</code>);
<code class="function">ioctl</code>( <code class="varname">ttyfd</code>, 'pgid', <code class="varname">pid</code>);
<code class="function">signal</code>( <code class="constant">SIGHUP</code>, <code class="constant">SIG_DFL</code>);
<code class="function">sprintf</code>( <code class="varname">te</code>, "TTY=%s", <code class="varname">tty</code>);
<code class="function">putenv</code>( <code class="varname">te</code>);
<code class="function">close</code>( 0);
<code class="function">close</code>( 1);
<code class="function">close</code>( 2);
<code class="function">dup</code>( <code class="varname">ttyfd</code>);
<code class="function">dup</code>( <code class="varname">ttyfd</code>);
<code class="function">dup</code>( <code class="varname">ttyfd</code>);
<code class="function">close</code>( <code class="varname">ptyfd</code>);
<code class="function">close</code>( <code class="varname">ttyfd</code>);
<code class="function">execv</code>( <code class="varname">com</code>[0], <code class="varname">com</code>);
<code class="function">fprintf</code>( <code class="varname">stderr</code>, "cannot exec %s\n", <code class="varname">com</code>[0]);
<code class="function">exit</code>( 0);
}
<code class="function">close</code>( <code class="varname">ttyfd</code>);
<code class="varname">t</code> = <code class="varname">tsaved</code>;
<code class="varname">t</code>.<code class="varname">c_iflag</code> &amp;= ~ <code class="constant">ICRNL</code>;
<code class="varname">t</code>.<code class="varname">c_oflag</code> &amp;= ~ <code class="constant">ONLCR</code>;
<code class="varname">t</code>.<code class="varname">c_lflag</code> &amp;= ~ (<code class="constant">ISIG</code>|<code class="constant">ICANON</code>|<code class="constant">ECHO</code>|<code class="constant">ECHOE</code>|<code class="constant">ECHONL</code>);
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VMIN</code>] = 1;
<code class="varname">t</code>.<code class="varname">c_cc</code>[<code class="constant">VTIME</code>] = 0;
<code class="function">tcsetattr</code>( 0, <code class="constant">TCSANOW</code>, &amp;<code class="varname">t</code>);
<code class="varname">fid</code> = <code class="function">spawn_thread</code>(<code class="varname">feeder</code>, "f", <code class="constant">B_NORMAL_PRIORITY</code>,
<code class="varname">this</code>);
<code class="varname">did</code> = <code class="function">spawn_thread</code>(<code class="varname">drainer</code>, "d", <code class="constant">B_NORMAL_PRIORITY</code>,
<code class="varname">this</code>);
if (<code class="varname">fid</code> &lt; 0 || <code class="varname">did</code> &lt; 0) {
<code class="function">fprintf</code>( <code class="varname">stderr</code>, "cannot spawn a thread\n");
return (<code class="constant">FALSE</code>);
}
<code class="function">resume_thread</code>( <code class="varname">fid</code>);
<code class="function">resume_thread</code>( <code class="varname">did</code>);
return (<code class="constant">TRUE</code>);
}
</pre><p>
With the subshell launched, <span class="application">script</span> must complete its own
initialization. Its own tty mode is set to "no echo" and "raw" (no
interpretation). This gives complete control of tty services to the
remote shell and its minions ("stty," text editors, etc). Finally, the
feeder and drainer threads are started.
</p><p>
That concludes the <code class="classname">PTY</code> class. With so much work already done, the
"script" app is almost a footnote. The boffo Script class, below, is
compactly defined with multiple inheritance.
</p><pre class="programlisting cpp">
struct <code class="classname">Script</code>: <code class="classname">BApplication</code>, <code class="classname">PTY</code>
{
<code class="methodname">Script</code>(<span class="type">char *</span><code class="parameter">sig</code>, <span class="type">char *</span><code class="parameter">c</code>[], <span class="type">char *</span><code class="parameter">log</code>): <code class="classname">BApplication</code>(<code class="parameter">sig</code>)
{
<code class="varname">com</code> = <code class="parameter">c</code>;
<code class="varname">logfd</code> = <code class="function">creat</code>( <code class="varname">log</code>, 0666);
<code class="methodname">Run</code>( );
}
<span class="type">void</span> <code class="methodname">ReadyToRun</code>()
{
if (<code class="varname">logfd</code> &lt; 0)
<code class="function">fprintf</code>( <code class="filename">stderr</code>, "cannot create logfile\n");
else if (!<code class="methodname">Init</code>(<code class="varname">com</code>))
<code class="methodname">Quit</code>();
}
<span class="type">void</span> <code class="methodname">Done</code>()
{
<code class="methodname">mLock</code>();
<code class="methodname">Quit</code>();
<code class="methodname">Unlock</code>( );
}
<span class="type">int</span> <code class="methodname">GetData</code>(<span class="type">uchar</span> <code class="parameter">data</code>[], <span class="type">uint</span> <code class="parameter">n</code>)
{
return (<code class="function">read</code>(0, <code class="parameter">data</code>, <code class="parameter">n</code>));
}
<span class="type">int</span> <code class="methodname">PutData</code>(<span class="type">uchar</span> <code class="parameter">data</code>[], <span class="type">uint</span> <code class="parameter">n</code>)
{
<code class="function">write</code>( <code class="varname">logfd</code>, <code class="parameter">data</code>, <code class="parameter">n</code>);
return (<code class="function">write</code>( 1, <code class="parameter">data</code>, <code class="parameter">n</code>));
}
<span class="type">char **</span><code class="varname">com</code>;
<span class="type">int</span> <code class="varname">logfd</code>;
};
<code class="function">main</code>()
{
<span class="type">static char *</span><code class="varname">com</code>[] = { "/bin/sh", 0 };
<code class="classname">Script</code> <code class="varname">s</code>( "application/script", <code class="varname">com</code>, "typescript");
return (0);
}
</pre><p>
The <code class="methodname">GetData()</code> hook is called by
<code class="classname">PTY</code> to get more data for the slave side. The
<code class="methodname">PutData()</code> hook is called when data from the slave
side has arrived. The <code class="methodname">Done()</code> hook is called when
<code class="classname">PTY</code> has detected that the subshell has exited. The
Be app can initiate a shutdown at any time by inducing a
<code class="classname">PTY</code> destructor call.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id781812"></a>Usage</h3></div></div></div><p>
To compile <span class="application">script</span>, clip out the code to
<code class="filename">script.cpp</code>, and type
</p><pre class="screen">
gcc script.cpp -lbe
</pre><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id781833"></a>Example 1: Capture some command output.</h4></div></div></div><pre class="screen">
$ script
sh-2.02# date
Fri Apr 16 02:01:26 CDT 1999
sh-2.02# exit
$ cat typescript
sh-2.02# date
Fri Apr 16 02:01:26 CDT 1999
sh-2.02# exit
$
</pre><p>
(Hmm, says something about my schedule.)
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id781852"></a>Example 2: A fully scripted Terminal.</h4></div></div></div><pre class="screen">
Terminal script
</pre><p>
Of course, this doesn't work if you are accessing your machine remotely
(e.g., using "telnet"); see Example 1 for this situation.
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id781869"></a>Example 3: Interactive program under program control.</h4></div></div></div><pre class="screen">
(sleep 1; echo bc; echo 1; echo '2^99999/2^99998'; sleep 60)
| script
</pre><p>
Compare the behavior and output with the earlier <code class="command">bc</code> example; the "sleep
60" is needed because an early EOF to <span class="application">script</span> will cause it to shut
down, and lose the long calculation. Given a command language and
bi-direction data flow to/from the target program, you can create a
powerful program-driving utility (like the popular <span class="application">"expect"</span>).
</p></div></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id781904"></a>Caveat</h3></div></div></div><p>
The content of "typescript" is a chronological record of all output, both
from programs and your own echoed input. It will likely contain ASCII
Return (octal 015), Bell (007), Backspace (010), Tab (011), and any ANSI
escape sequences. All these effects are faithfully reproduced with
</p><pre class="screen">
cat typescript
</pre><p>
You can see the uninterpreted stream by using a text editor or with
</p><pre class="screen">
od -c typescript
</pre><p>
"typescript" is quite different from Terminal copy-n-paste, where you are
selecting inert glyphs—different tools for different purposes.
</p></div></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering4-16-2"></a>Be Engineering Insights: Duty Now for the Future</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Howard</span> <span class="surname">Berkey</span></span></div></div></div><p>
Besides being the name of a great DEVO album, the title of this article
represents what I'd like to do with my space in this week's newsletter.
That is, describe some of the upcoming features in BeOS networking.
</p><p>
For Genki, the most visible change from a developer's standpoint is the
new C++ networking API. This was shipped in the 4.1 beta CD's
Experimental folder, and will be a part of the main Genki distribution.
The entire kit may be accessed by including
<code class="filename">NetworkKit.h</code> and linking
against <code class="filename">libnet.so</code>
and <code class="filename">libnetapi.so</code>. The new API introduces three main
classes: <code class="classname">BNetEndpoint</code>, <code class="classname">BNetAddress</code>,
and <code class="classname">BNetBuffer</code>. These classes
simplify creating and using network data connections, addresses, and
data, respectively. Documentation is forthcoming; until then, this
newsletter article and comments in the headers should be enough to get
you started.
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782020"></a>BNetEndpoint</h3></div></div></div><p>
The <code class="classname">BNetEndpoint</code> class abstracts network communication endpoints—think
of it as a "socket object." <code class="classname">BNetEndpoint</code> takes care of all the necessary
initialization in its constructor; it makes creating network connections
very simple:
</p><pre class="programlisting cpp">
<span class="type">int32</span> <code class="function">send_data</code>(<span class="type">const char *</span><code class="parameter">host</code>, <span class="type">short</span> <code class="parameter">port</code>,
<span class="type">const void*</span><code class="parameter">data</code>, <span class="type">int32</span> <code class="parameter">datalen</code>)
{
<code class="classname">BNetEndpoint</code> <code class="varname">comm</code>(<code class="constant">SOCK_STREAM</code>);
<span class="type">int32</span> <code class="varname">rc</code> = -1;
if(<code class="varname">comm</code>.<code class="methodname">InitCheck</code>() == <code class="constant">B_NO_ERROR</code>)
{
if(<code class="varname">comm</code>.<code class="methodname">Connect</code>(<code class="parameter">host</code>, <code class="parameter">port</code>) == <code class="constant">B_NO_ERROR</code>)
{
<code class="varname">rc</code> = <code class="varname">comm</code>.<code class="methodname">Send</code>(<code class="parameter">data</code>, <code class="parameter">datalen</code>);
}
}
return <code class="varname">rc</code>;
}
</pre><p>
The above code creates a <acronym class="acronym" title="Transmission Control Protocol">TCP</acronym>
connection to the specified hostname and
port, sends the data passed in, and automatically closes the connection,
returning the amount of data sent, or -1 on failure. As you can see, much
of the setup and housekeeping typically required when doing network
programming is taken care of by the <code class="classname">BNetEndpoint</code> object.
</p><p>
Many <code class="classname">BNetEndpoint</code> member functions map orthogonally
to the <acronym class="acronym">BSD</acronym> sockets
<acronym class="acronym">API</acronym>; it's also possible to get the socket descriptor out of a
<code class="classname">BNetEndpoint</code> for use with the standard sockets API. See
<code class="filename">NetEndpoint.h</code>
for details.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782200"></a>BNetAddress</h3></div></div></div><p>
Just as you can think of <code class="classname">BNetEndpoint</code> as a class that encapsulates the
<acronym class="acronym">BSD</acronym> sockets <acronym class="acronym">API</acronym>, <code class="classname">BNetAddress</code>
is a class that encapsulates the
functionality found in <code class="filename">netdb.h</code>.
<code class="classname">BNetAddress</code> is a convenient way to
store, resolve, and manipulate network addresses.
</p><p>
<code class="classname">BNetAddress</code> is used to represent network addresses, and provide useful
access to a network address in a variety of formats. <code class="classname">BNetAddress</code> provides
various ways to get and set a network address, converting to or from the
chosen representation into a generic internal one.
</p><p>
<code class="methodname">BNetAddress::SetTo()</code> is used to set the object to refer to the address
passed in, which can be specified in a variety of formats (omitting
<code class="methodname">InitCheck()</code>ing):
</p><pre class="programlisting cpp">
<span class="type">void</span> <code class="function">addrs</code>()
{
BNetAddress <code class="varname">addr</code>;
struct sockaddr_in <code class="varname">sa</code>;
in_addr <code class="varname">ia</code>;
<code class="varname">addr</code>.<code class="methodname">SetTo</code>("www.be.com", 80);
<code class="varname">addr</code>.<code class="methodname">SetTo</code>("www.be.com", "tcp", "http");
<span class="comment">// www.be.com == 207.126.103.9</span>
<code class="varname">ia</code>.<code class="varname">s_addr</code> = <code class="function">inet_addr</code>("207.126.103.9");
<code class="varname">addr</code>.<code class="methodname">SetTo</code>(<code class="varname">ia</code>, 80);
<code class="varname">sa</code>.<code class="varname">sin_family</code> = <code class="constant">AF_INET</code>;
<code class="varname">sa</code>.<code class="varname">sin_port</code> = <code class="function">htons</code>(80);
<code class="varname">sa</code>.<code class="varname">sin_addr</code> = <code class="function">inet_addr</code>("207.126.103.9");
<code class="varname">addr</code>.<code class="methodname">SetTo</code>(<code class="varname">sa</code>);
}
</pre><p>
All calls to <code class="code">addr.SetTo</code> in the above example are equivalent.
</p><p>
Similarly, <code class="methodname">BNetAddress::GetAddr()</code> returns the address referred to by the
<code class="classname">BNetAddress</code> object in a variety of formats. For example, the following
code converts <span class="type">sockaddr_in</span> structures into human-readable hostnames:
</p><pre class="programlisting cpp">
<span class="type">void</span> <code class="function">sin_convert</code>(<span class="type">const struct sockaddr_in &amp;</span><code class="parameter">sa</code>)
{
<code class="classname">BNetAddress</code> <code class="varname">addr</code>(<code class="parameter">sa</code>);
<span class="type">char</span> <code class="varname">host</code>[256];
<span class="type">unsigned short</span> <code class="varname">port</code>;
<code class="varname">addr</code>.<code class="methodname">GetAddr</code>(<code class="varname">host</code>, <code class="varname">port</code>);
cout &lt;&lt; "Host: " &lt;&lt; <code class="varname">host</code> &lt;&lt; "Port: " &lt;&lt; <code class="varname">port</code> &lt;&lt; endl;
}
</pre><p>
Likewise, to resolve a host name into a sockaddr_in:
</p><pre class="programlisting cpp">
<span class="type">struct sockaddr_in</span> <code class="function">resolve</code>(<span class="type">const char *</span><code class="parameter">host</code>)
{
<code class="classname">BNetAddress</code> <code class="varname">addr</code>(<code class="parameter">host</code>);
<span class="type">struct sockaddr_in</span> <code class="varname">sa</code>;
<code class="varname">addr</code>.<code class="methodname">GetAddr</code>(<code class="varname">sa</code>);
return <code class="varname">sa</code>;
}
</pre><p>
As you can see from these examples, <code class="classname">BNetAddress</code> has a number of
constructors that mirror its <code class="methodname">SetTo()</code>
member functions. <code class="classname">BNetAddress</code> also
takes care of any byte order conversion that's necessary with network
addresses.
</p><p>
<code class="classname">BNetEndpoint</code> is designed to work closely with
<code class="classname">BNetAddress</code>; any addresses
it expects can be represented by a <code class="classname">BNetAddress</code>, such as in
<code class="methodname">BNetEndpoint::SendTo()</code>.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782572"></a>BNetBuffer</h3></div></div></div><p>
Unlike <code class="classname">BNetAddress</code> and <code class="classname">BNetEndpoint</code>,
<code class="classname">BNetBuffer</code> has no analog in the <acronym class="acronym">BSD</acronym>
sockets <acronym class="acronym">API</acronym>. <code class="classname">BNetBuffer</code> is
a convenient way to manage network data that
is to be sent across the wire, including mundane tasks such as byte order
conversion. <code class="classname">BNetBuffer</code> is a network data container class. As I mention in
<code class="filename">NetBuffer.h</code>: <code class="classname">BNetBuffer</code>
is a dynamic buffer useful for storing data to
be sent across the network. Data is inserted into and removed from the
object using one of the many <code class="methodname">Append()</code>
and <code class="methodname">Remove()</code> member functions. Access
to the raw stored data is possible. The <code class="classname">BNetEndpoint</code>
class has a <code class="methodname">Send()</code> and
<code class="methodname">Receive()</code> function for use with
<code class="classname">BNetBuffer</code>. Network byte order conversion
is done automatically for all appropriate integral types in the <code class="methodname">Append()</code>
and <code class="methodname">Remove()</code> functions for that type.
</p><p>
<code class="classname">BNetBuffer</code> is best illustrated by an example. Suppose you have a
transaction protocol between a network client and a server application
which specifies that whenever the client receives a <code class="classname">BMessage</code>, it forwards
it to the server over the network connection, along with a client
timestamp and a transaction ID, which is a monotonically increasing
<span class="type">uint32</span>. It could be coded thusly:
</p><pre class="programlisting cpp">
<span class="type">static uint32</span> gTransID = 0;
[...]
<span class="type">int32</span> <code class="function">forwardBMessageToServer</code>(<span class="type">const <code class="classname">BMessage</code> &amp;</span><code class="parameter">msg</code>,
BNetEndpoint &amp;serverConn)
{
BNetBuffer packet, sizepack;
bigtime_t timestamp;
uint32 trans_id, size;
timestamp = system_time();
trans_id = atomic_add(&amp;gTransID, 1);
packet.AppendUInt32(trans_id);
packet.AppendUInt64(timestamp);
packet.AppendMessage(msg);
size = packet.Size();
sizepack.AppendUInt32(size);
serverConn.Send(sizepack);
return serverConn.Send(packet);
}
</pre><p>
Voila! Networked <code class="classname">BMessage</code>s. <code class="classname">BNetBuffer</code>
does all the necessary byte order
conversion, so the server simply needs to call the matching <code class="methodname">Remove()</code>
member functions to get at the data the client was sending.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782730"></a>Archiving</h3></div></div></div><p>
Astute readers of the header files will have noticed by now that the C++
Networking API classes are all derived from <code class="classname">BArchivable</code>. In the case of
<code class="classname">BNetAddress</code> and <code class="classname">BNetBuffer</code>,
the archived data is just what you would
expect; a byte-gender-neutral archive of the address info or network
data, respectively.
</p><p>
For <code class="classname">BNetEndpoint</code>, it goes one step further—network connections are
archived. For example, if you're connected to a host and port when you
archive the <code class="classname">BNetEndpoint</code> object, later instantiation of that archive
returns a <code class="classname">BNetEndpoint</code> that is connected to the same host and port.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782775"></a>Debugging</h3></div></div></div><p>
There's also a class called <code class="classname">BNetDebug</code>, which provides (surprise!) some
debugging functionality. Calling <code class="code">BNetDebug::Enable(true)</code> turns Net API
debugging on; you can then call <code class="methodname">BNetDebug::Dump()</code> with buffers of data
for a nice hd-like output when you run your program from the Terminal.
Many <code class="classname">BNetEndpoint</code> calls generate debugging output when debugging is
enabled; play around and you'll see what I mean. The debug messages are
printed to <code class="filename">stderr</code>.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id782811"></a>Wrapping It Up</h3></div></div></div><p>
This overview should be enough to get you started with the new C++
networking API. But what would a newsletter article be without some
sample code for you to play with? You can find the source code for a
simple FTP client I wrote using the new networking API at:
ftp://ftp.be.com/pub/samples/network kit/FtpClient.zip
</p></div></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="DevWorkshop4-16"></a>Developers' Workshop: Media Kit Basics: Muxamania Part 1: The Media
Control Panel Application</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Owen</span> <span class="surname">Smith</span></span></div></div></div><p>
This week I've cooked up one of several Media Kit goodies that was
requested at the BeDC. It's a node, completely encapsulated in a media
add-on, that allows you to select one of several inputs to pass to the
output.
</p><p>
This code works with the upcoming beta release, genki/5. Go grab it from:
</p><p>
ftp://ftp.be.com/pub/samples/media_kit/Selector.zip
</p><p>
In this article, I'll illustrate how to create a simple UI for the node.
In the next installment, I plan to show you how the node handles buffers
and manages connections.
</p><p>
In Selector, I want users to be able to select the input they want routed
to the output. But, since the acrid smoke from BeDC preparations still
hasn't cleared from my Kube, I don't want to spend a lot of time working
on the UI. I'm looking for the simplest fire-and-forget mechanism I can
come up with.
</p><p>
In this case, <code class="methodname">BMediaRoster::StartControlPanel()</code> is definitely the answer.
It's designed to take care of all the details of running a UI for our
node, with no sticky residue. Unless your node says otherwise,
<code class="methodname">StartControlPanel()</code> simply launches your add-on as an application. The ID
of the node that you're editing will be passed to the newly launched
application as a command line argument.
</p><p>
There's one piece that is left for you, the add-on writer, to fill in:
you must determine what the add-on does once it's launched. Generally,
this involves displaying a window containing controls for editing the
node.
</p><p>
I've created a simple class to do most of the work for you:
<code class="classname">MediaControlPanelApp</code>. This
<code class="classname">BApplication</code>-derived class does the following:
</p><ul class="itemizedlist"><li><p>
It parses the command line to get the node IDs that will be passed to
it, and launches control panels for each of the nodes it's editing.
</p></li><li><p>
It keeps track of all currently running control panels, and quits
when all control panels have been closed.
</p></li><li><p>
It provides a virtual function <code class="methodname">CreateControlPanel()</code> (not related to
<code class="methodname">BMediaRoster::StartControlPanel()</code>!) which gets called when a new
control panel must be created for a node. By default, it creates a
simple window that shrinkwraps around the default media theme's view
for the node. However, you can override this function to create custom
<code class="classname">BWindow</code>s for your own kinds of nodes if you wish.
</p></li></ul><p>
Now, what does it take to get this awesome functionality in your add-on?
</p><pre class="programlisting cpp">
int main()
{
MediaControlPanelApp app;
app.Run();
return 0;
}
</pre><p>
... Not too much, I guess.
</p><p>
In conclusion, I'd like to extend greetings to all of the developers I
managed to meet in person at the BeDC. Even jacked up on a week's worth
of Dew and adrenaline, I somehow managed to relax a bit (especially after
the presentations were over!), and it was a singular treat for me to hang
out with you, the people that make this job really worthwhile.
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Gassee4-16"></a>More Better Formats...and a New Wing for the Great Roach Motel</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Jean-Louis</span> <span class="surname">Gassée</span></span></div></div></div><p>
It is a thing of beauty. We must take our hats off to Microsoft, again --
this time for two reasons: last week's announcement of MS Audio and the
soon-to-be-released Office 2000. Seriously, one has to admire the
strategic foresight and speedy execution, not to mention the way that
Microsoft moves the battle to a new field, leaving behind browser and
contractual skirmishes.
</p><p>
Let's start with the roach motel metaphor. It refers to file formats and
their use to trap files and their owners—and their wallets. As the old
slogan goes, "They can get in, but they can't check out."
</p><p>
Let's say that your business is
<acronym class="acronym" title="Computer Aided Design">CAD</acronym> software.
Files store complex designs
in a structured way, ready to be fed again into a kind of interpreter
that can further edit the design and render it on a screen or on paper,
on some <acronym class="acronym" title="Printed Circuit Board">PCB</acronym>
layout, or on chip-making equipment. The file format helps
your business in two ways, apart from offering rich constructs,
reliability and speed: 1) It can keep your competitors from seducing your
customers; and 2) It can help you generate revenue with new releases.
</p><p>
Both outcomes exhibit the same wonderful, hard to unscramble, mix of
legitimate and manipulative characteristics. In some cases, the law is on
your side and lets you protect trade secrets or even grants you a patent,
as we've seen for some formats. Or, you can keep your competitors
guessing or running if you hide and modify at the appropriate pace. In
addition, a new and richer format "encourages" customers to upgrade if
they want to continue exchanging files. In any given group, one or two
users of the new version is all it takes, and the newer/richer <acronym class="acronym">CAD</acronym> cannot
be read by the old version, without some information being lost.
</p><p>
We saw this game played with Office 97. Initially, in spite of using the
same three-letter suffix, Word 95 could not read the <code class="filename">.doc</code> files stored by
Word 97 users, thus encouraging users to move to the new version. But
users were not encouraged, they were enraged, thus in turn encouraging
Microsoft to issue a fix to allow interoperation of <code class="filename">.doc</code> files. In
another instance, the use of the <acronym class="acronym">RTF</acronym> format is now suggested by the Word
assistant. RTF is referred to as an exchange format that other non-MS
word processors can read. But if my information is correct, this is only
partly true. If you use "shapes" in your Word 97 document and store it in
<acronym class="acronym">RTF</acronym> format, you might believe the document will "interoperate" with
<acronym class="acronym">RTF</acronym>-capable word processors, but you might lose the drawings inserted in
the text. Again, I'm not saying this is automatically a bad thing, but
you see how it could be manipulated, used to thwart competitors and to
get more revenue from the installed base. A good example of the hard-to-
unscramble mix. But this is the old style roach motel.
</p><p>
Windows begat Explorer and Explorer begat Microsoft's version of <acronym class="acronym">HTML</acronym>,
thus advancing the fulfillment of Nathan Myrhvold, Microsoft's chief
scientist. In a white paper discussed in a celebrated New Yorker article,
Nathan saw an opportunity in the future for Microsoft to get a "vig" on
Net transactions. Controlling the de facto standard for <acronym class="acronym">HTML</acronym> is a good
first step. Try raising money for a Web company whose site will use a
version of HTML Explorer doesn't "speak." Explorer is a roach motel of
larger dimension than Office. And it's expanding.
</p><p>
Last week we had the announcement of MS Audio, a new format presented as
full of goodies: smaller than MP3, higher sound quality, copy protection,
and permission features. One has to salute again the speed and quality of
response to the threat posed by MP3, a format Microsoft didn't control,
and to the Secure Digital Music Initiative, a future standard being
designed by a consortium of music publishers, their own response to the
dangerously libertarian MP3. Explorer begat MS Audio, and we can imagine
what could happen to the non-MS audio formats.
</p><p>
And next month, or in June, we'll have Office 2000, which takes the game
to yet another level. The default format for Office will embrace an
extension of <acronym class="acronym">HTML</acronym>, Microsoft's own <acronym class="acronym">XML</acronym>. The good news is that it uses the
Web as a publishing and exchange medium. The other news is that Microsoft
now has even more control over the formats used for business and
entertainment on the Web.
</p><p>
In my country of birth, government officials (and cultural pundits --
when different) worry about US domination through the primacy of American
English, the US dollar, and Hollywood entertainment. The way they see it,
if you mint all these tokens of human commerce, it gives you an
advantage. I don't think all French people will convert to English any
time soon, but, for computers, all you need is a Windows or Office
upgrade and the new tokens are in circulation. This is what I meant when
I wondered if Microsoft hadn't succeeded in moving past the antitrust
suit—even before its conclusion—in a truly awesome way.
</p></div></div><div id="footer"><hr /><div id="footerT">Prev: <a href="Issue4-15.html">Issue 4-15, April 14, 1999</a>  Up: <a href="volume4.html">Volume 4: 1999</a>  Next: <a href="Issue4-17.html">Issue 4-17, April 28, 1999</a> </div><div id="footerB"><div id="footerBL"><a href="Issue4-15.html" title="Issue 4-15, April 14, 1999"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a href="volume4.html" title="Volume 4: 1999"><img src="./images/navigation/up.png" alt="Up" /></a> <a href="Issue4-17.html" title="Issue 4-17, April 28, 1999"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="footerBR"><div><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div></div><div id="footerBC"><a href="http://www.access-company.com/home.html" title="ACCESS Co."><img alt="Access Company" src="./images/access_logo.png" /></a></div></div></div><div id="licenseFooter"><div id="licenseFooterBL"><a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by-nc-nd/3.0/88x31.png" /></a></div><div id="licenseFooterBR"><a href="./LegalNotice.html">Legal Notice</a></div><div id="licenseFooterBC"><span id="licenseText">This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative
Commons Attribution-Non commercial-No Derivative Works 3.0 License</a>.</span></div></div></body></html>