/* dlp.c:  Pilot DLP protocol
 *
 * Copyright (c) 1996, 1997, Kenneth Albanowski
 *
 * This is free software, licensed under the GNU Public License V2.
 * See the file COPYING for details.
 */

/*@+matchanyintegral@*/
/*@-predboolint@*/
/*@-boolops@*/ 
 
#include <stdio.h>
#include "pi-source.h"
#include "pi-socket.h"
#include "pi-dlp.h"
#include "pi-syspkt.h"

#define get_date(ptr) (dlp_ptohdate((ptr)))                        

#define set_date(ptr,val) (dlp_htopdate((val),(ptr)))

#define DLP_BUF_SIZE 0xffff
static /*@checked@*/ unsigned char dlp_buf[DLP_BUF_SIZE];
static /*@checked@*/ unsigned char exec_buf[DLP_BUF_SIZE];

char * dlp_errorlist[] = { 
  "No error",
  "General System error",
  "Illegal Function",
  "Out of memory",
  "Invalid parameter",
  "Not found",
  "None Open",
  "Already Open",
  "Too many Open",
  "Already Exists",
  "Cannot Open",
  "Record deleted",
  "Record busy",
  "Operation not supported",
  "-Unused-",
  "Read only",
  "Not enough space",
  "Limit exceeded",
  "Sync cancelled",
  "Bad arg wrapper",
  "Argument missing",
  "Bad argument size"
};

char * dlp_strerror(int error) {
  if (error<0)
    error = -error;
  if ((unsigned int)error>sizeof(dlp_errorlist))
    return "Unknown error";
  else
    return dlp_errorlist[error];
}

int dlp_trace = 0;

#ifndef NO_DLP_TRACE
#define DLP_TRACE

#define Trace(name) \
  if (dlp_trace)    \
    fprintf(stderr, "DLP %d: %s\n", sd, #name);
#define Expect(count)    \
  if (result < count) {  \
    if (result < 0) {    \
      if (dlp_trace)     \
        fprintf(stderr, "Result: Error: %s (%d)\n", dlp_errorlist[-result], result); \
    } else {             \
      if (dlp_trace)     \
        fprintf(stderr, "Result: Read %d bytes, expected at least %d\n", result, count); \
      result = -128;     \
    }                    \
    return result;       \
  } else                 \
    if (dlp_trace)       \
      fprintf(stderr, "Result: No error, %d bytes\n", result);
  
#else

#define Trace(name)
#define Expect(count)   \
  if (result < count) { \
    if (result >= 0)    \
      result = -128;    \
    return result;      \
  }
  
#endif


#if 0
/* Eventual code to dynamically allocate buffer */
void * dlp_buffer(int sd, int cmd, int arg, int arglen, struct pi_socket * ps)
{
  unsigned char * buf;
  *ps = pi_find_socket(sd);
  if (*ps)
    return 0;
  
  len = 50 + msglen;
  if (ps->dlpbuflen < len) {
    ps->dlpbuflen = len+1024;
    if (ps->dlpbuf) 
      ps->dlpbuf = realloc(ps->dlpbuf, ps->dlpbuflen);
    else
      ps->dlpbuf = malloc(ps->dlpbuflen);
  }
  
  buf = ps->dlpbuf;
  
  buf[0] = (unsigned char)cmd;
  buf[1] = etc.
  
}
#endif

int dlp_exec(int sd, int cmd, int arg, 
             const unsigned char /*@null@*/ *msg, int msglen, 
             unsigned char /*@out@*/ /*@null@*/ *result, int maxlen)
 /*@modifies *result, exec_buf;@*/
 /*@-predboolint -boolops@*/
{
  int i;
  int err;
  
  exec_buf[0] = (unsigned char)cmd;
  if (msg && arg && msglen) {
    memcpy(&exec_buf[6], msg, msglen);
    exec_buf[1] = (unsigned char)1;
    exec_buf[2] = (unsigned char)(arg | 0x80);
    exec_buf[3] = (unsigned char)0;
    set_short(exec_buf+4, msglen);
    i = msglen+6;
  } else {
    exec_buf[1] = (unsigned char)0;
    i = 2;
  }

  if (pi_write(sd, &exec_buf[0], i)<i) {
    errno = -EIO;
    return -1;
  }

  i = pi_read(sd, &exec_buf[0], DLP_BUF_SIZE);

  err = get_short(exec_buf+2);
  
  if (err != 0) {
    errno = -EIO;
    return -err;
  }

  if (exec_buf[0] != (unsigned char)(cmd | 0x80)) { /* received wrong response */
    errno = -ENOMSG;
    return -1;
  }
  
  if ((exec_buf[1] == (unsigned char)0) || (result==0)) /* no return blocks or buffers */
    return 0; 
    
  /* assume only one return block */
  if ((exec_buf[4] & 0xC0) == 0xC0) { /* Long arg */
  	i = get_long(exec_buf+6);
  	
  	if (i>maxlen)
  	  i = maxlen;
  	  
  	memcpy(result, &exec_buf[10], i);
  } else if (exec_buf[4] & 0x80) { /* Short arg */
  	i = get_short(exec_buf+6);
  	
  	if (i>maxlen)
  	  i = maxlen;
  	  
  	memcpy(result, &exec_buf[8], i);
  } else { /* Tiny arg */
  	i = (int)exec_buf[5];

  	if (i>maxlen)
  	  i = maxlen;
  	  
  	memcpy(result, &exec_buf[6], i);
  }

  return i;
  
}

/* These conversion functions are strictly for use within the DLP layer.
   This particular date/time format does not occur anywhere else within the
   Pilot or its communications. */
   
/* Notice: These conversion functions apply a possibly incorrect timezone
   correction. They use the local time on the UNIX box, and transmit this
   directly to the Pilot. This assumes that the Pilot has the same local
   time. If the Pilot is communicating from a different timezone, this is
   not necessarily correct.
   
   It would be possible to compare the current time on the Pilot with the
   current time on the UNIX box, and use that as the timezone offset, but
   this would break if the Pilot had the wrong time, or one or the either
   didn't have the proper local (wall) time.
   
   In any case, since the (possibly incorrect) timezone correction is
   applied both way, there is no immediate problem.
                                                                   -- KJA
   */
   
static time_t dlp_ptohdate(unsigned const char * data)
{
        struct tm t;
        
        t.tm_sec   = (int)data[6];
        t.tm_min   = (int)data[5];
        t.tm_hour  = (int)data[4];
        t.tm_mday  = (int)data[3];
        t.tm_mon   = (int)data[2]-1;
        t.tm_year  = ((data[0]<<8) | data[1])-1900;
        t.tm_wday  = 0;
        t.tm_yday  = 0;
        t.tm_isdst = -1;
        
        return mktime(&t);
}

static void dlp_htopdate(time_t time, unsigned char * data) /*@+ptrnegate@*/
{
        struct tm * t = localtime(&time);
        int y;

        if (!t)
          abort();
        
        y = t->tm_year+1900;
        
        data[7] = (unsigned char)0; /* packing spacer */
        data[6] = (unsigned char)t->tm_sec;
        data[5] = (unsigned char)t->tm_min;
        data[4] = (unsigned char)t->tm_hour;
        data[3] = (unsigned char)t->tm_mday;
        data[2] = (unsigned char)(t->tm_mon+1);
        data[0] = (unsigned char)((y >> 8) & 0xff);
        data[1] = (unsigned char)((y >> 0) & 0xff);
        
        return;
}

int dlp_GetSysDateTime(int sd, time_t * t)
{
  unsigned char buf[8];
  int result;
  
  Trace(GetSysDateTime);
  
  result = dlp_exec(sd, 0x13, 0x20, 0, 0, buf, 8);
  
  Expect(8);
  
  *t = dlp_ptohdate(buf);

#ifdef DLP_TRACE
  if (dlp_trace) {
    fprintf(stderr, "   Read: Time: %s", ctime(t));
  }
#endif
  
  return result;
}

int dlp_SetSysDateTime(int sd, time_t time)
{
  unsigned char buf[8];
  int result;
  dlp_htopdate(time, buf);

  Trace(ReadSysInfo);

#ifdef DLP_TRACE
  if (dlp_trace) {
    fprintf(stderr, "  Wrote: Time: %s", ctime(&time));
  }
#endif
  
  result = dlp_exec(sd, 0x14, 0x20, buf, 8, 0, 0);
  
  Expect(0);
  
  return result;
}

/* begin struct SI
 *  Byte    number		byte(0)
 *  Byte    more		byte(1)
 *  Byte    unused		byte(2)
 *  Byte    count		byte(3)
 *  Byte    totalSize		byte(4)
 *  Byte    cardNo		byte(5)
 *  Word    cardVersion		short(6)
 *  DlpDateTimeType crDate	date(8)
 *  DWord   romSize		long(16)
 *  DWord   ramSize		long(20)
 *  DWord   freeRam		long(24)
 *  Byte    cardNameSize	byte(28)
 *  Byte    cardManufSize	byte(29)
 *  Char[0] cardNameAndManuf	byte(30)
 * struct read access */
#define SI_number(ptr)		get_byte((ptr)+0)
#define SI_more(ptr)		get_byte((ptr)+1)
#define SI_unused(ptr)		get_byte((ptr)+2)
#define SI_count(ptr)		get_byte((ptr)+3)
#define SI_totalSize(ptr)		get_byte((ptr)+4)
#define SI_cardNo(ptr)		get_byte((ptr)+5)
#define SI_cardVersion(ptr)		get_short((ptr)+6)
#define SI_crDate(ptr)		get_date((ptr)+8)
#define SI_romSize(ptr)		get_long((ptr)+16)
#define SI_ramSize(ptr)		get_long((ptr)+20)
#define SI_freeRam(ptr)		get_long((ptr)+24)
#define SI_cardNameSize(ptr)		get_byte((ptr)+28)
#define SI_cardManufSize(ptr)		get_byte((ptr)+29)
#define SI_cardNameAndManuf(ptr,idx)	get_byte((ptr)+30+1*(idx))
#define ptr_SI_cardNameAndManuf(ptr,idx)	((ptr)+30+1*(idx))
#define sizeof_SI		(30)
 /* end struct SI */

/* begin struct SIRequest
 *  Byte    cardNo		byte(0)
 *  Byte    unused		byte(1)
 * struct write access */
#define SIRequest_cardNo(ptr,val)		set_byte((ptr)+0,(val))
#define SIRequest_unused(ptr,val)		set_byte((ptr)+1,(val))
#define sizeof_SIRequest		(2)
 /* end struct SIRequest */

int dlp_ReadStorageInfo(int sd, int cardno, struct CardInfo * c)
{
  int result;
  int len1,len2;
  
  SIRequest_cardNo(dlp_buf, cardno);
  SIRequest_unused(dlp_buf, 0);
  
  Trace(ReadStorageInfo);
  
#ifdef DLP_TRACE
  if (dlp_trace) {
    fprintf(stderr, " Wrote: Cardno: %d\n", cardno);
  }
#endif  
  
  result = dlp_exec(sd, 0x15, 0x20, dlp_buf, 2, dlp_buf, 256+26);
  
  c->more = 0;
  
  Expect(sizeof_SI);
  
  c->more = SI_more(dlp_buf) || (SI_count(dlp_buf) > 1);
  
  c->card =	SI_cardNo(dlp_buf);
  c->version =	SI_cardVersion(dlp_buf);
  c->creation = SI_crDate(dlp_buf);
  c->romSize =	SI_romSize(dlp_buf);
  c->ramSize =	SI_ramSize(dlp_buf);
  c->ramFree =	SI_freeRam(dlp_buf);
  
  len1 = SI_cardNameSize(dlp_buf);
  memcpy(c->name, ptr_SI_cardNameAndManuf(dlp_buf,0), len1);
  c->name[len1] = '\0';
  
  len2 = SI_cardManufSize(dlp_buf);
  memcpy(c->manufacturer, ptr_SI_cardNameAndManuf(dlp_buf, len1), len2);
  c->manufacturer[len2] = '\0';
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Cardno: %d, Card Version: %d, Creation time: %s",
      c->card, c->version, ctime(&c->creation));
    fprintf(stderr, "        Total ROM: %lu, Total RAM: %lu, Free RAM: %lu\n",
      c->romSize, c->ramSize, c->ramFree);
    fprintf(stderr, "        Card name: '%s'\n", c->name);
    fprintf(stderr, "        Manufacturer name: '%s'\n", c->manufacturer);
    fprintf(stderr, "        More: %s\n", c->more ? "Yes" : "No");
  }
#endif
  
  return result;
}


int dlp_ReadSysInfo(int sd, struct SysInfo * s)
{
  int result;
  
  Trace(ReadSysInfo);
  
  result = dlp_exec(sd, 0x12, 0x20, NULL, 0, dlp_buf, 256);
  
  Expect(10);
  
  s->romVersion = get_long(dlp_buf);
  s->locale = get_long(dlp_buf+4);
  /* dlp_buf+8 is a filler byte */
  s->nameLength = get_byte(dlp_buf+9);
  memcpy(s->name, dlp_buf+10, s->nameLength);
  s->name[s->nameLength] = '\0';

#ifdef DLP_TRACE
  if (dlp_trace) {    
    fprintf(stderr, "  Read: ROM Version: 0x%8.8lX, Localization ID: 0x%8.8lX\n",
      (unsigned long)s->romVersion, (unsigned long)s->locale);
    fprintf(stderr, "        Name '%s'\n", s->name);
  }
#endif
  
  return result;
}

int dlp_ReadDBList(int sd, int cardno, int flags, int start, struct DBInfo * info)
{
  int result;

  dlp_buf[0] = (unsigned char)flags;
  dlp_buf[1] = (unsigned char)cardno;
  set_short(dlp_buf+2, start);

  Trace(ReadDBList);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Cardno: %d, Start: %d, Flags:", cardno, start);
    if (flags & dlpDBListROM)
      fprintf(stderr, " ROM");
    if (flags & dlpDBListRAM)
      fprintf(stderr, " RAM");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X)\n", flags);
  }
#endif
  
  result = dlp_exec(sd, 0x16, 0x20, dlp_buf, 4, dlp_buf, 48+32);
  
  info->more = 0;
  
  Expect(48);
  
  info->more = get_byte(dlp_buf+2);
  if (pi_version(sd) > 0x0100) /* PalmOS 2.0 has additional flag */
    info->miscFlags = get_byte(dlp_buf+5);
  else
    info->miscFlags = 0;
  info->flags = get_short(dlp_buf+6);
  info->type = get_long(dlp_buf+8);
  info->creator = get_long(dlp_buf+12);
  info->version = get_short(dlp_buf+16);
  info->modnum = get_long(dlp_buf+18);
  info->createDate = get_date(dlp_buf+22);
  info->modifyDate = get_date(dlp_buf+30);
  info->backupDate = get_date(dlp_buf+38);
  info->index = get_short(dlp_buf+46);
  strncpy(info->name, (char*)dlp_buf+48, 32);
  info->name[32] = '\0';

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Name: '%s', Version: %d, More: %s\n",
      info->name, info->version, info->more ? "Yes" : "No");
    fprintf(stderr, "        Creator: '%s'", printlong(info->creator));
    fprintf(stderr, ", Type: '%s', Flags:", printlong(info->type));
    if (info->flags & dlpDBFlagResource)
      fprintf(stderr, " Resource");
    if (info->flags & dlpDBFlagReadOnly)
      fprintf(stderr, " ReadOnly");
    if (info->flags & dlpDBFlagAppInfoDirty)
      fprintf(stderr, " AppInfoDirty");
    if (info->flags & dlpDBFlagBackup)
      fprintf(stderr, " Backup");
    if (info->flags & dlpDBFlagReset)
      fprintf(stderr, " Reset");
    if (info->flags & dlpDBFlagNewer)
      fprintf(stderr, " Newer");
    if (info->flags & dlpDBFlagOpen)
      fprintf(stderr, " Open");
    if (!info->flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X)\n", info->flags);
    fprintf(stderr, "        Modnum: %ld, Index: %d, Creation date: %s",
      info->modnum, info->index, ctime(&info->createDate));
    fprintf(stderr, "        Modification date: %s", ctime(&info->modifyDate));
    fprintf(stderr, "        Backup date: %s", ctime(&info->backupDate));
  }
#endif
    
  return result;
}

int dlp_FindDBInfo(int sd, int cardno, int start, char * dbname, unsigned long type, unsigned long creator, struct DBInfo * info)
{
  int i;
  
  /* This function does not match any DLP layer function, but is intended as
     a shortcut for programs looking for databases. It uses a fairly
     byzantine mechanism for ordering the RAM databases before the ROM ones.
     You must feed the "index" slot from the returned info in as start the
     next time round. */
  
  if (start < 0x1000) {
    i=start;
    while(dlp_ReadDBList(sd, cardno, 0x80, i, info)>0) {
      if( 
         ((!dbname) || (strcmp(info->name,dbname)==0)) &&
         ((!type) || (info->type==type)) &&
         ((!creator) || (info->creator==creator))
        )
        goto found;
      i=info->index+1;
    }
    start = 0x1000;
  }
  
  i=start & 0xFFF;
  while(dlp_ReadDBList(sd, cardno, 0x40, i, info)>0) {
    if( 
       ((!dbname) || (strcmp(info->name,dbname)==0)) &&
       ((!type) || (info->type==type)) &&
       ((!creator) || (info->creator==creator))
      ) {
      info->index |= 0x1000;
      goto found;
    }
    i=info->index+1;
  }
  
  return -1;

found:

  return 0;
}

int dlp_OpenDB(int sd, int cardno, int mode, char * name, int * dbhandle)
{
  unsigned char handle;
  int result;

  dlp_buf[0] = (unsigned char)cardno;
  dlp_buf[1] = (unsigned char)mode;
  strcpy((char*)dlp_buf+2, name);

  Trace(OpenDB);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Cardno: %d, Name: '%s', Mode:", cardno, name);
    if (mode & dlpOpenRead)
      fprintf(stderr, " Read");
    if (mode & dlpOpenWrite)
      fprintf(stderr, " Write");
    if (mode & dlpOpenExclusive)
      fprintf(stderr, " Exclusive");
    if (mode & dlpOpenSecret)
      fprintf(stderr, " Secret");
    if (!mode)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X)\n", mode);
  }
#endif
  
  result = dlp_exec(sd, 0x17, 0x20, &dlp_buf[0], strlen(name)+3, &handle, 1);
  
  Expect(1);
  
  *dbhandle = (int)handle;

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Handle: %d\n", (int)handle);
  }
#endif
  
  return result;
}



int dlp_DeleteDB(int sd, int card, const char * name)
{
  int result;
  
  dlp_buf[0] = (unsigned char)card;
  dlp_buf[1] = (unsigned char)0;
  strcpy((char*)dlp_buf+2, name);

  Trace(DeleteDB);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Cardno: %d, Name: '%s'\n", card, name);
  }
#endif
  
  result = dlp_exec(sd, 0x1A, 0x20, dlp_buf, 2+strlen(name)+1, 0, 0);
  
  Expect(0);
  
  return result;
}

int dlp_CreateDB(int sd, long creator, long type, int cardno, 
                 int flags, int version, const char * name, int * dbhandle)
{
  unsigned char handle;
  int result;

  set_long(dlp_buf, creator);
  set_long(dlp_buf+4, type);
  set_byte(dlp_buf+8, cardno);
  set_byte(dlp_buf+9, 0);
  set_short(dlp_buf+10, flags);
  set_short(dlp_buf+12, version);
  strcpy((char*)dlp_buf+14, name);
  
  Trace(CreateDB);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Card: %d, version: %d, name '%s'\n", cardno, version, name);
    fprintf(stderr, "        DB Flags:");
    if (flags & dlpDBFlagResource)
      fprintf(stderr, " Resource");
    if (flags & dlpDBFlagReadOnly)
      fprintf(stderr, " ReadOnly");
    if (flags & dlpDBFlagAppInfoDirty)
      fprintf(stderr, " AppInfoDirty");
    if (flags & dlpDBFlagBackup)
      fprintf(stderr, " Backup");
    if (flags & dlpDBFlagReset)
      fprintf(stderr, " Reset");
    if (flags & dlpDBFlagNewer)
      fprintf(stderr, " Newer");
    if (flags & dlpDBFlagOpen)
      fprintf(stderr, " Open");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), Creator: '%s'", flags, printlong(creator));
    fprintf(stderr, ", Type: '%s'\n", printlong(type));
  }
#endif  

  result = dlp_exec(sd, 0x18, 0x20, dlp_buf, 14+strlen(name)+1, &handle, 1);
  
  Expect(1);
  
  if (dbhandle)
    *dbhandle = (int)handle;

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Handle: %d\n", (int)handle);
  }
#endif

  return result;
}


int dlp_CloseDB(int sd, int dbhandle)
{
  unsigned char handle = (unsigned char)dbhandle;
  int result;

  Trace(CloseDB);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", dbhandle);
  }
#endif
  
  result = dlp_exec(sd, 0x19, 0x20, &handle, 1, 0, 0);

  Expect(0);
  
  return result;
}

int dlp_CloseDB_All(int sd)
{
  int result;

  Trace(CloseDB_all);
  
  result = dlp_exec(sd, 0x19, 0x21, 0, 0, 0, 0);
  
  Expect(0);
  
  return result;
}

int dlp_CallApplication(int sd, unsigned long creator, unsigned long type, int action, 
                        int length, void * data,
                        unsigned long * retcode, int maxretlen, int * retlen, void * retdata)
{
  int result;
  int version = pi_version(sd);
  
  if (version >= 0x0101) { /* PalmOS 2.0 call encoding */
    set_long(dlp_buf+0, creator);
    set_long(dlp_buf+4, type);
    set_short(dlp_buf+8, action);
    set_long(dlp_buf+10, length);
    set_long(dlp_buf+14, 0);
    set_long(dlp_buf+18, 0);
    
    if (length+22 > DLP_BUF_SIZE) {
    	fprintf(stderr, "Data too large\n");
    	return -131;
    }
    
    memcpy(dlp_buf+22, data, length);

    Trace(CallApplicationV2);

#ifdef DLP_TRACE  
    if (dlp_trace) {  
      fprintf(stderr, " Wrote: Creator: '%s',", printlong(creator));
      fprintf(stderr, " Type: '%s', Action code: %d, and %d bytes of data:\n",
        printlong(type), action, length);
      dumpdata(data, length);
    }
#endif

    result = dlp_exec(sd, 0x28, 0x21, dlp_buf, 22 + length, dlp_buf, 0xffff);
  
    Expect(16);
  
    if (retcode)
      *retcode = get_long(dlp_buf);
    
    result -= 16;
  
    if (retlen)
      *retlen = result;
    if (retdata)
      memcpy(retdata, dlp_buf+16, result > maxretlen ? maxretlen : result);
  
#ifdef DLP_TRACE  
    if (dlp_trace) {  
      fprintf(stderr, "  Read: Result: %lu (0x%8.8lX), and %d bytes:\n",
        get_long(dlp_buf), get_long(dlp_buf+4), result);
      dumpdata(dlp_buf+16,result);
    }
#endif
  
    return result;


  } else { /* PalmOS 1.0 call encoding */
    set_long(dlp_buf+0, creator);
    set_short(dlp_buf+4, action);
    set_short(dlp_buf+6, length);
    memcpy(dlp_buf+6, data, length);

    Trace(CallApplicationV10);

#ifdef DLP_TRACE
    if (dlp_trace) {  
      fprintf(stderr, " Wrote: Creator: '%s', Action code: %d, and %d bytes of data:\n",
        printlong(creator), action, length);
      dumpdata(data, length);
    }
#endif

    result = dlp_exec(sd, 0x28, 0x20 , dlp_buf, 8, dlp_buf, 0xffff);
  
    Expect(6);
  
    if (retcode)
      *retcode = get_short(dlp_buf+2);
    
    result -= 6;
  
    if (retlen)
      *retlen = result;
    if (retdata)
      memcpy(retdata, dlp_buf+6, result > maxretlen ? maxretlen : result);
  
#ifdef DLP_TRACE  
    if (dlp_trace) {  
      fprintf(stderr, "  Read: Action: %d, Result: %d (0x%4.4X), and %d bytes:\n",
        get_short(dlp_buf), get_short(dlp_buf+2), get_short(dlp_buf+2), result);
      dumpdata(dlp_buf+6, result);
    }
#endif
  
    return result;

  }  
  
}

int dlp_ResetSystem(int sd)
{
  int result;

  Trace(ResetSystem);
  
  result = dlp_exec(sd, 0x29, 0, 0, 0, 0, 0);
  
  Expect(0);

  return result;
}

int dlp_AddSyncLogEntry(int sd, char * entry)
{ 
  int result;

  Trace(AddSyncLogEntry);
  
#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr," Wrote: Entry:\n");
    dumpdata((unsigned char *)entry,strlen(entry));
  }
#endif

  result = dlp_exec(sd, 0x2A, 0x20, (unsigned char*)entry, strlen(entry), 0, 0);

  Expect(0);
  
  return result;
}

int dlp_ReadOpenDBInfo(int sd, int dbhandle, int * records)
{
  unsigned char buf[2];
  int result;

  Trace(ReadOpenDBInfo);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", dbhandle);
  }
#endif
  
  set_byte(dlp_buf, (unsigned char)dbhandle);
  result = dlp_exec(sd, 0x2B, 0x20, dlp_buf, 1, buf, 2);

  Expect(2);
  
  if (records)
    *records = get_short(buf);
      
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: %d records\n", get_short(buf));
  }
#endif
  
  return result;
}

int dlp_MoveCategory(int sd, int handle, int fromcat, int tocat)
{
  int result;
  
  set_byte(dlp_buf+0, handle);
  set_byte(dlp_buf+1, fromcat);
  set_byte(dlp_buf+2, tocat);
  set_byte(dlp_buf+3, 0);

  Trace(MoveCategory);

#ifdef DLP_TRACE  
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, From: %d, To: %d\n", handle, fromcat, tocat);
  }
#endif
  
  result = dlp_exec(sd, 0x2C, 0x20, dlp_buf, 4, 0, 0);
  
  Expect(0);
  
  return result;
}


int dlp_OpenConduit(int sd)
{
  int result;

  Trace(OpenConduit);

  result = dlp_exec(sd, 0x2E, 0, 0, 0, 0, 0);

  Expect(0);
  
  return result;
}

int dlp_EndOfSync(int sd, int status)
{
  int result;
  struct pi_socket * ps;
  
  set_short(dlp_buf, status);

  Trace(EndOfSync);
  
  result = dlp_exec(sd, 0x2F, 0x20, dlp_buf, 2, 0, 0);

  Expect(0);
  
  /* Messy code to set end-of-sync flag on socket 
     so pi_close won't do it for us */
  if (result == 0)
    if ( (ps = find_pi_socket(sd)) )
      ps->connected |= 2;
  
  return result;
}

int dlp_AbortSync(int sd) {
  struct pi_socket * ps;

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "DLP %d: AbortSync\nResult: Whatever\n", sd);
  }
#endif
  
  /* Set end-of-sync flag on socket so pi_close won't do a dlp_EndOfSync */
  if ( (ps = find_pi_socket(sd)) )
    ps->connected |= 2;

  return pi_close(sd);
}

int dlp_WriteUserInfo(int sd, struct PilotUser *User)
{
  int result;

  Trace(WriteUserInfo);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: UID: 0x%8.8lX, VID: 0x%8.8lX, PCID: 0x%8.8lX\n", 
      User->userID, User->viewerID, User->lastSyncPC);
    fprintf(stderr, "        Last sync date: %s", ctime(&User->lastSyncDate));
    fprintf(stderr, "        Successful sync date: %s", ctime(&User->successfulSyncDate));
    fprintf(stderr, "        User name '%s'\n", User->username);
  }
#endif
  
  set_long(dlp_buf, User->userID);
  set_long(dlp_buf+4, User->viewerID);
  set_long(dlp_buf+8, User->lastSyncPC);
  set_date(dlp_buf+12, User->lastSyncDate);
  set_byte(dlp_buf+20, 0xff);
  set_byte(dlp_buf+21, strlen(User->username)+1);
  strcpy((char*)dlp_buf+22, User->username);

  result = dlp_exec(sd, 0x11, 0x20, dlp_buf, 22+strlen(User->username)+1, NULL, 0);
  
  Expect(0);
  
  return result;
}
                        
int dlp_ReadUserInfo(int sd, struct PilotUser* User)
{
  int result;
  int userlen;

  Trace(ReadUserInfo);

  result = dlp_exec(sd, 0x10, 0x00, NULL, 0, dlp_buf, DLP_BUF_SIZE);

  Expect(30);

  userlen = get_byte(dlp_buf+28);

  User->userID = get_long(dlp_buf);
  User->viewerID = get_long(dlp_buf+4);
  User->lastSyncPC = get_long(dlp_buf+8);
  User->successfulSyncDate = get_date(dlp_buf+12);
  User->lastSyncDate = get_date(dlp_buf+20);
  User->passwordLength = get_byte(dlp_buf+29);
  memcpy(User->username, dlp_buf+30, userlen);
  User->username[userlen] = '\0';
  memcpy(User->password, dlp_buf+30+userlen, User->passwordLength);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: UID: 0x%8.8lX, VID: 0x%8.8lX, PCID: 0x%8.8lX\n", 
      User->userID, User->viewerID, User->lastSyncPC);
    fprintf(stderr, "        Last sync date: %s", ctime(&User->lastSyncDate));
    fprintf(stderr, "        Successful sync date: %s", ctime(&User->successfulSyncDate));
    fprintf(stderr, "        User name '%s'", User->username);
    if (User->passwordLength) {
      fprintf(stderr, ", Password of %d bytes:\n", User->passwordLength);
      dumpdata((unsigned char *)User->password,User->passwordLength);
    }
    else
      fprintf(stderr, ", No password\n");
  }
#endif

  return result;
}

int dlp_ReadNetSyncInfo(int sd, struct NetSyncInfo * i)
{
  int result;
  int p;
  
  if (pi_version(sd)<0x0101)
    return -129; /* This call only functions under PalmOS 2.0 */

  Trace(ReadNetSyncInfo);

  result = dlp_exec(sd, 0x36, 0x20, NULL, 0, dlp_buf, DLP_BUF_SIZE);

  Expect(24);
  

  i->lanSync = get_byte(dlp_buf);
  p = 24;

  i->hostName[0] = 0;
  memcpy(i->hostName, dlp_buf+p, get_short(dlp_buf+18));
  p += get_short(dlp_buf+18);

  i->hostAddress[0] = 0;
  memcpy(i->hostAddress, dlp_buf+p, get_short(dlp_buf+20));
  p += get_short(dlp_buf+20);

  i->hostSubnetMask[0] = 0;
  memcpy(i->hostSubnetMask, dlp_buf+p, get_short(dlp_buf+22));
  p += get_short(dlp_buf+22);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Active: %d\n", get_byte(dlp_buf));
    fprintf(stderr, "        PC hostname: '%s', address '%s', mask '%s'\n",
      i->hostName, i->hostAddress, i->hostSubnetMask);
  }
#endif

  return result;
}

int dlp_WriteNetSyncInfo(int sd, struct NetSyncInfo * i)
{
  int result;
  int p;
  
  if (pi_version(sd)<0x0101)
    return -129;

  Trace(WriteNetSyncInfo);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Wrote: Active: %d\n", get_byte(dlp_buf));
    fprintf(stderr, "        PC hostname: '%s', address '%s', mask '%s'\n",
      i->hostName, i->hostAddress, i->hostSubnetMask);
  }
#endif
  
  set_byte(dlp_buf, 0x80|0x40|0x20|0x10); /* Change all settings */
  set_byte(dlp_buf+1, i->lanSync);
  set_long(dlp_buf+2, 0);  /* Reserved1 */
  set_long(dlp_buf+6, 0);  /* Reserved2 */
  set_long(dlp_buf+10, 0); /* Reserved3 */
  set_long(dlp_buf+14, 0); /* Reserved4 */
  set_short(dlp_buf+18, strlen(i->hostName)+1);
  set_short(dlp_buf+20, strlen(i->hostAddress)+1);
  set_short(dlp_buf+22, strlen(i->hostSubnetMask)+1);
  p = 24;
  strcpy((char *)dlp_buf+p, i->hostName);
  p += strlen(i->hostName)+1;
  strcpy((char *)dlp_buf+p, i->hostAddress);
  p += strlen(i->hostAddress)+1;
  strcpy((char *)dlp_buf+p, i->hostSubnetMask);
  p += strlen(i->hostSubnetMask)+1;
  
  result = dlp_exec(sd, 0x37, 0x20, dlp_buf, p, dlp_buf, DLP_BUF_SIZE);

  Expect(0);

  return result;
}

int dlp_RPC(int sd, struct RPC_params * p, unsigned long * result)
{
  int i;
  unsigned char * c;
  
  /* RPC through DLP breaks all the rules and isn't well documented to boot */
  
  long D0=0,A0=0;
  int err;
  
  dlp_buf[0] = 0x2D;
  dlp_buf[1] = 1;
  dlp_buf[2] = 0; /* Unknown filler */
  dlp_buf[3] = 0;

  InvertRPC(p);
  
  set_short(dlp_buf+4, p->trap);
  set_long(dlp_buf+6, D0);
  set_long(dlp_buf+10, A0);
  set_short(dlp_buf+14, p->args);
    
  c = dlp_buf+16;
  for(i=p->args-1;i>=0;i--) {
    set_byte(c, p->param[i].byRef); c++;
    set_byte(c, p->param[i].size); c++;
    if(p->param[i].data)
      memcpy(c, p->param[i].data, p->param[i].size);
    c += p->param[i].size;
    if(p->param[i].size & 1)
      *c++ = 0;
  }

  pi_write(sd, dlp_buf, c-dlp_buf);
  
  err = 0;
  
  if(p->reply) {
    int l = pi_read(sd, dlp_buf, c-dlp_buf+2);
 
    if (l < 0)
      err = l;
    else if (l < 6) 
      err = -1;
    else if (dlp_buf[0] != 0xAD)
      err = -2;
    else if (get_short(dlp_buf+2))
      err = -get_short(dlp_buf+2);
    else {
      D0 = get_long(dlp_buf+8);
      A0 = get_long(dlp_buf+12);
      c = dlp_buf+18;
      for(i=p->args-1;i>=0;i--) {
        if(p->param[i].byRef && p->param[i].data)
          memcpy(p->param[i].data, c+2, p->param[i].size);
        c += 2 + ((p->param[i].size+1)& ~1);
      }
    }
  }
  
  UninvertRPC(p);
  
  if (result)
    if(p->reply==RPC_PtrReply)
      *result = A0;
    else if (p->reply==RPC_IntReply)
      *result = D0;
  
  return err;
}

int dlp_ReadFeature(int sd, unsigned long creator, unsigned int num, unsigned long * feature)
{
  int result;
  
  if (pi_version(sd)<0x0101) {
    struct RPC_params p;
    
    Trace(ReadFeatureV1);

    if (feature) {
      int val;
      unsigned long result;

#ifdef DLP_TRACE
      if (dlp_trace) {  
        fprintf(stderr, " Wrote: Creator: '%s', Number: %d\n", 
          printlong(creator), num);
      }
#endif

      *feature = 0x12345678;

      PackRPC(&p, 0xA27B, RPC_IntReply, 
         RPC_Long(creator), RPC_Short(num), RPC_LongPtr(feature), RPC_End);
      
      val = dlp_RPC(sd, &p, &result);
      

#ifdef DLP_TRACE
      if (dlp_trace) {  
        if (val < 0)
          fprintf(stderr, "Result: Error: %s (%d)\n", dlp_errorlist[-val], val); 
        else if (result)
          fprintf(stderr, "FtrGet error 0x%8.8lX\n", (unsigned long)result);
        else
          fprintf(stderr, "  Read: Feature: 0x%8.8lX\n", (unsigned long)*feature);
      }
#endif
      
      if (val < 0)
        return val;
      if (result)
        return -result;
    }
    
    return 0;
  }    

  Trace(ReadFeatureV2);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Creator: '%s', Number: %d\n", 
      printlong(creator), num);
  }
#endif
  
  set_long(dlp_buf, creator);
  set_short(dlp_buf+4, num);
  
  result = dlp_exec(sd, 0x38, 0x20, dlp_buf, 6, dlp_buf, DLP_BUF_SIZE);

  Expect(4);

  if (feature)
    *feature = (unsigned long)get_long(dlp_buf);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Feature: 0x%8.8lX\n", (unsigned long)get_long(dlp_buf));
  }
#endif

  return result;
}

int dlp_ResetLastSyncPC(int sd)
{
  struct PilotUser U;
  int err;
  
  if ((err = dlp_ReadUserInfo(sd, &U))<0)
    return err;
  U.lastSyncPC = 0;
  return dlp_WriteUserInfo(sd, &U);
}

int dlp_ResetDBIndex(int sd, int dbhandle)
{
  int result;
  struct pi_socket * ps;

  if ((ps = find_pi_socket(sd)))
    ps->dlprecord=0;

  Trace(ResetDBIndex);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", dbhandle);
  }
#endif
  
  result = dlp_exec(sd, 0x30, 0x20, dlp_buf, 1, NULL, 0);
  
  Expect(0);

  return result;
}


int dlp_ReadRecordIDList(int sd, int dbhandle, int sort, 
                         int start, int max, recordid_t * IDs, int *count)
{
  int result, i, ret;
  unsigned int nbytes;
  unsigned char * p; 
  
  set_byte(dlp_buf,    dbhandle);
  set_byte(dlp_buf+1,  sort?0x80:0);
  set_short(dlp_buf+2, start);
  set_short(dlp_buf+4, max);

  Trace(ReadRecordIDList);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Sort: %s, Start: %d, Max: %d\n",
      dbhandle, sort ? "Yes" : "No", start, max);
  }
#endif

  nbytes = max * 4 + 2;
  if (nbytes > DLP_BUF_SIZE)
    nbytes = DLP_BUF_SIZE;
  
  result = dlp_exec(sd, 0x31, 0x20, dlp_buf, 6, dlp_buf, nbytes);
  
  Expect(2);
  
  ret = get_short(dlp_buf);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Read: %d IDs:\n", ret);
      dumpdata(dlp_buf+2, ret*4);
  }
#endif

  for (i = 0, p = dlp_buf+2; i < ret; i++, p+=4)
    IDs[i] = get_long(p);
  
  if (count)
    *count = i;
  
  return nbytes;
}

int dlp_WriteRecord(int sd, int dbhandle, int flags,
                 recordid_t recID, int catID, void* data, int length, recordid_t * NewID)
{
  unsigned char buf[4];
  int result;

  set_byte(dlp_buf, dbhandle);
  set_byte(dlp_buf+1, 0);
  set_long(dlp_buf+2, recID);
  set_byte(dlp_buf+6, flags);
  set_byte(dlp_buf+7, catID);
  
  if(length == -1)
  	length = strlen((char*)data)+1;

  if (length+8 > DLP_BUF_SIZE) {
    fprintf(stderr, "Data too large\n");
    return -131;
  }
  	
  memcpy(dlp_buf+8, data, length);

  Trace(WriteRecord);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, RecordID: 0x%8.8lX, Category: %d\n",
      dbhandle, (unsigned long)recID, catID);
    fprintf(stderr, "        Flags:");
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes of data: \n", flags, length);
    dumpdata(data, length);
  }
#endif

  result = dlp_exec(sd, 0x21, 0x20, dlp_buf, 8+length, buf, 4);
  
  Expect(4);
  
  if(NewID)
    if(result == 4)
      *NewID = get_long(buf); /* New record ID */
    else
      *NewID = 0;

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Record ID: 0x%8.8lX\n", (unsigned long)get_long(buf));
  }
#endif
      
  return result;
}

int dlp_DeleteRecord(int sd, int dbhandle, int all, recordid_t recID)
{
  int result;
  int flags = all ? 0x80 : 0;

  set_byte(dlp_buf, dbhandle);
  set_byte(dlp_buf+1, flags);
  set_long(dlp_buf+2, recID);
  
  Trace(DeleteRecord);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, RecordID: %8.8lX, All: %s\n",
      dbhandle, (unsigned long)recID, all ? "Yes" : "No");
  }
#endif
  
  result = dlp_exec(sd, 0x22, 0x20, dlp_buf, 6, 0, 0);
  
  Expect(0);
  
  return result;
}

int dlp_DeleteCategory(int sd, int dbhandle, int category)
{
  int result;
  int flags = 0x40;
  
  if (pi_version(sd) < 0x0101) {
    /* Emulate if not connected to PalmOS 2.0 */
    int i,r,cat,attr;
    recordid_t id;

    Trace(DeleteCategoryV1);

#ifdef DLP_TRACE
    if (dlp_trace) {  
      fprintf(stderr, " Emulating with: Handle: %d, Category: %d\n",
        dbhandle, category & 0xff);
    }
#endif

    for(i=0;dlp_ReadRecordByIndex(sd, dbhandle, i, NULL, &id, NULL, &attr, &cat)>=0;i++) {
      if ((cat != category) || (attr & dlpRecAttrDeleted) || (attr & dlpRecAttrArchived)) 
        continue;
      r = dlp_DeleteRecord(sd, dbhandle, 0, id);
      if (r<0)
        return r;
      i--; /* Sigh, deleting the record moves it to the end. */
    }
    return 0;
  }

  set_byte(dlp_buf, dbhandle);
  set_byte(dlp_buf+1, flags);
  set_long(dlp_buf+2, category & 0xff);
  
  Trace(DeleteCategoryV2);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Category: %d\n",
      dbhandle, category & 0xff);
  }
#endif
  
  result = dlp_exec(sd, 0x22, 0x20, dlp_buf, 6, 0, 0);
  
  Expect(0);
  
  return result;
}


int dlp_ReadResourceByType(int sd, int fHandle, unsigned long type, int id, void* buffer, 
                          int* index, int* size)
{
  int result;
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_long(dlp_buf+2, type);
  set_short(dlp_buf+6, id);
  set_short(dlp_buf+8, 0); /* Offset into record */
  set_short(dlp_buf+10, buffer ? DLP_BUF_SIZE : 0); /* length to return */

  Trace(ReadResourceByType);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Type: '%s', ID: %d\n",
      fHandle, printlong(type), id);
  }
#endif

  result = dlp_exec(sd, 0x23, 0x21, dlp_buf, 12, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Type: '%s', ID: %d, Index: %d, and %d bytes:\n",
      printlong(type), id, get_short(dlp_buf+6), result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif
  
  if (index)
    *index = get_short(dlp_buf+6);
  if (size)
    *size = get_short(dlp_buf+8);
  if (buffer)
    memcpy(buffer, dlp_buf+10, result-10);

  return result-10;
}

int dlp_ReadResourceByIndex(int sd, int fHandle, int index, void* buffer,
                          unsigned long* type, int * id, int* size)
{
  int result;
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, index);
  set_short(dlp_buf+4, 0); /* Offset into record */
  set_short(dlp_buf+6, buffer ? DLP_BUF_SIZE : 0); /* length to return */

  Trace(ReadResourceByIndex);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Index: %d\n",
      fHandle, index);
  }
#endif

  result = dlp_exec(sd, 0x23, 0x20, dlp_buf, 8, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: Type: '%s', ID: %d, Index: %d, and %d bytes:\n",
      printlong(get_long(dlp_buf)), get_short(dlp_buf+4), index, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif

  if (type) 
    *type = get_long(dlp_buf);
  if (id)
    *id = get_short(dlp_buf+4);
  if (size)
    *size = get_short(dlp_buf+8);
  if (buffer)
    memcpy(buffer, dlp_buf+10, result-10);
   
  return result-10;
}

int dlp_WriteResource(int sd, int dbhandle, unsigned long type, int id,
                 const void* data, int length)
{
  int result;
  
  set_byte(dlp_buf, dbhandle);
  set_byte(dlp_buf+1, 0);
  set_long(dlp_buf+2, type);
  set_short(dlp_buf+6, id);
  set_short(dlp_buf+8, length);

  if (length+10 > DLP_BUF_SIZE) {
    fprintf(stderr, "Data too large\n");
    return -131;
  }

  memcpy(dlp_buf+10, data, length);
  
  Trace(WriteResource);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Type: '%s', ID: %d, and %d bytes:\n",
      printlong(type), id, length);
    dumpdata(data, length);
  }
#endif

  result = dlp_exec(sd, 0x24, 0x20, dlp_buf, 10+length, NULL, 0);
  
  Expect(0);
  
  return result;
}

int dlp_DeleteResource(int sd, int dbhandle, int all, unsigned long restype, int resID)
{
  int result;
  int flags = all ? 0x80 : 0;

  set_byte(dlp_buf, dbhandle);
  set_byte(dlp_buf+1, flags);
  set_long(dlp_buf+2, restype);
  set_short(dlp_buf+6, resID);

  Trace(DeleteResource);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Type: '%s', ID: %d, All: %s\n",
      printlong(restype), resID, all ? "Yes" : "No");
  }
#endif
  
  result = dlp_exec(sd, 0x25, 0x20, dlp_buf, 8, NULL, 0);
  
  Expect(0);
  
  return result;
}

int dlp_ReadAppBlock(int sd, int fHandle, int offset,
                           void* dbuf, int dlen)
{
  int result;
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, offset);
  set_short(dlp_buf+4, dlen);
  
  Trace(ReadAppBlock);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Offset: %d, Max Length: %d\n",
      fHandle, offset, dlen);
  }
#endif
  
  result = dlp_exec(sd, 0x1b, 0x20, dlp_buf, 6, dlp_buf, DLP_BUF_SIZE);
  
  Expect(2);
  
  if (dbuf)
    memcpy(dbuf, dlp_buf+2, result-2);
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: %d bytes:\n",
      result-2);
    dumpdata(dlp_buf+2, result-2);
  }
#endif
  
  return result-2;
}

int dlp_WriteAppBlock(int sd, int fHandle, const /*@unique@*/ void* data, int length)
{
  int result;
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, length);

  if (length+4 > DLP_BUF_SIZE) {
    fprintf(stderr, "Data too large\n");
    return -131;
  }
  
  memcpy(dlp_buf+4, data, length);

  Trace(WriteAppBlock);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, %d bytes:\n",
      fHandle, length);
    dumpdata(data, length);
  }
#endif

  result = dlp_exec(sd, 0x1c, 0x20, dlp_buf, length+4, NULL, 0);
  
  Expect(0);
  
  return result;
}

int dlp_ReadSortBlock(int sd, int fHandle, int offset,
                           void* dbuf, int dlen)
{
  int result;
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, offset);
  set_short(dlp_buf+4, dlen);

  Trace(ReadSortBlock);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Offset: %d, Max Length: %d\n",
      fHandle, offset, dlen);
  }
#endif
  
  result = dlp_exec(sd, 0x1d, 0x20, dlp_buf, 6, dlp_buf, DLP_BUF_SIZE);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, "  Read: %d bytes:\n",
      result-2);
    dumpdata(dlp_buf+2, result-2);
  }
#endif
  
  if (dbuf)
    memcpy(dbuf, dlp_buf+2, result-2);
  return result-2;
}

int dlp_WriteSortBlock(int sd, int fHandle, const /*@unique@*/ void* data, int length)
{
  int result;
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, length);

  if (length+4 > DLP_BUF_SIZE) {
    fprintf(stderr, "Data too large\n");
    return -131;
  }
  
  memcpy(dlp_buf+4, data, length);

  Trace(WriteSortBlock);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, %d bytes:\n",
      fHandle, length);
    dumpdata(data, length);
  }
#endif

  result = dlp_exec(sd, 0x1e, 0x20, dlp_buf, length+4, NULL, 0);

  Expect(0);
  
  return result;
}

int dlp_CleanUpDatabase(int sd, int fHandle)
{
  int result;
  unsigned char handle = fHandle;

  Trace(CleanUpDatabase);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", fHandle);
  }
#endif

  result = dlp_exec(sd, 0x26, 0x20, &handle, 1, NULL, 0);
  
  Expect(0);
  
  return result;
}

int dlp_ResetSyncFlags(int sd, int fHandle)
{
  int result;
  unsigned char handle = fHandle;

  Trace(ResetSyncFlags);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", fHandle);
  }
#endif
  
  result = dlp_exec(sd, 0x27, 0x20, &handle, 1, NULL, 0);
  
  Expect(0);
  
  return result;
}

int dlp_ReadNextRecInCategory(int sd, int fHandle, int incategory, void* buffer,
                          recordid_t* id, int* index, int* size, int* attr)
{
  int result;
#ifdef DLP_TRACE
  int flags;
#endif

  if (pi_version(sd) < 0x0101) {
    /* Emulate for PalmOS 1.0 */
    int cat;
    int r;
    struct pi_socket * ps;

    Trace(ReadNextRecInCategoryV1);

#ifdef DLP_TRACE
    if (dlp_trace) {  
      fprintf(stderr, " Emulating with: Handle: %d, Category: %d\n", fHandle, incategory);
    }
#endif

    if ((ps = find_pi_socket(sd))==0)
      return -130;
    
    for(;;) {
      /* Fetch next modified record (in any category) */
      r = dlp_ReadRecordByIndex(sd, fHandle, ps->dlprecord, 0, 0, 0, 0, &cat);
      
      if (r<0)
        break;
      
      if (cat != incategory) {
        ps->dlprecord++;
        continue;
      }

      r = dlp_ReadRecordByIndex(sd, fHandle, ps->dlprecord, buffer, id, size, attr, &cat);
      
      if (r >= 0) {
        if (index)
          *index = ps->dlprecord;
        ps->dlprecord++;
      } else {
        /* If none found, reset modified pointer so that another search on a different
           (or the same!) category will work */
        ps->dlprecord = 0;
      }
      
      break;
    }
    
    return r;
  }

  Trace(ReadNextRecInCategoryV2);

  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, incategory);
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Category: %d\n", fHandle, incategory);
  }
#endif
  
  result = dlp_exec(sd, 0x32, 0x20, dlp_buf, 2, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    flags = get_byte(dlp_buf+8);
    fprintf(stderr, "  Read: ID: 0x%8.8lX, Index: %d, Category: %d\n        Flags:",
      (unsigned long)get_long(dlp_buf), get_short(dlp_buf+4), (int)get_byte(dlp_buf+9));
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes:\n", flags, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif

  if (id)
    *id = get_long(dlp_buf);
  if (index)
    *index = get_short(dlp_buf+4);
  if (size)
    *size = get_short(dlp_buf+6);
  if (attr)
    *attr = get_byte(dlp_buf+8);
  if (buffer)    
    memcpy(buffer, dlp_buf+10, result-10);
    
  return result-10;
}


int dlp_ReadAppPreference(int sd, unsigned long creator, int id, int backup,
                          int maxsize, void* buffer, int * size, int * version)
{
  int result;

  if (pi_version(sd) < 0x0101) {
    /* Emulate on PalmOS 1.0 */
    int db;
    int r;

    Trace(ReadAppPreferenceV1);

#ifdef DLP_TRACE
    if (dlp_trace) {  
      fprintf(stderr, "  Emulating with: Creator: '%s', Id: %d, Size: %d, Backup: %d\n", 
                      printlong(creator), id, buffer ? maxsize : 0, backup ? 0x80 : 0);
    }
#endif
    
    r = dlp_OpenDB(sd, 0, dlpOpenRead, "System Preferences", &db);
    if (r<0)
      return r;
    
    r = dlp_ReadResourceByType(sd, db, creator, id, buffer, NULL, size);
    
    
    if (r<0) {
      dlp_CloseDB(sd, db);
      return r;
    }

    if (size)
      *size -= 2;
    
    if (version)
      *version = get_short(buffer);
    
    if (r>2) {
      r-=2;
      memmove(buffer, ((char*)buffer)+2, r);
    } else {
      r = 0;
    }
    
    dlp_CloseDB(sd,db);

    return r;
  }

  Trace(ReadAppPreferenceV2);

  set_long(dlp_buf, creator);
  set_short(dlp_buf+4, id);
  set_short(dlp_buf+6, buffer ? maxsize : 0);
  set_byte(dlp_buf+8, backup ? 0x80 : 0);
  set_byte(dlp_buf+9, 0); /* Reserved */
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Creator: '%s', Id: %d, Size: %d, Backup: %d\n", 
                printlong(creator), id, buffer ? maxsize : 0, backup ? 0x80 : 0);
  }
#endif
  
  result = dlp_exec(sd, 0x34, 0x20, dlp_buf, 10, dlp_buf, DLP_BUF_SIZE);
  
  Expect(6);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Read: Version: %d, Total size: %d, Read %d bytes:\n", 
                   get_short(dlp_buf), get_short(dlp_buf+2), get_short(dlp_buf+4));
    dumpdata(dlp_buf+6, get_short(dlp_buf+4));
  }
#endif

  if (version)
    *version = get_short(dlp_buf);
  if (size && !buffer)
    *size = get_short(dlp_buf+2); /* Total size */
  if (size && buffer)
    *size = get_short(dlp_buf+4); /* Size returned */
  if (buffer)
    memcpy(buffer, dlp_buf+6, get_short(dlp_buf+4));
    
  return get_short(dlp_buf+4);
}

int dlp_WriteAppPreference(int sd, unsigned long creator, int id, int backup,
                          int version, void * buffer, int size)
{
  int result;

  if (pi_version(sd) < 0x0101) {
    /* Emulate on PalmOS 1.0 */
    int db;
    int r;

    Trace(WriteAppPreferenceV1);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Creator: '%s', Id: %d, Version: %d, Backup: %d, and %d bytes:\n", 
                    printlong(creator), id, version, backup ? 0x80 : 0, size);
    dumpdata(buffer, size);
  }
#endif
    
    r = dlp_OpenDB(sd, 0, dlpOpenWrite, "System Preferences", &db);
    if (r<0)
      return r;
      
    if (buffer && size) {
      memcpy(dlp_buf + 2, buffer, size);
      set_short(dlp_buf, version);
      r = dlp_WriteResource(sd, db, creator, id, dlp_buf, size);
    } else
      r = dlp_WriteResource(sd, db, creator, id, NULL, 0);

    dlp_CloseDB(sd, db);
    
    return r;
  }

  Trace(WriteAppPreferenceV2);

  set_long(dlp_buf, creator);
  set_short(dlp_buf+4, id);
  set_short(dlp_buf+6, version);
  set_short(dlp_buf+8, size);
  set_byte(dlp_buf+10, backup ? 0x80 : 0);
  set_byte(dlp_buf+11, 0); /* Reserved */
  
  if (size+12 > DLP_BUF_SIZE) {
    fprintf(stderr, "Data too large\n");
    return -131;
  }
  
  memcpy(dlp_buf+12, buffer, size);
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Creator: '%s', Id: %d, Version: %d, Backup: %d, and %d bytes:\n", 
                    printlong(creator), id, version, backup ? 0x80 : 0, size);
    dumpdata(buffer, size);
  }
#endif
  
  result = dlp_exec(sd, 0x35, 0x20, dlp_buf, 12+size, NULL, 0);
  
  Expect(0);

  return result;
}

int dlp_ReadNextModifiedRecInCategory(int sd, int fHandle, int incategory, void* buffer,
                          recordid_t* id, int* index, int* size, int* attr)
{
  int result;
#ifdef DLP_TRACE
  int flags;
#endif

  if (pi_version(sd) < 0x0101) {
    /* Emulate for PalmOS 1.0 */
    int cat;
    int r;

    Trace(ReadNextModifiedRecInCategoryV1);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Emulating with: Handle: %d, Category: %d\n", fHandle, incategory);
  }
#endif
    
    do {
      /* Fetch next modified record (in any category) */
      r = dlp_ReadNextModifiedRec(sd, fHandle, buffer, id, index, size, attr, &cat);
    
      /* If none found, reset modified pointer so that another search on a different
         (or the same!) category will start from the beginning */
      if (r < 0)
        dlp_ResetDBIndex(sd, fHandle);
          
      /* Loop until we fail to get a record or a record is found in the proper category */
    } while ((r >= 0) && (cat != incategory));
    
    return r;
  }

  Trace(ReadNextModifiedRecInCategoryV2);

  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, incategory);
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Category: %d\n", fHandle, incategory);
  }
#endif
  
  result = dlp_exec(sd, 0x33, 0x20, dlp_buf, 2, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    flags = get_byte(dlp_buf+8);
    fprintf(stderr, "  Read: ID: 0x%8.8lX, Index: %d, Category: %d\n        Flags:",
      (unsigned long)get_long(dlp_buf), get_short(dlp_buf+4), (int)get_byte(dlp_buf+9));
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes:\n", flags, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif

  if (id)
    *id = get_long(dlp_buf);
  if (index)
    *index = get_short(dlp_buf+4);
  if (size)
    *size = get_short(dlp_buf+6);
  if (attr)
    *attr = get_byte(dlp_buf+8);
  if (buffer)    
    memcpy(buffer, dlp_buf+10, result-10);
    
  return result-10;
}


int dlp_ReadNextModifiedRec(int sd, int fHandle, void* buffer,
                          recordid_t* id, int* index, int* size, int* attr, int* category)
{
  unsigned char handle = fHandle;
  int result;
#ifdef DLP_TRACE
  int flags;
#endif

  Trace(ReadNextModifiedRec);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d\n", fHandle);
  }
#endif
  
  result = dlp_exec(sd, 0x1f, 0x20, &handle, 1, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    flags = get_byte(dlp_buf+8);
    fprintf(stderr, "  Read: ID: 0x%8.8lX, Index: %d, Category: %d\n        Flags:",
      (unsigned long)get_long(dlp_buf), get_short(dlp_buf+4), (int)get_byte(dlp_buf+9));
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes:\n", flags, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif

  if (id)
    *id = get_long(dlp_buf);
  if (index)
    *index = get_short(dlp_buf+4);
  if (size)
    *size = get_short(dlp_buf+6);
  if (attr)
    *attr = get_byte(dlp_buf+8);
  if (category)
    *category = get_byte(dlp_buf+9);
  if (buffer)    
    memcpy(buffer, dlp_buf+10, result-10);
    
  return result-10;
}

int dlp_ReadRecordById(int sd, int fHandle, recordid_t id, void* buffer, 
                          int* index, int* size, int* attr, int* category)
{
  int result;
#ifdef DLP_TRACE
  int flags;
#endif
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_long(dlp_buf+2, id);
  set_short(dlp_buf+6, 0); /* Offset into record */
  set_short(dlp_buf+8, buffer ? DLP_BUF_SIZE : 0); /* length to return */

  Trace(ReadRecordById);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Record ID: 0x%8.8lX\n", fHandle, id);
  }
#endif
  
  result = dlp_exec(sd, 0x20, 0x20, dlp_buf, 10, dlp_buf, DLP_BUF_SIZE);

  Expect(10);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    flags = get_byte(dlp_buf+8);
    fprintf(stderr, "  Read: ID: 0x%8.8lX, Index: %d, Category: %d\n        Flags:",
      (unsigned long)get_long(dlp_buf), get_short(dlp_buf+4), (int)get_byte(dlp_buf+9));
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes:\n", flags, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif
  
  /*id = get_long(dlp_buf);*/
  if (index)
    *index = get_short(dlp_buf+4);
  if (size)
    *size = get_short(dlp_buf+6);
  if (attr)
    *attr = get_byte(dlp_buf+8);
  if (category)
    *category = get_byte(dlp_buf+9);
  if (buffer)
    memcpy(buffer, dlp_buf+10, result-10);

  return result-10;
}

int dlp_ReadRecordByIndex(int sd, int fHandle, int index, void* buffer,
                          recordid_t* id, int* size, int* attr, int* category)
{
  int result;
#ifdef DLP_TRACE
  int flags;
#endif
  
  set_byte(dlp_buf, fHandle);
  set_byte(dlp_buf+1, 0x00);
  set_short(dlp_buf+2, index);
  set_short(dlp_buf+4, 0); /* Offset into record */
  set_short(dlp_buf+6, buffer ? DLP_BUF_SIZE : 0); /* length to return */

  Trace(ReadRecordByIndex);

#ifdef DLP_TRACE
  if (dlp_trace) {  
    fprintf(stderr, " Wrote: Handle: %d, Index: %d\n", fHandle, index);
  }
#endif  

  result = dlp_exec(sd, 0x20, 0x21, dlp_buf, 8, dlp_buf, DLP_BUF_SIZE);
  
  Expect(10);
  
#ifdef DLP_TRACE
  if (dlp_trace) {  
    flags = get_byte(dlp_buf+8);
    fprintf(stderr, "  Read: ID: 0x%8.8lX, Index: %d, Category: %d\n        Flags:",
      (unsigned long)get_long(dlp_buf), get_short(dlp_buf+4), (int)get_byte(dlp_buf+9));
    if (flags & dlpRecAttrDeleted)
      fprintf(stderr, " Deleted");
    if (flags & dlpRecAttrDirty)
      fprintf(stderr, " Dirty");
    if (flags & dlpRecAttrBusy)
      fprintf(stderr, " Busy");
    if (flags & dlpRecAttrSecret)
      fprintf(stderr, " Secret");
    if (flags & dlpRecAttrArchived)
      fprintf(stderr, " Archive");
    if (!flags)
      fprintf(stderr, " None");
    fprintf(stderr, " (0x%2.2X), and %d bytes:\n", flags, result-10);
    dumpdata(dlp_buf+10, result-10);
  }
#endif

  if (id)
    *id = get_long(dlp_buf);
  /*get_short(dlp_buf+4) == index*/
  if (size)
    *size = get_short(dlp_buf+6);
  if (attr)
    *attr = get_byte(dlp_buf+8);
  if (category)
    *category = get_byte(dlp_buf+9);
  if (buffer)
    memcpy(buffer, dlp_buf+10, result-10);

  return result-10;
}
