Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1 | /**************************************************************************** |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 2 | * Copyright 2018-2020,2021 Thomas E. Dickey * |
| 3 | * Copyright 2008-2012,2016 Free Software Foundation, Inc. * |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 4 | * * |
| 5 | * Permission is hereby granted, free of charge, to any person obtaining a * |
| 6 | * copy of this software and associated documentation files (the * |
| 7 | * "Software"), to deal in the Software without restriction, including * |
| 8 | * without limitation the rights to use, copy, modify, merge, publish, * |
| 9 | * distribute, distribute with modifications, sublicense, and/or sell * |
| 10 | * copies of the Software, and to permit persons to whom the Software is * |
| 11 | * furnished to do so, subject to the following conditions: * |
| 12 | * * |
| 13 | * The above copyright notice and this permission notice shall be included * |
| 14 | * in all copies or substantial portions of the Software. * |
| 15 | * * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * |
| 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * |
| 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * |
| 19 | * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * |
| 20 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * |
| 21 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * |
| 22 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * |
| 23 | * * |
| 24 | * Except as contained in this notice, the name(s) of the above copyright * |
| 25 | * holders shall not be used in advertising or otherwise to promote the * |
| 26 | * sale, use or other dealings in this Software without prior written * |
| 27 | * authorization. * |
| 28 | ****************************************************************************/ |
| 29 | |
| 30 | /*************************************************************************** |
| 31 | * * |
| 32 | * Author : Juergen Pfeifer * |
| 33 | * * |
| 34 | ***************************************************************************/ |
| 35 | |
| 36 | #include "form.priv.h" |
| 37 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 38 | MODULE_ID("$Id: fty_generic.c,v 1.15 2021/03/27 23:49:53 tom Exp $") |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 39 | |
| 40 | /* |
| 41 | * This is not a full implementation of a field type, but adds some |
| 42 | * support for higher level languages with some restrictions to interop |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 43 | * with C language. In particular, the collection of arguments for the |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 44 | * various fieldtypes is not based on the vararg C mechanism, but on a |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 45 | * iterator based callback mechanism that allows the high level language |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 46 | * to provide the arguments as a structure. Most languages have mechanisms |
| 47 | * to layout structures so that they can be passed to C. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 48 | * |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 49 | * The languages can register a new generic fieldtype dynamically and store |
| 50 | * a handle (key) to the calling object as an argument. Together with that |
| 51 | * it can register a freearg callback, so that the high level language |
| 52 | * remains in control of the memory management of the arguments they pass. |
| 53 | * The design idea is, that the high-level language - typically a OO |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 54 | * language like C# or Java, uses its own dispatching mechanisms |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 55 | * (polymorphism) to call the proper check routines responsible for the |
| 56 | * argument type. So these language implement typically only one generic |
| 57 | * fieldtype they register with the forms library using this call. |
| 58 | * |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 59 | * For that purpose we have extended the fieldtype structure by a new element |
| 60 | * that gets the arguments from a single struct passed by the caller. |
| 61 | * |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 62 | */ |
| 63 | #if NCURSES_INTEROP_FUNCS |
| 64 | |
| 65 | /*--------------------------------------------------------------------------- |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 66 | | Facility : libnform |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 67 | | Function : static void *Generic_This_Type( void * arg ) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 68 | | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 69 | | Description : We interpret the passed arg just as a handle the |
| 70 | | calling language uses to keep track of its allocated |
| 71 | | argument structures. We can simply copy it back. |
| 72 | | |
| 73 | | Return Values : Pointer to argument structure |
| 74 | +--------------------------------------------------------------------------*/ |
| 75 | static void * |
| 76 | Generic_This_Type(void *arg) |
| 77 | { |
| 78 | return (arg); |
| 79 | } |
| 80 | |
| 81 | /*--------------------------------------------------------------------------- |
| 82 | | Facility : libnform |
| 83 | | Function : FIELDTYPE *_nc_generic_fieldtype( |
| 84 | | bool (* const field_check)(FIELD *,const void *), |
| 85 | | bool (* const char_check) (int, const void *), |
| 86 | | bool (*const next)(FORM*,FIELD*,const void*), |
| 87 | | bool (*const prev)(FORM*,FIELD*,const void*), |
| 88 | | void (*freecallback)(void*)) |
| 89 | | |
| 90 | | Description : Create a new fieldtype. The application programmer must |
| 91 | | write a field_check and a char_check function and give |
| 92 | | them as input to this call. A callback to allow the |
| 93 | | release of the allocated memory must also be provided. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 94 | | For generic field types, we provide some more |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 95 | | information about the field as parameters. |
| 96 | | |
| 97 | | If an error occurs, errno is set to |
| 98 | | E_BAD_ARGUMENT - invalid arguments |
| 99 | | E_SYSTEM_ERROR - system error (no memory) |
| 100 | | |
| 101 | | Return Values : Fieldtype pointer or NULL if error occurred |
| 102 | +--------------------------------------------------------------------------*/ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 103 | FORM_EXPORT(FIELDTYPE *) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 104 | _nc_generic_fieldtype(bool (*const field_check) (FORM *, FIELD *, const void *), |
| 105 | bool (*const char_check) (int, FORM *, FIELD *, const |
| 106 | void *), |
| 107 | bool (*const next) (FORM *, FIELD *, const void *), |
| 108 | bool (*const prev) (FORM *, FIELD *, const void *), |
| 109 | void (*freecallback) (void *)) |
| 110 | { |
| 111 | int code = E_SYSTEM_ERROR; |
| 112 | FIELDTYPE *res = (FIELDTYPE *)0; |
| 113 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 114 | TR_FUNC_BFR(5); |
| 115 | |
| 116 | T((T_CALLED("_nc_generic_fieldtype(%s,%s,%s,%s,%s)"), |
| 117 | TR_FUNC_ARG(0, field_check), |
| 118 | TR_FUNC_ARG(1, char_check), |
| 119 | TR_FUNC_ARG(2, next), |
| 120 | TR_FUNC_ARG(3, prev), |
| 121 | TR_FUNC_ARG(4, freecallback))); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 122 | |
| 123 | if (field_check || char_check) |
| 124 | { |
| 125 | res = typeMalloc(FIELDTYPE, 1); |
| 126 | |
| 127 | if (res) |
| 128 | { |
| 129 | *res = *_nc_Default_FieldType; |
| 130 | SetStatus(res, (_HAS_ARGS | _GENERIC)); |
| 131 | res->fieldcheck.gfcheck = field_check; |
| 132 | res->charcheck.gccheck = char_check; |
| 133 | res->genericarg = Generic_This_Type; |
| 134 | res->freearg = freecallback; |
| 135 | res->enum_next.gnext = next; |
| 136 | res->enum_prev.gprev = prev; |
| 137 | code = E_OK; |
| 138 | } |
| 139 | } |
| 140 | else |
| 141 | code = E_BAD_ARGUMENT; |
| 142 | |
| 143 | if (E_OK != code) |
| 144 | SET_ERROR(code); |
| 145 | |
| 146 | returnFieldType(res); |
| 147 | } |
| 148 | |
| 149 | /*--------------------------------------------------------------------------- |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 150 | | Facility : libnform |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 151 | | Function : static TypeArgument *GenericArgument( |
| 152 | | const FIELDTYPE* typ, |
| 153 | | int (*argiterator)(void**), |
| 154 | | int* err) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 155 | | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 156 | | Description : The iterator callback must browse through all fieldtype |
| 157 | | parameters that have an argument associated with the |
| 158 | | type. The iterator returns 1 if the operation to get |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 159 | | the next element was successful, 0 otherwise. If the |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 160 | | iterator could move to the next argument, it fills |
| 161 | | the void* pointer representing the argument into the |
| 162 | | location provided as argument to the iterator. |
| 163 | | The err reference is used to keep track of errors. |
| 164 | | |
| 165 | | Return Values : Pointer to argument structure |
| 166 | +--------------------------------------------------------------------------*/ |
| 167 | static TypeArgument * |
| 168 | GenericArgument(const FIELDTYPE *typ, |
| 169 | int (*argiterator) (void **), int *err) |
| 170 | { |
| 171 | TypeArgument *res = (TypeArgument *)0; |
| 172 | |
| 173 | if (typ != 0 && (typ->status & _HAS_ARGS) != 0 && err != 0 && argiterator != 0) |
| 174 | { |
| 175 | if (typ->status & _LINKED_TYPE) |
| 176 | { |
| 177 | /* Composite fieldtypes keep track internally of their own memory */ |
| 178 | TypeArgument *p = typeMalloc(TypeArgument, 1); |
| 179 | |
| 180 | if (p) |
| 181 | { |
| 182 | p->left = GenericArgument(typ->left, argiterator, err); |
| 183 | p->right = GenericArgument(typ->right, argiterator, err); |
| 184 | return p; |
| 185 | } |
| 186 | else |
| 187 | *err += 1; |
| 188 | } |
| 189 | else |
| 190 | { |
| 191 | assert(typ->genericarg != (void *)0); |
| 192 | if (typ->genericarg == 0) |
| 193 | *err += 1; |
| 194 | else |
| 195 | { |
| 196 | void *argp; |
| 197 | int valid = argiterator(&argp); |
| 198 | |
| 199 | if (valid == 0 || argp == 0 || |
| 200 | !(res = (TypeArgument *)typ->genericarg(argp))) |
| 201 | { |
| 202 | *err += 1; |
| 203 | } |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | return res; |
| 208 | } |
| 209 | |
| 210 | /*--------------------------------------------------------------------------- |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 211 | | Facility : libnform |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 212 | | Function : int _nc_set_generic_fieldtype( |
| 213 | | FIELD* field, |
| 214 | | FIELDTYPE* ftyp, |
| 215 | | int (*argiterator)(void**)) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 216 | | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 217 | | Description : Assign the fieldtype to the field and use the iterator |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 218 | | mechanism to get the arguments when a check is |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 219 | | performed. |
| 220 | | |
| 221 | | Return Values : E_OK if all went well |
| 222 | | E_SYSTEM_ERROR if an error occurred |
| 223 | +--------------------------------------------------------------------------*/ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 224 | FORM_EXPORT(int) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 225 | _nc_set_generic_fieldtype(FIELD *field, |
| 226 | FIELDTYPE *ftyp, |
| 227 | int (*argiterator) (void **)) |
| 228 | { |
| 229 | int code = E_SYSTEM_ERROR; |
| 230 | int err = 0; |
| 231 | |
| 232 | if (field) |
| 233 | { |
| 234 | if (field && field->type) |
| 235 | _nc_Free_Type(field); |
| 236 | |
| 237 | field->type = ftyp; |
| 238 | if (ftyp) |
| 239 | { |
| 240 | if (argiterator) |
| 241 | { |
| 242 | /* The precondition is that the iterator is reset */ |
| 243 | field->arg = (void *)GenericArgument(field->type, argiterator, &err); |
| 244 | |
| 245 | if (err) |
| 246 | { |
| 247 | _nc_Free_Argument(field->type, (TypeArgument *)(field->arg)); |
| 248 | field->type = (FIELDTYPE *)0; |
| 249 | field->arg = (void *)0; |
| 250 | } |
| 251 | else |
| 252 | { |
| 253 | code = E_OK; |
| 254 | if (field->type) |
| 255 | field->type->ref++; |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | else |
| 260 | { |
| 261 | field->arg = (void *)0; |
| 262 | code = E_OK; |
| 263 | } |
| 264 | } |
| 265 | return code; |
| 266 | } |
| 267 | |
| 268 | /*--------------------------------------------------------------------------- |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 269 | | Facility : libnform |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 270 | | Function : WINDOW* _nc_form_cursor( |
| 271 | | FORM* form, |
| 272 | | int *pRow, int *pCol) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 273 | | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 274 | | Description : Get the current position of the form cursor position |
| 275 | | We also return the field window |
| 276 | | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 277 | | Return Values : The field's Window or NULL on error |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 278 | +--------------------------------------------------------------------------*/ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 279 | FORM_EXPORT(WINDOW *) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 280 | _nc_form_cursor(const FORM *form, int *pRow, int *pCol) |
| 281 | { |
| 282 | int code = E_SYSTEM_ERROR; |
| 283 | WINDOW *res = (WINDOW *)0; |
| 284 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 285 | if (form != 0 && pRow != 0 && pCol != 0) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 286 | { |
| 287 | *pRow = form->currow; |
| 288 | *pCol = form->curcol; |
| 289 | res = form->w; |
| 290 | code = E_OK; |
| 291 | } |
| 292 | if (code != E_OK) |
| 293 | SET_ERROR(code); |
| 294 | return res; |
| 295 | } |
| 296 | |
| 297 | #else |
| 298 | extern void _nc_fty_generic(void); |
| 299 | void |
| 300 | _nc_fty_generic(void) |
| 301 | { |
| 302 | } |
| 303 | #endif |
| 304 | |
| 305 | /* fty_generic.c ends here */ |