#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "debug.h"
#include "kernel.h"
#include "swis.h"

#include "buffer.h"
#include "pinboard.h"
#include "base.h"
#include "module.h"
#include "pinfilter.h"
#include "upcall.h"
#include "veneers.h"
#include "mystring.h"
#include "paths.h"
#include "msgtrans.h"
#include "fs.h"

/* This function is autogenerated by ResGen, and returns a pointer to a
 * ResourceFS resource file data block */
extern void *Resources(void);

#define TASK 0x4b534154

#define ICONSPRITES "Pinboard_IconSprites %0"
#define OBEYRUN "%Set Obey$File %0|M%Obey %*0"
#define PINPATH_DEFAULT "<Choices$Write>.Pinboard.SavedFiles."
#define PINPATH_DEFAULT_NODOT "<Choices$Write>.Pinboard.SavedFiles"
#define SAVEDFILES_LOCATION "Pinboard:"
#define PINBOARD_CHOICES "<Choices$Write>.Pinboard"
#define PINPATH_CHOICES "Choices:Pinboard.Path"

static int messagetrans[5]={0,0,0,0,0};

/* pending values for *BackDrop */
static int backdrop_otc_pending=-1;
static int backdrop_blend_pending=-1;
static int backdrop_cache_pending=-1;
static int backdrop_fullscreen_pending=-1;

void *module_pw;

extern void edump(buffer_entry *);

static _kernel_oserror *path_canon(char *source,char **dest)
{
  _kernel_oserror *e;
  int size;

  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,source,0,0,0,0,&size);
  if (e) return e;

  *dest=(char *)malloc(-size+2);
  if (!*dest) return NULL;

  return _swix(OS_FSControl,_INR(0,5),37,source,*dest,0,0,-size+2);
}

static _kernel_oserror *path_check_exists(char *path)
{
  _kernel_oserror *e;
  int state;

  if ((e=fs_get_types(path,&state,NULL))!=NULL) return e;
  if (state!=0) return NULL;

  return msgtrans_errorlookup_1("PinNF",path);
}

static _kernel_oserror *path_length(char *path,int *length)
{
  _kernel_oserror *e;

  if (strncmp(path,"Pinboard:",9)==0) {
    *length=strlen(path);
    return NULL;
  }

  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,path,0,0,0,0,length);

  *length=1-(*length);

  return e;
}

static _kernel_oserror *path_expand(char *source,char *dest,int dest_size)
{
  if (strncmp(source,"Pinboard:",9)==0) {
    strcpy(dest,source);
    return NULL;
  } else {
    return _swix(OS_FSControl,_INR(0,5),37,source,dest,0,0,dest_size);
  }
}

static _kernel_oserror *path_set_default(char *config_path)
{
  // canonicalise the default
  _kernel_oserror *e;
  int length;
  char *buffer;

  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,config_path,NULL,NULL,NULL,0,&length);
  if (e) return e;

  buffer=(char *)malloc(-length+2);
  if (!buffer) return msgtrans_errorlookup("EINMEM");

  e=_swix(OS_FSControl,_INR(0,5),37,config_path,buffer,NULL,NULL,-length+2);
  if (!e) {
    strcpy(buffer+strlen(buffer),".");
    e=_swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",buffer,strlen(buffer),0,0);
  }
  free(buffer);

  return e;
}

static _kernel_oserror *path_set(void)
{
  // preserve old path if possible
  char *oldpath;
  _kernel_oserror *e;
  int type,length;
  char *newpath,*pathbuffer=NULL;

  oldpath=getenv("Pinboard$Path");
  if (oldpath) {
    _swix(OS_SetVarVal,_INR(0,4),"Pinboard$OldPath",oldpath,strlen(oldpath),0,0);
  }

  // see if we have one in choices...
  e=_swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(4),5,PINPATH_CHOICES,&type,&length);
  if (e) return e;

  if (type==1) {
    pathbuffer=(char *)malloc(1+length);
    if (!pathbuffer) {
      return msgtrans_errorlookup("EINMEM");
    }
    _swix(OS_File,_INR(0,3),255,PINPATH_CHOICES,pathbuffer,0);
    // ensure null terminated
    // remove any terminating linefeeds, etc.
    while (length>0 && (*(pathbuffer+length-1)<33 || *(pathbuffer+length-1)=='.')) length--;
    if (length==0) {
      newpath=PINPATH_DEFAULT_NODOT;
    } else {
      *(pathbuffer+length)=0;
      newpath=pathbuffer;
    }
  } else {
    newpath=PINPATH_DEFAULT_NODOT;
  }

  e=path_set_default(newpath);
  if (pathbuffer) free(pathbuffer);
  if (e) return e;

  // ensure we have the Choices:Pinboard directory
  e=_swix(OS_File,_INR(0,1)|_IN(4),8,PINBOARD_CHOICES,0);
  if (e) return e;

  // ensure we have the saved files directory
  e=_swix(OS_File,_INR(0,1)|_IN(4),8,SAVEDFILES_LOCATION,0);

  return e;
}

static void path_unset(void)
{
  // reset path to previous one
  char *oldpath=getenv("Pinboard$OldPath");
  if (oldpath) {
    _swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",oldpath,strlen(oldpath),0,0);
    _swix(OS_SetVarVal,_INR(0,4),"Pinboard$OldPath",0,-1,0,0);
  }
}

/*
static _kernel_oserror *obey_set(void)
{
  // preserve current setting
  char *oldobey;

  oldobey=getenv("Alias$@RunType_FEB");
  if (oldobey) {
    _swix(OS_SetVarVal,_INR(0,4),"OldAlias$@RunType_FEB",oldobey,strlen(oldobey),0,0);
  }

  // set our override
  _swix(OS_SetVarVal,_INR(0,4),"Alias$@RunType_FEB",OBEYRUN,sizeof(OBEYRUN),0,0); // not a disaster if this doesn't go

}

static void obey_unset(void *pw)
{
  char *oldobey;
  oldobey=getenv("OldAlias$@RunType_FEB");

  if (oldobey) {
    _swix(OS_SetVarVal,_INR(0,4),"Alias$@RunType_FEB",oldobey,strlen(oldobey),0,0);
  } else {
    _swix(OS_SetVarVal,_INR(0,4),"Alias$@RunType_FEB",0,-1,0,0);
  }
  _swix(OS_SetVarVal,_INR(0,4),"OldAlias$@RunType_FEB",0,-1,0,0);
}
*/

static void messagetrans_final(void)
{
  if (messagetrans[4]) {
    _swix(MessageTrans_CloseFile,_IN(0),messagetrans);
    messagetrans[4]=0;
  }
#ifndef ROM_MODULE
  _swix(ResourceFS_DeregisterFiles,_IN(0),Resources());
#endif
}

_kernel_oserror *module_init(const char *cmd_tail, int podule_base, void *pw)
{
  _kernel_oserror *e=NULL;

  IGNORE(cmd_tail);
  IGNORE(podule_base);
  IGNORE(pw);

  debug_initialise(Module_Title, "$.debug", "");
  debug_set_device(REPORTER_OUTPUT);
//  debug_set_device(FILE_OUTPUT);
//  debug_set_unbuffered_files(1);
  debugf("\n");

  module_pw = pw;

  if ((e=buffer_init())!=NULL) return e;

#ifndef ROM_MODULE
  if ((e=_swix(ResourceFS_RegisterFiles, _IN(0), Resources()))!=NULL) goto error_messagetrans;
#endif

  if (messagetrans[4]==0) {
    e=_swix(MessageTrans_OpenFile,_INR(0,2), messagetrans, Module_MessagesFile ,0);
    if (e) {
      _swix(ResourceFS_DeregisterFiles,_IN(0), Resources());
      buffer_final();
      return e;
    }
    messagetrans[4]=1;
  }

  // do we have a path in our choices?
  e=path_set();
  if (e) goto error_path;

  e=_swix(OS_Claim,_INR(0,2),29,upcall_entry,pw);
  if (e) goto error_upcall;

  e=_swix(OS_Claim,_INR(0,2),15,fscontrolv_entry,pw);
  if (e) goto error_obey;

  _swix(OS_SetVarVal,_INR(0,4),"Alias$IconSprites",ICONSPRITES,sizeof(ICONSPRITES),0,0); // not a disaster if this doesn't go

  taskhandle=0;

  return NULL;


error_obey:
    _swix(OS_Release,_INR(0,2),29,upcall_entry,pw);

error_upcall:
    path_unset();

error_path:
    messagetrans_final();

error_messagetrans:
    buffer_final();
    return e;
}

_kernel_oserror *module_final(int fatal, int podule, void *pw)
{
  IGNORE(fatal);
  IGNORE(podule);
  IGNORE(pw);

  _swix(OS_Release,_INR(0,2),29,upcall_entry,pw);
  _swix(OS_Release,_INR(0,2),15,fscontrolv_entry,pw);

  if (taskhandle!=0 && taskhandle!=-1) {
    // need to terminate the task abruptly
    // and remove the filter
    pinfilter_stop(taskhandle);
    _swix(Wimp_CloseDown,_INR(0,1),taskhandle,TASK);
  }
  taskhandle=0;

  if (flex_da!=0) {
    _swix(OS_DynamicArea,_INR(0,1),1,flex_da);
    flex_da=0;
  }

  buffer_final();

  path_unset();

//   obey_unset();
  _swix(OS_SetVarVal,_INR(0,4),"Alias$IconSprites",NULL,-1,0,0);

  messagetrans_final();

  return NULL;
}

void module_service(int service_number, _kernel_swi_regs *r, void *pw)
{
  IGNORE(pw);

  // resfs start/stop
  switch (service_number) {
  case 0x11: // Service_Memory
    if (r->r[2]==(int)Image__RO_Base) r->r[1]=0;
    break;

  case 0x27: // PostReset
    taskhandle=0;
    break;

  case 0x49: // StartWimp
    if (taskhandle==0) {
      r->r[0]=(int)"Desktop_Pinboard";
      r->r[1]=0;
      taskhandle=-1;
    }
    break;

  case 0x4a: //StartedWimp
    if (taskhandle==-1) taskhandle=0;
    break;

  case 0x4b: //StartFiler
    filer_taskhandle=r->r[0];
    break;

  case 0x53: //WimpCloseDown
    if (r->r[0]==0) tasklist_remove((unsigned int)r->r[2]);
    break;

#ifndef ROM_MODULE
  case 0x60: // ResourceFSStarting
    ((struct {void (*fn)(void *, void *, void *, void *);}*)&r->r[2])->fn(Resources(), 0, 0, (void *)r->r[3]);
    break;
#endif
  }
}

static int config_namecheck(void)
{
  // At bootup we allow *Pin and *AddTinyDir to add invalid file paths without erroring
  // The icons then get displayed as blanks

  return getenv("Pinboard$NoPathCheck")==NULL;
}


static const char *readargs[]={NULL, // desktop_pinboard
                               "path", // addtinydir
                               "path", // removetinydir
                               "Grid=G/S,IconiseToIconBar=ITIB/S", //pinboard
                               "path/A,X/E/A,Y/E/A,Locked=L/S", //pin
                               "path,Centre=C/S,Tile=T/S,Scale=S/S,N=NoRecache/S,Remove=R/S,Colour=Col/E,TextColour=TC/E,OutlineTextColour=OTC/E,BlendedText=BT/S,CacheJPEG=CJ/S,FullScreen=F/S", // backdrop
                               "OutlineTextColour=OTC/E,BlendedText=BT/S,CacheJPEG=CJ/S,FullScreen=F/S", // backdrop2
                               "Grid=G/S,IconiseToIconBar=ITIB/S,IconiseToTopLeft=ITTL/S,IconiseToBottomLeft=ITBL/S,IconiseToTopRight=ITTR/S,IconiseToBottomRight=ITBR/S,IconiseStackVertical=ISV/S,TidyToTopLeft=TTTL/S,TidyToBottomLeft=TTBL/S,TidyToTopRight=TTTR/S,TidyToBottomRight=TTBR/S,TidyStackVertical=TSV/S,AutoSave=AS/S,ConfirmSave=CS/S,SmallIcons=SI/S,LargeIcons=LI/S,OverwriteRename=OR/S,OverwriteConfirm=OC/S,DisableAdjust=DA/S", //pinboardoptions
                               "CarouselTime=CT/E,AutoSave=AS/K,IconSize=IS/K,Overwrite=O/K,DisableAdjust=DA/S,EnableAdjust=EA/S,DClickHold=DCH/E,MenuInfo=MI/S,DisableMenuInfo=DMI/S,ConfirmDelete=CD/K,IconbarOpacity=IO/E", //pinboard2options
                               "path/A", // pinboard_iconsprites
                               "path/A,X/E/A,Y/E/A,Locked=L/S", //xpin
                               "path/A", // xaddtinydir
                               "Font/K,File/K,Relocate/S", // pinboardstickies
                               "On/S,Off/S,TopLeft=TL/S,TopCentre=TC/S,TopRight=TR/S,Left=L/S,Centre=C/S,Right=R/S,BottomLeft=BL/S,BottomCentre=BC/S,BottomRight=BR/S,path,XOffset=X/E,YOffset=Y/E,XScale=XS/K,YScale=YS/K", // pinboardwatermark
                               "path/A,Lock=L/S,Unlock=U/S,Alias=A/K", //pinboardmodifypin

};

static int get_int(char *offset)
{
  if (offset==0 || offset==(char *)0x7fffffff) return 0;

  if (*offset!=0) return 0;

  return (*(offset+1)) | (*(offset+2)<<8) | (*(offset+3)<<16) | (*(offset+4)<<24);
}

static int get_scale(char *ptr)
{
  // accept input as a decimal and convert to fixed point format
  int value;
  char *endptr;

  value=(int)strtol(ptr,&endptr,10) * 65536;

  if (*endptr=='.') {
    // try to add a decimal part
    int divisor=1;
    int decimal=0;

    while (isdigit(*(++endptr)) && divisor<10000) {
      divisor*=10;
      decimal=(decimal*10)+(*endptr-'0');
    }

    value+=(decimal * 65536) / divisor;
  }

  return value;
}

_kernel_oserror *module_commands(const char *arg_string, int argc, int cmd_no, void *pw)
{
  _kernel_oserror *e=NULL;
  int optbuffer[64];

  buffer_entry *newentry;
  char *sourcepath;
  int pathlength;

  IGNORE(argc);
  IGNORE(pw);

  if (readargs[cmd_no]) {
    e=_swix(OS_ReadArgs,_INR(0,3),readargs[cmd_no],arg_string,optbuffer,256);
    if (e) return e;
  }

  switch (cmd_no) {
  case CMD_Desktop_Pinboard: //Desktop_Pinboard
    switch (taskhandle) {
    case -1:
      _swix(Wimp_SlotSize,_INR(0,1),96*1024,-1);
      _swix(OS_Module,_INR(0,2),2,"Pinboard",0);
      break;

    case 0:
      e=(_kernel_oserror *)"\000\000\000\000Desktop";
      return _swix(MessageTrans_ErrorLookup,_INR(0,3),e,messagetrans,0,0);

    default:
      e=(_kernel_oserror *)"\000\000\000\000TaskRun";
      return _swix(MessageTrans_ErrorLookup,_INR(0,3),e,messagetrans,0,0);
    }
    break;

  case CMD_AddTinyDir: //AddTinyDir
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.add_tinydir.path,pathlength);
      if (config_namecheck()) {
        e=path_check_exists(newentry->data.add_tinydir.path);
        if (e) {
          newentry->hdr.type=BUFFER_INVALID;
          return e;
        }
        newentry->hdr.type=BUFFER_ADD_TINYDIR;
      } else {
        newentry->hdr.type=BUFFER_ADD_XTINYDIR;
      }
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1,&newentry);
      if (e) return e;
      newentry->data.add_tinydir.path[0]=0;
      newentry->hdr.type=BUFFER_ADD_TINYDIR;
    }
    pollword|=1;
    break;

  case CMD_RemoveTinyDir: //RemoveTinyDir
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.remove_tinydir.path,pathlength);
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1,&newentry);
      if (e) return e;
      newentry->data.remove_tinydir.path[0]=0;
    }
    newentry->hdr.type=BUFFER_REMOVE_TINYDIR;
    pollword|=1;
    break;

  case CMD_Pinboard: // Pinboard
    e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_clear_pinboard),&newentry);
    if (e) return e;
    newentry->hdr.type=BUFFER_CLEAR_PINBOARD;
    newentry->data.clear_pinboard.snap_to_grid=optbuffer[0]!=0;
    newentry->data.clear_pinboard.iconise_to_iconbar=optbuffer[1]!=0;
    pollword|=1;
    break;

  case CMD_Pin: //pin
    sourcepath=(char *)optbuffer[0];
    e=path_length(sourcepath,&pathlength);
    if (e) return e;
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_add_pin),&newentry);
    if (e) return e;
    path_expand(sourcepath,newentry->data.add_pin.path,pathlength);

    if (config_namecheck()) {
      e=path_check_exists(newentry->data.add_pin.path);
      if (e) {
        // unable to do, so return the error
        newentry->hdr.type=BUFFER_INVALID;
        return e;
      }
      newentry->hdr.type=BUFFER_ADD_PIN;
    } else {
      newentry->hdr.type=BUFFER_ADD_XPIN;
    }

    newentry->data.add_pin.x=get_int((char *)optbuffer[1]);
    newentry->data.add_pin.y=get_int((char *)optbuffer[2]);
    newentry->data.add_pin.locked=optbuffer[3]!=0;
    pollword|=1;
    break;

  case CMD_BackDrop: //backdrop
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_set_backdrop),&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.set_backdrop.path,pathlength);
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1+sizeof(_buffer_set_backdrop),&newentry);
      if (e) return e;
      newentry->data.set_backdrop.path[0]=0;
    }

    newentry->hdr.type=BUFFER_SET_BACKDROP;
    newentry->data.set_backdrop.style=BD_CENTRED;
    //if (optbuffer[1]) newentry->data.set_backdrop.style=BD_CENTRED;
    if (optbuffer[2]) newentry->data.set_backdrop.style=BD_TILED;
    if (optbuffer[3]) newentry->data.set_backdrop.style=BD_SCALED;
    if (optbuffer[11]) newentry->data.set_backdrop.style=BD_FULLSCREEN;
    newentry->data.set_backdrop.remove=optbuffer[5]!=0;
    newentry->data.set_backdrop.colour=optbuffer[6]?get_int((char *)optbuffer[6]):-1;
    newentry->data.set_backdrop.text_colour=optbuffer[7]?get_int((char *)optbuffer[7]):-1;

    // text render type
    newentry->data.set_backdrop.render_type=TEXT_TYPE_DEFAULT;
    if (optbuffer[9]) newentry->data.set_backdrop.render_type=TEXT_TYPE_BLEND;
    if (optbuffer[8]) {
      newentry->data.set_backdrop.render_type=TEXT_TYPE_OUTLINE;
      newentry->data.set_backdrop.outline_colour=get_int((char *)optbuffer[8]);
    }

    // cache jpeg option
    newentry->data.set_backdrop.cacheJPEG=optbuffer[10]!=0;

    // anything from BackDrop2?  These override *BackDrop for one attempt only.
    if (backdrop_otc_pending!=-1) {
      newentry->data.set_backdrop.render_type=TEXT_TYPE_OUTLINE;
      newentry->data.set_backdrop.outline_colour=backdrop_otc_pending;
      backdrop_otc_pending=-1;
    }

    if (backdrop_blend_pending!=-1) {
      newentry->data.set_backdrop.render_type=TEXT_TYPE_BLEND;
      backdrop_blend_pending=-1;
    }

    if (backdrop_cache_pending!=-1) {
      newentry->data.set_backdrop.cacheJPEG=backdrop_cache_pending;
      backdrop_cache_pending=-1;
    }

    if (backdrop_fullscreen_pending!=-1) {
      newentry->data.set_backdrop.style=BD_FULLSCREEN;
      backdrop_fullscreen_pending=-1;
    }

    pollword|=1;
    break;

  case CMD_BackDrop2: //backdrop2 - extra parameters for the next backdrop call
    // some older things don't like the extra parameters, so we have split them off.
    // you need to issue this command first, then *BackDrop as normal (not the other way round)

    if (optbuffer[0]) backdrop_otc_pending=get_int((char *)optbuffer[0]); else backdrop_otc_pending=-1;
    if (optbuffer[1]) backdrop_blend_pending=1; else backdrop_blend_pending=-1;
    if (optbuffer[2]) backdrop_cache_pending=1; else backdrop_cache_pending=-1;
    if (optbuffer[3]) backdrop_fullscreen_pending=1; else backdrop_fullscreen_pending=-1;
    break;

  case CMD_PinboardOptions: //pinboardoptions
    {
      e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_options),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_SET_OPTIONS;
      newentry->data.set_options.snap_to_grid=optbuffer[0]!=0;
      newentry->data.set_options.iconise_corner=ICONIZE_TO_BUTTON;
      if (optbuffer[1]) newentry->data.set_options.iconise_corner=ICONIZE_TO_ICONBAR;
      if (optbuffer[2]) newentry->data.set_options.iconise_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[3]) newentry->data.set_options.iconise_corner=ICONIZE_TO_BOTTOMLEFT;
      if (optbuffer[4]) newentry->data.set_options.iconise_corner=ICONIZE_TO_TOPRIGHT;
      if (optbuffer[5]) newentry->data.set_options.iconise_corner=ICONIZE_TO_BOTTOMRIGHT;
      newentry->data.set_options.iconise_stack=optbuffer[6]?ICONIZE_VERTICAL:ICONIZE_HORIZONTAL;
      newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[7]) newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[8]) newentry->data.set_options.tidy_corner=ICONIZE_TO_BOTTOMLEFT;
      if (optbuffer[9]) newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPRIGHT;
      if (optbuffer[10]) newentry->data.set_options.tidy_corner=ICONIZE_TO_BOTTOMRIGHT;

      newentry->data.set_options.tidy_stack=optbuffer[11]?ICONIZE_VERTICAL:ICONIZE_HORIZONTAL;

      /* extra options will soon be moved fully to Pinboard2Options */
      newentry->data.set_options.autosave=AUTOSAVE_NONE;
      if (optbuffer[12]) newentry->data.set_options.autosave=AUTOSAVE_AUTO;
      if (optbuffer[13]) newentry->data.set_options.autosave=AUTOSAVE_CONFIRM;
      newentry->data.set_options.iconsize=ICONSIZE_NORMAL;
      if (optbuffer[14]) newentry->data.set_options.iconsize=ICONSIZE_SMALL;
      if (optbuffer[15]) newentry->data.set_options.iconsize=ICONSIZE_DOUBLE;

      newentry->data.set_options.overwrite=OVERWRITE_ALWAYS;
      if (optbuffer[16]) newentry->data.set_options.overwrite=OVERWRITE_RENAME;
      if (optbuffer[17]) newentry->data.set_options.overwrite=OVERWRITE_CONFIRM;

      newentry->data.set_options.disable_adjust=optbuffer[18]!=0;
      pollword|=1;
    }
    break;

  case CMD_Pinboard2Options:
    {
      e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_options2),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_SET_OPTIONS2;
      newentry->data.set_options2.carousel_time=optbuffer[0] ? get_int((char *)optbuffer[0]) : -1;

      newentry->data.set_options2.autosave=AUTOSAVE_UNSPEC;
      if (optbuffer[1]) {
        if (strcasecmp((char *)optbuffer[1],"off")==0) newentry->data.set_options2.autosave=AUTOSAVE_NONE;
        else if (strcasecmp((char *)optbuffer[1],"on")==0) newentry->data.set_options2.autosave=AUTOSAVE_AUTO;
        else if (strcasecmp((char *)optbuffer[1],"confirm")==0) newentry->data.set_options2.autosave=AUTOSAVE_CONFIRM;
      }

      newentry->data.set_options2.iconsize=ICONSIZE_UNSPEC;
      if (optbuffer[2]) {
        if (strcasecmp((char *)optbuffer[2],"normal")==0) newentry->data.set_options2.iconsize=ICONSIZE_NORMAL;
        else if (strcasecmp((char *)optbuffer[2],"small")==0) newentry->data.set_options2.iconsize=ICONSIZE_SMALL;
        else if (strcasecmp((char *)optbuffer[2],"large")==0) newentry->data.set_options2.iconsize=ICONSIZE_DOUBLE;
      }

      newentry->data.set_options2.overwrite=OVERWRITE_UNSPEC;
      if (optbuffer[3]) {
        if (strcasecmp((char *)optbuffer[3],"always")==0) newentry->data.set_options2.overwrite=OVERWRITE_ALWAYS;
        else if (strcasecmp((char *)optbuffer[3],"rename")==0) newentry->data.set_options2.overwrite=OVERWRITE_RENAME;
        else if (strcasecmp((char *)optbuffer[3],"confirm")==0) newentry->data.set_options2.overwrite=OVERWRITE_CONFIRM;
      }

      newentry->data.set_options2.disable_adjust=-1;
      if (optbuffer[4]) newentry->data.set_options2.disable_adjust=1;
      if (optbuffer[5]) newentry->data.set_options2.disable_adjust=0;

      if (optbuffer[6]) newentry->data.set_options2.dclick_hold=get_int((char *)optbuffer[6]); else newentry->data.set_options2.dclick_hold=-1;

      newentry->data.set_options2.menu_info=-1;
      if (optbuffer[7]) newentry->data.set_options2.menu_info=1;
      if (optbuffer[8]) newentry->data.set_options2.menu_info=0;

      newentry->data.set_options2.confirm_delete=-1;
      if (optbuffer[9]) {
        if (strcasecmp((char *)optbuffer[9],"off")==0) newentry->data.set_options2.confirm_delete=0;
        else if (strcasecmp((char *)optbuffer[9],"on")==0) newentry->data.set_options2.confirm_delete=1;
      }
      newentry->data.set_options2.iconbar_opacity=-1;
      if (optbuffer[10]) {
        newentry->data.set_options2.iconbar_opacity=get_int((char *)optbuffer[10]);
        if (newentry->data.set_options2.iconbar_opacity<0) newentry->data.set_options2.iconbar_opacity=0;
        if (newentry->data.set_options2.iconbar_opacity>100) newentry->data.set_options2.iconbar_opacity=100;
      }
      pollword|=1;
    }
    break;

  case CMD_Pinboard_IconSprites: // pinboard_iconsprites
    {
      //int len;
      char *cmd,*dest=NULL;
      cmd=(char *)malloc(16+strlen((char *)optbuffer[0]));
      if (!cmd) return NULL; // don't error on fail

      // we need to expand any variables.  If the path is <Obey$Dir>.!Sprites, Obey$Dir may be different
      // by the time we get to read the file later...
      e=path_canon((char *)optbuffer[0],&dest);

      if (!e && dest) {
        e=buffer_push(sizeof(_buffer_hdr)+1+strlen(dest),&newentry);
        if (!e) {
          newentry->hdr.type=BUFFER_ICONSPRITES;
          strcpy(newentry->data.iconsprites.filename,dest);
          pollword|=1;
        }
      }
      if (dest) free(dest);

      // now pass it on to the Wimp properly and this time return any errors
      sprintf(cmd,"%%IconSprites %s",(char *)optbuffer[0]);
      e=_swix(OS_CLI,_IN(0),cmd);
      free(cmd);
    }
    break;

  case CMD_XPin: // xpin
    sourcepath=(char *)optbuffer[0];
    pathlength=1+strlen(sourcepath);
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_add_pin),&newentry);
    if (e) return e; // this one is fatal
    strcpy(newentry->data.add_pin.path,sourcepath);

    newentry->hdr.type=BUFFER_ADD_XPIN;
    newentry->data.add_pin.x=get_int((char *)optbuffer[1]);
    newentry->data.add_pin.y=get_int((char *)optbuffer[2]);
    newentry->data.add_pin.locked=optbuffer[3]!=0;
    pollword|=1;
    break;

  case CMD_XAddTinyDir: // XAddTinyDir
    sourcepath=(char *)optbuffer[0];
    pathlength=1+strlen(sourcepath);
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
    if (e) return e; // this one is fatal
    strcpy(newentry->data.add_tinydir.path,sourcepath);
    newentry->hdr.type=BUFFER_ADD_XTINYDIR;
    pollword|=1;
    break;

  case CMD_PinboardStickies:
    {
      char *font=(char *)optbuffer[0];
      sourcepath=(char *)optbuffer[1];
      if (font==NULL) font="";
      if (sourcepath==NULL) sourcepath="";
      e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_sticky)+1+strlen(font)+1+strlen(sourcepath),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_SET_STICKY;
      strcpy(newentry->data.set_sticky.data,font);
      strcpy(newentry->data.set_sticky.data+1+strlen(font),sourcepath);
      newentry->data.set_sticky.relocate=optbuffer[2]!=0;
      pollword|=1;
      break;
    }

  case CMD_PinboardWatermark:
    sourcepath=(char *)optbuffer[11];
    if (sourcepath==NULL) sourcepath="";
    e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_watermark)+1+strlen(sourcepath),&newentry);
    if (e) return e;
    newentry->hdr.type=BUFFER_SET_WATERMARK;
    if (optbuffer[0]) newentry->data.watermark.enable=1; else newentry->data.watermark.enable=0;
    newentry->data.watermark.location=WATERMARK_BOTTOMRIGHT;
    if (optbuffer[2]) newentry->data.watermark.location=WATERMARK_TOPLEFT;
    if (optbuffer[3]) newentry->data.watermark.location=WATERMARK_TOPCENTRE;
    if (optbuffer[4]) newentry->data.watermark.location=WATERMARK_TOPRIGHT;
    if (optbuffer[5]) newentry->data.watermark.location=WATERMARK_LEFT;
    if (optbuffer[6]) newentry->data.watermark.location=WATERMARK_CENTRE;
    if (optbuffer[7]) newentry->data.watermark.location=WATERMARK_RIGHT;
    if (optbuffer[8]) newentry->data.watermark.location=WATERMARK_BOTTOMLEFT;
    if (optbuffer[9]) newentry->data.watermark.location=WATERMARK_BOTTOMCENTRE;
    if (optbuffer[10]) newentry->data.watermark.location=WATERMARK_BOTTOMRIGHT;
    strcpy(newentry->data.watermark.filename,sourcepath);
    if (optbuffer[12]) newentry->data.watermark.x_offset=get_int((char *)optbuffer[12]); else newentry->data.watermark.x_offset=0;
    if (optbuffer[13]) newentry->data.watermark.y_offset=get_int((char *)optbuffer[13]); else newentry->data.watermark.y_offset=0;
    if (optbuffer[14]) newentry->data.watermark.x_scale=get_scale((char *)optbuffer[14]); else newentry->data.watermark.x_scale=65536;
    if (optbuffer[15]) newentry->data.watermark.y_scale=get_scale((char *)optbuffer[15]); else newentry->data.watermark.y_scale=65536;

    pollword|=1;

    break;

    case CMD_PinboardModifyPin:
    {
      int alias_length=0;
      if (optbuffer[3]) alias_length=1+strlen((char *)optbuffer[3]);

      sourcepath=(char *)optbuffer[0];
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+alias_length+1+sizeof(_buffer_tweak_pin),&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.tweak_pin.path,pathlength);

      newentry->hdr.type=BUFFER_TWEAK_PIN;
      newentry->data.tweak_pin.lock=optbuffer[1]?1:0;
      newentry->data.tweak_pin.unlock=optbuffer[2]?1:0;
      newentry->data.tweak_pin.alias=optbuffer[3]?1:0;
      if (alias_length) {
        debugf("opt offset %d\n",(int)newentry->data.tweak_pin.path+pathlength+1-(int)newentry);
        strcpy(newentry->data.tweak_pin.path+1+strlen(newentry->data.tweak_pin.path),(char *)optbuffer[3]);
//        edump(newentry);//debugf("alias length %d '%s'\n",alias_length,newentry->data.tweak_pin.path+pathlength+1);
      }

      pollword|=1;
      break;
    }
  }

  return e;
}
