xref: /dragonfly/contrib/less/less.h (revision 7485684f)
1 /*
2  * Copyright (C) 1984-2023  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9 
10 #define NEWBOT 1
11 
12 /*
13  * Standard include file for "less".
14  */
15 
16 /*
17  * Defines for MSDOS_COMPILER.
18  */
19 #define MSOFTC          1   /* Microsoft C */
20 #define BORLANDC        2   /* Borland C */
21 #define WIN32C          3   /* Windows (Borland C or Microsoft C) */
22 #define DJGPPC          4   /* DJGPP C */
23 
24 /*
25  * Include the file of compile-time options.
26  * The <> make cc search for it in -I., not srcdir.
27  */
28 #include <defines.h>
29 
30 #ifdef _SEQUENT_
31 /*
32  * Kludge for Sequent Dynix systems that have sigsetmask, but
33  * it's not compatible with the way less calls it.
34  * {{ Do other systems need this? }}
35  */
36 #undef HAVE_SIGSETMASK
37 #endif
38 
39 /*
40  * Language details.
41  */
42 #if HAVE_CONST
43 #define constant        const
44 #else
45 #define constant
46 #endif
47 
48 #define public          /* PUBLIC FUNCTION */
49 
50 /* Library function declarations */
51 
52 #if HAVE_SYS_TYPES_H
53 #include <sys/types.h>
54 #endif
55 #if HAVE_STDIO_H
56 #include <stdio.h>
57 #endif
58 #if HAVE_FCNTL_H
59 #include <fcntl.h>
60 #endif
61 #if HAVE_UNISTD_H
62 #include <unistd.h>
63 #endif
64 #if HAVE_CTYPE_H
65 #include <ctype.h>
66 #endif
67 #if HAVE_WCTYPE_H
68 #include <wctype.h>
69 #endif
70 #if HAVE_LIMITS_H
71 #include <limits.h>
72 #endif
73 #if HAVE_STDINT_H
74 #include <stdint.h>
75 #endif
76 #if HAVE_STDLIB_H
77 #include <stdlib.h>
78 #endif
79 #if HAVE_STRING_H
80 #include <string.h>
81 #endif
82 
83 #if HAVE_STDCKDINT_H
84 #include <stdckdint.h>
85 #else
86 /*
87  * These substitutes for C23 stdckdint macros do not set *R on overflow,
88  * and they assume A and B are nonnegative.  That is good enough for us.
89  */
90 #define ckd_add(r, a, b) help_ckd_add(r, a, b, sizeof *(r), signed_expr(*(r)))
91 #define ckd_mul(r, a, b) help_ckd_mul(r, a, b, sizeof *(r), signed_expr(*(r)))
92 /* True if the integer expression E, after promotion, is signed.  */
93 #define signed_expr(e) ((TRUE ? 0 : e) - 1 < 0)
94 #endif
95 
96 #if defined UINTMAX_MAX
97 typedef uintmax_t uintmax;
98 #elif defined ULLONG_MAX
99 typedef unsigned long long uintmax;
100 #else
101 typedef unsigned long uintmax;
102 #endif
103 
104 /* OS-specific includes */
105 #ifdef _OSK
106 #include <modes.h>
107 #include <strings.h>
108 #endif
109 
110 #ifdef __TANDEM
111 #include <floss.h>
112 #endif
113 
114 #if MSDOS_COMPILER==WIN32C || OS2
115 #include <io.h>
116 #endif
117 
118 #if MSDOS_COMPILER==DJGPPC
119 #include <io.h>
120 #include <sys/exceptn.h>
121 #include <conio.h>
122 #include <pc.h>
123 #endif
124 
125 #if !HAVE_STDLIB_H
126 char *getenv();
127 off_t lseek();
128 void *calloc();
129 void free();
130 #endif
131 
132 /*
133  * Simple lowercase test which can be used during option processing
134  * (before options are parsed which might tell us what charset to use).
135  */
136 #define ASCII_IS_UPPER(c)       ((c) >= 'A' && (c) <= 'Z')
137 #define ASCII_IS_LOWER(c)       ((c) >= 'a' && (c) <= 'z')
138 #define ASCII_TO_UPPER(c)       ((c) - 'a' + 'A')
139 #define ASCII_TO_LOWER(c)       ((c) - 'A' + 'a')
140 
141 #undef IS_UPPER
142 #undef IS_LOWER
143 #undef TO_UPPER
144 #undef TO_LOWER
145 #undef IS_SPACE
146 #undef IS_DIGIT
147 
148 #if HAVE_WCTYPE
149 #define IS_UPPER(c)     iswupper(c)
150 #define IS_LOWER(c)     iswlower(c)
151 #define TO_UPPER(c)     towupper(c)
152 #define TO_LOWER(c)     towlower(c)
153 #else
154 #if HAVE_UPPER_LOWER
155 #define IS_UPPER(c)     isupper((unsigned char) (c))
156 #define IS_LOWER(c)     islower((unsigned char) (c))
157 #define TO_UPPER(c)     toupper((unsigned char) (c))
158 #define TO_LOWER(c)     tolower((unsigned char) (c))
159 #else
160 #define IS_UPPER(c)     ASCII_IS_UPPER(c)
161 #define IS_LOWER(c)     ASCII_IS_LOWER(c)
162 #define TO_UPPER(c)     ASCII_TO_UPPER(c)
163 #define TO_LOWER(c)     ASCII_TO_LOWER(c)
164 #endif
165 #endif
166 
167 #ifdef isspace
168 #define IS_SPACE(c)     isspace((unsigned char)(c))
169 #else
170 #define IS_SPACE(c)     ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f')
171 #endif
172 
173 #ifdef isdigit
174 #define IS_DIGIT(c)     isdigit((unsigned char)(c))
175 #else
176 #define IS_DIGIT(c)     ((c) >= '0' && (c) <= '9')
177 #endif
178 
179 #define IS_CSI_START(c) (((LWCHAR)(c)) == ESC || (((LWCHAR)(c)) == CSI))
180 
181 #ifndef NULL
182 #define NULL    0
183 #endif
184 
185 #ifndef TRUE
186 #define TRUE            1
187 #endif
188 #ifndef FALSE
189 #define FALSE           0
190 #endif
191 
192 #define OPT_OFF         0
193 #define OPT_ON          1
194 #define OPT_ONPLUS      2
195 
196 #if !HAVE_MEMCPY
197 #ifndef memcpy
198 #define memcpy(to,from,len)     bcopy((from),(to),(len))
199 #endif
200 #endif
201 
202 #if HAVE_SNPRINTF
203 #define SNPRINTF1(str, size, fmt, v1)             snprintf((str), (size), (fmt), (v1))
204 #define SNPRINTF2(str, size, fmt, v1, v2)         snprintf((str), (size), (fmt), (v1), (v2))
205 #define SNPRINTF3(str, size, fmt, v1, v2, v3)     snprintf((str), (size), (fmt), (v1), (v2), (v3))
206 #define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) snprintf((str), (size), (fmt), (v1), (v2), (v3), (v4))
207 #else
208 /* Use unsafe sprintf if we don't have snprintf. */
209 #define SNPRINTF1(str, size, fmt, v1)             sprintf((str), (fmt), (v1))
210 #define SNPRINTF2(str, size, fmt, v1, v2)         sprintf((str), (fmt), (v1), (v2))
211 #define SNPRINTF3(str, size, fmt, v1, v2, v3)     sprintf((str), (fmt), (v1), (v2), (v3))
212 #define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) sprintf((str), (fmt), (v1), (v2), (v3), (v4))
213 #endif
214 
215 #define BAD_LSEEK       ((off_t)-1)
216 
217 #ifndef SEEK_SET
218 #define SEEK_SET 0
219 #endif
220 #ifndef SEEK_END
221 #define SEEK_END 2
222 #endif
223 
224 #ifndef CHAR_BIT
225 #define CHAR_BIT 8
226 #endif
227 
228 /*
229  * Upper bound on the string length of an integer converted to string.
230  * 302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
231  * add 1 for integer division truncation; add 1 more for a minus sign.
232  */
233 #define INT_STRLEN_BOUND(t) ((sizeof(t) * CHAR_BIT - 1) * 302 / 1000 + 1 + 1)
234 
235 /*
236  * Special types and constants.
237  */
238 typedef unsigned long LWCHAR;
239 typedef off_t           POSITION;
240 typedef off_t           LINENUM;
241 #define MIN_LINENUM_WIDTH   7   /* Default min printing width of a line number */
242 #define MAX_LINENUM_WIDTH   16  /* Max width of a line number */
243 #define MAX_STATUSCOL_WIDTH 4   /* Max width of the status column */
244 #define MAX_UTF_CHAR_LEN    6   /* Max bytes in one UTF-8 char */
245 #define MAX_PRCHAR_LEN      31  /* Max chars in prchar() result */
246 
247 #define NULL_POSITION   ((POSITION)(-1))
248 
249 /*
250  * Flags for open()
251  */
252 #if MSDOS_COMPILER || OS2
253 #define OPEN_READ       (O_RDONLY|O_BINARY)
254 #else
255 #ifdef _OSK
256 #define OPEN_READ       (S_IREAD)
257 #else
258 #ifdef O_RDONLY
259 #define OPEN_READ       (O_RDONLY)
260 #else
261 #define OPEN_READ       (0)
262 #endif
263 #endif
264 #endif
265 
266 #if defined(O_WRONLY) && defined(O_APPEND)
267 #define OPEN_APPEND     (O_APPEND|O_WRONLY)
268 #else
269 #ifdef _OSK
270 #define OPEN_APPEND     (S_IWRITE)
271 #else
272 #define OPEN_APPEND     (1)
273 #endif
274 #endif
275 
276 /*
277  * Flags for creat()
278  */
279 #if MSDOS_COMPILER
280 #define CREAT_RW        (S_IREAD|S_IWRITE)
281 #else
282 #define CREAT_RW        0644
283 #endif
284 
285 /*
286  * Set a file descriptor to binary mode.
287  */
288 #if MSDOS_COMPILER==MSOFTC
289 #define SET_BINARY(f)   _setmode(f, _O_BINARY);
290 #else
291 #if MSDOS_COMPILER || OS2
292 #define SET_BINARY(f)   setmode(f, O_BINARY)
293 #else
294 #define SET_BINARY(f)
295 #endif
296 #endif
297 
298 /*
299  * Does the shell treat "?" as a metacharacter?
300  */
301 #if MSDOS_COMPILER || OS2 || _OSK
302 #define SHELL_META_QUEST 0
303 #else
304 #define SHELL_META_QUEST 1
305 #endif
306 
307 #define SPACES_IN_FILENAMES 1
308 
309 /*
310  * An IFILE represents an input file.
311  */
312 #define IFILE           void*
313 #define NULL_IFILE      ((IFILE)NULL)
314 
315 /*
316  * The structure used to represent a "screen position".
317  * This consists of a file position, and a screen line number.
318  * The meaning is that the line starting at the given file
319  * position is displayed on the ln-th line of the screen.
320  * (Screen lines before ln are empty.)
321  */
322 struct scrpos
323 {
324         POSITION pos;
325         int ln;
326 };
327 
328 typedef union parg
329 {
330         char *p_string;
331         int p_int;
332         LINENUM p_linenum;
333         char p_char;
334 } PARG;
335 
336 #define NULL_PARG       ((PARG *)NULL)
337 
338 struct textlist
339 {
340         char *string;
341         char *endstring;
342 };
343 
344 struct wchar_range
345 {
346         LWCHAR first, last;
347 };
348 
349 struct wchar_range_table
350 {
351         struct wchar_range *table;
352         int count;
353 };
354 
355 #if HAVE_POLL
356 typedef short POLL_EVENTS;
357 #endif
358 
359 #define EOI             (-1)
360 
361 #define READ_ERR        (-1)
362 #define READ_INTR       (-2)
363 #define READ_AGAIN      (-3)
364 
365 /*
366  * A fraction is represented by a long n; the fraction is n/NUM_FRAC_DENOM.
367  * To avoid overflow problems, 0 <= n < NUM_FRAC_DENUM <= LONG_MAX/100.
368  */
369 #define NUM_FRAC_DENOM                  1000000
370 #define NUM_LOG_FRAC_DENOM              6
371 
372 /* How quiet should we be? */
373 #define NOT_QUIET       0       /* Ring bell at eof and for errors */
374 #define LITTLE_QUIET    1       /* Ring bell only for errors */
375 #define VERY_QUIET      2       /* Never ring bell */
376 
377 /* How should we prompt? */
378 #define PR_SHORT        0       /* Prompt with colon */
379 #define PR_MEDIUM       1       /* Prompt with message */
380 #define PR_LONG         2       /* Prompt with longer message */
381 
382 /* How should we handle backspaces? */
383 #define BS_SPECIAL      0       /* Do special things for underlining and bold */
384 #define BS_NORMAL       1       /* \b treated as normal char; actually output */
385 #define BS_CONTROL      2       /* \b treated as control char; prints as ^H */
386 
387 /* How should we search? */
388 #define SRCH_FORW       (1 << 0)  /* Search forward from current position */
389 #define SRCH_BACK       (1 << 1)  /* Search backward from current position */
390 #define SRCH_NO_MOVE    (1 << 2)  /* Highlight, but don't move */
391 #define SRCH_INCR       (1 << 3)  /* Incremental search */
392 #define SRCH_FIND_ALL   (1 << 4)  /* Find and highlight all matches */
393 #define SRCH_NO_MATCH   (1 << 8)  /* Search for non-matching lines */
394 #define SRCH_PAST_EOF   (1 << 9)  /* Search past end-of-file, into next file */
395 #define SRCH_FIRST_FILE (1 << 10) /* Search starting at the first file */
396 #define SRCH_NO_REGEX   (1 << 12) /* Don't use regular expressions */
397 #define SRCH_FILTER     (1 << 13) /* Search is for '&' (filter) command */
398 #define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */
399 #define SRCH_WRAP       (1 << 15) /* Wrap-around search (continue at BOF/EOF) */
400 #define SRCH_SUBSEARCH(i) (1 << (16+(i))) /* Search for subpattern */
401 /* {{ Depends on NUM_SEARCH_COLORS==5 }} */
402 #define SRCH_SUBSEARCH_ALL (SRCH_SUBSEARCH(1)|SRCH_SUBSEARCH(2)|SRCH_SUBSEARCH(3)|SRCH_SUBSEARCH(4)|SRCH_SUBSEARCH(5))
403 
404 #define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
405                                 (((t) & ~SRCH_FORW) | SRCH_BACK) : \
406                                 (((t) & ~SRCH_BACK) | SRCH_FORW))
407 
408 /* */
409 #define NO_MCA          0
410 #define MCA_DONE        1
411 #define MCA_MORE        2
412 
413 #define CC_OK           0       /* Char was accepted & processed */
414 #define CC_QUIT         1       /* Char was a request to abort current cmd */
415 #define CC_ERROR        2       /* Char could not be accepted due to error */
416 #define CC_PASS         3       /* Char was rejected (internal) */
417 
418 #define CF_QUIT_ON_ERASE 0001   /* Abort cmd if its entirely erased */
419 
420 /* Special char bit-flags used to tell put_line() to do something special */
421 #define AT_NORMAL       (0)
422 #define AT_UNDERLINE    (1 << 0)
423 #define AT_BOLD         (1 << 1)
424 #define AT_BLINK        (1 << 2)
425 #define AT_STANDOUT     (1 << 3)
426 #define AT_ANSI         (1 << 4)  /* Content-supplied "ANSI" escape sequence */
427 #define AT_BINARY       (1 << 5)  /* LESS*BINFMT representation */
428 #define AT_HILITE       (1 << 6)  /* Internal highlights (e.g., for search) */
429 
430 #define AT_COLOR_SHIFT    8
431 #define AT_NUM_COLORS     16
432 #define AT_COLOR          ((AT_NUM_COLORS-1) << AT_COLOR_SHIFT)
433 #define AT_COLOR_ATTN     (1 << AT_COLOR_SHIFT)
434 #define AT_COLOR_BIN      (2 << AT_COLOR_SHIFT)
435 #define AT_COLOR_CTRL     (3 << AT_COLOR_SHIFT)
436 #define AT_COLOR_ERROR    (4 << AT_COLOR_SHIFT)
437 #define AT_COLOR_LINENUM  (5 << AT_COLOR_SHIFT)
438 #define AT_COLOR_MARK     (6 << AT_COLOR_SHIFT)
439 #define AT_COLOR_PROMPT   (7 << AT_COLOR_SHIFT)
440 #define AT_COLOR_RSCROLL  (8 << AT_COLOR_SHIFT)
441 #define AT_COLOR_HEADER   (9 << AT_COLOR_SHIFT)
442 #define AT_COLOR_SEARCH   (10 << AT_COLOR_SHIFT)
443 #define AT_COLOR_SUBSEARCH(i) ((10+(i)) << AT_COLOR_SHIFT)
444 #define NUM_SEARCH_COLORS (AT_NUM_COLORS-10-1)
445 
446 typedef enum { CT_NULL, CT_4BIT, CT_6BIT } COLOR_TYPE;
447 
448 typedef enum {
449 	CV_BLUE     = 1,
450 	CV_GREEN    = 2,
451 	CV_RED      = 4,
452 	CV_BRIGHT   = 8,
453 	CV_NOCHANGE = -2,
454 	CV_ERROR    = -1
455 } COLOR_VALUE;
456 
457 /* ANSI states */
458 #define ANSI_MID    1
459 #define ANSI_ERR    2
460 #define ANSI_END    3
461 
462 #if '0' == 240
463 #define IS_EBCDIC_HOST 1
464 #endif
465 
466 #if IS_EBCDIC_HOST
467 /*
468  * Long definition for EBCDIC.
469  * Since the argument is usually a constant, this macro normally compiles
470  * into a constant.
471  */
472 #define CONTROL(c) ( \
473         (c)=='[' ? '\047' : \
474         (c)=='a' ? '\001' : \
475         (c)=='b' ? '\002' : \
476         (c)=='c' ? '\003' : \
477         (c)=='d' ? '\067' : \
478         (c)=='e' ? '\055' : \
479         (c)=='f' ? '\056' : \
480         (c)=='g' ? '\057' : \
481         (c)=='h' ? '\026' : \
482         (c)=='i' ? '\005' : \
483         (c)=='j' ? '\025' : \
484         (c)=='k' ? '\013' : \
485         (c)=='l' ? '\014' : \
486         (c)=='m' ? '\015' : \
487         (c)=='n' ? '\016' : \
488         (c)=='o' ? '\017' : \
489         (c)=='p' ? '\020' : \
490         (c)=='q' ? '\021' : \
491         (c)=='r' ? '\022' : \
492         (c)=='s' ? '\023' : \
493         (c)=='t' ? '\074' : \
494         (c)=='u' ? '\075' : \
495         (c)=='v' ? '\062' : \
496         (c)=='w' ? '\046' : \
497         (c)=='x' ? '\030' : \
498         (c)=='y' ? '\031' : \
499         (c)=='z' ? '\077' : \
500         (c)=='A' ? '\001' : \
501         (c)=='B' ? '\002' : \
502         (c)=='C' ? '\003' : \
503         (c)=='D' ? '\067' : \
504         (c)=='E' ? '\055' : \
505         (c)=='F' ? '\056' : \
506         (c)=='G' ? '\057' : \
507         (c)=='H' ? '\026' : \
508         (c)=='I' ? '\005' : \
509         (c)=='J' ? '\025' : \
510         (c)=='K' ? '\013' : \
511         (c)=='L' ? '\014' : \
512         (c)=='M' ? '\015' : \
513         (c)=='N' ? '\016' : \
514         (c)=='O' ? '\017' : \
515         (c)=='P' ? '\020' : \
516         (c)=='Q' ? '\021' : \
517         (c)=='R' ? '\022' : \
518         (c)=='S' ? '\023' : \
519         (c)=='T' ? '\074' : \
520         (c)=='U' ? '\075' : \
521         (c)=='V' ? '\062' : \
522         (c)=='W' ? '\046' : \
523         (c)=='X' ? '\030' : \
524         (c)=='Y' ? '\031' : \
525         (c)=='Z' ? '\077' : \
526         (c)=='|' ? '\031' : \
527         (c)=='\\' ? '\034' : \
528         (c)=='^' ? '\036' : \
529         (c)&077)
530 #else
531 #define CONTROL(c)      ((c)&037)
532 #endif /* IS_EBCDIC_HOST */
533 
534 #define ESC             CONTROL('[')
535 #define ESCS            "\33"
536 #define CSI             ((unsigned char)'\233')
537 #define CHAR_END_COMMAND 0x40000000
538 
539 #if _OSK_MWC32
540 #define LSIGNAL(sig,func)       os9_signal(sig,func)
541 #else
542 #define LSIGNAL(sig,func)       signal(sig,func)
543 #endif
544 
545 #if HAVE_SIGPROCMASK
546 #if HAVE_SIGSET_T
547 #else
548 #undef HAVE_SIGPROCMASK
549 #endif
550 #endif
551 #if HAVE_SIGPROCMASK
552 #if HAVE_SIGEMPTYSET
553 #else
554 #undef  sigemptyset
555 #define sigemptyset(mp) *(mp) = 0
556 #endif
557 #endif
558 
559 #define S_INTERRUPT     01
560 #define S_STOP          02
561 #define S_WINCH         04
562 #define ABORT_SIGS()    (sigs & (S_INTERRUPT|S_STOP))
563 
564 #ifdef EXIT_SUCCESS
565 #define QUIT_OK         EXIT_SUCCESS
566 #else
567 #define QUIT_OK         0
568 #endif
569 #ifdef EXIT_FAILURE
570 #define QUIT_ERROR      EXIT_FAILURE
571 #define QUIT_INTERRUPT  (EXIT_FAILURE+1)
572 #else
573 #define QUIT_ERROR      1
574 #define QUIT_INTERRUPT  2
575 #endif
576 #define QUIT_SAVED_STATUS (-1)
577 
578 #define FOLLOW_DESC     0
579 #define FOLLOW_NAME     1
580 
581 /* filestate flags */
582 #define CH_CANSEEK      001
583 #define CH_KEEPOPEN     002
584 #define CH_POPENED      004
585 #define CH_HELPFILE     010
586 #define CH_NODATA       020     /* Special case for zero length files */
587 
588 #define ch_zero()       ((POSITION)0)
589 
590 #define FAKE_HELPFILE   "@/\\less/\\help/\\file/\\@"
591 #define FAKE_EMPTYFILE  "@/\\less/\\empty/\\file/\\@"
592 
593 /* Flags for cvt_text */
594 #define CVT_TO_LC       01      /* Convert upper-case to lower-case */
595 #define CVT_BS          02      /* Do backspace processing */
596 #define CVT_CRLF        04      /* Remove CR after LF */
597 #define CVT_ANSI        010     /* Remove ANSI escape sequences */
598 
599 #if HAVE_TIME_T
600 #define time_type       time_t
601 #else
602 #define time_type       long
603 #endif
604 
605 /* X11 mouse reporting definitions */
606 #define X11MOUSE_BUTTON1    0 /* Left button press */
607 #define X11MOUSE_BUTTON2    1 /* Middle button press */
608 #define X11MOUSE_BUTTON3    2 /* Right button press */
609 #define X11MOUSE_BUTTON_REL 3 /* Button release */
610 #define X11MOUSE_WHEEL_UP   0x40 /* Wheel scroll up */
611 #define X11MOUSE_WHEEL_DOWN 0x41 /* Wheel scroll down */
612 #define X11MOUSE_OFFSET     0x20 /* Added to button & pos bytes to create a char */
613 
614 #if LESSTEST
615 #define LESS_DUMP_CHAR CONTROL(']')
616 #endif
617 
618 struct mlist;
619 struct loption;
620 struct hilite_tree;
621 struct ansi_state;
622 #include "pattern.h"
623 #include "xbuf.h"
624 #include "funcs.h"
625 
626 /* Functions not included in funcs.h */
627 void postoa(POSITION, char*, int);
628 void linenumtoa(LINENUM, char*, int);
629 void inttoa(int, char*, int);
630 int lstrtoi(char*, char**, int);
631 POSITION lstrtopos(char*, char**, int);
632 unsigned long lstrtoul(char*, char**, int);
633 #if MSDOS_COMPILER==WIN32C
634 int pclose(FILE*);
635 #endif
636