Issue 2-47, November 26, 1997

Be Engineering Insights: Buried Treasures...

By Dominic Giampaolo

In a system like the BeOS, much effort goes into documenting the specifics of programming the BeOS and not as much is spent documenting more common parts of the libraries, such as the C library.

This makes sense, since there are numerous books on the C library and POSIX that cover it in adequate detail. Unfortunately, when some of us incorrigible C weenies add functions or extensions to the C library it means that they tend not to get documented.

Thus, it pays to peruse the header files found in:

/boot/develop/headers/posix/

If you poke around in this directory, one "interesting" header you'll find is parsedate.h. This is the header file for the parsedate() function that I added to the BeOS C library (which is part of libroot.so). The parsedate() routine is a sophisticated date parsing function that knows how to convert from a human-readable string into a standard time_t variable (i.e., the number of seconds since January 1, 1970). It even knows about time zones.

The parsedate() routine is pretty nifty, because it understands common time formats like "Mon, June 10th, 1993 10:00:03 am GMT". In fact, parsedate() understands virtually every imaginable verbose time format. This includes all the typical formats that you'd see in an e-mail message or Usenet news posting, or that other programs would print using ctime() or strftime(). The initial list of date formats supported by parsedate() was generated by culling the Date: line from around 80,000 news postings, so it's reliably comprehensive.

Having a routine that can parse such strings and return a canonical integer time is very useful if you ever have to parse an e-mail header or input a date that was output from another program. One drawback to using time formats such as these is that their rigidity makes them difficult for users to type. This becomes an issue if you need users to input a date as part of your program and you want to use parsedate().

To remedy the problem of inflexible time formats, parsedate() also understands many natural date and time specifications. The parsedate.h file alludes to this, but doesn't really go into detail about what these formats are. The parsedate function accepts the following "natural" time formats (square brackets indicate an optional item):

yesterday
today
tomorrow
[last | next] dayname (monday, tuesday, etc;
                       e.g., last monday)
[last | next] hour
[last | next] week
[last | next] month
[last | next] year
number minute  (e.g. 15 min, -30 minutes)
number hour    (e.g. 4 hours, -1 hour)
number day     (e.g. 5 days, -3 days)
number month   (e.g. 1 month, -2 months)
number year    (e.g. 2 years, -3 years)

Some slightly less casual but still fairly loose formats also work:

11/10/97 3pm
Dec 25th 5:00pm
Friday July 9th
10am Sunday

Because parsedate() understands these types of formats, you can use it in a variety of situations. It's easy to imagine a reminder/calendar program that, as an option, lets users just type a day (i.e., remind me next Thursday of ...). It's also possible to imagine a mail filter which would scan message text for date strings and build a calendar automatically (strings like "this Friday" are easily detectable).

parsedate() is also helpful in the BeOS Find panel. For example, if you need to find everything created in the last two days, you can do a find by last modification time and simply enter the string "-2 days". Other common finds might be everything modified since "yesterday" or "last week".

Another aspect of parsedate() is that when there's a choice of how to interpret a date, it assumes you want a date after the current time. For example, if today is Wednesday and you give a time of "monday", parsedate() interprets that to mean next Monday. If that's not what you want, you may have to be more explicit or use a relative time format like "-2 days".

We'll add support for other "casual" date formats as we come across them (I've added about 10 more since I started writing this article). If you have suggestions for other common formats, send them to us and we'll see about adding them to the list (barring ambiguity problems).

To wrap things up, here is simple demonstration program that shows you how to use parsedate() and lets you play with inputs to it.

/*
  This is a simple program to demonstrate using parsedate()
  on the BeOS.
  dbg@be.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <parsedate.h>

int
main(int argc, char **argv)
{
  char     str[256];
  time_t   remind_time;
  struct   tm *tm;

  printf("\nI am Chronos, the keeper of time.\n");
  printf("I am feared by ctime() implementors everywhere.\n");
  printf("Enter a time and I will parse it!\n\n");

  while (fgets(str, sizeof(str), stdin) != NULL) {
    if (str[0] == '\n')
      continue;
    if (strcmp(str, "quit\n") == 0 ||
        strcmp(str, "exit\n") == 0)
      break;

    /*
      woo-hoo! here it is...dun dun da nah...
      the call to parsedate()!

      pretty complex eh? The -1 argument means to parse
      relative to now.
    */

    remind_time = parsedate(str, -1);

    if (remind_time == ~0) {
      fprintf(stderr,
        "\nHmmm:\n  %sis not recognized.\n", str);
      fprintf(stderr,
        "You should tell my trusty assistant dbg@be.com\n\n");
      continue;
    }

    printf("\nThe canonical form for the time you entered was:\n");

    /*
      convert to a struct tm which has all the time fields
      broken out
    */

    tm = localtime(&remind_time);

    printf("  Year: %d Month: %d Day: %d Hour: %.2d:%.2d\n",
      tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
      tm->tm_min);

    /*
      also print the result of ctime()
    */

    printf("  %s\n", ctime(&remind_time));
  }
}

Have fun and maybe next time we'll poke around in that mysterious looking header file called malloc_internal.h...


A Remembrance of Things Past: Writing Mass Storage Device Drivers

By Dmitriy Budko

When was the last time you used a RAM drive? Do you still remember what a RAM drive is?

On my first PC (an AT 286, 12 MHz, 2 MB RAM) a RAM disk was the only workable way to use more than 1 MB of memory while running under MS-DOS. Unlike creaky MS-DOS, a current OS like the BeOS usually uses free memory in the file system cache; in general, caching algorithms provide much better performance than a simple RAM disk. Recently though, I was overcome by inexplicable nostalgia for the good old days and I decided to write a driver for that "disk" of yesteryear.

So if you have memory to burn, you may want to try using some of your surplus as a RAM "disk." If nothing else you'll find out that a RAM disk driver is an excellent example of using a device driver not only as a hardware interface but also to emulate it! And if you develop a file system driver, a big RAM disk can speed up the testing.

I assume that you know the basics of BeOS device drivers, so I'll focus on features specific to mass storage devices (floppy drive, IDE disk, IEEE 1394 [FireWire] disk) and on some tricks related to the volatile nature of RAM.

The driver has to support all standard entry points. In addition it has to handle some mass storage device I/O control codes.

I tried to make the driver as simple as possible, so it supports only one RAM disk. Its dynamic configuration is almost nonexistent, and its emulation of the hard drive seek process is very primitive. This simplification let me forget about synchronization problems.

Here's the driver source code. You can find the original source, unaltered for or by e-mail transmission, at:

ftp://ftp.be.com/pub/samples/preview/drivers/ramdrive.zip

/****************** cut here ******************************/

#include <OS.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <stdlib.h>

status_t vd_open(const char *name, uint32 flags,
                 void **cookie);
status_t vd_free (void *cookie);
status_t vd_close(void *cookie);
status_t vd_control(void *cookie, uint32 msg, void *buf,
                    size_t size);
status_t vd_read(void *cookie, off_t pos, void *buf,
                 size_t *count);
status_t vd_write(void *cookie, off_t pos, constvoid *buf,
                  size_t *count);

static void format_ram_drive(void* buf);
static uchar* create_ram_drive_area(size_t drive_size);
static status_t delete_ram_drive_area(void);
static void emulate_seek(off_t pos);

#define RAM_DRIVE_RELEASE_MEMORY  (B_DEVICE_OP_CODES_END+1)
#define RAM_DRIVE_EMULATE_SEEK    (B_DEVICE_OP_CODES_END+2)

#define RAM_DRIVE_SIZE        (8*1024*1024)
#define RAM_BLOCK_SIZE        512
#define MAX_SEEK_TIME         1000.0      /* microseconds */
#define PREFETCH_BUFFER_SIZE  (32*1024)

static const char* const
    ram_drive_area_name = "RAM drive area";
uchar icon_disk[B_LARGE_ICON * B_LARGE_ICON];
uchar icon_disk_mini[B_MINI_ICON * B_MINI_ICON];
int   emulate_seek_flag = FALSE;
uchar * ram = NULL;

static const char *vd_name[] = {
  "disk/virtual/ram_drive",
  NULL
};

device_hooks vd_devices = {
  vd_open,
  vd_close,
  vd_free,
  vd_control,
  vd_read,
  vd_write
};

status_t
init_driver(void)
{
  dprintf("vd driver: %s %s, init_driver()\n",
    __DATE__, __TIME__);
  ram = create_ram_drive_area(RAM_DRIVE_SIZE);
  if(ram == NULL)
    return B_ERROR;
  return B_NO_ERROR;
}

void
uninit_driver(void)
{
  dprintf("vd driver: uninit_driver()\n");
}

const char**
publish_devices()
{
  dprintf("vd driver: publish_devices()\n");
  return vd_name;
}

device_hooks*
find_device(const char* name)
{
  dprintf("vd driver: find_device()\n");
  return &vd_devices;
}

status_t
vd_open(const char *dname, uint32 flags, void **cookie)
{
  dprintf("vd driver: open(%s)\n", dname);
  return B_NO_ERROR;
}

status_t
vd_free(void *cookie)
{
  dprintf("vd driver: free()\n");
  return B_NO_ERROR;
}

status_t
vd_close(void *cookie)
{
  dprintf("vd driver: close()\n");
  return B_NO_ERROR;
}

status_t
vd_read(void *cookie, off_t pos, void *buf, size_t *count)
{
  size_t len;
  status_t ret = B_NO_ERROR;

  if(pos >= RAM_DRIVE_SIZE)
  {
    len = 0;
  }
  else
  {
    len = (pos + (*count) > RAM_DRIVE_SIZE) ?
            (RAM_DRIVE_SIZE - pos) : (*count);
    emulate_seek(pos);
    memcpy(buf, ram+pos, len);
  }
  *count = len;
  return ret;
}

status_t
vd_write(void *cookie, off_t pos, constvoid *buf,
         size_t *count)
{
  size_t len;
  status_t ret = B_NO_ERROR;

  if(pos >= RAM_DRIVE_SIZE)
  {
    len = 0;
  }
  else
  {
    len = (pos + (*count) > RAM_DRIVE_SIZE) ?
            (RAM_DRIVE_SIZE - pos) : (*count);
    emulate_seek(pos);
    memcpy(ram+pos, buf, len);
  }
  *count = len;
  return ret;
}

status_t
vd_control(void *cookie, uint32 ioctl, void *arg1,
           size_t len)
{
  device_geometry  *dinfo;

  dprintf("vd driver: control(%d)\n", ioctl);
  switch (ioctl)
  {

  /* generic mass storage device IO control codes */

  case B_GET_GEOMETRY:
    dinfo = (device_geometry *) arg1;

    dinfo->sectors_per_track =
      RAM_DRIVE_SIZE/RAM_BLOCK_SIZE;
    dinfo->cylinder_count  = 1;
    dinfo->head_count      = 1;

    dinfo->bytes_per_sector = RAM_BLOCK_SIZE;
    dinfo->removable = FALSE ;
    dinfo->read_only = FALSE;
    dinfo->device_type = B_DISK;
    dinfo->write_once = FALSE;

    return B_NO_ERROR;

  case B_FORMAT_DEVICE:
    format_ram_drive(ram);
    return B_NO_ERROR;

  case B_GET_DEVICE_SIZE:
    *(size_t*)arg1 = RAM_DRIVE_SIZE;
    return B_NO_ERROR;

  case B_GET_ICON:
    switch (((device_icon *)arg1)->icon_size)
    {
    case B_LARGE_ICON:
      memcpy(((device_icon *)arg1)->icon_data, icon_disk,
        B_LARGE_ICON * B_LARGE_ICON);
      break;

    case B_MINI_ICON:
      memcpy(((device_icon *)arg1)->icon_data,
        icon_disk_mini, B_MINI_ICON * B_MINI_ICON);
      break;

    default:
      return B_BAD_TYPE;
    }
    return B_NO_ERROR;

  /* device specific IO control codes */

  case RAM_DRIVE_RELEASE_MEMORY:
    return delete_ram_drive_area();

  case RAM_DRIVE_EMULATE_SEEK:
    emulate_seek_flag = *(int*)arg1;
    return B_NO_ERROR;

  default:
    return B_ERROR;
  }
}

static void format_ram_drive(void* buf)
{
  static const char format_str[16] = "RAM drive   ";
  uchar* ptr = (uchar*)buf;
  off_t i;

  dprintf("vd driver: format_ram_drive(%08x)\n", buf);
  for(i=0; i<RAM_DRIVE_SIZE/16; i++)
  {
    memcpy(ptr, format_str, 16);
    ptr += 16;
  }

}

static uchar*
create_ram_drive_area(size_t drive_size)
{
  void* addr;
  area_id area = find_area(ram_drive_area_name);

  if(area == B_NAME_NOT_FOUND)
  {
    area = create_area(ram_drive_area_name,
      &addr,
      B_ANY_KERNEL_ADDRESS,/*kernel team will own this area*/
      drive_size,
      B_LAZY_LOCK,
      B_READ_AREA | B_WRITE_AREA);

    if((area==B_ERROR) ||
       (area==B_NO_MEMORY) ||
       (area==B_BAD_VALUE))
      addr = NULL;
  }
  else
  {
    area_info info;

    get_area_info(area, &info);
    addr = info.address;
  }
  return (uchar*)addr;
}

static status_t
delete_ram_drive_area(void)
{
  area_id  area = find_area(ram_drive_area_name);

  if(area == B_NAME_NOT_FOUND)
    return B_ERROR;
  else
    return delete_area(area);
}

static void
emulate_seek(off_t pos)
{
  static off_t old_pos = 0;

  if(!emulate_seek_flag)
    return;

  if(abs(pos-old_pos)>PREFETCH_BUFFER_SIZE)
  {
    old_pos = pos;
    snooze((int)(rand()* MAX_SEEK_TIME)/RAND_MAX);
  }
}

/****************** cut here ******************************/

What happens is that init_driver() creates the kernel memory area that's used to store the data. The driver can't use malloc()/free() because when the driver is unloaded the memory (and all data) is lost. It's actually present somewhere, but there's no easy way to find it when the driver is loaded again.

So init_driver() calls create_ram_drive_area(), which tries to find the previously created memory area with the name "RAM drive area." If the search is unsuccessful the driver is loaded the first time. In that case create_ram_drive_area() creates the memory area. It uses a currently undocumented flag, B_ANY_KERNEL_ADDRESS. This flag gives ownership of this memory area to the kernel, so the area will not be deleted when the application that opened the driver quits.

It also uses the B_LAZY_LOCK flag so the driver doesn't consume RAM until the first time you use it. I could have used B_FULL_LOCK and put the memory allocation in vd_open(), but being lazy I didn't want to provide a critical section synchronization for it.

vd_open() can be called a few times simultaneously; init_driver() cannot. publish_devices() creates the device file in /dev/disk/virtual/ram_drive. The /virtual/ram_drive part of the name is arbitrary, but /dev/disk is mandatory if you want to use a standard disk setup program—like DriveSetup—to configure the RAM disk.

vd_open(), vd_free(), and vd_close() do nothing and return success.

vd_read() and vd_write() check to see if the caller tries to read/write over the end of the disk. They adjust the requested length accordingly, then simply copy the data from/to the requested offset in the RAM area to/from the caller's buffer.

If emulate_seek_flag is set the driver calls emulate_seek(). emulate_seek() is a crude imitation of real hard drive mechanics and caching. The current implementation is not in a critical section so it is capable of multiple concurrent seeks. It would be great to have a real drive with an infinite number of head arms! Readers are welcome to create a more realistic model.

Still, with this function implemented, the RAM drive is better suited for testing a file system driver. It can block the caller's thread as a driver for a real drive would do.

vd_control() handles four generic I/O control codes for a mass storage device: B_GET_GEOMETRY, B_FORMAT_DEVICE, B_GET_DEVICE_SIZE, B_GET_ICON. The first three are mandatory; the last one is optional.

This driver has zero-initialized arrays for large and small icons. The real driver should provide more pleasing icons than black rectangles.

RAM_DRIVE_RELEASE_MEMORY deletes the allocated memory area and thus destroys all information on the RAM drive. RAM_DRIVE_EMULATE_SEEK sets or clears the emulate_seek_flag. A control panel application for the RAM drive could send such commands.

Now here's how to build and install the driver:

  1. Instruct the linker to produce an add-on image.

  2. Disable linking to the default shared system libraries.

  3. Export the driver's entry points—for example, by exporting everything.

  4. Place a copy of the appropriate kernel file (kernel_joe, kernel_mac, or kernel_intel) in your project directory. Link against this file.

  5. Copy the compiled driver in the /boot/home/config/add-ons/kernel/drivers directory.

  6. Use your favorite disk format program or BeOS preferences/DriveSetup to partition and/or install BFS on the /dev/disk/virtual/ram_drive device.

That's it. You're all set!

I hope that this simple device driver may help get somebody started in BeOS driver development. If any seasoned device drivers out there have feature requests or comments (why does Be have such-and-such stupid restriction or does not have such-and-such device driver API ...), let me know and I'll try to implement them. The Intel port in particular may require same changes: @#$% ISA 16 MB limit on DMA, PCI interrupt sharing, ISA PnP, etc.

Speak now and you can make your life running the BeOS on Intel that much easier!


Developers' Workshop: BYOB!

By Doug Wright

"Developers' Workshop" is a new weekly feature that provides answers to our developers' questions. Each week, a Be technical support or documentation professional will choose a question (or two) sent in by an actual developer and provide an answer.

We've created a new section on our website. Please send us your Newsletter topic suggestions by visiting the website at: http://www.be.com/developers/suggestion_box.html.

Hello Be people. As the newest Developer Technical Support Engineer, I have spoken with some of you recently regarding specific problems but this is my first taste of the Be Newsletter audience. Playing live shows in a band once or twice a month has tamed my wild urge to run and hide in front of a crowd, but writing an article that persists over time definitely feels different then being live on stage.

So with a little nervousness, I present you with my first (not last!) bit of Be insight, in two parts. The subjects were inspired by many things but the titles were inspired by my favorite thing—BYOB!

Part I: Bring Your Own Bugs!

Melissa has graciously given me the task of evaluating bugs submitted by developers. In attempting to climb this ant hill I have found that some people are natural bug writers and others, well, are more like Hostess Twinkies. To help out all those American snack cakes out there, I will offer a few guidelines to writing a good bug (and the only *good* bugs are the ones you can hunt down and kill!).

Step 1: A reproducible case is needed. If you can't reproduce it, neither can we.

Step 2: Document each step necessary to reproduce the bug. For example:

  1. Open a Terminal

  2. Type twinky -old

  3. Open a Tracker window

  4. Create a folder called Hostess

  5. Right click the folder

  6. Crash!

Step 3: Document your machine configuration. Include all the hardware AND software your are running. For example, a developer recently forgot to mention that MALLOC_DEBUG was turned on.

Step 4: If the app that crashes is yours, include the code. Don't worry, that little window on the bug report form scrolls for a long time! Just paste it right in there.

Now I know you're thinking "Hey, I've got 50 source files, they won't all fit." You need to find the piece of code that is causing the crash and put it in a little test app.

HelloWorld is great for this. It has an app and a window and a view. Most bugs don't need more than that to cause trouble. HINT: This can also help you find bugs in your own code!

If the only way to cause the crash is to include your entire source, then zip it up and send it in to devservices@be.com with a reference to the bug number that you get when you submit the description using the web form.

Step 5: The last part of Step 4 is really Step 5. Write down your bug number so that you can contact us about it in the future.

If you skip any one of these steps, your bug will more than likely get classified as "Unreproducible." This is bad for you and for Be, because your bug will still be running around in the next release causing you and everyone else problems! If you have any trouble with the bug reporting process or you feel that we've made a grave error and classified your bug as a feature, please don't hesitate to fill out a Developer tech Support form in the Registered Developer Area and we will look into your problem.

Part II: Build Your Own Buttons!

Are you building that killer tractor app, and you want the interface to be oh-so-cool? It's not going to knock their socks of with a bunch of plain-jane buttons. You need to customize! So I've prepared some sample code to get you started making your very own buttons. Find the complete archive on the Be website at:

ftp://ftp.be.com/pub/samples/preview/intro/ButtonWorld.zip

The BPictureButton class is a button that takes two BPictures as arguments. You can build these pictures out of BBitmaps, a combination of BBitmaps and BButtons (for that grey look) or any other drawing routine. The two pictures each represent one of two states, on and off.

I started by scanning a familiar form. I used Photoshop on the Mac ;-( to save my scanned image in raw format. You need to remember the dimensions of the image and the bit depth. The width needs to be a multiple of four in order to use the command line tool craw to convert them into code. My images were 48 x 48 so I just typed "$craw 48 48 myrawimage" in a terminal.

(For more information on how to use craw check out...

Be Engineering Insights: craw, shex, and script: Excuse Me?

This is an oldie but a goodie! William Adams also addressed creating custom graphics from images in a News From The Front article...

Some of the samples he referred to have been moved, including mkimghdr, which is now at...

ftp://ftp.be.com/pub/samples/preview/interface_kit/mkimghdr.tgz.)

Craw generates an unsigned char array. blue4x4 and blue4x4on are the names of my two arrays. Now I'm ready to fill some bitmaps and create a button...

/* NOTE: This is all happening during the construction of a
         Window inheriting from a BWindow. */

BRect rect;
rect.Set(0,0,47,47);

//bitmaps for the pictures

BBitmap onBitmap(rect, B_COLOR_8_BIT );
BBitmap offBitmap(rect, B_COLOR_8_BIT );

//fill bitmap

onBitmap.SetBits(blue4x4on, 18432, 0, B_COLOR_8_BIT);
offBitmap.SetBits(blue4x4, 18432, 0, B_COLOR_8_BIT);

/* Next, I create two BPictures and draw my bitmaps
   in them. */

//tempview for creating the picture

BView *tempView =
  new BView(rect, "temp", B_FOLLOW_NONE, B_WILL_DRAW);
AddChild(tempView);

//create on picture

BPicture *on;
tempView->BeginPicture(new BPicture);
tempView->DrawBitmap(&onBitmap);
on = tempView->EndPicture();

//create off picture

BPicture *off;
tempView->BeginPicture(new BPicture);
tempView->DrawBitmap(&offBitmap);
off = tempView->EndPicture();

//get rid of tempview

RemoveChild(tempView);
delete tempView;

/* Finally I create my BPicture button and the other things
   that it needs including the message that will be sent to
   its target. */

//create a message for the button

BMessage *pictmsg = new BMessage(BUTTON_MSG);
pictmsg->AddString("text", "Picture Button");

//create a picture button using the two pictures

rect.Set( 120, 45, 167, 92 );
BPictureButton* pictureButton =
  new BPictureButton(rect, "picture", off, on, pictmsg,
  B_TWO_STATE_BUTTON);

/* The last argument for the BPictureButton is a flag for
   the mode. B_TWO_STATE_BUTTON behaves like a toggle
   switch. Turn it on with one click. Turn it off with
   another. A B_ONE_STATE_BUTTON is only on while you hold
   the mouse down. */

/* Once you have created your button you can add it as a
   child to the window. The buttonView is a BTextView to
   which we will send our message. */

// add view and button to window

AddChild(buttonView);
AddChild(pictureButton);

/* Finally in order to direct the message your button sends
   you need to assign it a target. */

// make the view the target of the buttons

pictureButton->SetTarget(buttonView);

Now when you click on the button, a message is sent to the buttonView which displays the string contained by the message. To see how the BStringView handles the message, please check out the complete sample. Have fun and remember, we're happy to have you BYOB at Be! If you need more information, don't hesitate to ask!


A Nice Comdex

By Jean-Louis Gassée

It is fashionable to complain about Comdex, from the bad food, the taxi lines, expensive hotel rooms, sore feet at the end of the day and silly carnival acts in the booths of companies who should know better. Perhaps, but I still like Comdex, and I liked this one even better, for two reasons.

The first is Umax. Our partner graciously hosted us on their booth in the main hall, providing us with the opportunity to show both the PowerPC and the Intel versions of the BeOS as befits their own business addressing both standards.

As expected we got both good reactions and blank stares, when not eyes rolling. Some visitors knew us, or had heard about us and were happy to get a progress report. Others had no idea we existed and a few questioned our sanity. When we got the opportunity, we disposed easily of the mental health question by pointing out the difference between OS/2 trying to dislodge Windows and the BeOS happily coexisting with the general-purpose OS.

This experience is a useful reminder of what awaits us in the Intel-based market. It's not just larger than the PowerPC market, it's much different and our reputation, the exposure we enjoy in the PowerPC segment, aren't worth much in the new space. The newer Intel version performed well during the week, much better than we had anticipated and, towards the end, we sneaked in a "just baked" port on an Intel-powered laptop.

The second reason to like Comdex this year is the abundance of technology coming out of gestation, ready to become a real product at Fry's some time in the next twelve months. Flat panels were big, literally and in their ubiquity. A forty-six-inch panel is still horribly expensive, but the smaller models are soon to grace the desktops of Corporate America.

Closer to our business, video cameras, ever higher-speed graphic cards, still cameras, IEEE 1394 connections, high-bandwidth disk adapters...all sing the song of better, faster, more affordable digital media.

This year we didn't hear the old saw: "The industry is becoming commoditized, boring, less innovative." Confusing, a little disorganized perhaps, but we like that, there is little room for a start-up such as ours in a perfectly stable and organized world.

As for the expensive rooms and the bad food, a minivan gets you a little out of the way, the hotel prices plummet and you even find restaurants without slot machines.


BeDevTalk Summary

BeDevTalk is an unmonitored discussion group in which technical information is shared by Be developers and interested parties. In this column, we summarize some of the active threads, listed by their subject lines as they appear, verbatim, in the mail.

To subscribe to BeDevTalk, visit the mailing list page on our web site: http://www.be.com/aboutbe/mailinglists.html.

NEW

Subject: Help with error...

We've all seen it...the "Data > 32K" compiler error message. What does it mean? It means you've asked for too much local data. The 32k limit is enforced by the compiler in order to be ANSI compliant, but beyond that, is biting off huge chunks of local data bad programming? Earl Malmrose saw some advantages in the practice:

Being local, you have automatic garbage collection of a sort - [the data] will never be leaked. You also have to write less code.

Jon Watte listed some reasons behind the limitation, among which:

In a multi-threaded environment, each thread has to be given its own stack...there is a very real trade-off between how many threads you can create, and how much stack space they get.

Nonetheless, there were objections to the "bad programming practice" characterization. It was contended that the negative citations were architecture specific (hardware or software)—that there were no intrinsic reasons why huge local data is bad. Then Osma Ahvenlampi added this:

In addition to the problems pointed out by others, note that it [relying on the stack for memory allocation] will not give you any kind of error recovery. What happens when there isn't enough memory to give you that big a stack frame? Crash, most likely.

Subject: Digital Signatures on the BeOS

Can a PGP-style signature be stored as an attribute of a file? (Sure.) But would the attribute itself affect the signature for the file? (Not if you didn't want it to—attributes can be selectively ignored.)

Expanding the equation, what if you wanted to verify attributes as well as data? Peter Folk suggested a 3-tier scheme:

  • PGP__SIG is a signature for the main data stream.

  • PGP_<attributename>_SIG is a signature for whichever attributes you care to sign (excluding PGP__SIG).

  • PGPSIG_SIG is a signature of all the PGP_*_SIG attributes.

This may be overkill, suggests Jon Watte:

Having one signature for each attribute is rather wasteful... Instead, you can sign the data with one signature, and the union of all non-changing [attributes] with another; that should be enough even for the most paranoid among us.

Speaking of paranoia, the thread veered into a discussion of reliable key retrieval and verification. Anthony Towns provided a tidy wish list that summarized the elements of the problem (paraphrased here):

  • Some way of storing public keys. This would naturally expand to include encryption as well as verification keys...

  • Some way of easily requesting keys. This is strongly related to the public key database: first you check it, then you check a public key server somewhere.

  • Library support for verifying signatures.

  • Convenient methods for signing files (including support for various signature algorithms).

  • An integrated encryption API.

But should Be be in the encryption business? Some folks think not.

Also, it was contended that the entire signature-in-an-attribute approach is flawed: Attributes can get lost in an ftp transfer (for example). Important signature information should be encoded in the data portion of a file.

Subject: BRect confusion

Should the Width() of a BRect return the "virtual" width of the rectangle, or the number of pixels the stroked and filled rect touches? The function returns the former, a practice that many developers are confused by; when you ask for the width of a rectangle, you should get a count of the number of (horizontal) pixels it encloses. But, goes the counter-argument, such a measurement would need to consider the pen size, so the status quo is proper. If you want the pixel-touched measure, you have to add the pen size to the rectangle width.

Eric Berdahl contends that the width+pen_size business is a product of Be's "center pen" approach. He would like to see Be adopt a more flexible pen model (i.e. "inset" and "outset" pens).

Subject: Be supporting developers

Devtalkers take a step back (or across, or something) and discuss Be's marketing approach, its seemingly unshakable association with Apple, whether a brainwash-the-CS- department approach can work, the role of free and eminently portable software in an OS company's success, and other non-technical matters that all broached the question: How shall Be thrive? Lots of opinions, or, at least, a lot of attitudes.

Side-stepping back into the tech stream, another question was raised: What's a Be app? Does a port count, or is there some other defining element. Some proposed litmus strips:

  • BWindow. If you don't have a BWindow, you're not a Be app.

  • BMessages. Non-UI apps (servers) can still qualify if they respond to BMessages.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.