root/lib/libform/fty_generic.c
/****************************************************************************
 * Copyright 2018-2020,2021 Thomas E. Dickey                                *
 * Copyright 2008-2012,2016 Free Software Foundation, Inc.                  *
 *                                                                          *
 * 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, distribute with modifications, 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 THE ABOVE COPYRIGHT HOLDERS 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(s) of the above copyright   *
 * holders shall not be used in advertising or otherwise to promote the     *
 * sale, use or other dealings in this Software without prior written       *
 * authorization.                                                           *
 ****************************************************************************/

/***************************************************************************
*                                                                          *
*  Author : Juergen Pfeifer                                                *
*                                                                          *
***************************************************************************/

#include "form.priv.h"

MODULE_ID("$Id: fty_generic.c,v 1.1 2023/10/17 09:52:10 nicm Exp $")

/*
 * This is not a full implementation of a field type, but adds some
 * support for higher level languages with some restrictions to interop
 * with C language. In particular, the collection of arguments for the
 * various fieldtypes is not based on the vararg C mechanism, but on a
 * iterator based callback mechanism that allows the high level language
 * to provide the arguments as a structure. Most languages have mechanisms
 * to layout structures so that they can be passed to C.
 *
 * The languages can register a new generic fieldtype dynamically and store
 * a handle (key) to the calling object as an argument. Together with that
 * it can register a freearg callback, so that the high level language
 * remains in control of the memory management of the arguments they pass.
 * The design idea is, that the high-level language - typically a OO
 * language like C# or Java, uses its own dispatching mechanisms
 * (polymorphism) to call the proper check routines responsible for the
 * argument type. So these language implement typically only one generic
 * fieldtype they register with the forms library using this call.
 *
 * For that purpose we have extended the fieldtype structure by a new element
 * that gets the arguments from a single struct passed by the caller.
 *
 */
#if NCURSES_INTEROP_FUNCS

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  static void *Generic_This_Type( void * arg )
|
|   Description   :  We interpret the passed arg just as a handle the
|                    calling language uses to keep track of its allocated
|                    argument structures. We can simply copy it back.
|
|   Return Values :  Pointer to argument structure
+--------------------------------------------------------------------------*/
static void *
Generic_This_Type(void *arg)
{
  return (arg);
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  FIELDTYPE *_nc_generic_fieldtype(
|                       bool (* const field_check)(FIELD *,const void *),
|                       bool (* const char_check) (int, const void *),
|                       bool (*const next)(FORM*,FIELD*,const void*),
|                       bool (*const prev)(FORM*,FIELD*,const void*),
|                       void (*freecallback)(void*))
|
|   Description   :  Create a new fieldtype. The application programmer must
|                    write a field_check and a char_check function and give
|                    them as input to this call. A callback to allow the
|                    release of the allocated memory must also be provided.
|                    For generic field types, we provide some more
|                    information about the field as parameters.
|
|                    If an error occurs, errno is set to
|                       E_BAD_ARGUMENT  - invalid arguments
|                       E_SYSTEM_ERROR  - system error (no memory)
|
|   Return Values :  Fieldtype pointer or NULL if error occurred
+--------------------------------------------------------------------------*/
FORM_EXPORT(FIELDTYPE *)
_nc_generic_fieldtype(bool (*const field_check) (FORM *, FIELD *, const void *),
                      bool (*const char_check) (int, FORM *, FIELD *, const
                                                void *),
                      bool (*const next) (FORM *, FIELD *, const void *),
                      bool (*const prev) (FORM *, FIELD *, const void *),
                      void (*freecallback) (void *))
{
  int code = E_SYSTEM_ERROR;
  FIELDTYPE *res = (FIELDTYPE *)0;

  TR_FUNC_BFR(5);

  T((T_CALLED("_nc_generic_fieldtype(%s,%s,%s,%s,%s)"),
     TR_FUNC_ARG(0, field_check),
     TR_FUNC_ARG(1, char_check),
     TR_FUNC_ARG(2, next),
     TR_FUNC_ARG(3, prev),
     TR_FUNC_ARG(4, freecallback)));

  if (field_check || char_check)
    {
      res = typeMalloc(FIELDTYPE, 1);

      if (res)
        {
          *res = *_nc_Default_FieldType;
          SetStatus(res, (_HAS_ARGS | _GENERIC));
          res->fieldcheck.gfcheck = field_check;
          res->charcheck.gccheck = char_check;
          res->genericarg = Generic_This_Type;
          res->freearg = freecallback;
          res->enum_next.gnext = next;
          res->enum_prev.gprev = prev;
          code = E_OK;
        }
    }
  else
    code = E_BAD_ARGUMENT;

  if (E_OK != code)
    SET_ERROR(code);

  returnFieldType(res);
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  static TypeArgument *GenericArgument(
|                      const FIELDTYPE* typ,
|                      int (*argiterator)(void**),
|                      int* err)
|
|   Description   :  The iterator callback must browse through all fieldtype
|                    parameters that have an argument associated with the
|                    type. The iterator returns 1 if the operation to get
|                    the next element was successful, 0 otherwise. If the
|                    iterator could move to the next argument, it fills
|                    the void* pointer representing the argument into the
|                    location provided as argument to the iterator.
|                    The err reference is used to keep track of errors.
|
|   Return Values :  Pointer to argument structure
+--------------------------------------------------------------------------*/
static TypeArgument *
GenericArgument(const FIELDTYPE *typ,
                int (*argiterator) (void **), int *err)
{
  TypeArgument *res = (TypeArgument *)0;

  if (typ != 0 && (typ->status & _HAS_ARGS) != 0 && err != 0 && argiterator != 0)
    {
      if (typ->status & _LINKED_TYPE)
        {
          /* Composite fieldtypes keep track internally of their own memory */
          TypeArgument *p = typeMalloc(TypeArgument, 1);

          if (p)
            {
              p->left = GenericArgument(typ->left, argiterator, err);
              p->right = GenericArgument(typ->right, argiterator, err);
              return p;
            }
          else
            *err += 1;
        }
      else
        {
          assert(typ->genericarg != (void *)0);
          if (typ->genericarg == 0)
            *err += 1;
          else
            {
              void *argp;
              int valid = argiterator(&argp);

              if (valid == 0 || argp == 0 ||
                  !(res = (TypeArgument *)typ->genericarg(argp)))
                {
                  *err += 1;
                }
            }
        }
    }
  return res;
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  int _nc_set_generic_fieldtype(
|                      FIELD* field,
|                      FIELDTYPE* ftyp,
|                      int (*argiterator)(void**))
|
|   Description   :  Assign the fieldtype to the field and use the iterator
|                    mechanism to get the arguments when a check is
|                    performed.
|
|   Return Values :  E_OK if all went well
|                    E_SYSTEM_ERROR if an error occurred
+--------------------------------------------------------------------------*/
FORM_EXPORT(int)
_nc_set_generic_fieldtype(FIELD *field,
                          FIELDTYPE *ftyp,
                          int (*argiterator) (void **))
{
  int code = E_SYSTEM_ERROR;
  int err = 0;

  if (field)
    {
      if (field && field->type)
        _nc_Free_Type(field);

      field->type = ftyp;
      if (ftyp)
        {
          if (argiterator)
            {
              /* The precondition is that the iterator is reset */
              field->arg = (void *)GenericArgument(field->type, argiterator, &err);

              if (err)
                {
                  _nc_Free_Argument(field->type, (TypeArgument *)(field->arg));
                  field->type = (FIELDTYPE *)0;
                  field->arg = (void *)0;
                }
              else
                {
                  code = E_OK;
                  if (field->type)
                    field->type->ref++;
                }
            }
        }
      else
        {
          field->arg = (void *)0;
          code = E_OK;
        }
    }
  return code;
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  WINDOW* _nc_form_cursor(
|                      FORM* form,
|                      int *pRow, int *pCol)
|
|   Description   :  Get the current position of the form cursor position
|                    We also return the field window
|
|   Return Values :  The field's Window or NULL on error
+--------------------------------------------------------------------------*/
FORM_EXPORT(WINDOW *)
_nc_form_cursor(const FORM *form, int *pRow, int *pCol)
{
  int code = E_SYSTEM_ERROR;
  WINDOW *res = (WINDOW *)0;

  if (form != 0 && pRow != 0 && pCol != 0)
    {
      *pRow = form->currow;
      *pCol = form->curcol;
      res = form->w;
      code = E_OK;
    }
  if (code != E_OK)
    SET_ERROR(code);
  return res;
}

#else
extern void _nc_fty_generic(void);
void
_nc_fty_generic(void)
{
}
#endif

/* fty_generic.c ends here */