コード例 #1
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int     roar_vs_volume_get    (roar_vs_t * vss, float * l, float * r, int * error) {
    struct roar_mixer_settings mixer;
    int channels;

    if ( vss == NULL || l == NULL || r == NULL ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &channels) == -1 ) {
        _seterrre();
        return -1;
    }

    if ( channels == 1 )
        mixer.mixer[1] = mixer.mixer[0];

    *l = mixer.mixer[0] / (float)mixer.scale;
    *r = mixer.mixer[1] / (float)mixer.scale;

    return 0;
}
コード例 #2
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
ssize_t roar_vs_position(roar_vs_t * vss, int backend, int * error) {
    struct roar_stream stream;
    struct roar_stream      out_stream;
    struct roar_stream_info out_info;
    size_t offset;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( roar_get_stream(vss->con, &stream, roar_stream_get_id(&(vss->stream))) == -1 ) {
        _seterrre();
        return -1;
    }

    switch (backend) {
    case ROAR_VS_BACKEND_NONE:
        return stream.pos;
        break;
    case ROAR_VS_BACKEND_FIRST:
        // _roar_vs_find_first_prim(vss);
        if ( vss->first_primid == -1 ) {
            _seterr(ROAR_ERROR_UNKNOWN);
            return -1;
        }

        roar_stream_new_by_id(&out_stream, vss->first_primid);

        if ( roar_stream_get_info(vss->con, &out_stream, &out_info) == -1 ) {
            _seterrre();
            return -1;
        }

        offset  = out_info.delay * vss->info.rate;
        offset /= 1000000;

        return stream.pos + offset;
        break;
    default:
        _seterr(ROAR_ERROR_NOTSUP);
        return -1;
        break;
    }

    _seterr(ROAR_ERROR_NOSYS);
    return -1;
}
コード例 #3
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
roar_mus_t roar_vs_latency(roar_vs_t * vss, int backend, int * error) {
    ssize_t pos  = roar_vs_position(vss, backend, error);
    ssize_t bps;  // byte per sample
    size_t  lioc; // local IO (byte) counter
    size_t  lpos; // local possition
    roar_mus_t  lag;

    _initerr();

    _ckvss(-1);

    if (pos == -1) {
        _seterrre();
        return 0;
    }

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return 0;
    }

    if ( vss->writec == 0 ) {
        lioc = vss->readc;
    } else {
        lioc = vss->writec;
    }

    bps = roar_info2samplesize(&(vss->info));

    if ( bps == -1 ) {
        _seterrre();
        return 0;
    }

    lpos = lioc / bps;

    lag = (roar_mus_t)lpos - (roar_mus_t)pos;

// we now have the lag in frames
// return value are ms
// so we need to multiply with 1s/ms and
// multiply by 1/rate

    lag *= 1000000; // 1s/ms
    lag /= vss->info.rate;

    if ( lag == 0 ) {
        _seterr(ROAR_ERROR_NONE);
    }

    return lag;
}
コード例 #4
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
ssize_t roar_vs_write(roar_vs_t * vss, const void * buf, size_t len, int * error) {
    ssize_t ret;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    ret = roar_vio_write(&(vss->vio), (void*)buf, len);

    if ( ret == -1 ) {
#ifdef EAGAIN
        if ( errno == EAGAIN )
            return 0;
#endif

#ifdef EWOULDBLOCK
        if ( errno == EWOULDBLOCK )
            return 0;
#endif

        _seterrre();
    } else {
        vss->writec += ret;
    }

    return ret;
}
コード例 #5
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int roar_vs_stream(roar_vs_t * vss, const struct roar_audio_info * info, int dir, int * error) {
    struct roar_stream_info sinfo;
    int ret;

    _ckvss(-1);

    if ( vss->flags & FLAG_STREAM ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( info != &(vss->info) )
        memcpy(&(vss->info), info, sizeof(struct roar_audio_info));

    ret = roar_vio_simple_new_stream_obj(&(vss->vio), vss->con, &(vss->stream),
                                         info->rate, info->channels, info->bits, info->codec,
                                         dir
                                        );

    if ( ret == -1 ) {
        _seterrre();
        return -1;
    }

    if ( roar_stream_get_info(vss->con, &(vss->stream), &sinfo) != -1 ) {
        vss->mixerid = sinfo.mixer;
        _roar_vs_find_first_prim(vss);
    }

    vss->flags |= FLAG_STREAM;

    return 0;
}
コード例 #6
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int     roar_vs_blocking (roar_vs_t * vss, int val, int * error) {
    int old = -1;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    old = vss->flags & FLAG_NONBLOCK ? ROAR_VS_FALSE : ROAR_VS_TRUE;

    _initerr();

    switch (val) {
    case ROAR_VS_TRUE:
        if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_BLOCK) == -1 ) {
            _seterrre();
            return -1;
        }
        vss->flags |= FLAG_NONBLOCK;
        vss->flags -= FLAG_NONBLOCK;
        return old;
        break;
    case ROAR_VS_FALSE:
        if ( roar_vio_nonblock(&(vss->vio), ROAR_SOCKET_NONBLOCK) == -1 ) {
            _seterrre();
            return -1;
        }
        vss->flags |= FLAG_NONBLOCK;
        return old;
        break;
    case ROAR_VS_TOGGLE:
        if ( old == ROAR_VS_TRUE ) {
            return roar_vs_blocking(vss, ROAR_VS_FALSE, error);
        } else {
            return roar_vs_blocking(vss, ROAR_VS_TRUE, error);
        }
        break;
    case ROAR_VS_ASK:
        return old;
        break;
    }

    _seterr(ROAR_ERROR_INVAL);
    return -1;
}
コード例 #7
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
struct roar_vio_calls  * roar_vs_vio_obj       (roar_vs_t * vss, int * error) {
    _ckvss(NULL);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return NULL;
    }

    return &(vss->vio);
}
コード例 #8
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
struct roar_stream     * roar_vs_stream_obj    (roar_vs_t * vss, int * error) {
    _ckvss(NULL);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return NULL;
    }

    return &(vss->stream);
}
コード例 #9
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
static int roar_vs_flag(roar_vs_t * vss, int flag, int val, int * error) {
    struct roar_stream_info info;
    int old = -1;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    if ( val != ROAR_VS_ASK )
        old = roar_vs_flag(vss, flag, ROAR_VS_ASK, error);

    _initerr();

    switch (val) {
    case ROAR_VS_TRUE:
    case ROAR_VS_FALSE:
        if ( roar_stream_set_flags(vss->con, &(vss->stream), flag,
                                   val == ROAR_VS_TRUE ? ROAR_SET_FLAG : ROAR_RESET_FLAG) == -1 ) {
            _seterrre();
            return -1;
        }
        return old;
        break;
    case ROAR_VS_TOGGLE:
        return roar_vs_flag(vss, flag, old == ROAR_VS_TRUE ? ROAR_VS_FALSE : ROAR_VS_TRUE, error);
        break;
    case ROAR_VS_ASK:
        if ( roar_stream_get_info(vss->con, &(vss->stream), &info) == -1 ) {
            _seterrre();
            return -1;
        }
        return info.flags & flag ? ROAR_VS_TRUE : ROAR_VS_FALSE;
        break;
    }

    _seterr(ROAR_ERROR_INVAL);
    return -1;
}
コード例 #10
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int     roar_vs_sync (roar_vs_t * vss, int wait, int * error) {
    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    if ( wait != ROAR_VS_NOWAIT ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( roar_vio_sync(&(vss->vio)) == -1 ) {
        _seterrre();
        return -1;
    }

    return 0;
}
コード例 #11
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int     roar_vs_role          (roar_vs_t * vss, int role, int * error) {
    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( roar_stream_set_role(vss->con, &(vss->stream), role) == -1 ) {
        _seterrre();
        return -1;
    }

    return 0;
}
コード例 #12
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int     roar_vs_meta          (roar_vs_t * vss, struct roar_keyval * kv, size_t len, int * error) {
    struct roar_meta meta;
    size_t i;
    int type;
    int ret = 0;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    meta.type   = ROAR_META_TYPE_NONE;
    meta.key[0] = 0;
    meta.value  = NULL;

    _initerr();

    if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_CLEAR, &meta) == -1 ) {
        _seterrre();
        ret = -1;
    }

    for (i = 0; i < len; i++) {
        type = roar_meta_inttype(kv[i].key);
        meta.type  = type;
        meta.value = kv[i].value;

        if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_ADD, &meta) == -1 ) {
            _seterrre();
            ret = -1;
        }
    }

    meta.type   = ROAR_META_TYPE_NONE;
    meta.key[0] = 0;
    meta.value  = NULL;
    if ( roar_stream_meta_set(vss->con, &(vss->stream), ROAR_META_MODE_FINALIZE, &meta) == -1 ) {
        _seterrre();
        ret = -1;
    }

    return ret;
}
コード例 #13
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
ssize_t roar_vs_read (roar_vs_t * vss,       void * buf, size_t len, int * error) {
    ssize_t ret;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    ret = roar_vio_read(&(vss->vio), buf, len);

    if ( ret == -1 ) {
        _seterrre();
    } else {
        vss->readc += ret;
    }

    return ret;
}
コード例 #14
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
int roar_vs_close(roar_vs_t * vss, int killit, int * error) {
    if ( killit != ROAR_VS_TRUE && killit != ROAR_VS_FALSE ) {
        _seterr(ROAR_ERROR_UNKNOWN);
        return -1;
    }

    _ckvss(-1);

    if ( vss->flags & FLAG_STREAM ) {
        if ( killit == ROAR_VS_TRUE ) {
            roar_kick(vss->con, ROAR_OT_STREAM, roar_stream_get_id(&(vss->stream)));
        }

        roar_vio_close(&(vss->vio));
    }

    if ( vss->con == &(vss->con_store) ) {
        roar_disconnect(vss->con);
    }

    roar_mm_free(vss);
    return 0;
}
コード例 #15
0
ファイル: vs.c プロジェクト: roaraudio/roaraudio
static int roar_vs_volume (roar_vs_t * vss, float * c, size_t channels, int * error) {
    struct roar_mixer_settings mixer;
    size_t i;
    register float s;
    int oldchannels;
    int handled;

    _ckvss(-1);

    if ( !(vss->flags & FLAG_STREAM) ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    if ( channels > ROAR_MAX_CHANNELS ) {
        _seterr(ROAR_ERROR_INVAL);
        return -1;
    }

    _initerr();

    if ( roar_get_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, &oldchannels) == -1 ) {
        _seterrre();
        return -1;
    }

    for (i = 0; i < channels; i++) {
        s = c[i] * 65535.0;
        if ( s > 66190.0 || s < -655.0 ) {
            _seterr(ROAR_ERROR_RANGE);
            return -1;
        } else if ( s > 65535.0 ) {
            s = 65535.0;
        } else if ( s <     0.0 ) {
            s = 0.0;
        }
        mixer.mixer[i] = s;
    }

    mixer.scale = 65535;

    if ( channels != oldchannels ) {
        handled = 0;
        switch (oldchannels) {
        case 1:
            if ( channels == 2 ) {
                mixer.mixer[0] = (mixer.mixer[0] + mixer.mixer[1]) / 2;
                handled = 1;
            }
            break;
        case 2:
            if ( channels == 1 ) {
                mixer.mixer[1] = mixer.mixer[0];
                handled = 1;
            }
            break;
        case 4:
            if ( channels == 1 ) {
                mixer.mixer[1] = mixer.mixer[0];
                mixer.mixer[2] = mixer.mixer[0];
                mixer.mixer[3] = mixer.mixer[0];
                handled = 1;
            } else if ( channels == 2 ) {
                mixer.mixer[2] = mixer.mixer[0];
                mixer.mixer[3] = mixer.mixer[1];
                handled = 1;
            }
            break;
        }
        if ( handled ) {
            channels = oldchannels;
        } else {
            _seterr(ROAR_ERROR_INVAL);
            return -1;
        }
    }

    if ( roar_set_vol(vss->con, roar_stream_get_id(&(vss->stream)), &mixer, channels) == -1 ) {
        _seterrre();
        return -1;
    }

    return 0;
}
コード例 #16
0
ファイル: sfs.c プロジェクト: jphartmann/cmslib
static int
devopen(FILE * f, const char * name, int oflags, const char * mde)
{
   struct fnarg fna = {0};
   const char * t;
   const char * s=name;
   int len;
   int fnftlen;
   int rv, rsn;
   struct exsbuff exs;
   struct exsbuff * pexs;
   int exsl=sizeof(exs);
   int nofile = 0;                    /* assume file exists          */
   int zero = 0;                      /* For SFS plist               */
   static const char blanks[10] = "          ";
   static const char noc[] = "NOCOMMIT";
   static const int nocl = sizeof(noc) - 1;
   const char * mode = mde;
   enum intent intent;

   #if DEBUG
   __WERROR("Opening %s for mode %s", name, mode);
   #endif
   switch (name[0])
   {
      case '/':
         s += strspn(s, "/");
         t=strpbrk(s, "/:");
         if (!t)
            return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_onesl,
               "Leading slash, but no further slash or colon.", name);
         len = t - s;
         if ('/' == *t)              /* Mode letter and maybe number */
         {
            if (2 < len)
               return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_modelong,
                  "File mode longer than two characters.", name);
            fna.mode = toupper(0xff & s[0]);
            fna.moden = 2 == len ? s[1] : ' ';
            s = t + 1;
            break;
         }
         /* ':' File pool                                            */
         if (8 < len)
            return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fplong,
               "File pool name longer than 8 characters.", name);
         memcpy(fna.filepool, s, len);
         s = t;
         /* Fall through                                             */
      case '~':                       /* File space                  */
         t = strchr(++s, '/');
         if (!t)
            return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fpfsunterm,
               "File space name not terminated with forward slash (/).", name);
         len = t - s;
         if (len)
         {
            if (8<len)
               return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fslong,
                  "File space name longer than 8 characters.", name);
            memcpy(fna.filespace, s, len);
         }
         for (;;)                     /* Look for directory levels   */
         {
            s += strspn(s, "/");
            t = strchr(s, '/');
            if (!t) break;
            if (fna.alen) fna.dir[fna.alen++] = '.';
            len = t - s;
            if (sizeof(fna.dir) < fna.alen + len)
               return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fslong,
                  "Directory path too long.", name);
            memcpy(fna.dir + fna.alen, s, len);
            fna.alen += len;
            s=t + 1;
         }
         break;
      case '.':
         return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_leaddot,
            "File name omitted.", name);
      default:
         fna.mode = (O_RDONLY & f->oflags) ? '*' : 'A';
   }

   t = strchr(s, '.');
   if (!t)
      return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_noft,
         "File type omitted.", name);
   len = t - s;
   fnftlen = strlen(s);
   if (17 < fnftlen)
      return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fnftlong,
         "File name and type too long.", name);

   memcpy(fna.fid, s, fnftlen);
   fna.fid[len] = ' ';
   fna.fidlen = fnftlen;

   fna.fid[fna.fidlen++] = ' ';
   #define PSF(x...) fna.fidlen += sprintf(fna.fid + fna.fidlen, x)
   if (fna.filepool[0])
   {
      PSF("%s:", fna.filepool);
      if (!fna.filespace[0])
         return _seterr(EINVAL, EINVAL, _einv_rsn_bad_fn_fsmissing,
            "File space not specified.", name);
   }
   if (fna.alen) PSF("%s.%s", fna.filespace, fna.dir);
   else PSF("%c", fna.mode);
   if (fna.moden) PSF(" %c", fna.moden);
#if DEBUG
   __WERROR("%d >%s<", fna.fidlen, fna.fid);
#endif

   #undef PDF

/*********************************************************************/
/* Now  look  for the file.  If user wants caseless, he will have to */
/* do the uppercasing.  We in turn make no noise.                    */
/*                                                                   */
/* Aw, c'm on.  If it is read, look for uppercase.                   */
/*********************************************************************/

   exs.exstype = 0;                   /* Assume not old              */
   rsn = 0;
   nofile = 0;
   tocsl(8, "DMSEXIST", &rv, &rsn, fna.fid, &fna.fidlen,
      &exs, &exsl, noc, &nocl);
   if (rv && 90220 == rsn && (O_RDONLY & oflags) && islower(0xff & fna.fid[0]))
   {
      int i;

      for (i = 0; fna.fidlen > i; i++)
      {
         fna.fid[i] = toupper(0xff & fna.fid[i]);
      }
      tocsl(8, "DMSEXIST", &rv, &rsn, fna.fid, &fna.fidlen,
         &exs, &exsl, noc, &nocl);
   }
   if (rv)
   {
      if (90220 == rsn) nofile = 1;
      else
      {
         char bfr[256];

         sprintf(bfr, "DMSEXIST reason %d file '%.*s'",
            rsn, fna.fidlen, fna.fid);
         return _seterr(EINVAL, EINVAL, _einv_rsn_bad_dmsexist,
            "CMS error locating file", bfr);
      }
   }
   else
   {
      #define M(x, y) f->x = exs.exsf ## y
      M(recform, recf);
      M(lrecl, recl);
      M(numrecs, recs);
      M(numblks, blks);
      #undef M
   }

/*********************************************************************/
/* Figure operation intent and set the keyword accordingly.          */
/*********************************************************************/

   if (nofile)
   {
      if (O_RDONLY & f->oflags) return ENOENT;
      intent = int_create;    /* If it ain't there, surely we create */
   }
   else
   {
      if (O_EXCL & f->oflags) return EEXISTS;
      if (O_RDONLY & f->oflags) intent = int_read;
      else if (O_TRUNC & f->oflags) intent = int_replace;
      else intent = int_update;
   }

   #define POP(x...) fna.optlen += sprintf(fna.opt + fna.optlen, x)

   POP("%s", sfsint[intent]);         /* Intent                      */
   #if DEBUG
   __WERROR("%d %s", fna.optlen, fna.opt);
   #endif

/*********************************************************************/
/* Process remaining part of mode string.                            */
/*                                                                   */
/* It  may  be  a  comma  and  lrecl=nnn, and SFS open options which */
/* should  not  include  new/read/replace, as we construct that word */
/* from the standard type.                                           */
/*********************************************************************/

   s = mode;

   while (s && *s)
   {
      if (0==strncasecmp(s, "lrecl=", 6))
      {
         int got=0;
         int lrecl;

         sscanf(s+6, "%d %n", &lrecl, &got);
         if (0==got)
            return _seterr(EINVAL, EINVAL, _einv_rsn_bad_lrecl,
               "Record length missing.", s);
         if (nofile) f->lrecl = lrecl;
         else
         s += 6 + got;
      }
      else if (0==strncasecmp(s, "recfm=", 6))
      {
         char recfm = toupper(0xff & s[6]);

         if ('F' != recfm && 'V' != recfm)
            return _seterr(EINVAL, EINVAL, _einv_rsn_bad_recfm,
               "Record format not valid.", s);
         if (!nofile && recfm != f->recform)
         {
            char bfr[2] = {f->recform};

            return _seterr(EINVAL, EINVAL, _einv_rsn_recfm_diff,
               "Record format not same as existing file.", bfr);
         }
         if (!(O_RDONLY & f->oflags)) POP(" %c", recfm);
         s += 7;
      }
      else
         return _seterr(EINVAL, EINVAL, _einv_rsn_bad_mode,
            "Unrecognised mode string.", s);

      if (*s && ',' != *s++)
         return _seterr(EINVAL, EINVAL, _einv_rsn_bad_mode_del,
            "Comma expected in mode string.", s);
   }

   /* Supply defaults if create                                      */
   if (int_create == intent)
   {
      if (!f->recform)
      {
         f->recform = 'V';
         POP(" %c", f->recform);
      }
      if (!f->lrecl)
      {
         /* Setting  the LRECL for V files is for the write function */
         /* to know when to cut a record; SFS will ignore the record */
         /* length?                                                  */
         if ('F' == f->recform) f->lrecl = 80;
         else if (O_BINARY & f->oflags) f->lrecl = 255;
      }
   }

   if (sizeof(fna.opt) - fna.optlen <= len)
      return _seterr(EINVAL, EINVAL, _einv_rsn_long_opt,
         "Option string too long.", s);

#if DEBUG
   __WERROR("fd %d mode '%s' options %d '%-*.*s' open flag %x",
      f->fd, mde, fna.optlen, fna.optlen, fna.optlen, fna.opt, f->oflags);
#endif
   #undef POP

/*********************************************************************/
/* And finally.                                                      */
/*********************************************************************/

   tocsl(15, "DMSOPEN ", &rv, &rsn, fna.fid, &fna.fidlen,
      fna.opt, &fna.optlen, f->token,
      &zero,                          /* Work unit                   */
      &zero,                          /* Wu error                    */
      &zero,                          /* Wu error length             */
      &zero,                          /* User data                   */
      &zero,                          /* User data length            */
      blanks,                         /* Create date                 */
      blanks,                         /* Create time                 */
      &f->lrecl                       /* Explicit record length      */
      );
   switch (rv)
   {
      case 0:
         break;
      case 4:
         break;
      case 8:
         switch (rsn)
         {
            case 90310:
               __sayf("fd %d %s bad keyword in list '%s'", f->fd, name, fna.opt);
               break;
            default:
               goto error;
         }
         return EINVAL;
      default:
error:
         __sayf("DMSOPEN fd %d rc=%d reason=%d.", f->fd, rv, rsn);
         return EINVAL;
   }

   if (!fna.mode) f->blksize = 0x1000;
   else
   {
      if (0) __sayf("FIXME " __FILE__ ".%d", __LINE__);
   }

   pexs = f->accwork = malloc(sizeof(struct exsbuff));
   if (!f) return ENOMEM;
   *pexs = exs;

   return 0;
}