//
// Copyright (C) 1999-2002 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <WScom.h>
#include <unistd.h>

extern "C" {
#include <png.h>
extern long PngReadFileToPixmap(Display* display,Window window,GC gc,
             char* filename,Pixmap* pixmap,Pixmap* pixmap_mask,long* w,long* h);
extern long PngWriteBufToFp(FILE* fp, char* buf,long width,long height);
};

void png_cexcept_error(png_structp png_ptr, png_const_charp msg){
   if(png_ptr){
     fprintf(stderr, "png file read error: %s\n", msg);
   }
}

long PngReadFileToPixmap(Display* display,Window window,GC gc,char* filename,Pixmap* pixmap,Pixmap* pixmap_mask,long* w,long* h){
    *pixmap_mask = (Pixmap)0;

    int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;
    int start_shift, msb_flag;
    unsigned int start_mask, udat;
    XWindowAttributes win_attr;

    red_mask = green_mask = blue_mask = 0;
    red_shift = green_shift = blue_shift = 0;

    FILE* ifile = fopen(filename,"r");
    if (ifile == NULL){
      return -1;
    }
    long display_depth = XDefaultDepth(display,XDefaultScreen(display));

    png_byte            sig[8];
    fread(sig, 1, 8, ifile);
    if (!png_check_sig(sig, 8)){
      fclose(ifile);
      return -1;
    }
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
      (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
    if (png_ptr == NULL){
      fclose(ifile);
      return -1;
    } 
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL){
      png_destroy_read_struct(&png_ptr, NULL, NULL);
      fclose(ifile);
      return -1;
    }

    png_init_io(png_ptr, ifile);
    png_set_sig_bytes(png_ptr, 8);
    png_read_info(png_ptr, info_ptr);
    png_uint_32 png_width;
    png_uint_32 png_height;
    int png_depth;
    int png_color_type;
    png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &png_depth,
            &png_color_type, NULL, NULL, NULL);
    if (png_depth == 16){
      png_set_strip_16(png_ptr);
    }
    png_uint_32 png_row_bytes = png_get_rowbytes(png_ptr, info_ptr);
    png_uint_32 png_channels = png_get_channels(png_ptr, info_ptr);

//printf("png_row_bytes=%d channels=%d  \n",png_row_bytes,png_channels);
//printf("depth=%d \n",png_depth);

    if (png_depth < 8){
      if (png_color_type == PNG_COLOR_TYPE_GRAY ){
        png_set_gray_1_2_4_to_8(png_ptr);
        png_row_bytes = png_width * 3;
        png_channels = 3;
      }else if (png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
        png_set_gray_1_2_4_to_8(png_ptr);
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }else{
        png_set_expand(png_ptr);
        png_row_bytes = png_width * 3;
        png_channels = 3;
      }
    }
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
//printf("PNG_INFO_tRNS\n");
      png_set_expand(png_ptr);
      png_row_bytes = png_width;
      if (png_channels == 1){
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }
      if (png_channels == 3){
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }
    }
    if (png_color_type == PNG_COLOR_TYPE_GRAY ||
        png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
      png_set_gray_to_rgb(png_ptr);
/*      png_row_bytes = png_width;*/
      if (png_channels < 2){
        png_row_bytes = png_width;
      }
    }

    if (png_color_type == PNG_COLOR_TYPE_PALETTE){
      png_set_palette_to_rgb(png_ptr);
      png_row_bytes = png_width * 3;
      png_channels = 3;
    }
    if (png_channels == 2){
      png_row_bytes = png_width * 4;
      png_channels = 4;
    }
    if (png_channels == 1){
      png_row_bytes = png_width * 3;
      png_channels = 3;
    }
    long rwidth = png_width;
    long rheight = png_height;
    long components = png_channels;

    unsigned char* buf = NULL;
//    if (components == 2){
//      buf = new unsigned char[png_row_bytes * png_height * sizeof(png_byte)*2];
//    }else{
      buf = new unsigned char[png_row_bytes * png_height * sizeof(png_byte)];
//    }
    if (buf == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
      fclose(ifile);
      return -1;
    }
    png_byte** png_row_ptrs = new png_bytep[png_height];
    if (png_row_ptrs == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
      fclose(ifile);
      return -1;
    }
    int i;
    for(i = 0; i < (int)png_height; i++){
//      if (components == 2){
//        png_row_ptrs[i] = (png_byte*)(buf + i * png_row_bytes*2);
//      }else{
        png_row_ptrs[i] = (png_byte*)(buf + i * png_row_bytes);
//      }
    }
    png_read_image(png_ptr, png_row_ptrs);
    png_read_end(png_ptr,NULL);
    delete png_row_ptrs;

    long vwidth = *w;
    long vheight = *h;
    long stretched =0;
    if (*w == 0 || *h == 0){
      *w = rwidth;
      *h = rheight;
    }else{
      if ((long)((double)rwidth * vheight/vwidth) < rheight){
        *w = (long)((double)vheight * rwidth/rheight);
      }else{
        *h = (long)((double)vwidth * rheight/rwidth);
      }
      stretched = 1;
    }
    vwidth = *w;
    vheight = *h;



    XImage* image = 0;
    XImage* image_mask = 0;
    Visual* visual = XDefaultVisual(display,XDefaultScreen(display));
    if (display_depth >16){
      image = XCreateImage(display,visual, display_depth,
                           ZPixmap,0,0,vwidth,vheight,32,0);
    }else
    if (display_depth >8){
      image = XCreateImage(display,visual, display_depth,
                           ZPixmap,0,0,vwidth,vheight,16,0);
    }else{
      image = XCreateImage(display,visual, display_depth,
                           ZPixmap,0,0,vwidth,vheight,8,0);
    }
    if (components == 4){
      image_mask = XCreateImage(display,visual, 1,
                           ZPixmap,0,0,vwidth,vheight,8,0);
    }
    msb_flag = (ImageByteOrder(display) == MSBFirst)?1:0;

    if (XGetWindowAttributes(display,
                     RootWindow(display, DefaultScreen(display)),
                     &win_attr) == 0) {
        fclose(ifile);
                 return -1;
    }
    //
    if ((win_attr.depth == 24) || (win_attr.depth == 16)) {
      if (win_attr.depth == 24) {
        start_shift = 24;
        start_mask = 0x80000000;
      }else{
        start_shift = 8;
        start_mask = 0x8000;
      }
      red_mask = win_attr.visual->red_mask;
      red_shift = start_shift;
      unsigned int n = start_mask;
      while (!(n & red_mask)) {
        n >>= 1;
        red_shift--;
      }
      green_mask = win_attr.visual->green_mask;
      green_shift = start_shift;
      n = start_mask;
      while (!(n & green_mask)) {
        n >>= 1;
        green_shift--;
      }
      blue_mask = win_attr.visual->blue_mask;
      blue_shift = start_shift;
      n = start_mask;
      while (!(n & blue_mask)) {
        n >>= 1;
        blue_shift--;
      }
    }

    char* data1 = new char[image->bytes_per_line * vheight];
    char* data2 = NULL;
    if (components == 4){
      data2 = new char[image_mask->bytes_per_line * vheight];
      memset(data2,0,sizeof(char)* image_mask->bytes_per_line * vheight);
    }
    unsigned char r,g,b;
    long ptr = 0;
    long ptr2 = 0;
    long ptr3 = 0;
    long j;
    if (image->bits_per_pixel ==32){
      if (components == 3 || components == 4){
        for(i=0; i<vheight; i++){
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                   + (long)((double)j*rwidth/vwidth);
              ptr *= 3;
              if (components == 4){
                ptr *= 4;
              }
            }
            r = buf[ptr++];
            g = buf[ptr++];
            b = buf[ptr++];
            if (components == 4){
              if( buf[ptr++]){
                ((unsigned char*)data2)[ptr3] |= (1<< (j%8));
              }
              if (j%8 == 7){
                ptr3++;
              }else if (j == vwidth -1){
                ptr3++;
              }
            }
            udat = 0;
            if (red_shift >= 0){
              udat |= (((int)r << red_shift) & red_mask);
            }else{
              udat |= (((int)r >> (-red_shift)) & red_mask);
            }
            if (green_shift >= 0){
              udat |= (((int)g << green_shift) & green_mask);
            }else{
              udat |= (((int)g >> (-green_shift)) & green_mask);
            }
            if (blue_shift >= 0){
              udat |= (((int)b << blue_shift) & blue_mask);
            }else{
              udat |= (((int)b >> (-blue_shift)) & blue_mask);
            }
            if (msb_flag){
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff000000)>>24;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff0000)>>16;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff00)>>8;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
            }else{
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff00)>>8;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff0000)>>16;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff000000)>>24;
            }
          }
          ptr = i * png_row_bytes;
        }
      }else{
        for(i=0; i<vheight; i++){
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                   + (long)((double)j*rwidth/vwidth);
            }
            r = buf[ptr];
            g = buf[ptr];
            b = buf[ptr++];
            if (msb_flag){
              ((unsigned char*)data1)[ptr2++] =  0;
              ((unsigned char*)data1)[ptr2++] =  b;
              ((unsigned char*)data1)[ptr2++] =  g;
              ((unsigned char*)data1)[ptr2++] =  r;
            }else{
              ((unsigned char*)data1)[ptr2++] =  r;
              ((unsigned char*)data1)[ptr2++] =  g;
              ((unsigned char*)data1)[ptr2++] =  b;
              ((unsigned char*)data1)[ptr2++] =  0;
            }
          }
          ptr = i * png_row_bytes;
        }
      }
    }else if (image->bits_per_pixel == 24){
      if (components == 3 || components == 4){
        for(i=0; i<vheight; i++){
          ptr2 = i * image->bytes_per_line;
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                   + (long)((double)j*rwidth/vwidth);
              ptr *= 3;
              if (components == 4){
                ptr *= 4;
              }
            }
            r = buf[ptr++];
            g = buf[ptr++];
            b = buf[ptr++];
            if (components == 4){
              if( buf[ptr++]){
                ((unsigned char*)data2)[ptr3] |= (1<< (j%8));
              }
              if (j%8 == 7){
                ptr3++;
              }else if (j == vwidth -1){
                ptr3++;
              }
            }

            udat = 0;
            if (red_shift >= 0){
              udat |= (((int)r << red_shift) & red_mask);
            }else{
              udat |= (((int)r >> (-red_shift)) & red_mask);
            }
            if (green_shift >= 0){
              udat |= (((int)g << green_shift) & green_mask);
            }else{
              udat |= (((int)g >> (-green_shift)) & green_mask);
            }
            if (blue_shift >= 0){
              udat |= (((int)b << blue_shift) & blue_mask);
            }else{
              udat |= (((int)b >> (-blue_shift)) & blue_mask);
            }

            if (msb_flag){
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff0000)>>16;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff00)>>8;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
            }else{
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff00)>>8;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff0000)>>16;
            }
          }
          ptr = i * png_row_bytes;
        }
      }else{
        for(i=0; i<vheight; i++){
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                   + (long)((double)j*rwidth/vwidth);
            }
            r = buf[ptr];
            g = buf[ptr];
            b = buf[ptr++];
            if (msb_flag){
              ((unsigned char*)data1)[ptr2++] =  b;
              ((unsigned char*)data1)[ptr2++] =  g;
              ((unsigned char*)data1)[ptr2++] =  r;
            }else{
              ((unsigned char*)data1)[ptr2++] =  r;
              ((unsigned char*)data1)[ptr2++] =  g;
              ((unsigned char*)data1)[ptr2++] =  b;
            }
          }
        }
      }
    }else if (image->bits_per_pixel ==16){
      if (components == 3 || components == 4){
        for(i=0; i<vheight; i++){
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight+0.5)*rwidth
                   + (long)((double)j*rwidth/vwidth + 0.5);
              ptr *= 3;
              if (components == 4){
                ptr *= 4;
              }
            }
            unsigned int rr = buf[ptr++];
            unsigned int gg = buf[ptr++];
            unsigned int bb = buf[ptr++];
            if (components == 4){
              if( buf[ptr++]){
                ((unsigned char*)data2)[ptr3] |= (1<< (j%8));
              }
              if (j%8 == 7){
                ptr3++;
              }else if (j == vwidth -1){
                ptr3++;
              }
            }

            udat = 0;
            if (red_shift >= 0){
              udat |= (((int)rr << red_shift) & red_mask);
            }else{
              udat |= (((int)rr >> (-red_shift)) & red_mask);
            }
            if (green_shift >= 0){
              udat |= (((int)gg << green_shift) & green_mask);
            }else{
              udat |= (((int)gg >> (-green_shift)) & green_mask);
            }
            if (blue_shift >= 0){
              udat |= (((int)bb << blue_shift) & blue_mask);
            }else{
              udat |= (((int)bb >> (-blue_shift)) & blue_mask);
            }
            if (msb_flag){
              ((unsigned char*)data1)[ptr2++] = (udat >> 8) & 0xff;
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
            }else{
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff);
              ((unsigned char*)data1)[ptr2++] = (udat & 0xff00)>>8;
            }
          }
          ptr = i * png_row_bytes;
        }
      }else{
        for(i=0; i<vheight; i++){
          for(j=0; j<vwidth; j++){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                   + (long)((double)j*rwidth/vwidth);
            }
            r = buf[ptr]>>3;
            g = buf[ptr]>>2;
            b = buf[ptr++]>>3;
            ((short*)data1)[ptr2++] =  r <<11 | g<<5 | b;
          }
          ptr = i * png_row_bytes;
        }
      }
    }else if (image->bits_per_pixel == 8){
//printf("components=%d\n",components);
      XColor col[5*5*5];
      Colormap cm = DefaultColormap(display,DefaultScreen(display));
      long k;
      long cnt=0;
      for(i=0; i<5; i++){
        for(j=0; j<5; j++){
          for(k=0; k<5; k++){
            if (i != 4){
              col[cnt].red = i*64*256;
            }else{
              col[cnt].red = 0xffff;
            }
            if (j != 4){
              col[cnt].green = j*64*256;
            }else{
              col[cnt].green = 0xffff;
            }
            if (k != 4){
              col[cnt].blue = k*64*256;
            }else{
              col[cnt].blue = 0xffff;
            }
            XAllocColor(display,cm,&(col[cnt]));
            cnt++;
          }
        }
      }

      long colptr = 0;
      long rr = 0,gg = 0,bb = 0;
      long tr1,tg1,tb1;
      signed char dr1,dg1,db1;
      for(i=0; i<vheight; i++){
        dr1 = 0;
        dg1 = 0;
        db1 = 0;
        for(j=0; j<vwidth; j++){

          if (components == 3 || components== 4){
            if (stretched != 0){
              ptr = (long)((double)i*rheight/vheight)*rwidth
                     + (long)((double)j*rwidth/vwidth);
              ptr *= 3;
              if (components == 4){
                ptr *= 4;
              }
            }
            tr1 = buf[ptr++];
            tg1 = buf[ptr++];
            tb1 = buf[ptr++];
            if (components == 4){
              if( buf[ptr++]){
                ((unsigned char*)data2)[ptr3] |= (1<< (j%8));
              }
              if (j%8 == 7){
                ptr3++;
              }else if (j == vwidth -1){
                ptr3++;
              }
            }
          }else{
            ptr = (long)((double)i*rheight/vheight)*rwidth
                     + (long)((double)j*rwidth/vwidth);
            tr1 = buf[ptr];
            tg1 = buf[ptr];
            tb1 = buf[ptr++];
          }
          if ((0 < tr1 + dr1) && (tr1 + dr1) < 256){
            if ((-32 < dr1) && (dr1 < 32)){
              rr = (tr1 + dr1 + 31) & 0x1c0;
            }else if (dr1 > 31){
              rr = (tr1 + 63) & 0x1c0;
            }else{
              rr = tr1  & 0x1c0;
            }
          }else if (tr1 + dr1 > 255){
            rr = 0x100;
          }else{
            rr = 0;
          }
          dr1 += (tr1 - rr);
          if (0 < tg1 + dg1  &&tg1 + dg1 < 256){
            if (-32 < dg1 && dg1 < 32 ){
              gg = (tg1 + dg1 + 31) & 0x1c0;
            }else if (dg1 > 31){
              gg = (tg1 + 63) & 0x1c0;
            }else{
              gg = tg1  & 0x1c0;
            }
          }else if (tg1 + dg1 > 255){
            gg = 0x100;
          }else{
            gg = 0;
          }
          dg1 += (tg1 - gg);

          if (0 < tb1 + db1 && tb1 + db1 < 256){
            if (-32 < db1 && db1 < 32){
              bb = (tb1 + db1 + 31) & 0x1c0;
            }else if (db1 > 31){
              bb = (tb1 + 63) & 0x1c0;
            }else{
              bb = tb1 & 0x1c0;
            }
          }else if (tb1 + db1 > 255){
            bb = 0x100;
          }else{
            bb = 0;
          }
          db1 += (tb1 - bb);
          db1 += (tb1 - bb);
          colptr = (rr>>6)*5*5  + (gg>>6)*5 + (bb>>6);
          data1[ptr2++] =  col[colptr].pixel;
        }
        ptr = i * png_row_bytes;
      }
    }
    image->data = data1;
    delete buf;

    Pixmap pix = XCreatePixmap(display,window, vwidth,vheight,display_depth);
    *pixmap = pix;
    XPutImage(display,pix,gc,image,0,0,0,0,vwidth,vheight);

    if (image->data != NULL){
      delete image->data;
      image->data = NULL;
    }
    XDestroyImage(image);

    if (image_mask != NULL){
      image_mask->data = data2;
      GC gc2;
      XGCValues values;

      values.foreground = 1;
      values.background = 0;
      Pixmap pix = XCreatePixmap(display,window, vwidth,vheight,1);
      gc2 = XCreateGC(display, pix, GCForeground | GCBackground, &values);

      *pixmap_mask = pix;
      XPutImage(display,pix,gc2,image_mask,0,0,0,0,vwidth,vheight);
      if (image_mask->data != NULL){
        delete image_mask->data;
        image_mask->data = NULL;
      }
      XFreeGC(display,gc2);
      XDestroyImage(image_mask);
    }


    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    fclose(ifile);
    return 0;
}

void png_write_fn(png_structp png_ptr, png_bytep buf, png_size_t len) {
  FILE* fp = (FILE*)png_get_io_ptr(png_ptr);
  int ret = fwrite(buf, 1, len, fp);
//printf("ret=%d len=%d\n",ret,len);
  if (ret < (int)len) {
     png_error(png_ptr, "Write error");
  }
}
 
void png_flush_fn(png_structp png_ptr) {
}
 
long PngWriteBufToFp(FILE* fp, char* buf,long width,long height) {
  png_structp png_ptr = png_create_write_struct(
       PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    return WS_ERR;
  }
 
  if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_write_struct(&png_ptr, NULL);
    return WS_ERR;
  }
 
  png_infop info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    return WS_ERR;
  }
 
  png_set_write_fn(png_ptr, fp, png_write_fn, png_flush_fn);
 
  long color_bpp = 3;
 
  png_set_IHDR( png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
                PNG_FILTER_TYPE_DEFAULT);
 
  void** png_row_ptrs = (void**)png_malloc(png_ptr, sizeof(void*) * height);
  for (int i = 0; i < height; ++i) {
    png_row_ptrs[i] = png_malloc(png_ptr, color_bpp * width);
    memcpy(png_row_ptrs[i], buf, color_bpp * width);
    buf += width * color_bpp;      
  }

  png_set_rows(png_ptr, info_ptr, (png_bytepp)png_row_ptrs);
  info_ptr->valid |= PNG_INFO_IDAT;
  png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
 
  for (int i = 0; i < height; ++i) {
    png_free(png_ptr, png_row_ptrs[i]);
  }
  png_free(png_ptr, png_row_ptrs);
  png_destroy_write_struct(&png_ptr, &info_ptr);

  return WS_NO_ERR;
}
