1 1.5 agc /* $NetBSD: line.c,v 1.5 2003/10/13 14:34:25 agc Exp $ */ 2 1.2 perry 3 1.1 cjs /* 4 1.5 agc * Copyright (c) 1988 Mark Nudelman 5 1.1 cjs * Copyright (c) 1988, 1993 6 1.1 cjs * The Regents of the University of California. All rights reserved. 7 1.1 cjs * 8 1.1 cjs * Redistribution and use in source and binary forms, with or without 9 1.1 cjs * modification, are permitted provided that the following conditions 10 1.1 cjs * are met: 11 1.1 cjs * 1. Redistributions of source code must retain the above copyright 12 1.1 cjs * notice, this list of conditions and the following disclaimer. 13 1.1 cjs * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 cjs * notice, this list of conditions and the following disclaimer in the 15 1.1 cjs * documentation and/or other materials provided with the distribution. 16 1.4 agc * 3. Neither the name of the University nor the names of its contributors 17 1.4 agc * may be used to endorse or promote products derived from this software 18 1.4 agc * without specific prior written permission. 19 1.4 agc * 20 1.4 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 1.4 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 1.4 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 1.4 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 1.4 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 1.4 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 1.4 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 1.4 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 1.4 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 1.4 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 1.4 agc * SUCH DAMAGE. 31 1.4 agc */ 32 1.4 agc 33 1.3 christos #include <sys/cdefs.h> 34 1.1 cjs #ifndef lint 35 1.3 christos #if 0 36 1.1 cjs static char sccsid[] = "@(#)line.c 8.1 (Berkeley) 6/6/93"; 37 1.3 christos #else 38 1.5 agc __RCSID("$NetBSD: line.c,v 1.5 2003/10/13 14:34:25 agc Exp $"); 39 1.3 christos #endif 40 1.1 cjs #endif /* not lint */ 41 1.1 cjs 42 1.1 cjs /* 43 1.1 cjs * Routines to manipulate the "line buffer". 44 1.1 cjs * The line buffer holds a line of output as it is being built 45 1.1 cjs * in preparation for output to the screen. 46 1.1 cjs * We keep track of the PRINTABLE length of the line as it is being built. 47 1.1 cjs */ 48 1.1 cjs 49 1.1 cjs #include <sys/types.h> 50 1.1 cjs #include <ctype.h> 51 1.3 christos 52 1.3 christos #include "less.h" 53 1.3 christos #include "extern.h" 54 1.1 cjs 55 1.1 cjs static char linebuf[1024]; /* Buffer which holds the current output line */ 56 1.1 cjs static char *curr; /* Pointer into linebuf */ 57 1.1 cjs static int column; /* Printable length, accounting for 58 1.1 cjs backspaces, etc. */ 59 1.1 cjs /* 60 1.1 cjs * A ridiculously complex state machine takes care of backspaces. The 61 1.1 cjs * complexity arises from the attempt to deal with all cases, especially 62 1.1 cjs * involving long lines with underlining, boldfacing or whatever. There 63 1.1 cjs * are still some cases which will break it. 64 1.1 cjs * 65 1.1 cjs * There are four states: 66 1.1 cjs * LN_NORMAL is the normal state (not in underline mode). 67 1.1 cjs * LN_UNDERLINE means we are in underline mode. We expect to get 68 1.1 cjs * either a sequence like "_\bX" or "X\b_" to continue 69 1.1 cjs * underline mode, or anything else to end underline mode. 70 1.1 cjs * LN_BOLDFACE means we are in boldface mode. We expect to get sequences 71 1.1 cjs * like "X\bX\b...X\bX" to continue boldface mode, or anything 72 1.1 cjs * else to end boldface mode. 73 1.1 cjs * LN_UL_X means we are one character after LN_UNDERLINE 74 1.1 cjs * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). 75 1.1 cjs * LN_UL_XB means we are one character after LN_UL_X 76 1.1 cjs * (we have gotten the backspace in "_\bX" or "X\b_"; 77 1.1 cjs * we expect one more ordinary character, 78 1.1 cjs * which will put us back in state LN_UNDERLINE). 79 1.1 cjs * LN_BO_X means we are one character after LN_BOLDFACE 80 1.1 cjs * (we have gotten the 'X' in "X\bX"). 81 1.1 cjs * LN_BO_XB means we are one character after LN_BO_X 82 1.1 cjs * (we have gotten the backspace in "X\bX"; 83 1.1 cjs * we expect one more 'X' which will put us back 84 1.1 cjs * in LN_BOLDFACE). 85 1.1 cjs */ 86 1.1 cjs static int ln_state; /* Currently in normal/underline/bold/etc mode? */ 87 1.1 cjs #define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ 88 1.1 cjs #define LN_UNDERLINE 1 /* In underline, need next char */ 89 1.1 cjs #define LN_UL_X 2 /* In underline, got char, need \b */ 90 1.1 cjs #define LN_UL_XB 3 /* In underline, got char & \b, need one more */ 91 1.1 cjs #define LN_BOLDFACE 4 /* In boldface, need next char */ 92 1.1 cjs #define LN_BO_X 5 /* In boldface, got char, need \b */ 93 1.1 cjs #define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ 94 1.1 cjs 95 1.1 cjs char *line; /* Pointer to the current line. 96 1.1 cjs Usually points to linebuf. */ 97 1.1 cjs /* 98 1.1 cjs * Rewind the line buffer. 99 1.1 cjs */ 100 1.3 christos void 101 1.1 cjs prewind() 102 1.1 cjs { 103 1.1 cjs line = curr = linebuf; 104 1.1 cjs ln_state = LN_NORMAL; 105 1.1 cjs column = 0; 106 1.1 cjs } 107 1.1 cjs 108 1.1 cjs /* 109 1.1 cjs * Append a character to the line buffer. 110 1.1 cjs * Expand tabs into spaces, handle underlining, boldfacing, etc. 111 1.1 cjs * Returns 0 if ok, 1 if couldn't fit in buffer. 112 1.1 cjs */ 113 1.1 cjs #define NEW_COLUMN(addon) \ 114 1.1 cjs if (column + addon + (ln_state ? ue_width : 0) > sc_width) \ 115 1.1 cjs return(1); \ 116 1.1 cjs else \ 117 1.1 cjs column += addon 118 1.1 cjs 119 1.3 christos int 120 1.1 cjs pappend(c) 121 1.1 cjs int c; 122 1.1 cjs { 123 1.1 cjs if (c == '\0') { 124 1.1 cjs /* 125 1.1 cjs * Terminate any special modes, if necessary. 126 1.1 cjs * Append a '\0' to the end of the line. 127 1.1 cjs */ 128 1.1 cjs switch (ln_state) { 129 1.1 cjs case LN_UL_X: 130 1.1 cjs curr[0] = curr[-1]; 131 1.1 cjs curr[-1] = UE_CHAR; 132 1.1 cjs curr++; 133 1.1 cjs break; 134 1.1 cjs case LN_BO_X: 135 1.1 cjs curr[0] = curr[-1]; 136 1.1 cjs curr[-1] = BE_CHAR; 137 1.1 cjs curr++; 138 1.1 cjs break; 139 1.1 cjs case LN_UL_XB: 140 1.1 cjs case LN_UNDERLINE: 141 1.1 cjs *curr++ = UE_CHAR; 142 1.1 cjs break; 143 1.1 cjs case LN_BO_XB: 144 1.1 cjs case LN_BOLDFACE: 145 1.1 cjs *curr++ = BE_CHAR; 146 1.1 cjs break; 147 1.1 cjs } 148 1.1 cjs ln_state = LN_NORMAL; 149 1.1 cjs *curr = '\0'; 150 1.1 cjs return(0); 151 1.1 cjs } 152 1.1 cjs 153 1.1 cjs if (curr > linebuf + sizeof(linebuf) - 12) 154 1.1 cjs /* 155 1.1 cjs * Almost out of room in the line buffer. 156 1.1 cjs * Don't take any chances. 157 1.1 cjs * {{ Linebuf is supposed to be big enough that this 158 1.1 cjs * will never happen, but may need to be made 159 1.1 cjs * bigger for wide screens or lots of backspaces. }} 160 1.1 cjs */ 161 1.1 cjs return(1); 162 1.1 cjs 163 1.1 cjs if (!bs_mode) { 164 1.1 cjs /* 165 1.1 cjs * Advance the state machine. 166 1.1 cjs */ 167 1.1 cjs switch (ln_state) { 168 1.1 cjs case LN_NORMAL: 169 1.1 cjs if (curr <= linebuf + 1 170 1.1 cjs || curr[-1] != (char)('H' | 0200)) 171 1.1 cjs break; 172 1.1 cjs column -= 2; 173 1.1 cjs if (c == curr[-2]) 174 1.1 cjs goto enter_boldface; 175 1.1 cjs if (c == '_' || curr[-2] == '_') 176 1.1 cjs goto enter_underline; 177 1.1 cjs curr -= 2; 178 1.1 cjs break; 179 1.1 cjs 180 1.1 cjs enter_boldface: 181 1.1 cjs /* 182 1.1 cjs * We have "X\bX" (including the current char). 183 1.1 cjs * Switch into boldface mode. 184 1.1 cjs */ 185 1.1 cjs column--; 186 1.1 cjs if (column + bo_width + be_width + 1 >= sc_width) 187 1.1 cjs /* 188 1.1 cjs * Not enough room left on the screen to 189 1.1 cjs * enter and exit boldface mode. 190 1.1 cjs */ 191 1.1 cjs return (1); 192 1.1 cjs 193 1.1 cjs if (bo_width > 0 && curr > linebuf + 2 194 1.1 cjs && curr[-3] == ' ') { 195 1.1 cjs /* 196 1.1 cjs * Special case for magic cookie terminals: 197 1.1 cjs * if the previous char was a space, replace 198 1.1 cjs * it with the "enter boldface" sequence. 199 1.1 cjs */ 200 1.1 cjs curr[-3] = BO_CHAR; 201 1.1 cjs column += bo_width-1; 202 1.1 cjs } else { 203 1.1 cjs curr[-1] = curr[-2]; 204 1.1 cjs curr[-2] = BO_CHAR; 205 1.1 cjs column += bo_width; 206 1.1 cjs curr++; 207 1.1 cjs } 208 1.1 cjs goto ln_bo_xb_case; 209 1.1 cjs 210 1.1 cjs enter_underline: 211 1.1 cjs /* 212 1.1 cjs * We have either "_\bX" or "X\b_" (including 213 1.1 cjs * the current char). Switch into underline mode. 214 1.1 cjs */ 215 1.1 cjs column--; 216 1.1 cjs if (column + ul_width + ue_width + 1 >= sc_width) 217 1.1 cjs /* 218 1.1 cjs * Not enough room left on the screen to 219 1.1 cjs * enter and exit underline mode. 220 1.1 cjs */ 221 1.1 cjs return (1); 222 1.1 cjs 223 1.1 cjs if (ul_width > 0 && 224 1.1 cjs curr > linebuf + 2 && curr[-3] == ' ') 225 1.1 cjs { 226 1.1 cjs /* 227 1.1 cjs * Special case for magic cookie terminals: 228 1.1 cjs * if the previous char was a space, replace 229 1.1 cjs * it with the "enter underline" sequence. 230 1.1 cjs */ 231 1.1 cjs curr[-3] = UL_CHAR; 232 1.1 cjs column += ul_width-1; 233 1.1 cjs } else 234 1.1 cjs { 235 1.1 cjs curr[-1] = curr[-2]; 236 1.1 cjs curr[-2] = UL_CHAR; 237 1.1 cjs column += ul_width; 238 1.1 cjs curr++; 239 1.1 cjs } 240 1.1 cjs goto ln_ul_xb_case; 241 1.1 cjs /*NOTREACHED*/ 242 1.1 cjs case LN_UL_XB: 243 1.1 cjs /* 244 1.1 cjs * Termination of a sequence "_\bX" or "X\b_". 245 1.1 cjs */ 246 1.1 cjs if (c != '_' && curr[-2] != '_' && c == curr[-2]) 247 1.1 cjs { 248 1.1 cjs /* 249 1.1 cjs * We seem to have run on from underlining 250 1.1 cjs * into boldfacing - this is a nasty fix, but 251 1.1 cjs * until this whole routine is rewritten as a 252 1.1 cjs * real DFA, ... well ... 253 1.1 cjs */ 254 1.1 cjs curr[0] = curr[-2]; 255 1.1 cjs curr[-2] = UE_CHAR; 256 1.1 cjs curr[-1] = BO_CHAR; 257 1.1 cjs curr += 2; /* char & non-existent backspace */ 258 1.1 cjs ln_state = LN_BO_XB; 259 1.1 cjs goto ln_bo_xb_case; 260 1.1 cjs } 261 1.1 cjs ln_ul_xb_case: 262 1.1 cjs if (c == '_') 263 1.1 cjs c = curr[-2]; 264 1.1 cjs curr -= 2; 265 1.1 cjs ln_state = LN_UNDERLINE; 266 1.1 cjs break; 267 1.1 cjs case LN_BO_XB: 268 1.1 cjs /* 269 1.1 cjs * Termination of a sequnce "X\bX". 270 1.1 cjs */ 271 1.1 cjs if (c != curr[-2] && (c == '_' || curr[-2] == '_')) 272 1.1 cjs { 273 1.1 cjs /* 274 1.1 cjs * We seem to have run on from 275 1.1 cjs * boldfacing into underlining. 276 1.1 cjs */ 277 1.1 cjs curr[0] = curr[-2]; 278 1.1 cjs curr[-2] = BE_CHAR; 279 1.1 cjs curr[-1] = UL_CHAR; 280 1.1 cjs curr += 2; /* char & non-existent backspace */ 281 1.1 cjs ln_state = LN_UL_XB; 282 1.1 cjs goto ln_ul_xb_case; 283 1.1 cjs } 284 1.1 cjs ln_bo_xb_case: 285 1.1 cjs curr -= 2; 286 1.1 cjs ln_state = LN_BOLDFACE; 287 1.1 cjs break; 288 1.1 cjs case LN_UNDERLINE: 289 1.1 cjs if (column + ue_width + bo_width + 1 + be_width >= sc_width) 290 1.1 cjs /* 291 1.1 cjs * We have just barely enough room to 292 1.1 cjs * exit underline mode and handle a possible 293 1.1 cjs * underline/boldface run on mixup. 294 1.1 cjs */ 295 1.1 cjs return (1); 296 1.1 cjs ln_state = LN_UL_X; 297 1.1 cjs break; 298 1.1 cjs case LN_BOLDFACE: 299 1.1 cjs if (c == '\b') 300 1.1 cjs { 301 1.1 cjs ln_state = LN_BO_XB; 302 1.1 cjs break; 303 1.1 cjs } 304 1.1 cjs if (column + be_width + ul_width + 1 + ue_width >= sc_width) 305 1.1 cjs /* 306 1.1 cjs * We have just barely enough room to 307 1.1 cjs * exit underline mode and handle a possible 308 1.1 cjs * underline/boldface run on mixup. 309 1.1 cjs */ 310 1.1 cjs return (1); 311 1.1 cjs ln_state = LN_BO_X; 312 1.1 cjs break; 313 1.1 cjs case LN_UL_X: 314 1.1 cjs if (c == '\b') 315 1.1 cjs ln_state = LN_UL_XB; 316 1.1 cjs else 317 1.1 cjs { 318 1.1 cjs /* 319 1.1 cjs * Exit underline mode. 320 1.1 cjs * We have to shuffle the chars a bit 321 1.1 cjs * to make this work. 322 1.1 cjs */ 323 1.1 cjs curr[0] = curr[-1]; 324 1.1 cjs curr[-1] = UE_CHAR; 325 1.1 cjs column += ue_width; 326 1.1 cjs if (ue_width > 0 && curr[0] == ' ') 327 1.1 cjs /* 328 1.1 cjs * Another special case for magic 329 1.1 cjs * cookie terminals: if the next 330 1.1 cjs * char is a space, replace it 331 1.1 cjs * with the "exit underline" sequence. 332 1.1 cjs */ 333 1.1 cjs column--; 334 1.1 cjs else 335 1.1 cjs curr++; 336 1.1 cjs ln_state = LN_NORMAL; 337 1.1 cjs } 338 1.1 cjs break; 339 1.1 cjs case LN_BO_X: 340 1.1 cjs if (c == '\b') 341 1.1 cjs ln_state = LN_BO_XB; 342 1.1 cjs else 343 1.1 cjs { 344 1.1 cjs /* 345 1.1 cjs * Exit boldface mode. 346 1.1 cjs * We have to shuffle the chars a bit 347 1.1 cjs * to make this work. 348 1.1 cjs */ 349 1.1 cjs curr[0] = curr[-1]; 350 1.1 cjs curr[-1] = BE_CHAR; 351 1.1 cjs column += be_width; 352 1.1 cjs if (be_width > 0 && curr[0] == ' ') 353 1.1 cjs /* 354 1.1 cjs * Another special case for magic 355 1.1 cjs * cookie terminals: if the next 356 1.1 cjs * char is a space, replace it 357 1.1 cjs * with the "exit boldface" sequence. 358 1.1 cjs */ 359 1.1 cjs column--; 360 1.1 cjs else 361 1.1 cjs curr++; 362 1.1 cjs ln_state = LN_NORMAL; 363 1.1 cjs } 364 1.1 cjs break; 365 1.1 cjs } 366 1.1 cjs } 367 1.1 cjs 368 1.1 cjs if (c == '\t') { 369 1.1 cjs /* 370 1.1 cjs * Expand a tab into spaces. 371 1.1 cjs */ 372 1.1 cjs do { 373 1.1 cjs NEW_COLUMN(1); 374 1.1 cjs } while ((column % tabstop) != 0); 375 1.1 cjs *curr++ = '\t'; 376 1.1 cjs return (0); 377 1.1 cjs } 378 1.1 cjs 379 1.1 cjs if (c == '\b') { 380 1.1 cjs if (ln_state == LN_NORMAL) 381 1.1 cjs NEW_COLUMN(2); 382 1.1 cjs else 383 1.1 cjs column--; 384 1.1 cjs *curr++ = ('H' | 0200); 385 1.1 cjs return(0); 386 1.1 cjs } 387 1.1 cjs 388 1.1 cjs if (CONTROL_CHAR(c)) { 389 1.1 cjs /* 390 1.1 cjs * Put a "^X" into the buffer. The 0200 bit is used to tell 391 1.1 cjs * put_line() to prefix the char with a ^. We don't actually 392 1.1 cjs * put the ^ in the buffer because we sometimes need to move 393 1.1 cjs * chars around, and such movement might separate the ^ from 394 1.1 cjs * its following character. 395 1.1 cjs */ 396 1.1 cjs NEW_COLUMN(2); 397 1.1 cjs *curr++ = (CARAT_CHAR(c) | 0200); 398 1.1 cjs return(0); 399 1.1 cjs } 400 1.1 cjs 401 1.1 cjs /* 402 1.1 cjs * Ordinary character. Just put it in the buffer. 403 1.1 cjs */ 404 1.1 cjs NEW_COLUMN(1); 405 1.1 cjs *curr++ = c; 406 1.1 cjs return (0); 407 1.1 cjs } 408 1.1 cjs 409 1.1 cjs /* 410 1.1 cjs * Analogous to forw_line(), but deals with "raw lines": 411 1.1 cjs * lines which are not split for screen width. 412 1.1 cjs * {{ This is supposed to be more efficient than forw_line(). }} 413 1.1 cjs */ 414 1.1 cjs off_t 415 1.1 cjs forw_raw_line(curr_pos) 416 1.1 cjs off_t curr_pos; 417 1.1 cjs { 418 1.3 christos char *p; 419 1.3 christos int c; 420 1.3 christos off_t new_pos; 421 1.1 cjs 422 1.1 cjs if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 423 1.1 cjs (c = ch_forw_get()) == EOI) 424 1.1 cjs return (NULL_POSITION); 425 1.1 cjs 426 1.1 cjs p = linebuf; 427 1.1 cjs 428 1.1 cjs for (;;) 429 1.1 cjs { 430 1.1 cjs if (c == '\n' || c == EOI) 431 1.1 cjs { 432 1.1 cjs new_pos = ch_tell(); 433 1.1 cjs break; 434 1.1 cjs } 435 1.1 cjs if (p >= &linebuf[sizeof(linebuf)-1]) 436 1.1 cjs { 437 1.1 cjs /* 438 1.1 cjs * Overflowed the input buffer. 439 1.1 cjs * Pretend the line ended here. 440 1.1 cjs * {{ The line buffer is supposed to be big 441 1.1 cjs * enough that this never happens. }} 442 1.1 cjs */ 443 1.1 cjs new_pos = ch_tell() - 1; 444 1.1 cjs break; 445 1.1 cjs } 446 1.1 cjs *p++ = c; 447 1.1 cjs c = ch_forw_get(); 448 1.1 cjs } 449 1.1 cjs *p = '\0'; 450 1.1 cjs line = linebuf; 451 1.1 cjs return (new_pos); 452 1.1 cjs } 453 1.1 cjs 454 1.1 cjs /* 455 1.1 cjs * Analogous to back_line(), but deals with "raw lines". 456 1.1 cjs * {{ This is supposed to be more efficient than back_line(). }} 457 1.1 cjs */ 458 1.1 cjs off_t 459 1.1 cjs back_raw_line(curr_pos) 460 1.1 cjs off_t curr_pos; 461 1.1 cjs { 462 1.3 christos char *p; 463 1.3 christos int c; 464 1.3 christos off_t new_pos; 465 1.1 cjs 466 1.1 cjs if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || 467 1.1 cjs ch_seek(curr_pos-1)) 468 1.1 cjs return (NULL_POSITION); 469 1.1 cjs 470 1.1 cjs p = &linebuf[sizeof(linebuf)]; 471 1.1 cjs *--p = '\0'; 472 1.1 cjs 473 1.1 cjs for (;;) 474 1.1 cjs { 475 1.1 cjs c = ch_back_get(); 476 1.1 cjs if (c == '\n') 477 1.1 cjs { 478 1.1 cjs /* 479 1.1 cjs * This is the newline ending the previous line. 480 1.1 cjs * We have hit the beginning of the line. 481 1.1 cjs */ 482 1.1 cjs new_pos = ch_tell() + 1; 483 1.1 cjs break; 484 1.1 cjs } 485 1.1 cjs if (c == EOI) 486 1.1 cjs { 487 1.1 cjs /* 488 1.1 cjs * We have hit the beginning of the file. 489 1.1 cjs * This must be the first line in the file. 490 1.1 cjs * This must, of course, be the beginning of the line. 491 1.1 cjs */ 492 1.1 cjs new_pos = (off_t)0; 493 1.1 cjs break; 494 1.1 cjs } 495 1.1 cjs if (p <= linebuf) 496 1.1 cjs { 497 1.1 cjs /* 498 1.1 cjs * Overflowed the input buffer. 499 1.1 cjs * Pretend the line ended here. 500 1.1 cjs */ 501 1.1 cjs new_pos = ch_tell() + 1; 502 1.1 cjs break; 503 1.1 cjs } 504 1.1 cjs *--p = c; 505 1.1 cjs } 506 1.1 cjs line = p; 507 1.1 cjs return (new_pos); 508 1.1 cjs } 509