1 1.43 mrg /* $NetBSD: internals.c,v 1.43 2023/08/01 07:56:23 mrg Exp $ */ 2 1.1 blymn 3 1.1 blymn /*- 4 1.1 blymn * Copyright (c) 1998-1999 Brett Lymn 5 1.1 blymn * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au) 6 1.1 blymn * All rights reserved. 7 1.1 blymn * 8 1.1 blymn * This code has been donated to The NetBSD Foundation by the Author. 9 1.1 blymn * 10 1.1 blymn * Redistribution and use in source and binary forms, with or without 11 1.1 blymn * modification, are permitted provided that the following conditions 12 1.1 blymn * are met: 13 1.1 blymn * 1. Redistributions of source code must retain the above copyright 14 1.1 blymn * notice, this list of conditions and the following disclaimer. 15 1.1 blymn * 2. The name of the author may not be used to endorse or promote products 16 1.18 blymn * derived from this software without specific prior written permission 17 1.1 blymn * 18 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 1.1 blymn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 blymn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 blymn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 blymn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 1.1 blymn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 blymn * 29 1.1 blymn * 30 1.1 blymn */ 31 1.29 lukem 32 1.29 lukem #include <sys/cdefs.h> 33 1.43 mrg __RCSID("$NetBSD: internals.c,v 1.43 2023/08/01 07:56:23 mrg Exp $"); 34 1.1 blymn 35 1.15 blymn #include <limits.h> 36 1.1 blymn #include <ctype.h> 37 1.1 blymn #include <stdio.h> 38 1.1 blymn #include <stdlib.h> 39 1.1 blymn #include <strings.h> 40 1.15 blymn #include <assert.h> 41 1.38 christos #include <err.h> 42 1.38 christos #include <stdarg.h> 43 1.1 blymn #include "internals.h" 44 1.1 blymn #include "form.h" 45 1.1 blymn 46 1.2 blymn #ifdef DEBUG 47 1.2 blymn /* 48 1.2 blymn * file handle to write debug info to, this will be initialised when 49 1.2 blymn * the form is first posted. 50 1.2 blymn */ 51 1.23 blymn 52 1.23 blymn /* 53 1.23 blymn * map the request numbers to strings for debug 54 1.23 blymn */ 55 1.38 christos static const char *reqs[] = { 56 1.23 blymn "NEXT_PAGE", "PREV_PAGE", "FIRST_PAGE", "LAST_PAGE", "NEXT_FIELD", 57 1.23 blymn "PREV_FIELD", "FIRST_FIELD", "LAST_FIELD", "SNEXT_FIELD", 58 1.23 blymn "SPREV_FIELD", "SFIRST_FIELD", "SLAST_FIELD", "LEFT_FIELD", 59 1.23 blymn "RIGHT_FIELD", "UP_FIELD", "DOWN_FIELD", "NEXT_CHAR", "PREV_CHAR", 60 1.23 blymn "NEXT_LINE", "PREV_LINE", "NEXT_WORD", "PREV_WORD", "BEG_FIELD", 61 1.23 blymn "END_FIELD", "BEG_LINE", "END_LINE", "LEFT_CHAR", "RIGHT_CHAR", 62 1.23 blymn "UP_CHAR", "DOWN_CHAR", "NEW_LINE", "INS_CHAR", "INS_LINE", 63 1.23 blymn "DEL_CHAR", "DEL_PREV", "DEL_LINE", "DEL_WORD", "CLR_EOL", 64 1.23 blymn "CLR_EOF", "CLR_FIELD", "OVL_MODE", "INS_MODE", "SCR_FLINE", 65 1.23 blymn "SCR_BLINE", "SCR_FPAGE", "SCR_BPAGE", "SCR_FHPAGE", "SCR_BHPAGE", 66 1.23 blymn "SCR_FCHAR", "SCR_BCHAR", "SCR_HFLINE", "SCR_HBLINE", "SCR_HFHALF", 67 1.23 blymn "SCR_HBHALF", "VALIDATION", "PREV_CHOICE", "NEXT_CHOICE" }; 68 1.2 blymn #endif 69 1.2 blymn 70 1.1 blymn /* define our own min function - this is not generic but will do here 71 1.1 blymn * (don't believe me? think about what value you would get 72 1.1 blymn * from min(x++, y++) 73 1.1 blymn */ 74 1.1 blymn #define min(a,b) (((a) > (b))? (b) : (a)) 75 1.1 blymn 76 1.1 blymn /* for the line joining function... */ 77 1.1 blymn #define JOIN_NEXT 1 78 1.1 blymn #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */ 79 1.1 blymn #define JOIN_PREV 3 80 1.1 blymn #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */ 81 1.1 blymn 82 1.15 blymn /* for the bump_lines function... */ 83 1.15 blymn #define _FORMI_USE_CURRENT -1 /* indicates current cursor pos to be used */ 84 1.15 blymn 85 1.30 blymn /* used in add_char for initial memory allocation for string in row */ 86 1.30 blymn #define INITIAL_LINE_ALLOC 16 87 1.30 blymn 88 1.30 blymn unsigned 89 1.30 blymn field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp); 90 1.1 blymn static void 91 1.8 blymn _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val); 92 1.8 blymn static void 93 1.1 blymn _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val); 94 1.1 blymn static int 95 1.30 blymn _formi_join_line(FIELD *field, _FORMI_FIELD_LINES **rowp, int direction); 96 1.1 blymn void 97 1.30 blymn _formi_hscroll_back(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt); 98 1.1 blymn void 99 1.30 blymn _formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt); 100 1.1 blymn static void 101 1.1 blymn _formi_scroll_back(FIELD *field, unsigned int amt); 102 1.1 blymn static void 103 1.1 blymn _formi_scroll_fwd(FIELD *field, unsigned int amt); 104 1.1 blymn static int 105 1.28 blymn _formi_set_cursor_xpos(FIELD *field, int no_scroll); 106 1.25 blymn static int 107 1.30 blymn find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp); 108 1.15 blymn static int 109 1.30 blymn split_line(FIELD *field, bool hard_split, unsigned pos, 110 1.30 blymn _FORMI_FIELD_LINES **rowp); 111 1.21 blymn static bool 112 1.22 blymn check_field_size(FIELD *field); 113 1.23 blymn static int 114 1.30 blymn add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c); 115 1.23 blymn static int 116 1.30 blymn tab_size(_FORMI_FIELD_LINES *row, unsigned int i); 117 1.23 blymn static unsigned int 118 1.30 blymn tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int len); 119 1.23 blymn static int 120 1.23 blymn tab_fit_window(FIELD *field, unsigned int pos, unsigned int window); 121 1.30 blymn static void 122 1.30 blymn add_to_free(FIELD *field, _FORMI_FIELD_LINES *line); 123 1.30 blymn static void 124 1.30 blymn adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line); 125 1.30 blymn static _FORMI_FIELD_LINES * 126 1.30 blymn copy_row(_FORMI_FIELD_LINES *row); 127 1.30 blymn static void 128 1.30 blymn destroy_row_list(_FORMI_FIELD_LINES *start); 129 1.30 blymn 130 1.30 blymn /* 131 1.30 blymn * Calculate the cursor y position to make the given row appear on the 132 1.30 blymn * field. This may be as simple as just changing the ypos (if at all) but 133 1.30 blymn * may encompass resetting the start_line of the field to place the line 134 1.30 blymn * at the bottom of the field. The field is assumed to be a multi-line one. 135 1.30 blymn */ 136 1.30 blymn static void 137 1.30 blymn adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line) 138 1.30 blymn { 139 1.30 blymn unsigned ypos; 140 1.30 blymn _FORMI_FIELD_LINES *rs; 141 1.30 blymn 142 1.30 blymn ypos = 0; 143 1.33 roy rs = field->alines; 144 1.30 blymn while (rs != line) { 145 1.30 blymn rs = rs->next; 146 1.30 blymn ypos++; 147 1.30 blymn } 148 1.15 blymn 149 1.30 blymn field->cursor_ypos = ypos; 150 1.33 roy field->start_line = field->alines; 151 1.30 blymn if (ypos > (field->rows - 1)) { 152 1.30 blymn /* 153 1.30 blymn * cur_line off the end of the field, 154 1.30 blymn * adjust start_line so fix this. 155 1.30 blymn */ 156 1.30 blymn field->cursor_ypos = field->rows - 1; 157 1.30 blymn ypos = ypos - (field->rows - 1); 158 1.30 blymn while (ypos > 0) { 159 1.30 blymn ypos--; 160 1.30 blymn field->start_line = field->start_line->next; 161 1.30 blymn } 162 1.30 blymn } 163 1.30 blymn } 164 1.1 blymn 165 1.30 blymn 166 1.30 blymn /* 167 1.30 blymn * Delete the given row and add it to the free list of the given field. 168 1.30 blymn */ 169 1.30 blymn static void 170 1.30 blymn add_to_free(FIELD *field, _FORMI_FIELD_LINES *line) 171 1.30 blymn { 172 1.30 blymn _FORMI_FIELD_LINES *saved; 173 1.30 blymn 174 1.30 blymn saved = line; 175 1.30 blymn 176 1.30 blymn /* don't remove if only one line... */ 177 1.30 blymn if ((line->prev == NULL) && (line->next == NULL)) 178 1.30 blymn return; 179 1.30 blymn 180 1.30 blymn if (line->prev == NULL) { 181 1.30 blymn /* handle top of list */ 182 1.33 roy field->alines = line->next; 183 1.33 roy field->alines->prev = NULL; 184 1.30 blymn 185 1.30 blymn if (field->cur_line == saved) 186 1.33 roy field->cur_line = field->alines; 187 1.30 blymn if (field->start_line == saved) 188 1.30 blymn field->start_line = saved; 189 1.30 blymn } else if (line->next == NULL) { 190 1.30 blymn /* handle bottom of list */ 191 1.30 blymn line->prev->next = NULL; 192 1.30 blymn if (field->cur_line == saved) 193 1.30 blymn field->cur_line = saved->prev; 194 1.30 blymn if (field->start_line == saved) 195 1.30 blymn field->cur_line = saved->prev; 196 1.30 blymn } else { 197 1.30 blymn saved->next->prev = saved->prev; 198 1.30 blymn saved->prev->next = saved->next; 199 1.30 blymn if (field->cur_line == saved) 200 1.30 blymn field->cur_line = saved->prev; 201 1.30 blymn if (field->start_line == saved) 202 1.30 blymn field->start_line = saved; 203 1.30 blymn } 204 1.30 blymn 205 1.30 blymn saved->next = field->free; 206 1.30 blymn field->free = saved; 207 1.30 blymn saved->prev = NULL; 208 1.30 blymn if (saved->next != NULL) 209 1.30 blymn saved->next->prev = line; 210 1.30 blymn } 211 1.30 blymn 212 1.30 blymn /* 213 1.30 blymn * Duplicate the given row, return the pointer to the new copy or 214 1.30 blymn * NULL if the copy fails. 215 1.30 blymn */ 216 1.30 blymn static _FORMI_FIELD_LINES * 217 1.30 blymn copy_row(_FORMI_FIELD_LINES *row) 218 1.30 blymn { 219 1.30 blymn _FORMI_FIELD_LINES *new; 220 1.30 blymn _formi_tab_t *tp, *newt; 221 1.30 blymn 222 1.40 christos if ((new = malloc(sizeof(*new))) == NULL) { 223 1.30 blymn return NULL; 224 1.30 blymn } 225 1.30 blymn 226 1.40 christos memcpy(new, row, sizeof(*new)); 227 1.30 blymn 228 1.30 blymn /* nuke the pointers from the source row so we don't get confused */ 229 1.30 blymn new->next = NULL; 230 1.30 blymn new->prev = NULL; 231 1.30 blymn new->tabs = NULL; 232 1.30 blymn 233 1.40 christos if ((new->string = malloc((size_t)new->allocated)) == NULL) { 234 1.30 blymn free(new); 235 1.30 blymn return NULL; 236 1.30 blymn } 237 1.30 blymn 238 1.30 blymn memcpy(new->string, row->string, (size_t) row->length + 1); 239 1.30 blymn 240 1.30 blymn if (row->tabs != NULL) { 241 1.30 blymn tp = row->tabs; 242 1.40 christos if ((new->tabs = malloc(sizeof(*new->tabs))) == NULL) { 243 1.30 blymn free(new->string); 244 1.30 blymn free(new); 245 1.30 blymn return NULL; 246 1.30 blymn } 247 1.30 blymn 248 1.40 christos memcpy(new->tabs, row->tabs, sizeof(*new->tabs)); 249 1.30 blymn new->tabs->back = NULL; 250 1.30 blymn new->tabs->fwd = NULL; 251 1.30 blymn 252 1.30 blymn tp = tp->fwd; 253 1.30 blymn newt = new->tabs; 254 1.30 blymn 255 1.30 blymn while (tp != NULL) { 256 1.40 christos if ((newt->fwd = malloc(sizeof(*newt->fwd))) == NULL) { 257 1.30 blymn /* error... unwind allocations */ 258 1.30 blymn tp = new->tabs; 259 1.30 blymn while (tp != NULL) { 260 1.30 blymn newt = tp->fwd; 261 1.30 blymn free(tp); 262 1.30 blymn tp = newt; 263 1.30 blymn } 264 1.30 blymn 265 1.30 blymn free(new->string); 266 1.30 blymn free(new); 267 1.30 blymn return NULL; 268 1.30 blymn } 269 1.30 blymn 270 1.40 christos memcpy(newt->fwd, tp, sizeof(*newt->fwd)); 271 1.30 blymn newt->fwd->back = newt; 272 1.30 blymn newt = newt->fwd; 273 1.30 blymn newt->fwd = NULL; 274 1.30 blymn tp = tp->fwd; 275 1.30 blymn } 276 1.30 blymn } 277 1.30 blymn 278 1.30 blymn return new; 279 1.30 blymn } 280 1.30 blymn 281 1.1 blymn /* 282 1.25 blymn * Initialise the row offset for a field, depending on the type of 283 1.25 blymn * field it is and the type of justification used. The justification 284 1.25 blymn * is only used on static single line fields, everything else will 285 1.25 blymn * have the cursor_xpos set to 0. 286 1.25 blymn */ 287 1.25 blymn void 288 1.25 blymn _formi_init_field_xpos(FIELD *field) 289 1.25 blymn { 290 1.25 blymn /* not static or is multi-line which are not justified, so 0 it is */ 291 1.25 blymn if (((field->opts & O_STATIC) != O_STATIC) || 292 1.25 blymn ((field->rows + field->nrows) != 1)) { 293 1.25 blymn field->cursor_xpos = 0; 294 1.25 blymn return; 295 1.25 blymn } 296 1.30 blymn 297 1.25 blymn switch (field->justification) { 298 1.25 blymn case JUSTIFY_RIGHT: 299 1.25 blymn field->cursor_xpos = field->cols - 1; 300 1.25 blymn break; 301 1.25 blymn 302 1.25 blymn case JUSTIFY_CENTER: 303 1.25 blymn field->cursor_xpos = (field->cols - 1) / 2; 304 1.25 blymn break; 305 1.25 blymn 306 1.25 blymn default: /* assume left justify */ 307 1.25 blymn field->cursor_xpos = 0; 308 1.25 blymn break; 309 1.25 blymn } 310 1.25 blymn } 311 1.25 blymn 312 1.30 blymn 313 1.25 blymn /* 314 1.4 blymn * Open the debug file if it is not already open.... 315 1.4 blymn */ 316 1.4 blymn #ifdef DEBUG 317 1.38 christos static FILE *dbg; 318 1.38 christos static const char dbg_file[] = "___form_dbg.out"; 319 1.38 christos 320 1.38 christos void 321 1.38 christos _formi_dbg_printf(const char *fmt, ...) 322 1.4 blymn { 323 1.38 christos va_list ap; 324 1.38 christos 325 1.38 christos if (dbg == NULL && (dbg = fopen(dbg_file, "w")) == NULL) { 326 1.38 christos warn("Cannot open debug file `%s'", dbg_file); 327 1.38 christos return; 328 1.4 blymn } 329 1.38 christos va_start(ap, fmt); 330 1.38 christos vfprintf(dbg, fmt, ap); 331 1.38 christos va_end(ap); 332 1.4 blymn } 333 1.4 blymn #endif 334 1.4 blymn 335 1.4 blymn /* 336 1.22 blymn * Check the sizing of the field, if the maximum size is set for a 337 1.22 blymn * dynamic field then check that the number of rows or columns does 338 1.22 blymn * not exceed the set maximum. The decision to check the rows or 339 1.22 blymn * columns is made on the basis of how many rows are in the field - 340 1.22 blymn * one row means the max applies to the number of columns otherwise it 341 1.22 blymn * applies to the number of rows. If the row/column count is less 342 1.22 blymn * than the maximum then return TRUE. 343 1.21 blymn * 344 1.21 blymn */ 345 1.22 blymn static bool 346 1.22 blymn check_field_size(FIELD *field) 347 1.21 blymn { 348 1.22 blymn if ((field->opts & O_STATIC) != O_STATIC) { 349 1.22 blymn /* dynamic field */ 350 1.22 blymn if (field->max == 0) /* unlimited */ 351 1.22 blymn return TRUE; 352 1.21 blymn 353 1.22 blymn if (field->rows == 1) { 354 1.33 roy return (field->alines->length < field->max); 355 1.22 blymn } else { 356 1.22 blymn return (field->row_count <= field->max); 357 1.22 blymn } 358 1.21 blymn } else { 359 1.22 blymn if ((field->rows + field->nrows) == 1) { 360 1.33 roy return (field->alines->length <= field->cols); 361 1.22 blymn } else { 362 1.22 blymn return (field->row_count <= (field->rows 363 1.22 blymn + field->nrows)); 364 1.22 blymn } 365 1.21 blymn } 366 1.21 blymn } 367 1.21 blymn 368 1.21 blymn /* 369 1.1 blymn * Set the form's current field to the first valid field on the page. 370 1.1 blymn * Assume the fields have been sorted and stitched. 371 1.1 blymn */ 372 1.1 blymn int 373 1.1 blymn _formi_pos_first_field(FORM *form) 374 1.1 blymn { 375 1.1 blymn FIELD *cur; 376 1.1 blymn int old_page; 377 1.1 blymn 378 1.1 blymn old_page = form->page; 379 1.1 blymn 380 1.1 blymn /* scan forward for an active page....*/ 381 1.1 blymn while (form->page_starts[form->page].in_use == 0) { 382 1.1 blymn form->page++; 383 1.1 blymn if (form->page > form->max_page) { 384 1.1 blymn form->page = old_page; 385 1.1 blymn return E_REQUEST_DENIED; 386 1.1 blymn } 387 1.1 blymn } 388 1.1 blymn 389 1.13 blymn /* then scan for a field we can use */ 390 1.1 blymn cur = form->fields[form->page_starts[form->page].first]; 391 1.1 blymn while ((cur->opts & (O_VISIBLE | O_ACTIVE)) 392 1.1 blymn != (O_VISIBLE | O_ACTIVE)) { 393 1.37 christos cur = TAILQ_NEXT(cur, glue); 394 1.37 christos if (cur == NULL) { 395 1.1 blymn form->page = old_page; 396 1.1 blymn return E_REQUEST_DENIED; 397 1.1 blymn } 398 1.1 blymn } 399 1.1 blymn 400 1.1 blymn form->cur_field = cur->index; 401 1.1 blymn return E_OK; 402 1.1 blymn } 403 1.1 blymn 404 1.1 blymn /* 405 1.1 blymn * Set the field to the next active and visible field, the fields are 406 1.1 blymn * traversed in index order in the direction given. If the parameter 407 1.1 blymn * use_sorted is TRUE then the sorted field list will be traversed instead 408 1.1 blymn * of using the field index. 409 1.1 blymn */ 410 1.1 blymn int 411 1.1 blymn _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted) 412 1.1 blymn { 413 1.1 blymn FIELD *cur; 414 1.1 blymn int i; 415 1.1 blymn 416 1.1 blymn i = form->cur_field; 417 1.1 blymn cur = form->fields[i]; 418 1.30 blymn 419 1.1 blymn do { 420 1.1 blymn if (direction == _FORMI_FORWARD) { 421 1.1 blymn if (use_sorted == TRUE) { 422 1.4 blymn if ((form->wrap == FALSE) && 423 1.37 christos (cur == TAILQ_LAST(&form->sorted_fields, 424 1.37 christos _formi_sort_head))) 425 1.4 blymn return E_REQUEST_DENIED; 426 1.37 christos cur = TAILQ_NEXT(cur, glue); 427 1.1 blymn i = cur->index; 428 1.1 blymn } else { 429 1.4 blymn if ((form->wrap == FALSE) && 430 1.4 blymn ((i + 1) >= form->field_count)) 431 1.4 blymn return E_REQUEST_DENIED; 432 1.1 blymn i++; 433 1.1 blymn if (i >= form->field_count) 434 1.1 blymn i = 0; 435 1.1 blymn } 436 1.1 blymn } else { 437 1.1 blymn if (use_sorted == TRUE) { 438 1.4 blymn if ((form->wrap == FALSE) && 439 1.37 christos (cur == TAILQ_FIRST(&form->sorted_fields))) 440 1.4 blymn return E_REQUEST_DENIED; 441 1.37 christos cur = TAILQ_PREV(cur, _formi_sort_head, glue); 442 1.1 blymn i = cur->index; 443 1.1 blymn } else { 444 1.4 blymn if ((form->wrap == FALSE) && (i <= 0)) 445 1.4 blymn return E_REQUEST_DENIED; 446 1.1 blymn i--; 447 1.1 blymn if (i < 0) 448 1.1 blymn i = form->field_count - 1; 449 1.1 blymn } 450 1.1 blymn } 451 1.30 blymn 452 1.1 blymn if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE)) 453 1.1 blymn == (O_VISIBLE | O_ACTIVE)) { 454 1.1 blymn form->cur_field = i; 455 1.1 blymn return E_OK; 456 1.1 blymn } 457 1.1 blymn } 458 1.1 blymn while (i != form->cur_field); 459 1.1 blymn 460 1.1 blymn return E_REQUEST_DENIED; 461 1.1 blymn } 462 1.1 blymn 463 1.1 blymn /* 464 1.30 blymn * Destroy the list of line structs passed by freeing all allocated 465 1.30 blymn * memory. 466 1.1 blymn */ 467 1.30 blymn static void 468 1.30 blymn destroy_row_list(_FORMI_FIELD_LINES *start) 469 1.1 blymn { 470 1.30 blymn _FORMI_FIELD_LINES *temp, *row; 471 1.30 blymn _formi_tab_t *tt, *tp; 472 1.30 blymn 473 1.30 blymn row = start; 474 1.30 blymn while (row != NULL) { 475 1.30 blymn if (row->tabs != NULL) { 476 1.30 blymn /* free up the tab linked list... */ 477 1.30 blymn tp = row->tabs; 478 1.30 blymn while (tp != NULL) { 479 1.30 blymn tt = tp->fwd; 480 1.30 blymn free(tp); 481 1.30 blymn tp = tt; 482 1.30 blymn } 483 1.30 blymn } 484 1.30 blymn 485 1.30 blymn if (row->string != NULL) 486 1.30 blymn free(row->string); 487 1.1 blymn 488 1.30 blymn temp = row->next; 489 1.30 blymn free(row); 490 1.30 blymn row = temp; 491 1.1 blymn } 492 1.1 blymn } 493 1.1 blymn 494 1.1 blymn /* 495 1.1 blymn * Word wrap the contents of the field's buffer 0 if this is allowed. 496 1.1 blymn * If the wrap is successful, that is, the row count nor the buffer 497 1.1 blymn * size is exceeded then the function will return E_OK, otherwise it 498 1.1 blymn * will return E_REQUEST_DENIED. 499 1.1 blymn */ 500 1.9 blymn int 501 1.30 blymn _formi_wrap_field(FIELD *field, _FORMI_FIELD_LINES *loc) 502 1.1 blymn { 503 1.30 blymn int width, wrap_err; 504 1.30 blymn unsigned int pos, saved_xpos, saved_ypos, saved_cur_xpos; 505 1.30 blymn unsigned int saved_row_count; 506 1.30 blymn _FORMI_FIELD_LINES *saved_row, *row, *row_backup, *saved_cur_line; 507 1.30 blymn _FORMI_FIELD_LINES *saved_start_line, *temp; 508 1.15 blymn 509 1.12 blymn if ((field->opts & O_STATIC) == O_STATIC) { 510 1.15 blymn if ((field->rows + field->nrows) == 1) { 511 1.12 blymn return E_OK; /* cannot wrap a single line */ 512 1.15 blymn } 513 1.15 blymn width = field->cols; 514 1.12 blymn } else { 515 1.15 blymn /* if we are limited to one line then don't try to wrap */ 516 1.15 blymn if ((field->drows + field->nrows) == 1) { 517 1.12 blymn return E_OK; 518 1.15 blymn } 519 1.30 blymn 520 1.15 blymn /* 521 1.15 blymn * hueristic - if a dynamic field has more than one line 522 1.15 blymn * on the screen then the field grows rows, otherwise 523 1.15 blymn * it grows columns, effectively a single line field. 524 1.15 blymn * This is documented AT&T behaviour. 525 1.15 blymn */ 526 1.15 blymn if (field->rows > 1) { 527 1.15 blymn width = field->cols; 528 1.15 blymn } else { 529 1.15 blymn return E_OK; 530 1.15 blymn } 531 1.12 blymn } 532 1.16 blymn 533 1.30 blymn row = loc; 534 1.30 blymn 535 1.16 blymn /* if we are not at the top of the field then back up one 536 1.16 blymn * row because we may be able to merge the current row into 537 1.16 blymn * the one above. 538 1.16 blymn */ 539 1.30 blymn if (row->prev != NULL) 540 1.30 blymn row = row->prev; 541 1.30 blymn 542 1.30 blymn saved_row = row; 543 1.30 blymn saved_xpos = field->row_xpos; 544 1.30 blymn saved_cur_xpos = field->cursor_xpos; 545 1.30 blymn saved_ypos = field->cursor_ypos; 546 1.30 blymn saved_row_count = field->row_count; 547 1.30 blymn 548 1.30 blymn /* 549 1.30 blymn * Save a copy of the lines affected, just in case things 550 1.30 blymn * don't work out. 551 1.30 blymn */ 552 1.30 blymn if ((row_backup = copy_row(row)) == NULL) 553 1.30 blymn return E_SYSTEM_ERROR; 554 1.30 blymn 555 1.30 blymn temp = row_backup; 556 1.30 blymn row = row->next; 557 1.30 blymn 558 1.30 blymn saved_cur_line = temp; 559 1.30 blymn saved_start_line = temp; 560 1.12 blymn 561 1.30 blymn while (row != NULL) { 562 1.30 blymn if ((temp->next = copy_row(row)) == NULL) { 563 1.30 blymn /* a row copy failed... free up allocations */ 564 1.30 blymn destroy_row_list(row_backup); 565 1.30 blymn return E_SYSTEM_ERROR; 566 1.30 blymn } 567 1.30 blymn 568 1.30 blymn temp->next->prev = temp; 569 1.30 blymn temp = temp->next; 570 1.30 blymn 571 1.30 blymn if (row == field->start_line) 572 1.30 blymn saved_start_line = temp; 573 1.30 blymn if (row == field->cur_line) 574 1.30 blymn saved_cur_line = temp; 575 1.30 blymn 576 1.30 blymn row = row->next; 577 1.30 blymn } 578 1.30 blymn 579 1.30 blymn row = saved_row; 580 1.30 blymn while (row != NULL) { 581 1.30 blymn pos = row->length - 1; 582 1.30 blymn if (row->expanded < width) { 583 1.1 blymn /* line may be too short, try joining some lines */ 584 1.30 blymn if ((row->hard_ret == TRUE) && (row->next != NULL)) { 585 1.30 blymn /* 586 1.30 blymn * Skip the line if it has a hard return 587 1.30 blymn * and it is not the last, we cannot join 588 1.30 blymn * anything to it. 589 1.15 blymn */ 590 1.30 blymn row = row->next; 591 1.1 blymn continue; 592 1.1 blymn } 593 1.30 blymn 594 1.35 joerg if (row->next == NULL) { 595 1.30 blymn /* 596 1.30 blymn * If there are no more lines and this line 597 1.30 blymn * is too short then our job is over. 598 1.30 blymn */ 599 1.30 blymn break; 600 1.30 blymn } 601 1.1 blymn 602 1.30 blymn if (_formi_join_line(field, &row, 603 1.15 blymn JOIN_NEXT_NW) == E_OK) { 604 1.30 blymn continue; 605 1.1 blymn } else 606 1.1 blymn break; 607 1.30 blymn } else if (row->expanded > width) { 608 1.30 blymn /* line is too long, split it */ 609 1.30 blymn 610 1.23 blymn /* 611 1.23 blymn * split on first whitespace before current word 612 1.23 blymn * if the line has tabs we need to work out where 613 1.23 blymn * the field border lies when the tabs are expanded. 614 1.23 blymn */ 615 1.30 blymn if (row->tabs == NULL) { 616 1.30 blymn pos = width - 1; 617 1.30 blymn if (pos >= row->expanded) 618 1.30 blymn pos = row->expanded - 1; 619 1.23 blymn } else { 620 1.30 blymn pos = tab_fit_len(row, field->cols); 621 1.23 blymn } 622 1.30 blymn 623 1.34 tnozaki if ((!isblank((unsigned char)row->string[pos])) && 624 1.15 blymn ((field->opts & O_WRAP) == O_WRAP)) { 625 1.34 tnozaki if (!isblank((unsigned char)row->string[pos - 1])) 626 1.30 blymn pos = find_sow((unsigned int) pos, 627 1.30 blymn &row); 628 1.16 blymn /* 629 1.16 blymn * If we cannot split the line then return 630 1.16 blymn * NO_ROOM so the driver can tell that it 631 1.16 blymn * should not autoskip (if that is enabled) 632 1.16 blymn */ 633 1.30 blymn if ((pos == 0) 634 1.34 tnozaki || (!isblank((unsigned char)row->string[pos - 1]))) { 635 1.30 blymn wrap_err = E_NO_ROOM; 636 1.30 blymn goto restore_and_exit; 637 1.1 blymn } 638 1.15 blymn } 639 1.1 blymn 640 1.16 blymn /* if we are at the end of the string and it has 641 1.16 blymn * a trailing blank, don't wrap the blank. 642 1.16 blymn */ 643 1.30 blymn if ((row->next == NULL) && (pos == row->length - 1) && 644 1.34 tnozaki (isblank((unsigned char)row->string[pos])) && 645 1.30 blymn row->expanded <= field->cols) 646 1.16 blymn continue; 647 1.17 blymn 648 1.17 blymn /* 649 1.17 blymn * otherwise, if we are still sitting on a 650 1.17 blymn * blank but not at the end of the line 651 1.17 blymn * move forward one char so the blank 652 1.17 blymn * is on the line boundary. 653 1.17 blymn */ 654 1.34 tnozaki if ((isblank((unsigned char)row->string[pos])) && 655 1.30 blymn (pos != row->length - 1)) 656 1.17 blymn pos++; 657 1.30 blymn 658 1.30 blymn if (split_line(field, FALSE, pos, &row) != E_OK) { 659 1.30 blymn wrap_err = E_REQUEST_DENIED; 660 1.30 blymn goto restore_and_exit; 661 1.15 blymn } 662 1.30 blymn } else 663 1.30 blymn /* line is exactly the right length, do next one */ 664 1.30 blymn row = row->next; 665 1.30 blymn } 666 1.30 blymn 667 1.30 blymn /* Check if we have not run out of room */ 668 1.30 blymn if ((((field->opts & O_STATIC) == O_STATIC) && 669 1.30 blymn field->row_count > (field->rows + field->nrows)) || 670 1.30 blymn ((field->max != 0) && (field->row_count > field->max))) { 671 1.30 blymn 672 1.30 blymn wrap_err = E_REQUEST_DENIED; 673 1.30 blymn 674 1.30 blymn restore_and_exit: 675 1.30 blymn if (saved_row->prev == NULL) { 676 1.33 roy field->alines = row_backup; 677 1.30 blymn } else { 678 1.30 blymn saved_row->prev->next = row_backup; 679 1.30 blymn row_backup->prev = saved_row->prev; 680 1.1 blymn } 681 1.30 blymn 682 1.30 blymn field->row_xpos = saved_xpos; 683 1.30 blymn field->cursor_xpos = saved_cur_xpos; 684 1.30 blymn field->cursor_ypos = saved_ypos; 685 1.30 blymn field->row_count = saved_row_count; 686 1.30 blymn field->start_line = saved_start_line; 687 1.30 blymn field->cur_line = saved_cur_line; 688 1.30 blymn 689 1.30 blymn destroy_row_list(saved_row); 690 1.30 blymn return wrap_err; 691 1.1 blymn } 692 1.1 blymn 693 1.30 blymn destroy_row_list(row_backup); 694 1.1 blymn return E_OK; 695 1.1 blymn } 696 1.1 blymn 697 1.1 blymn /* 698 1.1 blymn * Join the two lines that surround the location pos, the type 699 1.15 blymn * variable indicates the direction of the join, JOIN_NEXT will join 700 1.15 blymn * the next line to the current line, JOIN_PREV will join the current 701 1.15 blymn * line to the previous line, the new lines will be wrapped unless the 702 1.15 blymn * _NW versions of the directions are used. Returns E_OK if the join 703 1.15 blymn * was successful or E_REQUEST_DENIED if the join cannot happen. 704 1.1 blymn */ 705 1.1 blymn static int 706 1.30 blymn _formi_join_line(FIELD *field, _FORMI_FIELD_LINES **rowp, int direction) 707 1.1 blymn { 708 1.30 blymn int old_len, count; 709 1.15 blymn struct _formi_field_lines *saved; 710 1.30 blymn char *newp; 711 1.30 blymn _FORMI_FIELD_LINES *row = *rowp; 712 1.17 blymn 713 1.38 christos _formi_dbg_printf("%s: working on row %p, row_count = %d\n", 714 1.38 christos __func__, row, field->row_count); 715 1.30 blymn 716 1.1 blymn if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) { 717 1.30 blymn /* 718 1.30 blymn * See if there is another line following, or if the 719 1.30 blymn * line contains a hard return then we don't join 720 1.30 blymn * any lines to it. 721 1.30 blymn */ 722 1.30 blymn if ((row->next == NULL) || (row->hard_ret == TRUE)) { 723 1.1 blymn return E_REQUEST_DENIED; 724 1.15 blymn } 725 1.17 blymn 726 1.38 christos _formi_dbg_printf( 727 1.38 christos "%s: join_next before length = %d, expanded = %d", 728 1.38 christos __func__, row->length, row->expanded); 729 1.38 christos _formi_dbg_printf( 730 1.38 christos " :: next row length = %d, expanded = %d\n", 731 1.38 christos row->length, row->expanded); 732 1.30 blymn 733 1.30 blymn if (row->allocated < (row->length + row->next->length + 1)) { 734 1.30 blymn if ((newp = realloc(row->string, (size_t)(row->length + 735 1.30 blymn row->next->length 736 1.30 blymn + 1))) == NULL) 737 1.30 blymn return E_REQUEST_DENIED; 738 1.30 blymn row->string = newp; 739 1.30 blymn row->allocated = row->length + row->next->length + 1; 740 1.30 blymn } 741 1.30 blymn 742 1.30 blymn strcat(row->string, row->next->string); 743 1.30 blymn old_len = row->length; 744 1.30 blymn row->length += row->next->length; 745 1.30 blymn if (row->length > 0) 746 1.30 blymn row->expanded = 747 1.30 blymn _formi_tab_expanded_length(row->string, 0, 748 1.30 blymn row->length - 1); 749 1.30 blymn else 750 1.30 blymn row->expanded = 0; 751 1.17 blymn 752 1.30 blymn _formi_calculate_tabs(row); 753 1.30 blymn row->hard_ret = row->next->hard_ret; 754 1.30 blymn 755 1.30 blymn /* adjust current line if it is on the row being eaten */ 756 1.30 blymn if (field->cur_line == row->next) { 757 1.30 blymn field->cur_line = row; 758 1.30 blymn field->row_xpos += old_len; 759 1.30 blymn field->cursor_xpos = 760 1.30 blymn _formi_tab_expanded_length(row->string, 0, 761 1.30 blymn field->row_xpos); 762 1.30 blymn if (field->cursor_xpos > 0) 763 1.30 blymn field->cursor_xpos--; 764 1.30 blymn 765 1.30 blymn if (field->cursor_ypos > 0) 766 1.30 blymn field->cursor_ypos--; 767 1.30 blymn else { 768 1.30 blymn if (field->start_line->prev != NULL) 769 1.30 blymn field->start_line = 770 1.30 blymn field->start_line->prev; 771 1.30 blymn } 772 1.30 blymn } 773 1.30 blymn 774 1.30 blymn /* remove joined line record from the row list */ 775 1.30 blymn add_to_free(field, row->next); 776 1.30 blymn 777 1.38 christos _formi_dbg_printf( 778 1.38 christos "%s: exit length = %d, expanded = %d\n", 779 1.38 christos __func__, row->length, row->expanded); 780 1.1 blymn } else { 781 1.30 blymn if (row->prev == NULL) { 782 1.30 blymn return E_REQUEST_DENIED; 783 1.30 blymn } 784 1.30 blymn 785 1.30 blymn saved = row->prev; 786 1.30 blymn 787 1.30 blymn /* 788 1.30 blymn * Don't try to join if the line above has a hard 789 1.30 blymn * return on it. 790 1.30 blymn */ 791 1.30 blymn if (saved->hard_ret == TRUE) { 792 1.1 blymn return E_REQUEST_DENIED; 793 1.15 blymn } 794 1.30 blymn 795 1.38 christos _formi_dbg_printf( 796 1.38 christos "%s: join_prev before length = %d, expanded = %d", 797 1.38 christos __func__, row->length, row->expanded); 798 1.38 christos _formi_dbg_printf( 799 1.38 christos " :: prev row length = %d, expanded = %d\n", 800 1.38 christos saved->length, saved->expanded); 801 1.30 blymn 802 1.30 blymn if (saved->allocated < (row->length + saved->length + 1)) { 803 1.30 blymn if ((newp = realloc(saved->string, 804 1.30 blymn (size_t) (row->length + 805 1.30 blymn saved->length 806 1.30 blymn + 1))) == NULL) 807 1.30 blymn return E_REQUEST_DENIED; 808 1.30 blymn saved->string = newp; 809 1.30 blymn saved->allocated = row->length + saved->length + 1; 810 1.30 blymn } 811 1.30 blymn 812 1.30 blymn strcat(saved->string, row->string); 813 1.30 blymn old_len = saved->length; 814 1.30 blymn saved->length += row->length; 815 1.30 blymn if (saved->length > 0) 816 1.30 blymn saved->expanded = 817 1.30 blymn _formi_tab_expanded_length(saved->string, 0, 818 1.30 blymn saved->length - 1); 819 1.30 blymn else 820 1.30 blymn saved->length = 0; 821 1.30 blymn 822 1.30 blymn saved->hard_ret = row->hard_ret; 823 1.30 blymn 824 1.30 blymn /* adjust current line if it was on the row being eaten */ 825 1.30 blymn if (field->cur_line == row) { 826 1.30 blymn field->cur_line = saved; 827 1.30 blymn field->row_xpos += old_len; 828 1.30 blymn field->cursor_xpos = 829 1.30 blymn _formi_tab_expanded_length(saved->string, 0, 830 1.30 blymn field->row_xpos); 831 1.30 blymn if (field->cursor_xpos > 0) 832 1.30 blymn field->cursor_xpos--; 833 1.30 blymn } 834 1.30 blymn 835 1.30 blymn add_to_free(field, row); 836 1.30 blymn 837 1.38 christos _formi_dbg_printf( 838 1.38 christos "%s: exit length = %d, expanded = %d\n", __func__, 839 1.38 christos saved->length, saved->expanded); 840 1.30 blymn row = saved; 841 1.30 blymn } 842 1.30 blymn 843 1.30 blymn 844 1.30 blymn /* 845 1.30 blymn * Work out where the line lies in the field in relation to 846 1.30 blymn * the cursor_ypos. First count the rows from the start of 847 1.30 blymn * the field until we hit the row we just worked on. 848 1.30 blymn */ 849 1.30 blymn saved = field->start_line; 850 1.30 blymn count = 0; 851 1.30 blymn while (saved->next != NULL) { 852 1.30 blymn if (saved == row) 853 1.30 blymn break; 854 1.30 blymn count++; 855 1.30 blymn saved = saved->next; 856 1.1 blymn } 857 1.15 blymn 858 1.30 blymn /* now check if we need to adjust cursor_ypos */ 859 1.30 blymn if (field->cursor_ypos > count) { 860 1.30 blymn field->cursor_ypos--; 861 1.17 blymn } 862 1.17 blymn 863 1.15 blymn field->row_count--; 864 1.30 blymn *rowp = row; 865 1.30 blymn 866 1.1 blymn /* wrap the field if required, if this fails undo the change */ 867 1.1 blymn if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) { 868 1.30 blymn if (_formi_wrap_field(field, row) != E_OK) { 869 1.1 blymn return E_REQUEST_DENIED; 870 1.1 blymn } 871 1.1 blymn } 872 1.15 blymn 873 1.15 blymn return E_OK; 874 1.15 blymn } 875 1.15 blymn 876 1.15 blymn /* 877 1.30 blymn * Split the line at the given position, if possible. If hard_split is 878 1.30 blymn * TRUE then split the line regardless of the position, otherwise don't 879 1.30 blymn * split at the beginning of a line. 880 1.15 blymn */ 881 1.15 blymn static int 882 1.30 blymn split_line(FIELD *field, bool hard_split, unsigned pos, 883 1.30 blymn _FORMI_FIELD_LINES **rowp) 884 1.15 blymn { 885 1.30 blymn struct _formi_field_lines *new_line; 886 1.30 blymn char *newp; 887 1.30 blymn _FORMI_FIELD_LINES *row = *rowp; 888 1.15 blymn 889 1.30 blymn /* if asked to split right where the line already starts then 890 1.30 blymn * just return - nothing to do unless we are appending a line 891 1.30 blymn * to the buffer. 892 1.30 blymn */ 893 1.30 blymn if ((pos == 0) && (hard_split == FALSE)) 894 1.30 blymn return E_OK; 895 1.15 blymn 896 1.38 christos _formi_dbg_printf("%s: splitting line at %d\n", __func__, pos); 897 1.30 blymn 898 1.30 blymn /* Need an extra line struct, check free list first */ 899 1.30 blymn if (field->free != NULL) { 900 1.30 blymn new_line = field->free; 901 1.30 blymn field->free = new_line->next; 902 1.30 blymn if (field->free != NULL) 903 1.30 blymn field->free->prev = NULL; 904 1.30 blymn } else { 905 1.38 christos if ((new_line = malloc(sizeof(*new_line))) == NULL) 906 1.15 blymn return E_SYSTEM_ERROR; 907 1.30 blymn new_line->prev = NULL; 908 1.30 blymn new_line->next = NULL; 909 1.30 blymn new_line->allocated = 0; 910 1.30 blymn new_line->length = 0; 911 1.30 blymn new_line->expanded = 0; 912 1.30 blymn new_line->string = NULL; 913 1.30 blymn new_line->hard_ret = FALSE; 914 1.30 blymn new_line->tabs = NULL; 915 1.15 blymn } 916 1.30 blymn 917 1.38 christos _formi_dbg_printf("%s: enter: length = %d, expanded = %d\n", __func__, 918 1.38 christos row->length, row->expanded); 919 1.17 blymn 920 1.30 blymn assert((row->length < INT_MAX) && (row->expanded < INT_MAX)); 921 1.30 blymn 922 1.30 blymn 923 1.30 blymn /* add new line to the row list */ 924 1.30 blymn new_line->next = row->next; 925 1.30 blymn new_line->prev = row; 926 1.30 blymn row->next = new_line; 927 1.30 blymn if (new_line->next != NULL) 928 1.30 blymn new_line->next->prev = new_line; 929 1.30 blymn 930 1.30 blymn new_line->length = row->length - pos; 931 1.30 blymn if (new_line->length >= new_line->allocated) { 932 1.30 blymn if ((newp = realloc(new_line->string, 933 1.30 blymn (size_t) new_line->length + 1)) == NULL) 934 1.30 blymn return E_SYSTEM_ERROR; 935 1.30 blymn new_line->string = newp; 936 1.30 blymn new_line->allocated = new_line->length + 1; 937 1.30 blymn } 938 1.30 blymn 939 1.30 blymn strcpy(new_line->string, &row->string[pos]); 940 1.30 blymn 941 1.30 blymn row->length = pos; 942 1.30 blymn row->string[pos] = '\0'; 943 1.30 blymn 944 1.30 blymn if (row->length != 0) 945 1.30 blymn row->expanded = _formi_tab_expanded_length(row->string, 0, 946 1.30 blymn row->length - 1); 947 1.30 blymn else 948 1.30 blymn row->expanded = 0; 949 1.30 blymn _formi_calculate_tabs(row); 950 1.30 blymn 951 1.30 blymn if (new_line->length != 0) 952 1.30 blymn new_line->expanded = 953 1.30 blymn _formi_tab_expanded_length(new_line->string, 0, 954 1.30 blymn new_line->length - 1); 955 1.30 blymn else 956 1.30 blymn new_line->expanded = 0; 957 1.30 blymn 958 1.30 blymn _formi_calculate_tabs(new_line); 959 1.30 blymn 960 1.30 blymn /* 961 1.30 blymn * If the given row was the current line then adjust the 962 1.30 blymn * current line pointer if necessary 963 1.30 blymn */ 964 1.30 blymn if ((field->cur_line == row) && (field->row_xpos >= pos)) { 965 1.30 blymn field->cur_line = new_line; 966 1.30 blymn field->row_xpos -= pos; 967 1.30 blymn field->cursor_xpos = 968 1.30 blymn _formi_tab_expanded_length(new_line->string, 0, 969 1.30 blymn field->row_xpos); 970 1.30 blymn if (field->cursor_xpos > 0) 971 1.30 blymn field->cursor_xpos--; 972 1.30 blymn 973 1.30 blymn field->cursor_ypos++; 974 1.30 blymn if (field->cursor_ypos >= field->rows) { 975 1.30 blymn if (field->start_line->next != NULL) { 976 1.30 blymn field->start_line = field->start_line->next; 977 1.30 blymn field->cursor_ypos = field->rows - 1; 978 1.30 blymn } 979 1.30 blymn else 980 1.30 blymn assert(field->start_line->next == NULL); 981 1.30 blymn } 982 1.30 blymn } 983 1.17 blymn 984 1.30 blymn /* 985 1.30 blymn * If the line split had a hard return then replace the 986 1.30 blymn * current line's hard return with a soft return and carry 987 1.30 blymn * the hard return onto the line after. 988 1.17 blymn */ 989 1.30 blymn if (row->hard_ret == TRUE) { 990 1.30 blymn new_line->hard_ret = TRUE; 991 1.30 blymn row->hard_ret = FALSE; 992 1.17 blymn } 993 1.17 blymn 994 1.30 blymn /* 995 1.30 blymn * except where we are doing a hard split then the current 996 1.30 blymn * row must have a hard return on it too... 997 1.30 blymn */ 998 1.30 blymn if (hard_split == TRUE) { 999 1.30 blymn row->hard_ret = TRUE; 1000 1.30 blymn } 1001 1.15 blymn 1002 1.30 blymn assert(((row->expanded < INT_MAX) && 1003 1.30 blymn (new_line->expanded < INT_MAX) && 1004 1.30 blymn (row->length < INT_MAX) && 1005 1.30 blymn (new_line->length < INT_MAX))); 1006 1.30 blymn 1007 1.38 christos _formi_dbg_printf("%s: exit: ", __func__); 1008 1.38 christos _formi_dbg_printf("row.length = %d, row.expanded = %d, ", 1009 1.38 christos row->length, row->expanded); 1010 1.38 christos _formi_dbg_printf("next_line.length = %d, next_line.expanded = %d, ", 1011 1.38 christos new_line->length, new_line->expanded); 1012 1.38 christos _formi_dbg_printf("row_count = %d\n", field->row_count + 1); 1013 1.30 blymn 1014 1.17 blymn field->row_count++; 1015 1.30 blymn *rowp = new_line; 1016 1.17 blymn 1017 1.1 blymn return E_OK; 1018 1.1 blymn } 1019 1.1 blymn 1020 1.1 blymn /* 1021 1.1 blymn * skip the blanks in the given string, start at the index start and 1022 1.1 blymn * continue forward until either the end of the string or a non-blank 1023 1.1 blymn * character is found. Return the index of either the end of the string or 1024 1.1 blymn * the first non-blank character. 1025 1.1 blymn */ 1026 1.1 blymn unsigned 1027 1.8 blymn _formi_skip_blanks(char *string, unsigned int start) 1028 1.1 blymn { 1029 1.1 blymn unsigned int i; 1030 1.1 blymn 1031 1.1 blymn i = start; 1032 1.30 blymn 1033 1.34 tnozaki while ((string[i] != '\0') && isblank((unsigned char)string[i])) 1034 1.1 blymn i++; 1035 1.1 blymn 1036 1.1 blymn return i; 1037 1.1 blymn } 1038 1.1 blymn 1039 1.1 blymn /* 1040 1.30 blymn * Skip the blanks in the string associated with the given row, pass back 1041 1.30 blymn * the row and the offset at which the first non-blank is found. If no 1042 1.30 blymn * non-blank character is found then return the index to the last 1043 1.30 blymn * character on the last line. 1044 1.30 blymn */ 1045 1.30 blymn 1046 1.30 blymn unsigned 1047 1.30 blymn field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp) 1048 1.30 blymn { 1049 1.30 blymn unsigned int i; 1050 1.30 blymn _FORMI_FIELD_LINES *row, *last = NULL; 1051 1.30 blymn 1052 1.30 blymn row = *rowp; 1053 1.30 blymn i = start; 1054 1.30 blymn 1055 1.30 blymn do { 1056 1.30 blymn i = _formi_skip_blanks(&row->string[i], i); 1057 1.34 tnozaki if (!isblank((unsigned char)row->string[i])) { 1058 1.30 blymn last = row; 1059 1.30 blymn row = row->next; 1060 1.30 blymn /* 1061 1.30 blymn * don't reset if last line otherwise we will 1062 1.30 blymn * not be at the end of the string. 1063 1.30 blymn */ 1064 1.30 blymn if (row != NULL) 1065 1.30 blymn i = 0; 1066 1.30 blymn } else 1067 1.30 blymn break; 1068 1.30 blymn } 1069 1.30 blymn while (row != NULL); 1070 1.30 blymn 1071 1.30 blymn /* 1072 1.30 blymn * If we hit the end of the row list then point at the last row 1073 1.30 blymn * otherwise we return the row we found the blank on. 1074 1.30 blymn */ 1075 1.30 blymn if (row == NULL) 1076 1.30 blymn *rowp = last; 1077 1.30 blymn else 1078 1.30 blymn *rowp = row; 1079 1.30 blymn 1080 1.30 blymn return i; 1081 1.30 blymn } 1082 1.30 blymn 1083 1.30 blymn /* 1084 1.1 blymn * Return the index of the top left most field of the two given fields. 1085 1.1 blymn */ 1086 1.1 blymn static int 1087 1.1 blymn _formi_top_left(FORM *form, int a, int b) 1088 1.1 blymn { 1089 1.1 blymn /* lower row numbers always win here.... */ 1090 1.1 blymn if (form->fields[a]->form_row < form->fields[b]->form_row) 1091 1.1 blymn return a; 1092 1.1 blymn 1093 1.1 blymn if (form->fields[a]->form_row > form->fields[b]->form_row) 1094 1.1 blymn return b; 1095 1.1 blymn 1096 1.1 blymn /* rows must be equal, check columns */ 1097 1.1 blymn if (form->fields[a]->form_col < form->fields[b]->form_col) 1098 1.1 blymn return a; 1099 1.1 blymn 1100 1.1 blymn if (form->fields[a]->form_col > form->fields[b]->form_col) 1101 1.1 blymn return b; 1102 1.1 blymn 1103 1.1 blymn /* if we get here fields must be in exactly the same place, punt */ 1104 1.1 blymn return a; 1105 1.1 blymn } 1106 1.1 blymn 1107 1.1 blymn /* 1108 1.1 blymn * Return the index to the field that is the bottom-right-most of the 1109 1.1 blymn * two given fields. 1110 1.1 blymn */ 1111 1.1 blymn static int 1112 1.1 blymn _formi_bottom_right(FORM *form, int a, int b) 1113 1.1 blymn { 1114 1.1 blymn /* check the rows first, biggest row wins */ 1115 1.1 blymn if (form->fields[a]->form_row > form->fields[b]->form_row) 1116 1.1 blymn return a; 1117 1.1 blymn if (form->fields[a]->form_row < form->fields[b]->form_row) 1118 1.1 blymn return b; 1119 1.1 blymn 1120 1.1 blymn /* rows must be equal, check cols, biggest wins */ 1121 1.1 blymn if (form->fields[a]->form_col > form->fields[b]->form_col) 1122 1.1 blymn return a; 1123 1.1 blymn if (form->fields[a]->form_col < form->fields[b]->form_col) 1124 1.1 blymn return b; 1125 1.1 blymn 1126 1.1 blymn /* fields in the same place, punt */ 1127 1.1 blymn return a; 1128 1.1 blymn } 1129 1.1 blymn 1130 1.1 blymn /* 1131 1.1 blymn * Find the end of the current word in the string str, starting at 1132 1.1 blymn * offset - the end includes any trailing whitespace. If the end of 1133 1.1 blymn * the string is found before a new word then just return the offset 1134 1.30 blymn * to the end of the string. If do_join is TRUE then lines will be 1135 1.30 blymn * joined (without wrapping) until either the end of the field or the 1136 1.30 blymn * end of a word is found (whichever comes first). 1137 1.1 blymn */ 1138 1.1 blymn static int 1139 1.30 blymn find_eow(FIELD *cur, unsigned int offset, bool do_join, 1140 1.30 blymn _FORMI_FIELD_LINES **rowp) 1141 1.1 blymn { 1142 1.1 blymn int start; 1143 1.30 blymn _FORMI_FIELD_LINES *row; 1144 1.1 blymn 1145 1.30 blymn row = *rowp; 1146 1.1 blymn start = offset; 1147 1.1 blymn 1148 1.30 blymn do { 1149 1.30 blymn /* first skip any non-whitespace */ 1150 1.30 blymn while ((row->string[start] != '\0') 1151 1.34 tnozaki && !isblank((unsigned char)row->string[start])) 1152 1.30 blymn start++; 1153 1.30 blymn 1154 1.30 blymn /* see if we hit the end of the string */ 1155 1.30 blymn if (row->string[start] == '\0') { 1156 1.30 blymn if (do_join == TRUE) { 1157 1.30 blymn if (row->next == NULL) 1158 1.30 blymn return start; 1159 1.30 blymn 1160 1.30 blymn if (_formi_join_line(cur, &row, JOIN_NEXT_NW) 1161 1.30 blymn != E_OK) 1162 1.30 blymn return E_REQUEST_DENIED; 1163 1.30 blymn } else { 1164 1.30 blymn do { 1165 1.30 blymn if (row->next == NULL) { 1166 1.30 blymn *rowp = row; 1167 1.30 blymn return start; 1168 1.30 blymn } else { 1169 1.30 blymn row = row->next; 1170 1.30 blymn start = 0; 1171 1.30 blymn } 1172 1.30 blymn } while (row->length == 0); 1173 1.30 blymn } 1174 1.30 blymn } 1175 1.34 tnozaki } while (!isblank((unsigned char)row->string[start])); 1176 1.30 blymn 1177 1.30 blymn do { 1178 1.30 blymn /* otherwise skip the whitespace.... */ 1179 1.30 blymn while ((row->string[start] != '\0') 1180 1.34 tnozaki && isblank((unsigned char)row->string[start])) 1181 1.30 blymn start++; 1182 1.30 blymn 1183 1.30 blymn if (row->string[start] == '\0') { 1184 1.30 blymn if (do_join == TRUE) { 1185 1.30 blymn if (row->next == NULL) 1186 1.30 blymn return start; 1187 1.30 blymn 1188 1.30 blymn if (_formi_join_line(cur, &row, JOIN_NEXT_NW) 1189 1.30 blymn != E_OK) 1190 1.30 blymn return E_REQUEST_DENIED; 1191 1.30 blymn } else { 1192 1.30 blymn do { 1193 1.30 blymn if (row->next == NULL) { 1194 1.30 blymn *rowp = row; 1195 1.30 blymn return start; 1196 1.30 blymn } else { 1197 1.30 blymn row = row->next; 1198 1.30 blymn start = 0; 1199 1.30 blymn } 1200 1.30 blymn } while (row->length == 0); 1201 1.30 blymn } 1202 1.30 blymn } 1203 1.34 tnozaki } while (isblank((unsigned char)row->string[start])); 1204 1.30 blymn 1205 1.30 blymn *rowp = row; 1206 1.1 blymn return start; 1207 1.1 blymn } 1208 1.1 blymn 1209 1.1 blymn /* 1210 1.1 blymn * Find the beginning of the current word in the string str, starting 1211 1.1 blymn * at offset. 1212 1.1 blymn */ 1213 1.1 blymn static int 1214 1.30 blymn find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp) 1215 1.1 blymn { 1216 1.1 blymn int start; 1217 1.30 blymn char *str; 1218 1.30 blymn _FORMI_FIELD_LINES *row; 1219 1.1 blymn 1220 1.30 blymn row = *rowp; 1221 1.30 blymn str = row->string; 1222 1.1 blymn start = offset; 1223 1.1 blymn 1224 1.30 blymn do { 1225 1.30 blymn if (start > 0) { 1226 1.34 tnozaki if (isblank((unsigned char)str[start]) || 1227 1.34 tnozaki isblank((unsigned char)str[start - 1])) { 1228 1.34 tnozaki if (isblank((unsigned char)str[start - 1])) 1229 1.30 blymn start--; 1230 1.30 blymn /* skip the whitespace.... */ 1231 1.34 tnozaki while ((start >= 0) && 1232 1.34 tnozaki isblank((unsigned char)str[start])) 1233 1.30 blymn start--; 1234 1.30 blymn } 1235 1.30 blymn } 1236 1.30 blymn 1237 1.30 blymn /* see if we hit the start of the string */ 1238 1.30 blymn if (start < 0) { 1239 1.30 blymn do { 1240 1.30 blymn if (row->prev == NULL) { 1241 1.30 blymn *rowp = row; 1242 1.30 blymn start = 0; 1243 1.30 blymn return start; 1244 1.30 blymn } else { 1245 1.30 blymn row = row->prev; 1246 1.30 blymn str = row->string; 1247 1.30 blymn if (row->length > 0) 1248 1.30 blymn start = row->length - 1; 1249 1.30 blymn else 1250 1.30 blymn start = 0; 1251 1.30 blymn } 1252 1.30 blymn } while (row->length == 0); 1253 1.1 blymn } 1254 1.34 tnozaki } while (isblank((unsigned char)row->string[start])); 1255 1.30 blymn 1256 1.1 blymn /* see if we hit the start of the string */ 1257 1.30 blymn if (start < 0) { 1258 1.30 blymn *rowp = row; 1259 1.1 blymn return 0; 1260 1.30 blymn } 1261 1.1 blymn 1262 1.1 blymn /* now skip any non-whitespace */ 1263 1.30 blymn do { 1264 1.34 tnozaki while ((start >= 0) && !isblank((unsigned char)str[start])) 1265 1.30 blymn start--; 1266 1.1 blymn 1267 1.30 blymn 1268 1.30 blymn if (start < 0) { 1269 1.30 blymn do { 1270 1.30 blymn if (row->prev == NULL) { 1271 1.30 blymn *rowp = row; 1272 1.30 blymn start = 0; 1273 1.30 blymn return start; 1274 1.30 blymn } else { 1275 1.30 blymn row = row->prev; 1276 1.30 blymn str = row->string; 1277 1.30 blymn if (row->length > 0) 1278 1.30 blymn start = row->length - 1; 1279 1.30 blymn else 1280 1.30 blymn start = 0; 1281 1.30 blymn } 1282 1.30 blymn } while (row->length == 0); 1283 1.30 blymn } 1284 1.34 tnozaki } while (!isblank((unsigned char)str[start])); 1285 1.30 blymn 1286 1.30 blymn if (start > 0) { 1287 1.1 blymn start++; /* last loop has us pointing at a space, adjust */ 1288 1.30 blymn if (start >= row->length) { 1289 1.30 blymn if (row->next != NULL) { 1290 1.30 blymn start = 0; 1291 1.30 blymn row = row->next; 1292 1.30 blymn } else { 1293 1.30 blymn start = row->length - 1; 1294 1.30 blymn } 1295 1.30 blymn } 1296 1.30 blymn } 1297 1.1 blymn 1298 1.1 blymn if (start < 0) 1299 1.1 blymn start = 0; 1300 1.1 blymn 1301 1.30 blymn *rowp = row; 1302 1.1 blymn return start; 1303 1.1 blymn } 1304 1.1 blymn 1305 1.1 blymn /* 1306 1.1 blymn * Scroll the field forward the given number of lines. 1307 1.1 blymn */ 1308 1.1 blymn static void 1309 1.1 blymn _formi_scroll_fwd(FIELD *field, unsigned int amt) 1310 1.1 blymn { 1311 1.30 blymn unsigned int count; 1312 1.30 blymn _FORMI_FIELD_LINES *end_row; 1313 1.30 blymn 1314 1.30 blymn end_row = field->start_line; 1315 1.30 blymn /* walk the line structs forward to find the bottom of the field */ 1316 1.30 blymn count = field->rows - 1; 1317 1.30 blymn while ((count > 0) && (end_row->next != NULL)) 1318 1.30 blymn { 1319 1.30 blymn count--; 1320 1.30 blymn end_row = end_row->next; 1321 1.30 blymn } 1322 1.30 blymn 1323 1.30 blymn /* check if there are lines to scroll */ 1324 1.30 blymn if ((count > 0) && (end_row->next == NULL)) 1325 1.1 blymn return; 1326 1.1 blymn 1327 1.30 blymn /* 1328 1.30 blymn * ok, lines to scroll - do this by walking both the start_line 1329 1.30 blymn * and the end_row at the same time for amt lines, we stop when 1330 1.30 blymn * either we have done the number of lines or end_row hits the 1331 1.30 blymn * last line in the field. 1332 1.30 blymn */ 1333 1.30 blymn count = amt; 1334 1.30 blymn while ((count > 0) && (end_row->next != NULL)) { 1335 1.30 blymn count--; 1336 1.30 blymn field->start_line = field->start_line->next; 1337 1.30 blymn end_row = end_row->next; 1338 1.30 blymn } 1339 1.1 blymn } 1340 1.1 blymn 1341 1.1 blymn /* 1342 1.1 blymn * Scroll the field backward the given number of lines. 1343 1.1 blymn */ 1344 1.1 blymn static void 1345 1.1 blymn _formi_scroll_back(FIELD *field, unsigned int amt) 1346 1.1 blymn { 1347 1.30 blymn unsigned int count; 1348 1.30 blymn 1349 1.30 blymn /* check for lines above */ 1350 1.30 blymn if (field->start_line->prev == NULL) 1351 1.1 blymn return; 1352 1.1 blymn 1353 1.30 blymn /* 1354 1.30 blymn * Backward scroll is easy, follow row struct chain backward until 1355 1.30 blymn * the number of lines done or we reach the top of the field. 1356 1.30 blymn */ 1357 1.30 blymn count = amt; 1358 1.30 blymn while ((count > 0) && (field->start_line->prev != NULL)) { 1359 1.30 blymn count--; 1360 1.30 blymn field->start_line = field->start_line->prev; 1361 1.30 blymn } 1362 1.1 blymn } 1363 1.1 blymn 1364 1.1 blymn /* 1365 1.1 blymn * Scroll the field forward the given number of characters. 1366 1.1 blymn */ 1367 1.1 blymn void 1368 1.30 blymn _formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, int unsigned amt) 1369 1.1 blymn { 1370 1.30 blymn unsigned int end, scroll_amt, expanded; 1371 1.23 blymn _formi_tab_t *ts; 1372 1.23 blymn 1373 1.30 blymn 1374 1.30 blymn if ((row->tabs == NULL) || (row->tabs->in_use == FALSE)) { 1375 1.23 blymn /* if the line has no tabs things are easy... */ 1376 1.23 blymn end = field->start_char + field->cols + amt - 1; 1377 1.23 blymn scroll_amt = amt; 1378 1.30 blymn if (end > row->length) { 1379 1.30 blymn end = row->length; 1380 1.23 blymn scroll_amt = end - field->start_char - field->cols + 1; 1381 1.23 blymn } 1382 1.23 blymn } else { 1383 1.23 blymn /* 1384 1.23 blymn * If there are tabs we need to add on the scroll amount, 1385 1.23 blymn * find the last char position that will fit into 1386 1.23 blymn * the field and finally fix up the start_char. This 1387 1.23 blymn * is a lot of work but handling the case where there 1388 1.23 blymn * are not enough chars to scroll by amt is difficult. 1389 1.23 blymn */ 1390 1.23 blymn end = field->start_char + field->row_xpos + amt; 1391 1.30 blymn if (end >= row->length) 1392 1.30 blymn end = row->length - 1; 1393 1.23 blymn else { 1394 1.23 blymn expanded = _formi_tab_expanded_length( 1395 1.30 blymn row->string, 1396 1.23 blymn field->start_char + amt, 1397 1.23 blymn field->start_char + field->row_xpos + amt); 1398 1.30 blymn ts = row->tabs; 1399 1.23 blymn /* skip tabs to the lhs of our starting point */ 1400 1.23 blymn while ((ts != NULL) && (ts->in_use == TRUE) 1401 1.23 blymn && (ts->pos < end)) 1402 1.23 blymn ts = ts->fwd; 1403 1.30 blymn 1404 1.23 blymn while ((expanded <= field->cols) 1405 1.30 blymn && (end < row->length)) { 1406 1.30 blymn if (row->string[end] == '\t') { 1407 1.23 blymn assert((ts != NULL) 1408 1.23 blymn && (ts->in_use == TRUE)); 1409 1.23 blymn if (ts->pos == end) { 1410 1.23 blymn if ((expanded + ts->size) 1411 1.23 blymn > field->cols) 1412 1.23 blymn break; 1413 1.23 blymn expanded += ts->size; 1414 1.23 blymn ts = ts->fwd; 1415 1.23 blymn } 1416 1.23 blymn else 1417 1.23 blymn assert(ts->pos == end); 1418 1.23 blymn } else 1419 1.23 blymn expanded++; 1420 1.23 blymn end++; 1421 1.23 blymn } 1422 1.23 blymn } 1423 1.23 blymn 1424 1.23 blymn scroll_amt = tab_fit_window(field, end, field->cols); 1425 1.23 blymn if (scroll_amt < field->start_char) 1426 1.23 blymn scroll_amt = 1; 1427 1.23 blymn else 1428 1.23 blymn scroll_amt -= field->start_char; 1429 1.23 blymn 1430 1.23 blymn scroll_amt = min(scroll_amt, amt); 1431 1.23 blymn } 1432 1.23 blymn 1433 1.23 blymn field->start_char += scroll_amt; 1434 1.23 blymn field->cursor_xpos = 1435 1.30 blymn _formi_tab_expanded_length(row->string, 1436 1.23 blymn field->start_char, 1437 1.23 blymn field->row_xpos 1438 1.23 blymn + field->start_char) - 1; 1439 1.30 blymn 1440 1.1 blymn } 1441 1.30 blymn 1442 1.1 blymn /* 1443 1.1 blymn * Scroll the field backward the given number of characters. 1444 1.1 blymn */ 1445 1.1 blymn void 1446 1.30 blymn _formi_hscroll_back(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt) 1447 1.1 blymn { 1448 1.14 blymn field->start_char -= min(field->start_char, amt); 1449 1.23 blymn field->cursor_xpos = 1450 1.30 blymn _formi_tab_expanded_length(row->string, field->start_char, 1451 1.23 blymn field->row_xpos 1452 1.23 blymn + field->start_char) - 1; 1453 1.23 blymn if (field->cursor_xpos >= field->cols) { 1454 1.23 blymn field->row_xpos = 0; 1455 1.23 blymn field->cursor_xpos = 0; 1456 1.23 blymn } 1457 1.1 blymn } 1458 1.30 blymn 1459 1.1 blymn /* 1460 1.1 blymn * Find the different pages in the form fields and assign the form 1461 1.1 blymn * page_starts array with the information to find them. 1462 1.1 blymn */ 1463 1.1 blymn int 1464 1.1 blymn _formi_find_pages(FORM *form) 1465 1.1 blymn { 1466 1.1 blymn int i, cur_page = 0; 1467 1.1 blymn 1468 1.40 christos if ((form->page_starts = calloc((form->max_page + 1), 1469 1.40 christos sizeof(*form->page_starts))) == NULL) 1470 1.1 blymn return E_SYSTEM_ERROR; 1471 1.1 blymn 1472 1.40 christos for (i = 0; i < form->field_count; i++) { 1473 1.1 blymn if (form->fields[i]->page_break == 1) 1474 1.1 blymn cur_page++; 1475 1.1 blymn if (form->page_starts[cur_page].in_use == 0) { 1476 1.1 blymn form->page_starts[cur_page].in_use = 1; 1477 1.1 blymn form->page_starts[cur_page].first = i; 1478 1.1 blymn form->page_starts[cur_page].last = i; 1479 1.1 blymn form->page_starts[cur_page].top_left = i; 1480 1.1 blymn form->page_starts[cur_page].bottom_right = i; 1481 1.1 blymn } else { 1482 1.1 blymn form->page_starts[cur_page].last = i; 1483 1.1 blymn form->page_starts[cur_page].top_left = 1484 1.1 blymn _formi_top_left(form, 1485 1.1 blymn form->page_starts[cur_page].top_left, 1486 1.1 blymn i); 1487 1.1 blymn form->page_starts[cur_page].bottom_right = 1488 1.1 blymn _formi_bottom_right(form, 1489 1.1 blymn form->page_starts[cur_page].bottom_right, 1490 1.1 blymn i); 1491 1.1 blymn } 1492 1.1 blymn } 1493 1.30 blymn 1494 1.1 blymn return E_OK; 1495 1.1 blymn } 1496 1.1 blymn 1497 1.1 blymn /* 1498 1.1 blymn * Completely redraw the field of the given form. 1499 1.1 blymn */ 1500 1.9 blymn void 1501 1.1 blymn _formi_redraw_field(FORM *form, int field) 1502 1.1 blymn { 1503 1.30 blymn unsigned int pre, post, flen, slen, i, j, start, line; 1504 1.30 blymn unsigned int tab, cpos, len; 1505 1.23 blymn char *str, c; 1506 1.1 blymn FIELD *cur; 1507 1.30 blymn _FORMI_FIELD_LINES *row; 1508 1.1 blymn #ifdef DEBUG 1509 1.1 blymn char buffer[100]; 1510 1.1 blymn #endif 1511 1.1 blymn 1512 1.1 blymn cur = form->fields[field]; 1513 1.1 blymn flen = cur->cols; 1514 1.1 blymn slen = 0; 1515 1.1 blymn start = 0; 1516 1.30 blymn line = 0; 1517 1.1 blymn 1518 1.30 blymn for (row = cur->start_line; ((row != NULL) && (line < cur->rows)); 1519 1.30 blymn row = row->next, line++) { 1520 1.30 blymn wmove(form->scrwin, (int) (cur->form_row + line), 1521 1.15 blymn (int) cur->form_col); 1522 1.23 blymn if ((cur->rows + cur->nrows) == 1) { 1523 1.30 blymn if ((cur->cols + cur->start_char) >= row->length) 1524 1.30 blymn len = row->length; 1525 1.23 blymn else 1526 1.23 blymn len = cur->cols + cur->start_char; 1527 1.30 blymn if (row->string != NULL) 1528 1.30 blymn slen = _formi_tab_expanded_length( 1529 1.30 blymn row->string, cur->start_char, len); 1530 1.30 blymn else 1531 1.30 blymn slen = 0; 1532 1.30 blymn 1533 1.23 blymn if (slen > cur->cols) 1534 1.23 blymn slen = cur->cols; 1535 1.23 blymn slen += cur->start_char; 1536 1.23 blymn } else 1537 1.30 blymn slen = row->expanded; 1538 1.30 blymn 1539 1.13 blymn if ((cur->opts & O_STATIC) == O_STATIC) { 1540 1.13 blymn switch (cur->justification) { 1541 1.13 blymn case JUSTIFY_RIGHT: 1542 1.13 blymn post = 0; 1543 1.13 blymn if (flen < slen) 1544 1.13 blymn pre = 0; 1545 1.13 blymn else 1546 1.13 blymn pre = flen - slen; 1547 1.13 blymn break; 1548 1.13 blymn 1549 1.13 blymn case JUSTIFY_CENTER: 1550 1.13 blymn if (flen < slen) { 1551 1.13 blymn pre = 0; 1552 1.13 blymn post = 0; 1553 1.13 blymn } else { 1554 1.13 blymn pre = flen - slen; 1555 1.13 blymn post = pre = pre / 2; 1556 1.13 blymn /* get padding right if 1557 1.30 blymn centring is not even */ 1558 1.13 blymn if ((post + pre + slen) < flen) 1559 1.13 blymn post++; 1560 1.13 blymn } 1561 1.13 blymn break; 1562 1.13 blymn 1563 1.13 blymn case NO_JUSTIFICATION: 1564 1.13 blymn case JUSTIFY_LEFT: 1565 1.13 blymn default: 1566 1.1 blymn pre = 0; 1567 1.13 blymn if (flen <= slen) 1568 1.13 blymn post = 0; 1569 1.13 blymn else { 1570 1.13 blymn post = flen - slen; 1571 1.13 blymn if (post > flen) 1572 1.13 blymn post = flen; 1573 1.13 blymn } 1574 1.13 blymn break; 1575 1.1 blymn } 1576 1.13 blymn } else { 1577 1.13 blymn /* dynamic fields are not justified */ 1578 1.1 blymn pre = 0; 1579 1.1 blymn if (flen <= slen) 1580 1.1 blymn post = 0; 1581 1.1 blymn else { 1582 1.1 blymn post = flen - slen; 1583 1.1 blymn if (post > flen) 1584 1.1 blymn post = flen; 1585 1.1 blymn } 1586 1.1 blymn 1587 1.13 blymn /* but they do scroll.... */ 1588 1.30 blymn 1589 1.14 blymn if (pre > cur->start_char - start) 1590 1.14 blymn pre = pre - cur->start_char + start; 1591 1.13 blymn else 1592 1.13 blymn pre = 0; 1593 1.30 blymn 1594 1.14 blymn if (slen > cur->start_char) { 1595 1.14 blymn slen -= cur->start_char; 1596 1.22 blymn if (slen > flen) 1597 1.22 blymn post = 0; 1598 1.22 blymn else 1599 1.22 blymn post = flen - slen; 1600 1.30 blymn 1601 1.13 blymn if (post > flen) 1602 1.13 blymn post = flen; 1603 1.13 blymn } else { 1604 1.13 blymn slen = 0; 1605 1.13 blymn post = flen - pre; 1606 1.13 blymn } 1607 1.1 blymn } 1608 1.30 blymn 1609 1.30 blymn str = &row->string[cur->start_char]; 1610 1.30 blymn 1611 1.1 blymn #ifdef DEBUG 1612 1.38 christos _formi_dbg_printf( 1613 1.38 christos "%s: start=%d, pre=%d, slen=%d, flen=%d, post=%d, " 1614 1.38 christos "start_char=%d\n", __func__, 1615 1.38 christos start, pre, slen, flen, post, cur->start_char); 1616 1.38 christos if (str != NULL) { 1617 1.38 christos if (row->expanded != 0) { 1618 1.38 christos strncpy(buffer, str, flen); 1619 1.12 blymn } else { 1620 1.38 christos strcpy(buffer, "(empty)"); 1621 1.12 blymn } 1622 1.38 christos } else { 1623 1.38 christos strcpy(buffer, "(null)"); 1624 1.4 blymn } 1625 1.38 christos buffer[flen] = '\0'; 1626 1.38 christos _formi_dbg_printf("%s: %s\n", __func__, buffer); 1627 1.1 blymn #endif 1628 1.30 blymn 1629 1.41 blymn wattrset(form->scrwin, cur->back); 1630 1.41 blymn 1631 1.14 blymn for (i = start + cur->start_char; i < pre; i++) 1632 1.13 blymn waddch(form->scrwin, cur->pad); 1633 1.1 blymn 1634 1.38 christos _formi_dbg_printf("%s: will add %d chars\n", __func__, 1635 1.14 blymn min(slen, flen)); 1636 1.41 blymn wattrset(form->scrwin, cur->fore); 1637 1.23 blymn for (i = 0, cpos = cur->start_char; i < min(slen, flen); 1638 1.23 blymn i++, str++, cpos++) 1639 1.1 blymn { 1640 1.23 blymn c = *str; 1641 1.23 blymn tab = 0; /* just to shut gcc up */ 1642 1.38 christos _formi_dbg_printf("adding char str[%d]=%c\n", 1643 1.38 christos cpos + cur->start_char, c); 1644 1.13 blymn if (((cur->opts & O_PUBLIC) != O_PUBLIC)) { 1645 1.23 blymn if (c == '\t') 1646 1.30 blymn tab = add_tab(form, row, cpos, 1647 1.30 blymn cur->pad); 1648 1.41 blymn else { 1649 1.41 blymn wattrset(form->scrwin, cur->back); 1650 1.23 blymn waddch(form->scrwin, cur->pad); 1651 1.41 blymn wattrset(form->scrwin, cur->fore); 1652 1.41 blymn } 1653 1.13 blymn } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) { 1654 1.23 blymn if (c == '\t') 1655 1.30 blymn tab = add_tab(form, row, cpos, ' '); 1656 1.23 blymn else 1657 1.23 blymn waddch(form->scrwin, c); 1658 1.13 blymn } else { 1659 1.23 blymn if (c == '\t') 1660 1.30 blymn tab = add_tab(form, row, cpos, ' '); 1661 1.23 blymn else 1662 1.23 blymn waddch(form->scrwin, ' '); 1663 1.13 blymn } 1664 1.23 blymn 1665 1.23 blymn /* 1666 1.23 blymn * If we have had a tab then skip forward 1667 1.23 blymn * the requisite number of chars to keep 1668 1.23 blymn * things in sync. 1669 1.23 blymn */ 1670 1.23 blymn if (c == '\t') 1671 1.23 blymn i += tab - 1; 1672 1.1 blymn } 1673 1.1 blymn 1674 1.41 blymn wattrset(form->scrwin, cur->back); 1675 1.1 blymn for (i = 0; i < post; i++) 1676 1.13 blymn waddch(form->scrwin, cur->pad); 1677 1.1 blymn } 1678 1.16 blymn 1679 1.30 blymn for (i = line; i < cur->rows; i++) { 1680 1.30 blymn wmove(form->scrwin, (int) (cur->form_row + i), 1681 1.16 blymn (int) cur->form_col); 1682 1.24 blymn 1683 1.41 blymn wattrset(form->scrwin, cur->back); 1684 1.24 blymn 1685 1.30 blymn for (j = 0; j < cur->cols; j++) { 1686 1.16 blymn waddch(form->scrwin, cur->pad); 1687 1.16 blymn } 1688 1.16 blymn } 1689 1.16 blymn 1690 1.24 blymn wattrset(form->scrwin, cur->back); 1691 1.42 blymn 1692 1.42 blymn cur = form->fields[form->cur_field]; 1693 1.42 blymn wmove(form->scrwin, cur->form_row + cur->cursor_ypos, 1694 1.42 blymn cur->form_col + cur->cursor_xpos); 1695 1.42 blymn wcursyncup(form->scrwin); 1696 1.42 blymn 1697 1.1 blymn return; 1698 1.1 blymn } 1699 1.1 blymn 1700 1.1 blymn /* 1701 1.23 blymn * Add the correct number of the given character to simulate a tab 1702 1.23 blymn * in the field. 1703 1.23 blymn */ 1704 1.23 blymn static int 1705 1.30 blymn add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c) 1706 1.23 blymn { 1707 1.23 blymn int j; 1708 1.30 blymn _formi_tab_t *ts = row->tabs; 1709 1.23 blymn 1710 1.23 blymn while ((ts != NULL) && (ts->pos != i)) 1711 1.23 blymn ts = ts->fwd; 1712 1.23 blymn 1713 1.23 blymn assert(ts != NULL); 1714 1.30 blymn 1715 1.23 blymn for (j = 0; j < ts->size; j++) 1716 1.23 blymn waddch(form->scrwin, c); 1717 1.23 blymn 1718 1.23 blymn return ts->size; 1719 1.23 blymn } 1720 1.23 blymn 1721 1.30 blymn 1722 1.23 blymn /* 1723 1.1 blymn * Display the fields attached to the form that are on the current page 1724 1.1 blymn * on the screen. 1725 1.1 blymn * 1726 1.1 blymn */ 1727 1.1 blymn int 1728 1.1 blymn _formi_draw_page(FORM *form) 1729 1.1 blymn { 1730 1.1 blymn int i; 1731 1.30 blymn 1732 1.1 blymn if (form->page_starts[form->page].in_use == 0) 1733 1.1 blymn return E_BAD_ARGUMENT; 1734 1.1 blymn 1735 1.13 blymn wclear(form->scrwin); 1736 1.30 blymn 1737 1.1 blymn for (i = form->page_starts[form->page].first; 1738 1.1 blymn i <= form->page_starts[form->page].last; i++) 1739 1.1 blymn _formi_redraw_field(form, i); 1740 1.30 blymn 1741 1.1 blymn return E_OK; 1742 1.1 blymn } 1743 1.1 blymn 1744 1.1 blymn /* 1745 1.1 blymn * Add the character c at the position pos in buffer 0 of the given field 1746 1.1 blymn */ 1747 1.1 blymn int 1748 1.1 blymn _formi_add_char(FIELD *field, unsigned int pos, char c) 1749 1.1 blymn { 1750 1.21 blymn char *new, old_c; 1751 1.1 blymn unsigned int new_size; 1752 1.1 blymn int status; 1753 1.30 blymn _FORMI_FIELD_LINES *row, *temp, *next_temp; 1754 1.30 blymn 1755 1.30 blymn row = field->cur_line; 1756 1.1 blymn 1757 1.5 blymn /* 1758 1.5 blymn * If buffer has not had a string before, set it to a blank 1759 1.5 blymn * string. Everything should flow from there.... 1760 1.5 blymn */ 1761 1.30 blymn if (row->string == NULL) { 1762 1.40 christos if ((row->string = malloc((size_t)INITIAL_LINE_ALLOC)) == NULL) 1763 1.30 blymn return E_SYSTEM_ERROR; 1764 1.30 blymn row->string[0] = '\0'; 1765 1.30 blymn row->allocated = INITIAL_LINE_ALLOC; 1766 1.30 blymn row->length = 0; 1767 1.30 blymn row->expanded = 0; 1768 1.5 blymn } 1769 1.8 blymn 1770 1.8 blymn if (_formi_validate_char(field, c) != E_OK) { 1771 1.38 christos _formi_dbg_printf("%s: char %c failed char validation\n", 1772 1.38 christos __func__, c); 1773 1.8 blymn return E_INVALID_FIELD; 1774 1.8 blymn } 1775 1.23 blymn 1776 1.23 blymn if ((c == '\t') && (field->cols <= 8)) { 1777 1.38 christos _formi_dbg_printf("%s: field too small for a tab\n", __func__); 1778 1.23 blymn return E_NO_ROOM; 1779 1.23 blymn } 1780 1.30 blymn 1781 1.38 christos _formi_dbg_printf("%s: pos=%d, char=%c\n", __func__, pos, c); 1782 1.38 christos _formi_dbg_printf("%s: xpos=%d, row_pos=%d, start=%d\n", __func__, 1783 1.23 blymn field->cursor_xpos, field->row_xpos, field->start_char); 1784 1.38 christos _formi_dbg_printf("%s: length=%d(%d), allocated=%d\n", __func__, 1785 1.30 blymn row->expanded, row->length, row->allocated); 1786 1.38 christos _formi_dbg_printf("%s: %s\n", __func__, row->string); 1787 1.38 christos _formi_dbg_printf("%s: buf0_status=%d\n", __func__, field->buf0_status); 1788 1.1 blymn if (((field->opts & O_BLANK) == O_BLANK) && 1789 1.13 blymn (field->buf0_status == FALSE) && 1790 1.23 blymn ((field->row_xpos + field->start_char) == 0)) { 1791 1.33 roy row = field->alines; 1792 1.30 blymn if (row->next != NULL) { 1793 1.30 blymn /* shift all but one line structs to free list */ 1794 1.30 blymn temp = row->next; 1795 1.30 blymn do { 1796 1.30 blymn next_temp = temp->next; 1797 1.30 blymn add_to_free(field, temp); 1798 1.30 blymn temp = next_temp; 1799 1.30 blymn } while (temp != NULL); 1800 1.30 blymn } 1801 1.30 blymn 1802 1.30 blymn row->length = 0; 1803 1.30 blymn row->string[0] = '\0'; 1804 1.1 blymn pos = 0; 1805 1.1 blymn field->start_char = 0; 1806 1.30 blymn field->start_line = row; 1807 1.30 blymn field->cur_line = row; 1808 1.9 blymn field->row_count = 1; 1809 1.23 blymn field->row_xpos = 0; 1810 1.1 blymn field->cursor_ypos = 0; 1811 1.30 blymn row->expanded = 0; 1812 1.30 blymn row->length = 0; 1813 1.25 blymn _formi_init_field_xpos(field); 1814 1.1 blymn } 1815 1.1 blymn 1816 1.1 blymn 1817 1.1 blymn if ((field->overlay == 0) 1818 1.30 blymn || ((field->overlay == 1) && (pos >= row->length))) { 1819 1.10 blymn /* first check if the field can have more chars...*/ 1820 1.22 blymn if (check_field_size(field) == FALSE) 1821 1.10 blymn return E_REQUEST_DENIED; 1822 1.30 blymn 1823 1.30 blymn if (row->length + 2 1824 1.30 blymn >= row->allocated) { 1825 1.30 blymn new_size = row->allocated + 16 - (row->allocated % 16); 1826 1.40 christos if ((new = realloc(row->string, 1827 1.30 blymn (size_t) new_size )) == NULL) 1828 1.1 blymn return E_SYSTEM_ERROR; 1829 1.30 blymn row->allocated = new_size; 1830 1.30 blymn row->string = new; 1831 1.1 blymn } 1832 1.1 blymn } 1833 1.30 blymn 1834 1.30 blymn if ((field->overlay == 0) && (row->length > pos)) { 1835 1.40 christos memmove(&row->string[pos + 1], &row->string[pos], 1836 1.30 blymn (size_t) (row->length - pos + 1)); 1837 1.1 blymn } 1838 1.21 blymn 1839 1.30 blymn old_c = row->string[pos]; 1840 1.30 blymn row->string[pos] = c; 1841 1.30 blymn if (pos >= row->length) { 1842 1.1 blymn /* make sure the string is terminated if we are at the 1843 1.1 blymn * end of the string, the terminator would be missing 1844 1.39 msaitoh * if we are at the end of the field. 1845 1.1 blymn */ 1846 1.30 blymn row->string[pos + 1] = '\0'; 1847 1.1 blymn } 1848 1.1 blymn 1849 1.1 blymn /* only increment the length if we are inserting characters 1850 1.1 blymn * OR if we are at the end of the field in overlay mode. 1851 1.1 blymn */ 1852 1.1 blymn if ((field->overlay == 0) 1853 1.30 blymn || ((field->overlay == 1) && (pos >= row->length))) { 1854 1.30 blymn row->length++; 1855 1.30 blymn } 1856 1.30 blymn 1857 1.30 blymn _formi_calculate_tabs(row); 1858 1.30 blymn row->expanded = _formi_tab_expanded_length(row->string, 0, 1859 1.30 blymn row->length - 1); 1860 1.23 blymn 1861 1.1 blymn /* wrap the field, if needed */ 1862 1.30 blymn status = _formi_wrap_field(field, row); 1863 1.30 blymn 1864 1.30 blymn row = field->cur_line; 1865 1.30 blymn pos = field->row_xpos; 1866 1.23 blymn 1867 1.21 blymn /* 1868 1.21 blymn * check the wrap worked or that we have not exceeded the 1869 1.21 blymn * max field size - this can happen if the field is re-wrapped 1870 1.21 blymn * and the row count is increased past the set limit. 1871 1.21 blymn */ 1872 1.22 blymn if ((status != E_OK) || (check_field_size(field) == FALSE)) { 1873 1.21 blymn if ((field->overlay == 0) 1874 1.21 blymn || ((field->overlay == 1) 1875 1.30 blymn && (pos >= (row->length - 1) /*XXXX- append check???*/))) { 1876 1.21 blymn /* 1877 1.21 blymn * wrap failed for some reason, back out the 1878 1.21 blymn * char insert 1879 1.21 blymn */ 1880 1.40 christos memmove(&row->string[pos], &row->string[pos + 1], 1881 1.30 blymn (size_t) (row->length - pos)); 1882 1.30 blymn row->length--; 1883 1.23 blymn if (pos > 0) 1884 1.23 blymn pos--; 1885 1.21 blymn } else if (field->overlay == 1) { 1886 1.21 blymn /* back out character overlay */ 1887 1.30 blymn row->string[pos] = old_c; 1888 1.21 blymn } 1889 1.30 blymn 1890 1.30 blymn _formi_calculate_tabs(row); 1891 1.30 blymn 1892 1.30 blymn _formi_wrap_field(field, row); 1893 1.23 blymn /* 1894 1.23 blymn * If we are here then either the status is bad or we 1895 1.23 blymn * simply ran out of room. If the status is E_OK then 1896 1.23 blymn * we ran out of room, let the form driver know this. 1897 1.23 blymn */ 1898 1.23 blymn if (status == E_OK) 1899 1.23 blymn status = E_REQUEST_DENIED; 1900 1.30 blymn 1901 1.1 blymn } else { 1902 1.1 blymn field->buf0_status = TRUE; 1903 1.30 blymn field->row_xpos++; 1904 1.15 blymn if ((field->rows + field->nrows) == 1) { 1905 1.28 blymn status = _formi_set_cursor_xpos(field, FALSE); 1906 1.15 blymn } else { 1907 1.30 blymn field->cursor_xpos = 1908 1.30 blymn _formi_tab_expanded_length( 1909 1.30 blymn row->string, 0, field->row_xpos - 1); 1910 1.21 blymn 1911 1.16 blymn /* 1912 1.16 blymn * Annoying corner case - if we are right in 1913 1.16 blymn * the bottom right corner of the field we 1914 1.16 blymn * need to scroll the field one line so the 1915 1.16 blymn * cursor is positioned correctly in the 1916 1.16 blymn * field. 1917 1.16 blymn */ 1918 1.16 blymn if ((field->cursor_xpos >= field->cols) && 1919 1.16 blymn (field->cursor_ypos == (field->rows - 1))) { 1920 1.16 blymn field->cursor_ypos--; 1921 1.30 blymn field->start_line = field->start_line->next; 1922 1.16 blymn } 1923 1.1 blymn } 1924 1.1 blymn } 1925 1.30 blymn 1926 1.21 blymn assert((field->cursor_xpos <= field->cols) 1927 1.30 blymn && (field->cursor_ypos < 400000)); 1928 1.30 blymn 1929 1.38 christos _formi_dbg_printf("%s: xpos=%d, row_pos=%d, start=%d\n", __func__, 1930 1.23 blymn field->cursor_xpos, field->row_xpos, field->start_char); 1931 1.38 christos _formi_dbg_printf("%s: length=%d(%d), allocated=%d\n", __func__, 1932 1.30 blymn row->expanded, row->length, row->allocated); 1933 1.38 christos _formi_dbg_printf("%s: ypos=%d, start_line=%p\n", __func__, 1934 1.16 blymn field->cursor_ypos, field->start_line); 1935 1.38 christos _formi_dbg_printf("%s: %s\n", __func__, row->string); 1936 1.38 christos _formi_dbg_printf("%s: buf0_status=%d\n", __func__, field->buf0_status); 1937 1.38 christos _formi_dbg_printf("%s: status = %s\n", __func__, 1938 1.1 blymn (status == E_OK)? "OK" : "FAILED"); 1939 1.10 blymn return status; 1940 1.1 blymn } 1941 1.1 blymn 1942 1.1 blymn /* 1943 1.25 blymn * Set the position of the cursor on the screen in the row depending on 1944 1.25 blymn * where the current position in the string is and the justification 1945 1.25 blymn * that is to be applied to the field. Justification is only applied 1946 1.25 blymn * to single row, static fields. 1947 1.25 blymn */ 1948 1.25 blymn static int 1949 1.28 blymn _formi_set_cursor_xpos(FIELD *field, int noscroll) 1950 1.25 blymn { 1951 1.25 blymn int just, pos; 1952 1.25 blymn 1953 1.25 blymn just = field->justification; 1954 1.30 blymn pos = field->start_char + field->row_xpos; 1955 1.25 blymn 1956 1.38 christos _formi_dbg_printf( 1957 1.38 christos "%s: pos %d, start_char %d, row_xpos %d, xpos %d\n", __func__, 1958 1.38 christos pos, field->start_char, field->row_xpos, field->cursor_xpos); 1959 1.30 blymn 1960 1.25 blymn /* 1961 1.25 blymn * make sure we apply the correct justification to non-static 1962 1.25 blymn * fields. 1963 1.25 blymn */ 1964 1.25 blymn if (((field->rows + field->nrows) != 1) || 1965 1.25 blymn ((field->opts & O_STATIC) != O_STATIC)) 1966 1.25 blymn just = JUSTIFY_LEFT; 1967 1.30 blymn 1968 1.25 blymn switch (just) { 1969 1.25 blymn case JUSTIFY_RIGHT: 1970 1.25 blymn field->cursor_xpos = field->cols - 1 1971 1.25 blymn - _formi_tab_expanded_length( 1972 1.30 blymn field->cur_line->string, 0, 1973 1.30 blymn field->cur_line->length - 1) 1974 1.25 blymn + _formi_tab_expanded_length( 1975 1.30 blymn field->cur_line->string, 0, 1976 1.25 blymn field->row_xpos); 1977 1.25 blymn break; 1978 1.25 blymn 1979 1.25 blymn case JUSTIFY_CENTER: 1980 1.25 blymn field->cursor_xpos = ((field->cols - 1) 1981 1.25 blymn - _formi_tab_expanded_length( 1982 1.30 blymn field->cur_line->string, 0, 1983 1.30 blymn field->cur_line->length - 1) + 1) / 2 1984 1.30 blymn + _formi_tab_expanded_length(field->cur_line->string, 1985 1.25 blymn 0, field->row_xpos); 1986 1.30 blymn 1987 1.25 blymn if (field->cursor_xpos > (field->cols - 1)) 1988 1.25 blymn field->cursor_xpos = (field->cols - 1); 1989 1.25 blymn break; 1990 1.25 blymn 1991 1.25 blymn default: 1992 1.25 blymn field->cursor_xpos = _formi_tab_expanded_length( 1993 1.30 blymn field->cur_line->string, 1994 1.25 blymn field->start_char, 1995 1.25 blymn field->row_xpos + field->start_char); 1996 1.25 blymn if ((field->cursor_xpos <= (field->cols - 1)) && 1997 1.25 blymn ((field->start_char + field->row_xpos) 1998 1.30 blymn < field->cur_line->length)) 1999 1.25 blymn field->cursor_xpos--; 2000 1.25 blymn 2001 1.25 blymn if (field->cursor_xpos > (field->cols - 1)) { 2002 1.25 blymn if ((field->opts & O_STATIC) == O_STATIC) { 2003 1.25 blymn field->start_char = 0; 2004 1.25 blymn 2005 1.25 blymn if (field->row_xpos 2006 1.30 blymn == (field->cur_line->length - 1)) { 2007 1.25 blymn field->cursor_xpos = field->cols - 1; 2008 1.25 blymn } else { 2009 1.25 blymn field->cursor_xpos = 2010 1.25 blymn _formi_tab_expanded_length( 2011 1.30 blymn field->cur_line->string, 2012 1.25 blymn field->start_char, 2013 1.25 blymn field->row_xpos 2014 1.25 blymn + field->start_char 2015 1.25 blymn - 1) - 1; 2016 1.25 blymn } 2017 1.25 blymn } else { 2018 1.28 blymn if (noscroll == FALSE) { 2019 1.28 blymn field->start_char = 2020 1.28 blymn tab_fit_window( 2021 1.28 blymn field, 2022 1.28 blymn field->start_char 2023 1.28 blymn + field->row_xpos, 2024 1.28 blymn field->cols); 2025 1.28 blymn field->row_xpos = pos 2026 1.28 blymn - field->start_char; 2027 1.28 blymn field->cursor_xpos = 2028 1.28 blymn _formi_tab_expanded_length( 2029 1.30 blymn field->cur_line->string, 2030 1.28 blymn field->start_char, 2031 1.28 blymn field->row_xpos 2032 1.28 blymn + field->start_char - 1); 2033 1.28 blymn } else { 2034 1.28 blymn field->cursor_xpos = (field->cols - 1); 2035 1.28 blymn } 2036 1.25 blymn } 2037 1.30 blymn 2038 1.25 blymn } 2039 1.25 blymn break; 2040 1.25 blymn } 2041 1.30 blymn 2042 1.38 christos _formi_dbg_printf( 2043 1.38 christos "%s: pos %d, start_char %d, row_xpos %d, xpos %d\n", __func__, 2044 1.38 christos pos, field->start_char, field->row_xpos, field->cursor_xpos); 2045 1.25 blymn return E_OK; 2046 1.25 blymn } 2047 1.25 blymn 2048 1.25 blymn /* 2049 1.1 blymn * Manipulate the text in a field, this takes the given form and performs 2050 1.1 blymn * the passed driver command on the current text field. Returns 1 if the 2051 1.1 blymn * text field was modified. 2052 1.1 blymn */ 2053 1.1 blymn int 2054 1.1 blymn _formi_manipulate_field(FORM *form, int c) 2055 1.1 blymn { 2056 1.1 blymn FIELD *cur; 2057 1.20 blymn char *str, saved; 2058 1.30 blymn unsigned int start, end, pos, status, old_count, size; 2059 1.23 blymn unsigned int old_xpos, old_row_pos; 2060 1.30 blymn int len, wb; 2061 1.30 blymn bool eat_char; 2062 1.30 blymn _FORMI_FIELD_LINES *row, *rs; 2063 1.30 blymn 2064 1.1 blymn cur = form->fields[form->cur_field]; 2065 1.30 blymn if (cur->cur_line->string == NULL) 2066 1.24 blymn return E_REQUEST_DENIED; 2067 1.1 blymn 2068 1.38 christos _formi_dbg_printf("%s: request is REQ_%s\n", 2069 1.38 christos __func__, reqs[c - REQ_MIN_REQUEST]); 2070 1.38 christos _formi_dbg_printf( 2071 1.38 christos "%s: xpos=%d, row_pos=%d, start_char=%d, length=%d, allocated=%d\n", 2072 1.38 christos __func__, cur->cursor_xpos, cur->row_xpos, cur->start_char, 2073 1.38 christos cur->cur_line->length, cur->cur_line->allocated); 2074 1.38 christos _formi_dbg_printf("%s: start_line=%p, ypos=%d\n", __func__, 2075 1.38 christos cur->start_line, cur->cursor_ypos); 2076 1.30 blymn if (cur->cur_line->string == NULL) 2077 1.38 christos _formi_dbg_printf("%s: string=(null)\n", __func__); 2078 1.6 blymn else 2079 1.38 christos _formi_dbg_printf("%s: string=\"%s\"\n", __func__, 2080 1.38 christos cur->cur_line->string); 2081 1.6 blymn 2082 1.6 blymn /* Cannot manipulate a null string! */ 2083 1.30 blymn if (cur->cur_line->string == NULL) 2084 1.6 blymn return E_REQUEST_DENIED; 2085 1.23 blymn 2086 1.30 blymn saved = '\0'; 2087 1.30 blymn row = cur->cur_line; 2088 1.30 blymn 2089 1.1 blymn switch (c) { 2090 1.23 blymn case REQ_RIGHT_CHAR: 2091 1.23 blymn /* 2092 1.23 blymn * The right_char request performs the same function 2093 1.23 blymn * as the next_char request except that the cursor is 2094 1.23 blymn * not wrapped if it is at the end of the line, so 2095 1.23 blymn * check if the cursor is at the end of the line and 2096 1.23 blymn * deny the request otherwise just fall through to 2097 1.23 blymn * the next_char request handler. 2098 1.23 blymn */ 2099 1.27 blymn if (cur->cursor_xpos >= cur->cols - 1) 2100 1.23 blymn return E_REQUEST_DENIED; 2101 1.23 blymn 2102 1.23 blymn /* FALLTHRU */ 2103 1.30 blymn 2104 1.1 blymn case REQ_NEXT_CHAR: 2105 1.14 blymn /* for a dynamic field allow an offset of one more 2106 1.14 blymn * char so we can insert chars after end of string. 2107 1.14 blymn * Static fields cannot do this so deny request if 2108 1.14 blymn * cursor is at the end of the field. 2109 1.14 blymn */ 2110 1.14 blymn if (((cur->opts & O_STATIC) == O_STATIC) && 2111 1.23 blymn (cur->row_xpos == cur->cols - 1) && 2112 1.17 blymn ((cur->rows + cur->nrows) == 1)) 2113 1.1 blymn return E_REQUEST_DENIED; 2114 1.30 blymn 2115 1.30 blymn if (((cur->rows + cur->nrows) == 1) && 2116 1.30 blymn (cur->row_xpos + cur->start_char + 1) > row->length) 2117 1.14 blymn return E_REQUEST_DENIED; 2118 1.14 blymn 2119 1.17 blymn if ((cur->rows + cur->nrows) == 1) { 2120 1.23 blymn cur->row_xpos++; 2121 1.28 blymn _formi_set_cursor_xpos(cur, (c == REQ_RIGHT_CHAR)); 2122 1.17 blymn } else { 2123 1.30 blymn if (cur->cursor_xpos >= (row->expanded - 1)) { 2124 1.30 blymn if ((row->next == NULL) || 2125 1.27 blymn (c == REQ_RIGHT_CHAR)) 2126 1.17 blymn return E_REQUEST_DENIED; 2127 1.30 blymn 2128 1.17 blymn cur->cursor_xpos = 0; 2129 1.23 blymn cur->row_xpos = 0; 2130 1.30 blymn cur->cur_line = cur->cur_line->next; 2131 1.17 blymn if (cur->cursor_ypos == (cur->rows - 1)) 2132 1.30 blymn cur->start_line = 2133 1.30 blymn cur->start_line->next; 2134 1.17 blymn else 2135 1.17 blymn cur->cursor_ypos++; 2136 1.23 blymn } else { 2137 1.23 blymn old_xpos = cur->cursor_xpos; 2138 1.23 blymn old_row_pos = cur->row_xpos; 2139 1.30 blymn if (row->string[cur->row_xpos] == '\t') 2140 1.30 blymn cur->cursor_xpos += tab_size(row, 2141 1.23 blymn cur->row_xpos); 2142 1.23 blymn else 2143 1.23 blymn cur->cursor_xpos++; 2144 1.23 blymn cur->row_xpos++; 2145 1.23 blymn if (cur->cursor_xpos 2146 1.30 blymn >= row->expanded) { 2147 1.30 blymn if ((row->next == NULL) || 2148 1.27 blymn (c == REQ_RIGHT_CHAR)) { 2149 1.23 blymn cur->cursor_xpos = old_xpos; 2150 1.23 blymn cur->row_xpos = old_row_pos; 2151 1.23 blymn return E_REQUEST_DENIED; 2152 1.23 blymn } 2153 1.23 blymn 2154 1.23 blymn cur->cursor_xpos = 0; 2155 1.23 blymn cur->row_xpos = 0; 2156 1.30 blymn cur->cur_line = cur->cur_line->next; 2157 1.23 blymn if (cur->cursor_ypos 2158 1.23 blymn == (cur->rows - 1)) 2159 1.30 blymn cur->start_line = 2160 1.30 blymn cur->start_line->next; 2161 1.23 blymn else 2162 1.23 blymn cur->cursor_ypos++; 2163 1.23 blymn } 2164 1.23 blymn } 2165 1.1 blymn } 2166 1.30 blymn 2167 1.1 blymn break; 2168 1.30 blymn 2169 1.23 blymn case REQ_LEFT_CHAR: 2170 1.23 blymn /* 2171 1.23 blymn * The behaviour of left_char is the same as prev_char 2172 1.23 blymn * except that the cursor will not wrap if it has 2173 1.23 blymn * reached the LHS of the field, so just check this 2174 1.23 blymn * and fall through if we are not at the LHS. 2175 1.23 blymn */ 2176 1.23 blymn if (cur->cursor_xpos == 0) 2177 1.23 blymn return E_REQUEST_DENIED; 2178 1.23 blymn 2179 1.23 blymn /* FALLTHRU */ 2180 1.1 blymn case REQ_PREV_CHAR: 2181 1.17 blymn if ((cur->rows + cur->nrows) == 1) { 2182 1.23 blymn if (cur->row_xpos == 0) { 2183 1.17 blymn if (cur->start_char > 0) 2184 1.17 blymn cur->start_char--; 2185 1.17 blymn else 2186 1.17 blymn return E_REQUEST_DENIED; 2187 1.23 blymn } else { 2188 1.23 blymn cur->row_xpos--; 2189 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2190 1.23 blymn } 2191 1.17 blymn } else { 2192 1.17 blymn if ((cur->cursor_xpos == 0) && 2193 1.17 blymn (cur->cursor_ypos == 0) && 2194 1.30 blymn (cur->start_line->prev == NULL)) 2195 1.1 blymn return E_REQUEST_DENIED; 2196 1.17 blymn 2197 1.30 blymn pos = cur->row_xpos; 2198 1.17 blymn if (cur->cursor_xpos > 0) { 2199 1.30 blymn if (row->string[pos] == '\t') { 2200 1.30 blymn size = tab_size(row, pos); 2201 1.23 blymn if (size > cur->cursor_xpos) { 2202 1.23 blymn cur->cursor_xpos = 0; 2203 1.23 blymn cur->row_xpos = 0; 2204 1.23 blymn } else { 2205 1.23 blymn cur->row_xpos--; 2206 1.23 blymn cur->cursor_xpos -= size; 2207 1.23 blymn } 2208 1.23 blymn } else { 2209 1.23 blymn cur->cursor_xpos--; 2210 1.23 blymn cur->row_xpos--; 2211 1.23 blymn } 2212 1.17 blymn } else { 2213 1.30 blymn cur->cur_line = cur->cur_line->prev; 2214 1.17 blymn if (cur->cursor_ypos > 0) 2215 1.17 blymn cur->cursor_ypos--; 2216 1.17 blymn else 2217 1.30 blymn cur->start_line = 2218 1.30 blymn cur->start_line->prev; 2219 1.30 blymn row = cur->cur_line; 2220 1.30 blymn if (row->expanded > 0) { 2221 1.30 blymn cur->cursor_xpos = row->expanded - 1; 2222 1.30 blymn } else { 2223 1.30 blymn cur->cursor_xpos = 0; 2224 1.30 blymn } 2225 1.30 blymn 2226 1.30 blymn if (row->length > 0) 2227 1.30 blymn cur->row_xpos = row->length - 1; 2228 1.30 blymn else 2229 1.30 blymn cur->row_xpos = 0; 2230 1.17 blymn } 2231 1.17 blymn } 2232 1.30 blymn 2233 1.1 blymn break; 2234 1.23 blymn 2235 1.23 blymn case REQ_DOWN_CHAR: 2236 1.23 blymn /* 2237 1.23 blymn * The down_char request has the same functionality as 2238 1.23 blymn * the next_line request excepting that the field is not 2239 1.23 blymn * scrolled if the cursor is at the bottom of the field. 2240 1.23 blymn * Check to see if the cursor is at the bottom of the field 2241 1.23 blymn * and if it is then deny the request otherwise fall 2242 1.23 blymn * through to the next_line handler. 2243 1.23 blymn */ 2244 1.23 blymn if (cur->cursor_ypos >= cur->rows - 1) 2245 1.23 blymn return E_REQUEST_DENIED; 2246 1.23 blymn 2247 1.23 blymn /* FALLTHRU */ 2248 1.30 blymn 2249 1.1 blymn case REQ_NEXT_LINE: 2250 1.30 blymn if ((row->next == NULL) || (cur->cur_line->next == NULL)) 2251 1.19 blymn return E_REQUEST_DENIED; 2252 1.30 blymn 2253 1.30 blymn cur->cur_line = cur->cur_line->next; 2254 1.19 blymn if ((cur->cursor_ypos + 1) >= cur->rows) { 2255 1.30 blymn cur->start_line = cur->start_line->next; 2256 1.19 blymn } else 2257 1.19 blymn cur->cursor_ypos++; 2258 1.30 blymn row = cur->cur_line; 2259 1.30 blymn 2260 1.30 blymn if (row->length == 0) { 2261 1.30 blymn cur->row_xpos = 0; 2262 1.30 blymn cur->cursor_xpos = 0; 2263 1.30 blymn } else { 2264 1.30 blymn if (cur->cursor_xpos > (row->expanded - 1)) 2265 1.30 blymn cur->cursor_xpos = row->expanded - 1; 2266 1.30 blymn 2267 1.30 blymn cur->row_xpos = tab_fit_len(row, cur->cursor_xpos + 1); 2268 1.30 blymn if (cur->row_xpos == 0) 2269 1.30 blymn cur->cursor_xpos = 0; 2270 1.30 blymn else 2271 1.30 blymn cur->cursor_xpos = 2272 1.30 blymn _formi_tab_expanded_length( 2273 1.30 blymn row->string, 0, cur->row_xpos); 2274 1.30 blymn if (cur->cursor_xpos > 0) 2275 1.30 blymn cur->cursor_xpos--; 2276 1.30 blymn } 2277 1.1 blymn break; 2278 1.30 blymn 2279 1.23 blymn case REQ_UP_CHAR: 2280 1.23 blymn /* 2281 1.23 blymn * The up_char request has the same functionality as 2282 1.23 blymn * the prev_line request excepting the field is not 2283 1.23 blymn * scrolled, check if the cursor is at the top of the 2284 1.23 blymn * field, if it is deny the request otherwise fall 2285 1.23 blymn * through to the prev_line handler. 2286 1.23 blymn */ 2287 1.23 blymn if (cur->cursor_ypos == 0) 2288 1.23 blymn return E_REQUEST_DENIED; 2289 1.23 blymn 2290 1.23 blymn /* FALLTHRU */ 2291 1.30 blymn 2292 1.1 blymn case REQ_PREV_LINE: 2293 1.30 blymn if (cur->cur_line->prev == NULL) 2294 1.30 blymn return E_REQUEST_DENIED; 2295 1.30 blymn 2296 1.1 blymn if (cur->cursor_ypos == 0) { 2297 1.30 blymn if (cur->start_line->prev == NULL) 2298 1.1 blymn return E_REQUEST_DENIED; 2299 1.30 blymn cur->start_line = cur->start_line->prev; 2300 1.1 blymn } else 2301 1.1 blymn cur->cursor_ypos--; 2302 1.30 blymn 2303 1.30 blymn cur->cur_line = cur->cur_line->prev; 2304 1.30 blymn row = cur->cur_line; 2305 1.30 blymn 2306 1.30 blymn if (row->length == 0) { 2307 1.30 blymn cur->row_xpos = 0; 2308 1.30 blymn cur->cursor_xpos = 0; 2309 1.30 blymn } else { 2310 1.30 blymn if (cur->cursor_xpos > (row->expanded - 1)) 2311 1.30 blymn cur->cursor_xpos = row->expanded - 1; 2312 1.30 blymn 2313 1.30 blymn cur->row_xpos = tab_fit_len(row, cur->cursor_xpos + 1); 2314 1.30 blymn cur->cursor_xpos = 2315 1.30 blymn _formi_tab_expanded_length(row->string, 2316 1.30 blymn 0, cur->row_xpos); 2317 1.30 blymn if (cur->cursor_xpos > 0) 2318 1.30 blymn cur->cursor_xpos--; 2319 1.30 blymn } 2320 1.1 blymn break; 2321 1.30 blymn 2322 1.1 blymn case REQ_NEXT_WORD: 2323 1.30 blymn start = cur->row_xpos + cur->start_char; 2324 1.30 blymn str = row->string; 2325 1.1 blymn 2326 1.30 blymn wb = find_eow(cur, start, FALSE, &row); 2327 1.30 blymn if (wb < 0) 2328 1.30 blymn return wb; 2329 1.30 blymn 2330 1.30 blymn start = wb; 2331 1.1 blymn /* check if we hit the end */ 2332 1.1 blymn if (str[start] == '\0') 2333 1.1 blymn return E_REQUEST_DENIED; 2334 1.1 blymn 2335 1.1 blymn /* otherwise we must have found the start of a word...*/ 2336 1.18 blymn if ((cur->rows + cur->nrows) == 1) { 2337 1.18 blymn /* single line field */ 2338 1.23 blymn size = _formi_tab_expanded_length(str, 2339 1.23 blymn cur->start_char, start); 2340 1.23 blymn if (size < cur->cols) { 2341 1.23 blymn cur->row_xpos = start - cur->start_char; 2342 1.18 blymn } else { 2343 1.18 blymn cur->start_char = start; 2344 1.23 blymn cur->row_xpos = 0; 2345 1.18 blymn } 2346 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2347 1.30 blymn } else { 2348 1.18 blymn /* multiline field */ 2349 1.30 blymn cur->cur_line = row; 2350 1.30 blymn adjust_ypos(cur, row); 2351 1.30 blymn 2352 1.30 blymn cur->row_xpos = start; 2353 1.30 blymn cur->cursor_xpos = 2354 1.30 blymn _formi_tab_expanded_length( 2355 1.30 blymn row->string, 0, cur->row_xpos) - 1; 2356 1.1 blymn } 2357 1.1 blymn break; 2358 1.1 blymn 2359 1.1 blymn case REQ_PREV_WORD: 2360 1.30 blymn start = cur->start_char + cur->row_xpos; 2361 1.1 blymn if (cur->start_char > 0) 2362 1.1 blymn start--; 2363 1.30 blymn 2364 1.30 blymn if ((start == 0) && (row->prev == NULL)) 2365 1.1 blymn return E_REQUEST_DENIED; 2366 1.1 blymn 2367 1.30 blymn if (start == 0) { 2368 1.30 blymn row = row->prev; 2369 1.30 blymn if (row->length > 0) 2370 1.30 blymn start = row->length - 1; 2371 1.30 blymn else 2372 1.30 blymn start = 0; 2373 1.30 blymn } 2374 1.30 blymn 2375 1.30 blymn str = row->string; 2376 1.30 blymn 2377 1.30 blymn start = find_sow(start, &row); 2378 1.1 blymn 2379 1.18 blymn if ((cur->rows + cur->nrows) == 1) { 2380 1.18 blymn /* single line field */ 2381 1.23 blymn size = _formi_tab_expanded_length(str, 2382 1.23 blymn cur->start_char, start); 2383 1.23 blymn 2384 1.23 blymn if (start > cur->start_char) { 2385 1.23 blymn cur->row_xpos = start - cur->start_char; 2386 1.18 blymn } else { 2387 1.18 blymn cur->start_char = start; 2388 1.23 blymn cur->row_xpos = 0; 2389 1.18 blymn } 2390 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2391 1.1 blymn } else { 2392 1.18 blymn /* multiline field */ 2393 1.30 blymn cur->cur_line = row; 2394 1.30 blymn adjust_ypos(cur, row); 2395 1.30 blymn cur->row_xpos = start; 2396 1.30 blymn cur->cursor_xpos = 2397 1.30 blymn _formi_tab_expanded_length( 2398 1.30 blymn row->string, 0, 2399 1.30 blymn cur->row_xpos) - 1; 2400 1.1 blymn } 2401 1.18 blymn 2402 1.1 blymn break; 2403 1.23 blymn 2404 1.1 blymn case REQ_BEG_FIELD: 2405 1.1 blymn cur->start_char = 0; 2406 1.30 blymn while (cur->start_line->prev != NULL) 2407 1.30 blymn cur->start_line = cur->start_line->prev; 2408 1.30 blymn cur->cur_line = cur->start_line; 2409 1.23 blymn cur->row_xpos = 0; 2410 1.25 blymn _formi_init_field_xpos(cur); 2411 1.1 blymn cur->cursor_ypos = 0; 2412 1.1 blymn break; 2413 1.1 blymn 2414 1.1 blymn case REQ_BEG_LINE: 2415 1.23 blymn cur->row_xpos = 0; 2416 1.25 blymn _formi_init_field_xpos(cur); 2417 1.21 blymn cur->start_char = 0; 2418 1.1 blymn break; 2419 1.1 blymn 2420 1.14 blymn case REQ_END_FIELD: 2421 1.30 blymn while (cur->cur_line->next != NULL) 2422 1.30 blymn cur->cur_line = cur->cur_line->next; 2423 1.30 blymn 2424 1.14 blymn if (cur->row_count > cur->rows) { 2425 1.30 blymn cur->start_line = cur->cur_line; 2426 1.30 blymn pos = cur->rows - 1; 2427 1.30 blymn while (pos > 0) { 2428 1.30 blymn cur->start_line = cur->start_line->prev; 2429 1.30 blymn pos--; 2430 1.30 blymn } 2431 1.14 blymn cur->cursor_ypos = cur->rows - 1; 2432 1.14 blymn } else { 2433 1.14 blymn cur->cursor_ypos = cur->row_count - 1; 2434 1.14 blymn } 2435 1.14 blymn 2436 1.14 blymn /* we fall through here deliberately, we are on the 2437 1.14 blymn * correct row, now we need to get to the end of the 2438 1.14 blymn * line. 2439 1.14 blymn */ 2440 1.14 blymn /* FALLTHRU */ 2441 1.14 blymn 2442 1.1 blymn case REQ_END_LINE: 2443 1.30 blymn row = cur->cur_line; 2444 1.1 blymn 2445 1.17 blymn if ((cur->rows + cur->nrows) == 1) { 2446 1.30 blymn if (row->expanded > cur->cols - 1) { 2447 1.23 blymn if ((cur->opts & O_STATIC) != O_STATIC) { 2448 1.23 blymn cur->start_char = tab_fit_window( 2449 1.30 blymn cur, row->length, 2450 1.30 blymn cur->cols) + 1; 2451 1.30 blymn cur->row_xpos = row->length 2452 1.25 blymn - cur->start_char; 2453 1.23 blymn } else { 2454 1.25 blymn cur->start_char = 0; 2455 1.25 blymn cur->row_xpos = cur->cols - 1; 2456 1.23 blymn } 2457 1.17 blymn } else { 2458 1.30 blymn cur->row_xpos = row->length + 1; 2459 1.23 blymn cur->start_char = 0; 2460 1.17 blymn } 2461 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2462 1.1 blymn } else { 2463 1.30 blymn cur->row_xpos = row->length - 1; 2464 1.30 blymn cur->cursor_xpos = row->expanded - 1; 2465 1.30 blymn if (row->next == NULL) { 2466 1.23 blymn cur->row_xpos++; 2467 1.23 blymn cur->cursor_xpos++; 2468 1.1 blymn } 2469 1.1 blymn } 2470 1.1 blymn break; 2471 1.1 blymn 2472 1.1 blymn case REQ_NEW_LINE: 2473 1.30 blymn start = cur->start_char + cur->row_xpos; 2474 1.30 blymn if ((status = split_line(cur, TRUE, start, &row)) != E_OK) 2475 1.15 blymn return status; 2476 1.30 blymn cur->cur_line->hard_ret = TRUE; 2477 1.30 blymn cur->cursor_xpos = 0; 2478 1.30 blymn cur->row_xpos = 0; 2479 1.1 blymn break; 2480 1.1 blymn 2481 1.1 blymn case REQ_INS_CHAR: 2482 1.21 blymn if ((status = _formi_add_char(cur, cur->start_char 2483 1.23 blymn + cur->row_xpos, 2484 1.21 blymn cur->pad)) != E_OK) 2485 1.21 blymn return status; 2486 1.1 blymn break; 2487 1.1 blymn 2488 1.1 blymn case REQ_INS_LINE: 2489 1.30 blymn if ((status = split_line(cur, TRUE, 0, &row)) != E_OK) 2490 1.15 blymn return status; 2491 1.30 blymn cur->cur_line->hard_ret = TRUE; 2492 1.1 blymn break; 2493 1.1 blymn 2494 1.1 blymn case REQ_DEL_CHAR: 2495 1.30 blymn row = cur->cur_line; 2496 1.30 blymn start = cur->start_char + cur->row_xpos; 2497 1.30 blymn end = row->length - 1; 2498 1.30 blymn if ((start >= row->length) && (row->next == NULL)) 2499 1.23 blymn return E_REQUEST_DENIED; 2500 1.23 blymn 2501 1.30 blymn if ((start == row->length - 1) || (row->length == 0)) { 2502 1.20 blymn if ((cur->rows + cur->nrows) > 1) { 2503 1.23 blymn /* 2504 1.30 blymn * Firstly, check if the current line has 2505 1.30 blymn * a hard return. In this case we just 2506 1.30 blymn * want to "delete" the hard return and 2507 1.30 blymn * re-wrap the field. The hard return 2508 1.30 blymn * does not occupy a character space in 2509 1.30 blymn * the buffer but we must make it appear 2510 1.30 blymn * like it does for a deletion. 2511 1.30 blymn */ 2512 1.30 blymn if (row->hard_ret == TRUE) { 2513 1.30 blymn row->hard_ret = FALSE; 2514 1.30 blymn if (_formi_join_line(cur, &row, 2515 1.30 blymn JOIN_NEXT) 2516 1.30 blymn != E_OK) { 2517 1.30 blymn row->hard_ret = TRUE; 2518 1.30 blymn return 0; 2519 1.30 blymn } else { 2520 1.30 blymn return 1; 2521 1.30 blymn } 2522 1.30 blymn } 2523 1.30 blymn 2524 1.30 blymn /* 2525 1.23 blymn * If we have more than one row, join the 2526 1.23 blymn * next row to make things easier unless 2527 1.23 blymn * we are at the end of the string, in 2528 1.23 blymn * that case the join would fail but we 2529 1.23 blymn * really want to delete the last char 2530 1.23 blymn * in the field. 2531 1.23 blymn */ 2532 1.30 blymn if (row->next != NULL) { 2533 1.30 blymn if (_formi_join_line(cur, &row, 2534 1.20 blymn JOIN_NEXT_NW) 2535 1.20 blymn != E_OK) { 2536 1.20 blymn return E_REQUEST_DENIED; 2537 1.20 blymn } 2538 1.23 blymn } 2539 1.23 blymn } 2540 1.20 blymn } 2541 1.23 blymn 2542 1.30 blymn saved = row->string[start]; 2543 1.40 christos memmove(&row->string[start], &row->string[start + 1], 2544 1.30 blymn (size_t) (end - start + 1)); 2545 1.30 blymn row->string[end] = '\0'; 2546 1.30 blymn row->length--; 2547 1.30 blymn if (row->length > 0) 2548 1.30 blymn row->expanded = _formi_tab_expanded_length( 2549 1.30 blymn row->string, 0, row->length - 1); 2550 1.30 blymn else 2551 1.30 blymn row->expanded = 0; 2552 1.23 blymn 2553 1.23 blymn /* 2554 1.23 blymn * recalculate tabs for a single line field, multiline 2555 1.23 blymn * fields will do this when the field is wrapped. 2556 1.23 blymn */ 2557 1.23 blymn if ((cur->rows + cur->nrows) == 1) 2558 1.30 blymn _formi_calculate_tabs(row); 2559 1.23 blymn /* 2560 1.23 blymn * if we are at the end of the string then back the 2561 1.23 blymn * cursor pos up one to stick on the end of the line 2562 1.23 blymn */ 2563 1.30 blymn if (start == row->length) { 2564 1.30 blymn if (row->length > 1) { 2565 1.23 blymn if ((cur->rows + cur->nrows) == 1) { 2566 1.23 blymn pos = cur->row_xpos + cur->start_char; 2567 1.23 blymn cur->start_char = 2568 1.25 blymn tab_fit_window( 2569 1.25 blymn cur, 2570 1.25 blymn cur->start_char + cur->row_xpos, 2571 1.25 blymn cur->cols); 2572 1.23 blymn cur->row_xpos = pos - cur->start_char 2573 1.23 blymn - 1; 2574 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2575 1.23 blymn } else { 2576 1.23 blymn if (cur->row_xpos == 0) { 2577 1.30 blymn if (row->next != NULL) { 2578 1.23 blymn if (_formi_join_line( 2579 1.30 blymn cur, &row, 2580 1.23 blymn JOIN_PREV_NW) 2581 1.23 blymn != E_OK) { 2582 1.23 blymn return E_REQUEST_DENIED; 2583 1.23 blymn } 2584 1.23 blymn } else { 2585 1.23 blymn if (cur->row_count > 1) 2586 1.23 blymn cur->row_count--; 2587 1.23 blymn } 2588 1.23 blymn 2589 1.23 blymn } 2590 1.23 blymn 2591 1.30 blymn cur->row_xpos = start - 1; 2592 1.23 blymn cur->cursor_xpos = 2593 1.23 blymn _formi_tab_expanded_length( 2594 1.30 blymn row->string, 2595 1.30 blymn 0, cur->row_xpos - 1); 2596 1.23 blymn if ((cur->cursor_xpos > 0) 2597 1.30 blymn && (start != (row->expanded - 1))) 2598 1.23 blymn cur->cursor_xpos--; 2599 1.23 blymn } 2600 1.23 blymn 2601 1.23 blymn start--; 2602 1.23 blymn } else { 2603 1.23 blymn start = 0; 2604 1.23 blymn cur->row_xpos = 0; 2605 1.26 blymn _formi_init_field_xpos(cur); 2606 1.23 blymn } 2607 1.23 blymn } 2608 1.23 blymn 2609 1.20 blymn if ((cur->rows + cur->nrows) > 1) { 2610 1.30 blymn if (_formi_wrap_field(cur, row) != E_OK) { 2611 1.40 christos memmove(&row->string[start + 1], 2612 1.40 christos &row->string[start], 2613 1.30 blymn (size_t) (end - start)); 2614 1.30 blymn row->length++; 2615 1.30 blymn row->string[start] = saved; 2616 1.30 blymn _formi_wrap_field(cur, row); 2617 1.20 blymn return E_REQUEST_DENIED; 2618 1.15 blymn } 2619 1.1 blymn } 2620 1.1 blymn break; 2621 1.23 blymn 2622 1.1 blymn case REQ_DEL_PREV: 2623 1.19 blymn if ((cur->cursor_xpos == 0) && (cur->start_char == 0) 2624 1.30 blymn && (cur->start_line->prev == NULL) 2625 1.30 blymn && (cur->cursor_ypos == 0)) 2626 1.1 blymn return E_REQUEST_DENIED; 2627 1.1 blymn 2628 1.30 blymn row = cur->cur_line; 2629 1.30 blymn start = cur->row_xpos + cur->start_char; 2630 1.30 blymn end = row->length - 1; 2631 1.30 blymn eat_char = TRUE; 2632 1.19 blymn 2633 1.23 blymn if ((cur->start_char + cur->row_xpos) == 0) { 2634 1.30 blymn if (row->prev == NULL) 2635 1.30 blymn return E_REQUEST_DENIED; 2636 1.30 blymn 2637 1.23 blymn /* 2638 1.30 blymn * If we are a multiline field then check if 2639 1.30 blymn * the line above has a hard return. If it does 2640 1.30 blymn * then just "eat" the hard return and re-wrap 2641 1.30 blymn * the field. 2642 1.23 blymn */ 2643 1.30 blymn if (row->prev->hard_ret == TRUE) { 2644 1.30 blymn row->prev->hard_ret = FALSE; 2645 1.30 blymn if (_formi_join_line(cur, &row, 2646 1.30 blymn JOIN_PREV) != E_OK) { 2647 1.30 blymn row->prev->hard_ret = TRUE; 2648 1.30 blymn return 0; 2649 1.30 blymn } 2650 1.30 blymn 2651 1.30 blymn eat_char = FALSE; 2652 1.30 blymn } else { 2653 1.30 blymn start = row->prev->length; 2654 1.30 blymn /* 2655 1.30 blymn * Join this line to the previous 2656 1.30 blymn * one. 2657 1.30 blymn */ 2658 1.30 blymn if (_formi_join_line(cur, &row, 2659 1.23 blymn JOIN_PREV_NW) != E_OK) { 2660 1.30 blymn return 0; 2661 1.23 blymn } 2662 1.30 blymn end = row->length - 1; 2663 1.19 blymn } 2664 1.19 blymn } 2665 1.20 blymn 2666 1.30 blymn if (eat_char == TRUE) { 2667 1.30 blymn /* 2668 1.30 blymn * eat a char from the buffer. Normally we do 2669 1.30 blymn * this unless we have deleted a "hard return" 2670 1.30 blymn * in which case we just want to join the lines 2671 1.30 blymn * without losing a char. 2672 1.30 blymn */ 2673 1.30 blymn saved = row->string[start - 1]; 2674 1.40 christos memmove(&row->string[start - 1], &row->string[start], 2675 1.30 blymn (size_t) (end - start + 1)); 2676 1.30 blymn row->length--; 2677 1.30 blymn row->string[row->length] = '\0'; 2678 1.30 blymn row->expanded = _formi_tab_expanded_length( 2679 1.30 blymn row->string, 0, row->length - 1); 2680 1.30 blymn } 2681 1.19 blymn 2682 1.19 blymn if ((cur->rows + cur->nrows) == 1) { 2683 1.30 blymn _formi_calculate_tabs(row); 2684 1.23 blymn pos = cur->row_xpos + cur->start_char; 2685 1.23 blymn if (pos > 0) 2686 1.23 blymn pos--; 2687 1.23 blymn cur->start_char = 2688 1.23 blymn tab_fit_window(cur, 2689 1.23 blymn cur->start_char + cur->row_xpos, 2690 1.23 blymn cur->cols); 2691 1.23 blymn cur->row_xpos = pos - cur->start_char; 2692 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2693 1.1 blymn } else { 2694 1.30 blymn if (eat_char == TRUE) { 2695 1.30 blymn cur->row_xpos--; 2696 1.30 blymn if (cur->row_xpos > 0) 2697 1.30 blymn cur->cursor_xpos = 2698 1.30 blymn _formi_tab_expanded_length( 2699 1.30 blymn row->string, 0, 2700 1.30 blymn cur->row_xpos - 1); 2701 1.30 blymn else 2702 1.30 blymn cur->cursor_xpos = 0; 2703 1.19 blymn } 2704 1.19 blymn 2705 1.30 blymn if ((_formi_wrap_field(cur, row) != E_OK)) { 2706 1.40 christos memmove(&row->string[start], 2707 1.40 christos &row->string[start - 1], 2708 1.30 blymn (size_t) (end - start)); 2709 1.30 blymn row->length++; 2710 1.30 blymn row->string[start - 1] = saved; 2711 1.30 blymn row->string[row->length] = '\0'; 2712 1.30 blymn _formi_wrap_field(cur, row); 2713 1.30 blymn return E_REQUEST_DENIED; 2714 1.19 blymn } 2715 1.19 blymn } 2716 1.1 blymn break; 2717 1.23 blymn 2718 1.1 blymn case REQ_DEL_LINE: 2719 1.20 blymn if (((cur->rows + cur->nrows) == 1) || 2720 1.20 blymn (cur->row_count == 1)) { 2721 1.20 blymn /* single line case */ 2722 1.30 blymn row->length = 0; 2723 1.30 blymn row->expanded = row->length = 0; 2724 1.25 blymn cur->row_xpos = 0; 2725 1.25 blymn _formi_init_field_xpos(cur); 2726 1.23 blymn cur->cursor_ypos = 0; 2727 1.20 blymn } else { 2728 1.20 blymn /* multiline field */ 2729 1.20 blymn old_count = cur->row_count; 2730 1.1 blymn cur->row_count--; 2731 1.20 blymn if (cur->row_count == 0) 2732 1.20 blymn cur->row_count = 1; 2733 1.20 blymn 2734 1.30 blymn if (old_count == 1) { 2735 1.30 blymn row->expanded = row->length = 0; 2736 1.20 blymn cur->cursor_xpos = 0; 2737 1.23 blymn cur->row_xpos = 0; 2738 1.20 blymn cur->cursor_ypos = 0; 2739 1.30 blymn } else 2740 1.30 blymn add_to_free(cur, row); 2741 1.20 blymn 2742 1.30 blymn if (row->next == NULL) { 2743 1.15 blymn if (cur->cursor_ypos == 0) { 2744 1.30 blymn if (cur->start_line->prev != NULL) { 2745 1.30 blymn cur->start_line = 2746 1.30 blymn cur->start_line->prev; 2747 1.15 blymn } 2748 1.15 blymn } else { 2749 1.15 blymn cur->cursor_ypos--; 2750 1.15 blymn } 2751 1.15 blymn } 2752 1.20 blymn 2753 1.20 blymn if (old_count > 1) { 2754 1.30 blymn if (cur->cursor_xpos > row->expanded) { 2755 1.30 blymn cur->cursor_xpos = row->expanded - 1; 2756 1.30 blymn cur->row_xpos = row->length - 1; 2757 1.23 blymn } 2758 1.30 blymn 2759 1.33 roy cur->start_line = cur->alines; 2760 1.30 blymn rs = cur->start_line; 2761 1.30 blymn cur->cursor_ypos = 0; 2762 1.30 blymn while (rs != row) { 2763 1.30 blymn if (cur->cursor_ypos < cur->rows) 2764 1.30 blymn cur->cursor_ypos++; 2765 1.30 blymn else 2766 1.30 blymn cur->start_line = 2767 1.30 blymn cur->start_line->next; 2768 1.30 blymn rs = rs->next; 2769 1.20 blymn } 2770 1.20 blymn } 2771 1.15 blymn } 2772 1.1 blymn break; 2773 1.23 blymn 2774 1.1 blymn case REQ_DEL_WORD: 2775 1.30 blymn start = cur->start_char + cur->row_xpos; 2776 1.30 blymn str = row->string; 2777 1.30 blymn 2778 1.30 blymn wb = find_eow(cur, start, TRUE, &row); 2779 1.30 blymn if (wb < 0) 2780 1.30 blymn return wb; 2781 1.23 blymn 2782 1.30 blymn end = wb; 2783 1.23 blymn 2784 1.23 blymn /* 2785 1.23 blymn * If not at the start of a word then find the start, 2786 1.23 blymn * we cannot blindly call find_sow because this will 2787 1.23 blymn * skip back a word if we are already at the start of 2788 1.23 blymn * a word. 2789 1.23 blymn */ 2790 1.23 blymn if ((start > 0) 2791 1.34 tnozaki && !(isblank((unsigned char)str[start - 1]) && 2792 1.34 tnozaki !isblank((unsigned char)str[start]))) 2793 1.30 blymn start = find_sow(start, &row); 2794 1.30 blymn str = row->string; 2795 1.30 blymn /* XXXX hmmmm what if start and end on diff rows? XXXX */ 2796 1.40 christos memmove(&str[start], &str[end], 2797 1.30 blymn (size_t) (row->length - end + 1)); 2798 1.15 blymn len = end - start; 2799 1.30 blymn row->length -= len; 2800 1.23 blymn 2801 1.23 blymn if ((cur->rows + cur->nrows) > 1) { 2802 1.23 blymn row = cur->start_line + cur->cursor_ypos; 2803 1.30 blymn if (row->next != NULL) { 2804 1.23 blymn /* 2805 1.23 blymn * if not on the last row we need to 2806 1.23 blymn * join on the next row so the line 2807 1.23 blymn * will be re-wrapped. 2808 1.23 blymn */ 2809 1.30 blymn _formi_join_line(cur, &row, JOIN_NEXT_NW); 2810 1.23 blymn } 2811 1.30 blymn _formi_wrap_field(cur, row); 2812 1.30 blymn cur->row_xpos = start; 2813 1.23 blymn cur->cursor_xpos = _formi_tab_expanded_length( 2814 1.30 blymn row->string, 0, cur->row_xpos); 2815 1.30 blymn if (cur->cursor_xpos > 0) 2816 1.23 blymn cur->cursor_xpos--; 2817 1.23 blymn } else { 2818 1.30 blymn _formi_calculate_tabs(row); 2819 1.23 blymn cur->row_xpos = start - cur->start_char; 2820 1.23 blymn if (cur->row_xpos > 0) 2821 1.23 blymn cur->row_xpos--; 2822 1.28 blymn _formi_set_cursor_xpos(cur, FALSE); 2823 1.23 blymn } 2824 1.1 blymn break; 2825 1.1 blymn 2826 1.1 blymn case REQ_CLR_EOL: 2827 1.30 blymn row->string[cur->row_xpos + 1] = '\0'; 2828 1.30 blymn row->length = cur->row_xpos + 1; 2829 1.30 blymn row->expanded = cur->cursor_xpos + 1; 2830 1.1 blymn break; 2831 1.1 blymn 2832 1.1 blymn case REQ_CLR_EOF: 2833 1.30 blymn row = cur->cur_line->next; 2834 1.30 blymn while (row != NULL) { 2835 1.30 blymn rs = row->next; 2836 1.30 blymn add_to_free(cur, row); 2837 1.30 blymn row = rs; 2838 1.30 blymn cur->row_count--; 2839 1.30 blymn } 2840 1.1 blymn break; 2841 1.1 blymn 2842 1.1 blymn case REQ_CLR_FIELD: 2843 1.33 roy row = cur->alines->next; 2844 1.33 roy cur->cur_line = cur->alines; 2845 1.33 roy cur->start_line = cur->alines; 2846 1.30 blymn 2847 1.30 blymn while (row != NULL) { 2848 1.30 blymn rs = row->next; 2849 1.30 blymn add_to_free(cur, row); 2850 1.30 blymn row = rs; 2851 1.30 blymn } 2852 1.30 blymn 2853 1.33 roy cur->alines->string[0] = '\0'; 2854 1.33 roy cur->alines->length = 0; 2855 1.33 roy cur->alines->expanded = 0; 2856 1.15 blymn cur->row_count = 1; 2857 1.15 blymn cur->cursor_ypos = 0; 2858 1.23 blymn cur->row_xpos = 0; 2859 1.25 blymn _formi_init_field_xpos(cur); 2860 1.15 blymn cur->start_char = 0; 2861 1.1 blymn break; 2862 1.1 blymn 2863 1.1 blymn case REQ_OVL_MODE: 2864 1.1 blymn cur->overlay = 1; 2865 1.1 blymn break; 2866 1.1 blymn 2867 1.1 blymn case REQ_INS_MODE: 2868 1.1 blymn cur->overlay = 0; 2869 1.1 blymn break; 2870 1.1 blymn 2871 1.1 blymn case REQ_SCR_FLINE: 2872 1.1 blymn _formi_scroll_fwd(cur, 1); 2873 1.1 blymn break; 2874 1.1 blymn 2875 1.1 blymn case REQ_SCR_BLINE: 2876 1.1 blymn _formi_scroll_back(cur, 1); 2877 1.1 blymn break; 2878 1.1 blymn 2879 1.1 blymn case REQ_SCR_FPAGE: 2880 1.1 blymn _formi_scroll_fwd(cur, cur->rows); 2881 1.1 blymn break; 2882 1.1 blymn 2883 1.1 blymn case REQ_SCR_BPAGE: 2884 1.1 blymn _formi_scroll_back(cur, cur->rows); 2885 1.1 blymn break; 2886 1.1 blymn 2887 1.1 blymn case REQ_SCR_FHPAGE: 2888 1.1 blymn _formi_scroll_fwd(cur, cur->rows / 2); 2889 1.1 blymn break; 2890 1.1 blymn 2891 1.1 blymn case REQ_SCR_BHPAGE: 2892 1.1 blymn _formi_scroll_back(cur, cur->rows / 2); 2893 1.1 blymn break; 2894 1.1 blymn 2895 1.1 blymn case REQ_SCR_FCHAR: 2896 1.30 blymn _formi_hscroll_fwd(cur, row, 1); 2897 1.1 blymn break; 2898 1.1 blymn 2899 1.1 blymn case REQ_SCR_BCHAR: 2900 1.30 blymn _formi_hscroll_back(cur, row, 1); 2901 1.1 blymn break; 2902 1.1 blymn 2903 1.1 blymn case REQ_SCR_HFLINE: 2904 1.30 blymn _formi_hscroll_fwd(cur, row, cur->cols); 2905 1.1 blymn break; 2906 1.1 blymn 2907 1.1 blymn case REQ_SCR_HBLINE: 2908 1.30 blymn _formi_hscroll_back(cur, row, cur->cols); 2909 1.1 blymn break; 2910 1.1 blymn 2911 1.1 blymn case REQ_SCR_HFHALF: 2912 1.30 blymn _formi_hscroll_fwd(cur, row, cur->cols / 2); 2913 1.1 blymn break; 2914 1.1 blymn 2915 1.1 blymn case REQ_SCR_HBHALF: 2916 1.30 blymn _formi_hscroll_back(cur, row, cur->cols / 2); 2917 1.1 blymn break; 2918 1.1 blymn 2919 1.1 blymn default: 2920 1.1 blymn return 0; 2921 1.1 blymn } 2922 1.1 blymn 2923 1.38 christos _formi_dbg_printf( 2924 1.38 christos "%s: cursor_xpos=%d, row_xpos=%d, start_char=%d, length=%d, " 2925 1.38 christos "allocated=%d\n", __func__, cur->cursor_xpos, cur->row_xpos, 2926 1.38 christos cur->start_char, cur->cur_line->length, cur->cur_line->allocated); 2927 1.38 christos _formi_dbg_printf("%s: start_line=%p, ypos=%d\n", __func__, 2928 1.38 christos cur->start_line, cur->cursor_ypos); 2929 1.38 christos _formi_dbg_printf("%s: string=\"%s\"\n", __func__, 2930 1.38 christos cur->cur_line->string); 2931 1.23 blymn assert ((cur->cursor_xpos < INT_MAX) && (cur->row_xpos < INT_MAX) 2932 1.23 blymn && (cur->cursor_xpos >= cur->row_xpos)); 2933 1.1 blymn return 1; 2934 1.1 blymn } 2935 1.1 blymn 2936 1.1 blymn /* 2937 1.36 mbalmer * Validate the given character by passing it to any type character 2938 1.8 blymn * checking routines, if they exist. 2939 1.8 blymn */ 2940 1.8 blymn int 2941 1.8 blymn _formi_validate_char(FIELD *field, char c) 2942 1.8 blymn { 2943 1.8 blymn int ret_val; 2944 1.8 blymn 2945 1.8 blymn if (field->type == NULL) 2946 1.8 blymn return E_OK; 2947 1.8 blymn 2948 1.8 blymn ret_val = E_INVALID_FIELD; 2949 1.8 blymn _formi_do_char_validation(field, field->type, c, &ret_val); 2950 1.8 blymn 2951 1.8 blymn return ret_val; 2952 1.8 blymn } 2953 1.8 blymn 2954 1.8 blymn 2955 1.8 blymn /* 2956 1.8 blymn * Perform the validation of the character, invoke all field_type validation 2957 1.8 blymn * routines. If the field is ok then update ret_val to E_OK otherwise 2958 1.8 blymn * ret_val is not changed. 2959 1.8 blymn */ 2960 1.8 blymn static void 2961 1.8 blymn _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val) 2962 1.8 blymn { 2963 1.8 blymn if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { 2964 1.8 blymn _formi_do_char_validation(field, type->link->next, c, ret_val); 2965 1.8 blymn _formi_do_char_validation(field, type->link->prev, c, ret_val); 2966 1.8 blymn } else { 2967 1.8 blymn if (type->char_check == NULL) 2968 1.8 blymn *ret_val = E_OK; 2969 1.8 blymn else { 2970 1.8 blymn if (type->char_check((int)(unsigned char) c, 2971 1.8 blymn field->args) == TRUE) 2972 1.8 blymn *ret_val = E_OK; 2973 1.8 blymn } 2974 1.8 blymn } 2975 1.8 blymn } 2976 1.8 blymn 2977 1.8 blymn /* 2978 1.1 blymn * Validate the current field. If the field validation returns success then 2979 1.1 blymn * return E_OK otherwise return E_INVALID_FIELD. 2980 1.1 blymn * 2981 1.1 blymn */ 2982 1.1 blymn int 2983 1.1 blymn _formi_validate_field(FORM *form) 2984 1.1 blymn { 2985 1.1 blymn FIELD *cur; 2986 1.8 blymn int ret_val, count; 2987 1.1 blymn 2988 1.1 blymn 2989 1.1 blymn if ((form == NULL) || (form->fields == NULL) || 2990 1.1 blymn (form->fields[0] == NULL)) 2991 1.1 blymn return E_INVALID_FIELD; 2992 1.1 blymn 2993 1.1 blymn cur = form->fields[form->cur_field]; 2994 1.30 blymn 2995 1.30 blymn /* 2996 1.30 blymn * Sync the buffer if it has been modified so the field 2997 1.30 blymn * validation routines can use it and because this is 2998 1.30 blymn * the correct behaviour according to AT&T implementation. 2999 1.30 blymn */ 3000 1.30 blymn if ((cur->buf0_status == TRUE) 3001 1.30 blymn && ((ret_val = _formi_sync_buffer(cur)) != E_OK)) 3002 1.30 blymn return ret_val; 3003 1.30 blymn 3004 1.30 blymn /* 3005 1.30 blymn * If buffer is untouched then the string pointer may be 3006 1.30 blymn * NULL, see if this is ok or not. 3007 1.30 blymn */ 3008 1.30 blymn if (cur->buffers[0].string == NULL) { 3009 1.30 blymn if ((cur->opts & O_NULLOK) == O_NULLOK) 3010 1.30 blymn return E_OK; 3011 1.30 blymn else 3012 1.30 blymn return E_INVALID_FIELD; 3013 1.30 blymn } 3014 1.1 blymn 3015 1.30 blymn count = _formi_skip_blanks(cur->buffers[0].string, 0); 3016 1.9 blymn 3017 1.9 blymn /* check if we have a null field, depending on the nullok flag 3018 1.9 blymn * this may be acceptable or not.... 3019 1.9 blymn */ 3020 1.9 blymn if (cur->buffers[0].string[count] == '\0') { 3021 1.9 blymn if ((cur->opts & O_NULLOK) == O_NULLOK) 3022 1.9 blymn return E_OK; 3023 1.9 blymn else 3024 1.9 blymn return E_INVALID_FIELD; 3025 1.9 blymn } 3026 1.1 blymn 3027 1.11 blymn /* check if an unmodified field is ok */ 3028 1.11 blymn if (cur->buf0_status == 0) { 3029 1.11 blymn if ((cur->opts & O_PASSOK) == O_PASSOK) 3030 1.11 blymn return E_OK; 3031 1.11 blymn else 3032 1.11 blymn return E_INVALID_FIELD; 3033 1.11 blymn } 3034 1.11 blymn 3035 1.1 blymn /* if there is no type then just accept the field */ 3036 1.1 blymn if (cur->type == NULL) 3037 1.1 blymn return E_OK; 3038 1.1 blymn 3039 1.1 blymn ret_val = E_INVALID_FIELD; 3040 1.1 blymn _formi_do_validation(cur, cur->type, &ret_val); 3041 1.1 blymn 3042 1.1 blymn return ret_val; 3043 1.1 blymn } 3044 1.1 blymn 3045 1.1 blymn /* 3046 1.1 blymn * Perform the validation of the field, invoke all field_type validation 3047 1.1 blymn * routines. If the field is ok then update ret_val to E_OK otherwise 3048 1.1 blymn * ret_val is not changed. 3049 1.1 blymn */ 3050 1.1 blymn static void 3051 1.1 blymn _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val) 3052 1.1 blymn { 3053 1.1 blymn if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { 3054 1.1 blymn _formi_do_validation(field, type->link->next, ret_val); 3055 1.1 blymn _formi_do_validation(field, type->link->prev, ret_val); 3056 1.1 blymn } else { 3057 1.1 blymn if (type->field_check == NULL) 3058 1.1 blymn *ret_val = E_OK; 3059 1.1 blymn else { 3060 1.1 blymn if (type->field_check(field, field_buffer(field, 0)) 3061 1.1 blymn == TRUE) 3062 1.1 blymn *ret_val = E_OK; 3063 1.1 blymn } 3064 1.1 blymn } 3065 1.1 blymn } 3066 1.1 blymn 3067 1.1 blymn /* 3068 1.1 blymn * Select the next/previous choice for the field, the driver command 3069 1.1 blymn * selecting the direction will be passed in c. Return 1 if a choice 3070 1.1 blymn * selection succeeded, 0 otherwise. 3071 1.1 blymn */ 3072 1.1 blymn int 3073 1.1 blymn _formi_field_choice(FORM *form, int c) 3074 1.1 blymn { 3075 1.1 blymn FIELDTYPE *type; 3076 1.1 blymn FIELD *field; 3077 1.1 blymn 3078 1.1 blymn if ((form == NULL) || (form->fields == NULL) || 3079 1.1 blymn (form->fields[0] == NULL) || 3080 1.1 blymn (form->fields[form->cur_field]->type == NULL)) 3081 1.1 blymn return 0; 3082 1.1 blymn 3083 1.1 blymn field = form->fields[form->cur_field]; 3084 1.1 blymn type = field->type; 3085 1.1 blymn 3086 1.1 blymn switch (c) { 3087 1.1 blymn case REQ_NEXT_CHOICE: 3088 1.1 blymn if (type->next_choice == NULL) 3089 1.1 blymn return 0; 3090 1.1 blymn else 3091 1.1 blymn return type->next_choice(field, 3092 1.1 blymn field_buffer(field, 0)); 3093 1.1 blymn 3094 1.1 blymn case REQ_PREV_CHOICE: 3095 1.1 blymn if (type->prev_choice == NULL) 3096 1.1 blymn return 0; 3097 1.1 blymn else 3098 1.1 blymn return type->prev_choice(field, 3099 1.1 blymn field_buffer(field, 0)); 3100 1.1 blymn 3101 1.1 blymn default: /* should never happen! */ 3102 1.1 blymn return 0; 3103 1.1 blymn } 3104 1.1 blymn } 3105 1.1 blymn 3106 1.1 blymn /* 3107 1.1 blymn * Update the fields if they have changed. The parameter old has the 3108 1.1 blymn * previous current field as the current field may have been updated by 3109 1.1 blymn * the driver. Return 1 if the form page needs updating. 3110 1.1 blymn * 3111 1.1 blymn */ 3112 1.1 blymn int 3113 1.1 blymn _formi_update_field(FORM *form, int old_field) 3114 1.1 blymn { 3115 1.1 blymn int cur, i; 3116 1.1 blymn 3117 1.1 blymn cur = form->cur_field; 3118 1.1 blymn 3119 1.1 blymn if (old_field != cur) { 3120 1.1 blymn if (!((cur >= form->page_starts[form->page].first) && 3121 1.1 blymn (cur <= form->page_starts[form->page].last))) { 3122 1.1 blymn /* not on same page any more */ 3123 1.1 blymn for (i = 0; i < form->max_page; i++) { 3124 1.1 blymn if ((form->page_starts[i].in_use == 1) && 3125 1.1 blymn (form->page_starts[i].first <= cur) && 3126 1.1 blymn (form->page_starts[i].last >= cur)) { 3127 1.1 blymn form->page = i; 3128 1.1 blymn return 1; 3129 1.1 blymn } 3130 1.1 blymn } 3131 1.1 blymn } 3132 1.1 blymn } 3133 1.1 blymn 3134 1.1 blymn _formi_redraw_field(form, old_field); 3135 1.1 blymn _formi_redraw_field(form, form->cur_field); 3136 1.1 blymn return 0; 3137 1.1 blymn } 3138 1.1 blymn 3139 1.1 blymn /* 3140 1.1 blymn * Compare function for the field sorting 3141 1.1 blymn * 3142 1.1 blymn */ 3143 1.1 blymn static int 3144 1.1 blymn field_sort_compare(const void *one, const void *two) 3145 1.1 blymn { 3146 1.1 blymn const FIELD *a, *b; 3147 1.1 blymn int tl; 3148 1.1 blymn 3149 1.40 christos a = *(const FIELD **) __UNCONST(one); 3150 1.40 christos b = *(const FIELD **) __UNCONST(two); 3151 1.1 blymn 3152 1.1 blymn if (a == NULL) 3153 1.1 blymn return 1; 3154 1.1 blymn 3155 1.1 blymn if (b == NULL) 3156 1.1 blymn return -1; 3157 1.1 blymn 3158 1.1 blymn /* 3159 1.1 blymn * First check the page, we want the fields sorted by page. 3160 1.1 blymn * 3161 1.1 blymn */ 3162 1.1 blymn if (a->page != b->page) 3163 1.1 blymn return ((a->page > b->page)? 1 : -1); 3164 1.1 blymn 3165 1.1 blymn tl = _formi_top_left(a->parent, a->index, b->index); 3166 1.1 blymn 3167 1.1 blymn /* 3168 1.1 blymn * sort fields left to right, top to bottom so the top left is 3169 1.30 blymn * the lesser value.... 3170 1.1 blymn */ 3171 1.1 blymn return ((tl == a->index)? -1 : 1); 3172 1.1 blymn } 3173 1.1 blymn 3174 1.1 blymn /* 3175 1.1 blymn * Sort the fields in a form ready for driver traversal. 3176 1.1 blymn */ 3177 1.1 blymn void 3178 1.1 blymn _formi_sort_fields(FORM *form) 3179 1.1 blymn { 3180 1.1 blymn FIELD **sort_area; 3181 1.1 blymn int i; 3182 1.1 blymn 3183 1.37 christos TAILQ_INIT(&form->sorted_fields); 3184 1.1 blymn 3185 1.37 christos if ((sort_area = malloc(sizeof(*sort_area) * form->field_count)) 3186 1.1 blymn == NULL) 3187 1.1 blymn return; 3188 1.1 blymn 3189 1.40 christos memcpy(sort_area, form->fields, sizeof(*sort_area) * form->field_count); 3190 1.30 blymn qsort(sort_area, (size_t) form->field_count, sizeof(FIELD *), 3191 1.1 blymn field_sort_compare); 3192 1.1 blymn 3193 1.1 blymn for (i = 0; i < form->field_count; i++) 3194 1.37 christos TAILQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue); 3195 1.1 blymn 3196 1.1 blymn free(sort_area); 3197 1.1 blymn } 3198 1.1 blymn 3199 1.1 blymn /* 3200 1.1 blymn * Set the neighbours for all the fields in the given form. 3201 1.1 blymn */ 3202 1.1 blymn void 3203 1.1 blymn _formi_stitch_fields(FORM *form) 3204 1.1 blymn { 3205 1.1 blymn int above_row, below_row, end_above, end_below, cur_row, real_end; 3206 1.1 blymn FIELD *cur, *above, *below; 3207 1.1 blymn 3208 1.1 blymn /* 3209 1.1 blymn * check if the sorted fields circle queue is empty, just 3210 1.1 blymn * return if it is. 3211 1.1 blymn */ 3212 1.37 christos if (TAILQ_EMPTY(&form->sorted_fields)) 3213 1.1 blymn return; 3214 1.1 blymn 3215 1.1 blymn /* initially nothing is above..... */ 3216 1.1 blymn above_row = -1; 3217 1.1 blymn end_above = TRUE; 3218 1.1 blymn above = NULL; 3219 1.1 blymn 3220 1.1 blymn /* set up the first field as the current... */ 3221 1.37 christos cur = TAILQ_FIRST(&form->sorted_fields); 3222 1.1 blymn cur_row = cur->form_row; 3223 1.1 blymn 3224 1.1 blymn /* find the first field on the next row if any */ 3225 1.37 christos below = TAILQ_NEXT(cur, glue); 3226 1.1 blymn below_row = -1; 3227 1.1 blymn end_below = TRUE; 3228 1.1 blymn real_end = TRUE; 3229 1.37 christos while (below != NULL) { 3230 1.1 blymn if (below->form_row != cur_row) { 3231 1.1 blymn below_row = below->form_row; 3232 1.1 blymn end_below = FALSE; 3233 1.1 blymn real_end = FALSE; 3234 1.1 blymn break; 3235 1.1 blymn } 3236 1.37 christos below = TAILQ_NEXT(below, glue); 3237 1.1 blymn } 3238 1.1 blymn 3239 1.1 blymn /* walk the sorted fields, setting the neighbour pointers */ 3240 1.37 christos while (cur != NULL) { 3241 1.37 christos if (cur == TAILQ_FIRST(&form->sorted_fields)) 3242 1.1 blymn cur->left = NULL; 3243 1.1 blymn else 3244 1.37 christos cur->left = TAILQ_PREV(cur, _formi_sort_head, glue); 3245 1.1 blymn 3246 1.37 christos if (cur == TAILQ_LAST(&form->sorted_fields, _formi_sort_head)) 3247 1.1 blymn cur->right = NULL; 3248 1.1 blymn else 3249 1.37 christos cur->right = TAILQ_NEXT(cur, glue); 3250 1.1 blymn 3251 1.1 blymn if (end_above == TRUE) 3252 1.1 blymn cur->up = NULL; 3253 1.1 blymn else { 3254 1.1 blymn cur->up = above; 3255 1.37 christos above = TAILQ_NEXT(above, glue); 3256 1.1 blymn if (above_row != above->form_row) { 3257 1.1 blymn end_above = TRUE; 3258 1.1 blymn above_row = above->form_row; 3259 1.1 blymn } 3260 1.1 blymn } 3261 1.1 blymn 3262 1.1 blymn if (end_below == TRUE) 3263 1.1 blymn cur->down = NULL; 3264 1.1 blymn else { 3265 1.1 blymn cur->down = below; 3266 1.37 christos below = TAILQ_NEXT(below, glue); 3267 1.37 christos if (below == NULL) { 3268 1.1 blymn end_below = TRUE; 3269 1.1 blymn real_end = TRUE; 3270 1.1 blymn } else if (below_row != below->form_row) { 3271 1.1 blymn end_below = TRUE; 3272 1.1 blymn below_row = below->form_row; 3273 1.1 blymn } 3274 1.1 blymn } 3275 1.1 blymn 3276 1.37 christos cur = TAILQ_NEXT(cur, glue); 3277 1.37 christos if ((cur != NULL) 3278 1.1 blymn && (cur_row != cur->form_row)) { 3279 1.1 blymn cur_row = cur->form_row; 3280 1.1 blymn if (end_above == FALSE) { 3281 1.37 christos for (; above != 3282 1.37 christos TAILQ_FIRST(&form->sorted_fields); 3283 1.37 christos above = TAILQ_NEXT(above, glue)) { 3284 1.1 blymn if (above->form_row != above_row) { 3285 1.1 blymn above_row = above->form_row; 3286 1.1 blymn break; 3287 1.1 blymn } 3288 1.1 blymn } 3289 1.1 blymn } else if (above == NULL) { 3290 1.37 christos above = TAILQ_FIRST(&form->sorted_fields); 3291 1.1 blymn end_above = FALSE; 3292 1.1 blymn above_row = above->form_row; 3293 1.1 blymn } else 3294 1.1 blymn end_above = FALSE; 3295 1.1 blymn 3296 1.1 blymn if (end_below == FALSE) { 3297 1.1 blymn while (below_row == below->form_row) { 3298 1.37 christos below = TAILQ_NEXT(below, glue); 3299 1.37 christos if (below == NULL) { 3300 1.1 blymn real_end = TRUE; 3301 1.1 blymn end_below = TRUE; 3302 1.1 blymn break; 3303 1.1 blymn } 3304 1.1 blymn } 3305 1.1 blymn 3306 1.37 christos if (below != NULL) 3307 1.1 blymn below_row = below->form_row; 3308 1.1 blymn } else if (real_end == FALSE) 3309 1.1 blymn end_below = FALSE; 3310 1.1 blymn 3311 1.1 blymn } 3312 1.1 blymn } 3313 1.23 blymn } 3314 1.23 blymn 3315 1.23 blymn /* 3316 1.23 blymn * Calculate the length of the displayed line allowing for any tab 3317 1.23 blymn * characters that need to be expanded. We assume that the tab stops 3318 1.23 blymn * are 8 characters apart. The parameters start and end are the 3319 1.23 blymn * character positions in the string str we want to get the length of, 3320 1.23 blymn * the function returns the number of characters from the start 3321 1.23 blymn * position to the end position that should be displayed after any 3322 1.23 blymn * intervening tabs have been expanded. 3323 1.23 blymn */ 3324 1.23 blymn int 3325 1.23 blymn _formi_tab_expanded_length(char *str, unsigned int start, unsigned int end) 3326 1.23 blymn { 3327 1.23 blymn int len, start_len, i; 3328 1.23 blymn 3329 1.23 blymn /* if we have a null string then there is no length */ 3330 1.23 blymn if (str[0] == '\0') 3331 1.23 blymn return 0; 3332 1.23 blymn 3333 1.23 blymn len = 0; 3334 1.23 blymn start_len = 0; 3335 1.23 blymn 3336 1.23 blymn /* 3337 1.23 blymn * preceding tabs affect the length tabs in the span, so 3338 1.23 blymn * we need to calculate the length including the stuff before 3339 1.23 blymn * start and then subtract off the unwanted bit. 3340 1.23 blymn */ 3341 1.23 blymn for (i = 0; i <= end; i++) { 3342 1.23 blymn if (i == start) /* stash preamble length for later */ 3343 1.23 blymn start_len = len; 3344 1.23 blymn 3345 1.23 blymn if (str[i] == '\0') 3346 1.23 blymn break; 3347 1.23 blymn 3348 1.23 blymn if (str[i] == '\t') 3349 1.23 blymn len = len - (len % 8) + 8; 3350 1.23 blymn else 3351 1.23 blymn len++; 3352 1.23 blymn } 3353 1.23 blymn 3354 1.38 christos _formi_dbg_printf( 3355 1.38 christos "%s: start=%d, end=%d, expanded=%d (diff=%d)\n", __func__, 3356 1.38 christos start, end, (len - start_len), (end - start)); 3357 1.23 blymn 3358 1.23 blymn return (len - start_len); 3359 1.23 blymn } 3360 1.23 blymn 3361 1.23 blymn /* 3362 1.23 blymn * Calculate the tab stops on a given line in the field and set up 3363 1.23 blymn * the tabs list with the results. We do this by scanning the line for tab 3364 1.23 blymn * characters and if one is found, noting the position and the number of 3365 1.23 blymn * characters to get to the next tab stop. This information is kept to 3366 1.23 blymn * make manipulating the field (scrolling and so on) easier to handle. 3367 1.23 blymn */ 3368 1.23 blymn void 3369 1.30 blymn _formi_calculate_tabs(_FORMI_FIELD_LINES *row) 3370 1.23 blymn { 3371 1.30 blymn _formi_tab_t *ts = row->tabs, *old_ts = NULL, **tsp; 3372 1.23 blymn int i, j; 3373 1.23 blymn 3374 1.23 blymn /* 3375 1.23 blymn * If the line already has tabs then invalidate them by 3376 1.23 blymn * walking the list and killing the in_use flag. 3377 1.23 blymn */ 3378 1.23 blymn for (; ts != NULL; ts = ts->fwd) 3379 1.23 blymn ts->in_use = FALSE; 3380 1.23 blymn 3381 1.23 blymn 3382 1.23 blymn /* 3383 1.23 blymn * Now look for tabs in the row and record the info... 3384 1.23 blymn */ 3385 1.30 blymn tsp = &row->tabs; 3386 1.30 blymn for (i = 0, j = 0; i < row->length; i++, j++) { 3387 1.30 blymn if (row->string[i] == '\t') { 3388 1.23 blymn if (*tsp == NULL) { 3389 1.43 mrg if ((*tsp = malloc(sizeof(**tsp))) == NULL) 3390 1.23 blymn return; 3391 1.23 blymn (*tsp)->back = old_ts; 3392 1.23 blymn (*tsp)->fwd = NULL; 3393 1.23 blymn } 3394 1.23 blymn 3395 1.23 blymn (*tsp)->in_use = TRUE; 3396 1.30 blymn (*tsp)->pos = i; 3397 1.23 blymn (*tsp)->size = 8 - (j % 8); 3398 1.23 blymn j += (*tsp)->size - 1; 3399 1.23 blymn old_ts = *tsp; 3400 1.23 blymn tsp = &(*tsp)->fwd; 3401 1.23 blymn } 3402 1.23 blymn } 3403 1.23 blymn } 3404 1.23 blymn 3405 1.23 blymn /* 3406 1.23 blymn * Return the size of the tab padding for a tab character at the given 3407 1.23 blymn * position. Return 1 if there is not a tab char entry matching the 3408 1.23 blymn * given location. 3409 1.23 blymn */ 3410 1.23 blymn static int 3411 1.30 blymn tab_size(_FORMI_FIELD_LINES *row, unsigned int i) 3412 1.23 blymn { 3413 1.23 blymn _formi_tab_t *ts; 3414 1.23 blymn 3415 1.30 blymn ts = row->tabs; 3416 1.23 blymn while ((ts != NULL) && (ts->pos != i)) 3417 1.23 blymn ts = ts->fwd; 3418 1.23 blymn 3419 1.23 blymn if (ts == NULL) 3420 1.23 blymn return 1; 3421 1.23 blymn else 3422 1.23 blymn return ts->size; 3423 1.23 blymn } 3424 1.23 blymn 3425 1.23 blymn /* 3426 1.23 blymn * Find the character offset that corresponds to longest tab expanded 3427 1.23 blymn * string that will fit into the given window. Walk the string backwards 3428 1.23 blymn * evaluating the sizes of any tabs that are in the string. Note that 3429 1.23 blymn * using this function on a multi-line window will produce undefined 3430 1.23 blymn * results - it is really only required for a single row field. 3431 1.23 blymn */ 3432 1.23 blymn static int 3433 1.23 blymn tab_fit_window(FIELD *field, unsigned int pos, unsigned int window) 3434 1.23 blymn { 3435 1.23 blymn int scroll_amt, i; 3436 1.23 blymn _formi_tab_t *ts; 3437 1.23 blymn 3438 1.23 blymn /* first find the last tab */ 3439 1.33 roy ts = field->alines->tabs; 3440 1.23 blymn 3441 1.23 blymn /* 3442 1.23 blymn * unless there are no tabs - just return the window size, 3443 1.23 blymn * if there is enough room, otherwise 0. 3444 1.23 blymn */ 3445 1.23 blymn if (ts == NULL) { 3446 1.33 roy if (field->alines->length < window) 3447 1.23 blymn return 0; 3448 1.23 blymn else 3449 1.33 roy return field->alines->length - window + 1; 3450 1.23 blymn } 3451 1.23 blymn 3452 1.23 blymn while ((ts->fwd != NULL) && (ts->fwd->in_use == TRUE)) 3453 1.23 blymn ts = ts->fwd; 3454 1.23 blymn 3455 1.23 blymn /* 3456 1.23 blymn * now walk backwards finding the first tab that is to the 3457 1.23 blymn * left of our starting pos. 3458 1.23 blymn */ 3459 1.23 blymn while ((ts != NULL) && (ts->in_use == TRUE) && (ts->pos > pos)) 3460 1.23 blymn ts = ts->back; 3461 1.23 blymn 3462 1.23 blymn scroll_amt = 0; 3463 1.23 blymn for (i = pos; i >= 0; i--) { 3464 1.33 roy if (field->alines->string[i] == '\t') { 3465 1.23 blymn assert((ts != NULL) && (ts->in_use == TRUE)); 3466 1.23 blymn if (ts->pos == i) { 3467 1.23 blymn if ((scroll_amt + ts->size) > window) { 3468 1.23 blymn break; 3469 1.23 blymn } 3470 1.23 blymn scroll_amt += ts->size; 3471 1.23 blymn ts = ts->back; 3472 1.23 blymn } 3473 1.23 blymn else 3474 1.23 blymn assert(ts->pos == i); 3475 1.23 blymn } else { 3476 1.23 blymn scroll_amt++; 3477 1.23 blymn if (scroll_amt > window) 3478 1.23 blymn break; 3479 1.23 blymn } 3480 1.23 blymn } 3481 1.23 blymn 3482 1.23 blymn return ++i; 3483 1.23 blymn } 3484 1.23 blymn 3485 1.23 blymn /* 3486 1.23 blymn * Return the position of the last character that will fit into the 3487 1.23 blymn * given width after tabs have been expanded for a given row of a given 3488 1.23 blymn * field. 3489 1.23 blymn */ 3490 1.23 blymn static unsigned int 3491 1.30 blymn tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int width) 3492 1.23 blymn { 3493 1.23 blymn unsigned int pos, len, row_pos; 3494 1.23 blymn _formi_tab_t *ts; 3495 1.23 blymn 3496 1.30 blymn ts = row->tabs; 3497 1.30 blymn pos = 0; 3498 1.23 blymn len = 0; 3499 1.23 blymn row_pos = 0; 3500 1.30 blymn 3501 1.30 blymn if (width == 0) 3502 1.30 blymn return 0; 3503 1.30 blymn 3504 1.30 blymn while ((len < width) && (pos < row->length)) { 3505 1.30 blymn if (row->string[pos] == '\t') { 3506 1.23 blymn assert((ts != NULL) && (ts->in_use == TRUE)); 3507 1.23 blymn if (ts->pos == row_pos) { 3508 1.23 blymn if ((len + ts->size) > width) 3509 1.23 blymn break; 3510 1.23 blymn len += ts->size; 3511 1.23 blymn ts = ts->fwd; 3512 1.23 blymn } 3513 1.23 blymn else 3514 1.23 blymn assert(ts->pos == row_pos); 3515 1.23 blymn } else 3516 1.23 blymn len++; 3517 1.23 blymn pos++; 3518 1.23 blymn row_pos++; 3519 1.23 blymn } 3520 1.23 blymn 3521 1.23 blymn if (pos > 0) 3522 1.23 blymn pos--; 3523 1.23 blymn return pos; 3524 1.1 blymn } 3525 1.30 blymn 3526 1.30 blymn /* 3527 1.30 blymn * Sync the field line structures with the contents of buffer 0 for that 3528 1.30 blymn * field. We do this by walking all the line structures and concatenating 3529 1.30 blymn * all the strings into one single string in buffer 0. 3530 1.30 blymn */ 3531 1.30 blymn int 3532 1.30 blymn _formi_sync_buffer(FIELD *field) 3533 1.30 blymn { 3534 1.30 blymn _FORMI_FIELD_LINES *line; 3535 1.30 blymn char *nstr, *tmp; 3536 1.30 blymn unsigned length; 3537 1.30 blymn 3538 1.33 roy if (field->alines == NULL) 3539 1.30 blymn return E_BAD_ARGUMENT; 3540 1.30 blymn 3541 1.33 roy if (field->alines->string == NULL) 3542 1.30 blymn return E_BAD_ARGUMENT; 3543 1.30 blymn 3544 1.30 blymn /* 3545 1.30 blymn * init nstr up front, just in case there are no line contents, 3546 1.30 blymn * this could happen if the field just contains hard returns. 3547 1.30 blymn */ 3548 1.40 christos if ((nstr = malloc(sizeof(*nstr))) == NULL) 3549 1.30 blymn return E_SYSTEM_ERROR; 3550 1.30 blymn nstr[0] = '\0'; 3551 1.30 blymn 3552 1.33 roy line = field->alines; 3553 1.30 blymn length = 1; /* allow for terminating null */ 3554 1.30 blymn 3555 1.30 blymn while (line != NULL) { 3556 1.30 blymn if (line->length != 0) { 3557 1.30 blymn if ((tmp = realloc(nstr, 3558 1.30 blymn (size_t) (length + line->length))) 3559 1.30 blymn == NULL) { 3560 1.30 blymn if (nstr != NULL) 3561 1.30 blymn free(nstr); 3562 1.30 blymn return (E_SYSTEM_ERROR); 3563 1.30 blymn } 3564 1.30 blymn 3565 1.30 blymn nstr = tmp; 3566 1.30 blymn strcat(nstr, line->string); 3567 1.30 blymn length += line->length; 3568 1.30 blymn } 3569 1.30 blymn 3570 1.30 blymn line = line->next; 3571 1.30 blymn } 3572 1.30 blymn 3573 1.30 blymn if (field->buffers[0].string != NULL) 3574 1.30 blymn free(field->buffers[0].string); 3575 1.30 blymn field->buffers[0].allocated = length; 3576 1.30 blymn field->buffers[0].length = length - 1; 3577 1.30 blymn field->buffers[0].string = nstr; 3578 1.30 blymn return E_OK; 3579 1.30 blymn } 3580 1.30 blymn 3581 1.30 blymn 3582 1.30 blymn 3583