segment: Fix buffer overrun error in segmenter_detect_command_name__().
[pspp] / src / language / lexer / segment.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011, 2013, 2016 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/lexer/segment.h"
20
21 #include <limits.h>
22 #include <unistr.h>
23
24 #include "data/identifier.h"
25 #include "language/lexer/command-name.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/cast.h"
28
29 #include "gl/c-ctype.h"
30 #include "gl/c-strcase.h"
31 #include "gl/memchr2.h"
32
33 enum segmenter_state
34   {
35     S_SHBANG,
36     S_GENERAL,
37     S_COMMENT_1,
38     S_COMMENT_2,
39     S_DOCUMENT_1,
40     S_DOCUMENT_2,
41     S_DOCUMENT_3,
42     S_FILE_LABEL,
43     S_DO_REPEAT_1,
44     S_DO_REPEAT_2,
45     S_DO_REPEAT_3,
46     S_BEGIN_DATA_1,
47     S_BEGIN_DATA_2,
48     S_BEGIN_DATA_3,
49     S_BEGIN_DATA_4,
50     S_TITLE_1,
51     S_TITLE_2
52   };
53
54 #define SS_START_OF_LINE (1u << 0)
55 #define SS_START_OF_COMMAND (1u << 1)
56
57 static int segmenter_detect_command_name__ (const char *input,
58                                             size_t n, int ofs);
59
60 static int
61 segmenter_u8_to_uc__ (ucs4_t *puc, const char *input_, size_t n)
62 {
63   const uint8_t *input = CHAR_CAST (const uint8_t *, input_);
64   int mblen;
65
66   assert (n > 0);
67
68   mblen = u8_mbtoucr (puc, input, n);
69   return (mblen >= 0 ? mblen
70           : mblen == -2 ? -1
71           : u8_mbtouc (puc, input, n));
72 }
73
74 static int
75 segmenter_parse_shbang__ (struct segmenter *s, const char *input, size_t n,
76                           enum segment_type *type)
77 {
78   if (input[0] == '#')
79     {
80       if (n < 2)
81         return -1;
82       else if (input[1] == '!')
83         {
84           int ofs;
85
86           for (ofs = 2; ofs < n; ofs++)
87             if (input[ofs] == '\n' || input[ofs] == '\0')
88               {
89                 if (input[ofs] == '\n' && input[ofs - 1] == '\r')
90                   ofs--;
91
92                 s->state = S_GENERAL;
93                 s->substate = SS_START_OF_COMMAND;
94                 *type = SEG_SHBANG;
95                 return ofs;
96               }
97
98           return -1;
99         }
100     }
101
102   s->state = S_GENERAL;
103   s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
104   return segmenter_push (s, input, n, type);
105 }
106
107 static int
108 segmenter_parse_digraph__ (const char *seconds, struct segmenter *s,
109                            const char *input, size_t n,
110                            enum segment_type *type)
111 {
112   assert (s->state == S_GENERAL);
113
114   if (n < 2)
115     return -1;
116
117   *type = SEG_PUNCT;
118   s->substate = 0;
119   return input[1] != '\0' && strchr (seconds, input[1]) != NULL ? 2 : 1;
120 }
121
122 static int
123 skip_comment (const char *input, size_t n, size_t ofs)
124 {
125   for (; ofs < n; ofs++)
126     {
127       if (input[ofs] == '\n' || input[ofs] == '\0')
128         return ofs;
129       else if (input[ofs] == '*')
130         {
131           if (ofs + 1 >= n)
132             return -1;
133           else if (input[ofs + 1] == '/')
134             return ofs + 2;
135         }
136     }
137   return -1;
138 }
139
140 static int
141 skip_spaces_and_comments (const char *input, size_t n, int ofs)
142 {
143   while (ofs < n)
144     {
145       ucs4_t uc;
146       int mblen;
147
148       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
149       if (mblen < 0)
150         return -1;
151
152       if (uc == '/')
153         {
154           if (ofs + 1 >= n)
155             return -1;
156           else if (input[ofs + 1] != '*')
157             return ofs;
158
159           ofs = skip_comment (input, n, ofs + 2);
160           if (ofs < 0)
161             return -1;
162         }
163       else if (lex_uc_is_space (uc) && uc != '\n')
164         ofs += mblen;
165       else
166         return ofs;
167     }
168
169   return -1;
170 }
171
172 static int
173 is_end_of_line (const char *input, size_t n, int ofs)
174 {
175   if (input[ofs] == '\n' || input[ofs] == '\0')
176     return 1;
177   else if (input[ofs] == '\r')
178     {
179       if (ofs + 1 >= n)
180         return -1;
181       return input[ofs + 1] == '\n';
182     }
183   else
184     return 0;
185 }
186
187 static int
188 at_end_of_line (const char *input, size_t n, int ofs)
189 {
190   ofs = skip_spaces_and_comments (input, n, ofs);
191   if (ofs < 0)
192     return -1;
193
194   return is_end_of_line (input, n, ofs);
195 }
196
197 static int
198 segmenter_parse_newline__ (const char *input, size_t n,
199                            enum segment_type *type)
200 {
201   int ofs;
202
203   if (input[0] == '\n')
204     ofs = 1;
205   else
206     {
207       if (n < 2)
208         return -1;
209
210       assert (input[0] == '\r');
211       assert (input[1] == '\n');
212       ofs = 2;
213     }
214
215   *type = SEG_NEWLINE;
216   return ofs;
217 }
218
219 static int
220 skip_spaces (const char *input, size_t n, size_t ofs)
221 {
222   while (ofs < n)
223     {
224       ucs4_t uc;
225       int mblen;
226
227       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
228       if (mblen < 0)
229         return -1;
230
231       if (!lex_uc_is_space (uc) || uc == '\n' || uc == '\0')
232         return ofs;
233
234       ofs += mblen;
235     }
236
237   return -1;
238 }
239
240 static int
241 skip_digits (const char *input, size_t n, int ofs)
242 {
243   for (; ofs < n; ofs++)
244     if (!c_isdigit (input[ofs]))
245       return ofs;
246   return -1;
247 }
248
249 static int
250 segmenter_parse_number__ (struct segmenter *s, const char *input, size_t n,
251                           enum segment_type *type)
252 {
253   int ofs;
254
255   assert (s->state == S_GENERAL);
256
257   ofs = skip_digits (input, n, 0);
258   if (ofs < 0)
259     return -1;
260
261   if (input[ofs] == '.')
262     {
263       ofs = skip_digits (input, n, ofs + 1);
264       if (ofs < 0)
265         return -1;
266     }
267
268   if (ofs >= n)
269     return -1;
270   if (input[ofs] == 'e' || input[ofs] == 'E')
271     {
272       ofs++;
273       if (ofs >= n)
274         return -1;
275
276       if (input[ofs] == '+' || input[ofs] == '-')
277         {
278           ofs++;
279           if (ofs >= n)
280             return -1;
281         }
282
283       if (!c_isdigit (input[ofs]))
284         {
285           *type = SEG_EXPECTED_EXPONENT;
286           s->substate = 0;
287           return ofs;
288         }
289
290       ofs = skip_digits (input, n, ofs);
291       if (ofs < 0)
292         return -1;
293     }
294
295   if (input[ofs - 1] == '.')
296     {
297       int eol = at_end_of_line (input, n, ofs);
298       if (eol < 0)
299         return -1;
300       else if (eol)
301         ofs--;
302     }
303
304   *type = SEG_NUMBER;
305   s->substate = 0;
306   return ofs;
307 }
308
309 static bool
310 is_reserved_word (const char *s, int n)
311 {
312   char s0, s1, s2, s3;
313
314   s0 = c_toupper (s[0]);
315   switch (n)
316     {
317     case 2:
318       s1 = c_toupper (s[1]);
319       return ((s0 == 'B' && s1 == 'Y')
320               || (s0 == 'E' && s1 == 'Q')
321               || (s0 == 'G' && (s1 == 'E' || s1 == 'T'))
322               || (s0 == 'L' && (s1 == 'E' || s1 == 'T'))
323               || (s0 == 'N' && s1 == 'E')
324               || (s0 == 'O' && s1 == 'R')
325               || (s0 == 'T' && s1 == 'O'));
326
327     case 3:
328       s1 = c_toupper (s[1]);
329       s2 = c_toupper (s[2]);
330       return ((s0 == 'A' && ((s1 == 'L' && s2 == 'L')
331                              || (s1 == 'N' && s2 == 'D')))
332               || (s0 == 'N' && s1 == 'O' && s2 == 'T'));
333
334     case 4:
335       s1 = c_toupper (s[1]);
336       s2 = c_toupper (s[2]);
337       s3 = c_toupper (s[3]);
338       return s0 == 'W' && s1 == 'I' && s2 == 'T' && s3 == 'H';
339
340     default:
341       return false;
342     }
343 }
344
345 static int
346 segmenter_parse_comment_1__ (struct segmenter *s,
347                              const char *input, size_t n,
348                              enum segment_type *type)
349 {
350   int endcmd;
351   int ofs;
352
353   endcmd = -2;
354   ofs = 0;
355   while (ofs < n)
356     {
357       ucs4_t uc;
358       int mblen;
359
360       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
361       if (mblen < 0)
362         return -1;
363
364       switch (uc)
365         {
366         case '.':
367           endcmd = ofs;
368           break;
369
370         case '\n':
371           if (ofs > 1 && input[ofs - 1] == '\r')
372             ofs--;
373           /* Fall through. */
374         case '\0':
375           if (endcmd == -2 || uc == '\0')
376             {
377               /* Blank line ends comment command. */
378               s->state = S_GENERAL;
379               s->substate = SS_START_OF_COMMAND;
380               *type = SEG_SEPARATE_COMMANDS;
381               return ofs;
382             }
383           else if (endcmd >= 0)
384             {
385               /* '.' at end of line ends comment command. */
386               s->state = S_GENERAL;
387               s->substate = 0;
388               *type = SEG_COMMENT_COMMAND;
389               return endcmd;
390             }
391           else
392             {
393               /* Comment continues onto next line. */
394               *type = SEG_COMMENT_COMMAND;
395               s->state = S_COMMENT_2;
396               return ofs;
397             }
398           NOT_REACHED ();
399
400         default:
401           if (!lex_uc_is_space (uc))
402             endcmd = -1;
403           break;
404         }
405
406       ofs += mblen;
407     }
408   return -1;
409 }
410
411 static int
412 segmenter_parse_comment_2__ (struct segmenter *s, const char *input, size_t n,
413                              enum segment_type *type)
414 {
415   int new_cmd;
416   ucs4_t uc;
417   int mblen;
418   int ofs;
419
420   ofs = segmenter_parse_newline__ (input, n, type);
421   if (ofs < 0 || ofs >= n)
422     return -1;
423
424   mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
425   if (mblen < 0)
426     return -1;
427
428   if (uc == '+' || uc == '-' || uc == '.')
429     new_cmd = true;
430   else if (!lex_uc_is_space (uc))
431     switch (s->mode)
432       {
433       case SEG_MODE_INTERACTIVE:
434         new_cmd = false;
435         break;
436
437       case SEG_MODE_BATCH:
438         new_cmd = true;
439         break;
440
441       case SEG_MODE_AUTO:
442         new_cmd = segmenter_detect_command_name__ (input, n, ofs);
443         if (new_cmd < 0)
444           return -1;
445         break;
446
447       default:
448         NOT_REACHED ();
449       }
450   else
451     new_cmd = false;
452
453   if (new_cmd)
454     {
455       s->state = S_GENERAL;
456       s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
457     }
458   else
459     s->state = S_COMMENT_1;
460   return ofs;
461 }
462
463 static int
464 segmenter_parse_document_1__ (struct segmenter *s, const char *input, size_t n,
465                               enum segment_type *type)
466 {
467   bool end_cmd;
468   int ofs;
469
470   end_cmd = false;
471   ofs = 0;
472   while (ofs < n)
473     {
474       ucs4_t uc;
475       int mblen;
476
477       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
478       if (mblen < 0)
479         return -1;
480
481       switch (uc)
482         {
483         case '.':
484           end_cmd = true;
485           break;
486
487         case '\n':
488           if (ofs > 1 && input[ofs - 1] == '\r')
489             ofs--;
490
491           *type = SEG_DOCUMENT;
492           s->state = end_cmd ? S_DOCUMENT_3 : S_DOCUMENT_2;
493           return ofs;
494
495         case '\0':
496           *type = SEG_DOCUMENT;
497           s->state = S_DOCUMENT_3;
498           return ofs;
499
500         default:
501           if (!lex_uc_is_space (uc))
502             end_cmd = false;
503           break;
504         }
505
506       ofs += mblen;
507     }
508   return -1;
509 }
510
511 static int
512 segmenter_parse_document_2__ (struct segmenter *s, const char *input, size_t n,
513                               enum segment_type *type)
514 {
515   int ofs;
516
517   ofs = segmenter_parse_newline__ (input, n, type);
518   if (ofs < 0)
519     return -1;
520
521   s->state = S_DOCUMENT_1;
522   return ofs;
523 }
524
525 static int
526 segmenter_parse_document_3__ (struct segmenter *s, enum segment_type *type)
527 {
528   *type = SEG_END_COMMAND;
529   s->state = S_GENERAL;
530   s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
531   return 0;
532 }
533
534 static int
535 segmenter_unquoted (const char *input, size_t n, int ofs)
536
537 {
538   char c;
539
540   ofs = skip_spaces_and_comments (input, n, ofs);
541   if (ofs < 0)
542     return -1;
543
544   c = input[ofs];
545   return c != '\'' && c != '"' && c != '\n' && c != '\0';
546 }
547
548 static int
549 next_id_in_command (const struct segmenter *s, const char *input, size_t n,
550                     int ofs, char id[], size_t id_size)
551 {
552   struct segmenter sub;
553
554   assert (id_size > 0);
555
556   sub.mode = s->mode;
557   sub.state = S_GENERAL;
558   sub.substate = 0;
559   for (;;)
560     {
561       enum segment_type type;
562       int retval;
563
564       retval = segmenter_push (&sub, input + ofs, n - ofs, &type);
565       if (retval < 0)
566         {
567           id[0] = '\0';
568           return -1;
569         }
570
571       switch (type)
572         {
573         case SEG_SHBANG:
574         case SEG_SPACES:
575         case SEG_COMMENT:
576         case SEG_NEWLINE:
577           break;
578
579         case SEG_IDENTIFIER:
580           if (retval < id_size)
581             {
582               memcpy (id, input + ofs, retval);
583               id[retval] = '\0';
584               return ofs + retval;
585             }
586           /* fall through */
587
588         case SEG_NUMBER:
589         case SEG_QUOTED_STRING:
590         case SEG_HEX_STRING:
591         case SEG_UNICODE_STRING:
592         case SEG_UNQUOTED_STRING:
593         case SEG_RESERVED_WORD:
594         case SEG_PUNCT:
595         case SEG_COMMENT_COMMAND:
596         case SEG_DO_REPEAT_COMMAND:
597         case SEG_INLINE_DATA:
598         case SEG_START_DOCUMENT:
599         case SEG_DOCUMENT:
600         case SEG_START_COMMAND:
601         case SEG_SEPARATE_COMMANDS:
602         case SEG_END_COMMAND:
603         case SEG_END:
604         case SEG_EXPECTED_QUOTE:
605         case SEG_EXPECTED_EXPONENT:
606         case SEG_UNEXPECTED_DOT:
607         case SEG_UNEXPECTED_CHAR:
608           id[0] = '\0';
609           return ofs + retval;
610         }
611       ofs += retval;
612     }
613 }
614
615 static int
616 segmenter_parse_id__ (struct segmenter *s, const char *input, size_t n,
617                       enum segment_type *type)
618 {
619   ucs4_t uc;
620   int ofs;
621
622   assert (s->state == S_GENERAL);
623
624   ofs = u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, input), n);
625   for (;;)
626     {
627       int mblen;
628
629       if (ofs >= n)
630         return -1;
631
632       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
633       if (mblen < 0)
634         return -1;
635       else if (!lex_uc_is_idn (uc))
636         break;
637
638       ofs += mblen;
639     }
640
641   if (input[ofs - 1] == '.')
642     {
643       int eol = at_end_of_line (input, n, ofs);
644       if (eol < 0)
645         return -1;
646       else if (eol)
647         ofs--;
648     }
649
650   if (is_reserved_word (input, ofs))
651     *type = SEG_RESERVED_WORD;
652   else
653     *type = SEG_IDENTIFIER;
654
655   if (s->substate & SS_START_OF_COMMAND)
656     {
657       struct substring word = ss_buffer (input, ofs);
658
659       if (lex_id_match_n (ss_cstr ("COMMENT"), word, 4))
660         {
661           s->state = S_COMMENT_1;
662           return segmenter_parse_comment_1__ (s, input, n, type);
663         }
664       else if (lex_id_match (ss_cstr ("DOCUMENT"), word))
665         {
666           s->state = S_DOCUMENT_1;
667           *type = SEG_START_DOCUMENT;
668           return 0;
669         }
670       else if (lex_id_match (ss_cstr ("TITLE"), word)
671                || lex_id_match (ss_cstr ("SUBTITLE"), word))
672         {
673           int result = segmenter_unquoted (input, n, ofs);
674           if (result < 0)
675             return -1;
676           else if (result)
677             {
678               s->state = S_TITLE_1;
679               return ofs;
680             }
681         }
682       else if (lex_id_match (ss_cstr ("FILE"), word))
683         {
684           char id[16];
685
686           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
687             return -1;
688           else if (lex_id_match (ss_cstr ("LABEL"), ss_cstr (id)))
689             {
690               s->state = S_FILE_LABEL;
691               s->substate = 0;
692               return ofs;
693             }
694         }
695       else if (lex_id_match (ss_cstr ("DO"), word))
696         {
697           char id[16];
698
699           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
700             return -1;
701           else if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
702             {
703               s->state = S_DO_REPEAT_1;
704               s->substate = 0;
705               return ofs;
706             }
707         }
708       else if (lex_id_match (ss_cstr ("BEGIN"), word))
709         {
710           char id[16];
711           int ofs2;
712
713           ofs2 = next_id_in_command (s, input, n, ofs, id, sizeof id);
714           if (ofs2 < 0)
715             return -1;
716           else if (lex_id_match (ss_cstr ("DATA"), ss_cstr (id)))
717             {
718               int eol;
719
720               ofs2 = skip_spaces_and_comments (input, n, ofs2);
721               if (ofs2 < 0)
722                 return -1;
723
724               if (input[ofs2] == '.')
725                 {
726                   ofs2 = skip_spaces_and_comments (input, n, ofs2 + 1);
727                   if (ofs2 < 0)
728                     return -1;
729                 }
730
731               eol = is_end_of_line (input, n, ofs2);
732               if (eol < 0)
733                 return -1;
734               else if (eol)
735                 {
736                   if (memchr (input, '\n', ofs2))
737                     s->state = S_BEGIN_DATA_1;
738                   else
739                     s->state = S_BEGIN_DATA_2;
740                   s->substate = 0;
741                   return ofs;
742                 }
743             }
744         }
745     }
746
747   s->substate = 0;
748   return ofs;
749 }
750
751 static int
752 segmenter_parse_string__ (enum segment_type string_type,
753                           int ofs, struct segmenter *s,
754                           const char *input, size_t n, enum segment_type *type)
755 {
756   int quote = input[ofs];
757
758   ofs++;
759   while (ofs < n)
760     if (input[ofs] == quote)
761       {
762         ofs++;
763         if (ofs >= n)
764           return -1;
765         else if (input[ofs] == quote)
766           ofs++;
767         else
768           {
769             *type = string_type;
770             s->substate = 0;
771             return ofs;
772           }
773       }
774     else if (input[ofs] == '\n' || input[ofs] == '\0')
775       {
776         *type = SEG_EXPECTED_QUOTE;
777         s->substate = 0;
778         return ofs;
779       }
780     else
781       ofs++;
782
783   return -1;
784 }
785
786 static int
787 segmenter_maybe_parse_string__ (enum segment_type string_type,
788                                 struct segmenter *s,
789                                 const char *input, size_t n,
790                                 enum segment_type *type)
791 {
792   if (n < 2)
793     return -1;
794   else if (input[1] == '\'' || input[1] == '"')
795     return segmenter_parse_string__ (string_type, 1, s, input, n, type);
796   else
797     return segmenter_parse_id__ (s, input, n, type);
798 }
799
800 static int
801 segmenter_parse_mid_command__ (struct segmenter *s,
802                                const char *input, size_t n,
803                                enum segment_type *type)
804 {
805   ucs4_t uc;
806   int mblen;
807   int ofs;
808
809   assert (s->state == S_GENERAL);
810   assert (!(s->substate & SS_START_OF_LINE));
811
812   mblen = segmenter_u8_to_uc__ (&uc, input, n);
813   if (mblen < 0)
814     return -1;
815
816   switch (uc)
817     {
818     case '\n':
819       s->substate |= SS_START_OF_LINE;
820       *type = SEG_NEWLINE;
821       return 1;
822
823     case '/':
824       if (n == 1)
825         return -1;
826       else if (input[1] == '*')
827         {
828           ofs = skip_comment (input, n, 2);
829           if (ofs < 0)
830             return -1;
831
832           *type = SEG_COMMENT;
833           return ofs;
834         }
835       else
836         {
837           s->substate = 0;
838           *type = SEG_PUNCT;
839           return 1;
840         }
841
842     case '(': case ')': case ',': case '=': case '-':
843     case '[': case ']': case '&': case '|': case '+':
844       *type = SEG_PUNCT;
845       s->substate = 0;
846       return 1;
847
848     case '*':
849       if (s->substate & SS_START_OF_COMMAND)
850         {
851           /* '*' at the beginning of a command begins a comment. */
852           s->state = S_COMMENT_1;
853           return segmenter_parse_comment_1__ (s, input, n, type);
854         }
855       else
856         return segmenter_parse_digraph__ ("*", s, input, n, type);
857
858     case '<':
859       return segmenter_parse_digraph__ ("=>", s, input, n, type);
860
861     case '>':
862       return segmenter_parse_digraph__ ("=", s, input, n, type);
863
864     case '~':
865       return segmenter_parse_digraph__ ("=", s, input, n, type);
866
867     case '.':
868       if (n < 2)
869         return -1;
870       else if (c_isdigit (input[1]))
871         return segmenter_parse_number__ (s, input, n, type);
872       else
873         {
874           int eol = at_end_of_line (input, n, 1);
875           if (eol < 0)
876             return -1;
877
878           if (eol)
879             {
880               *type = SEG_END_COMMAND;
881               s->substate = SS_START_OF_COMMAND;
882             }
883           else
884             *type = SEG_UNEXPECTED_DOT;
885           return 1;
886         }
887       NOT_REACHED ();
888
889     case '0': case '1': case '2': case '3': case '4':
890     case '5': case '6': case '7': case '8': case '9':
891       return segmenter_parse_number__ (s, input, n, type);
892
893     case 'u': case 'U':
894       return segmenter_maybe_parse_string__ (SEG_UNICODE_STRING,
895                                            s, input, n, type);
896
897     case 'x': case 'X':
898       return segmenter_maybe_parse_string__ (SEG_HEX_STRING,
899                                              s, input, n, type);
900
901     case '\'': case '"':
902       return segmenter_parse_string__ (SEG_QUOTED_STRING, 0,
903                                        s, input, n, type);
904
905     default:
906       if (lex_uc_is_space (uc))
907         {
908           ofs = skip_spaces (input, n, mblen);
909           if (ofs < 0)
910             return -1;
911
912           if (input[ofs - 1] == '\r' && input[ofs] == '\n')
913             {
914               if (ofs == 1)
915                 {
916                   s->substate |= SS_START_OF_LINE;
917                   *type = SEG_NEWLINE;
918                   return 2;
919                 }
920               else
921                 ofs--;
922             }
923           *type = SEG_SPACES;
924           return ofs;
925         }
926       else if (lex_uc_is_id1 (uc))
927         return segmenter_parse_id__ (s, input, n, type);
928       else
929         {
930           *type = SEG_UNEXPECTED_CHAR;
931           s->substate = 0;
932           return mblen;
933         }
934     }
935 }
936
937 static int
938 compare_commands (const void *a_, const void *b_)
939 {
940   const char *const *ap = a_;
941   const char *const *bp = b_;
942   const char *a = *ap;
943   const char *b = *bp;
944
945   return c_strcasecmp (a, b);
946 }
947
948 static const char **
949 segmenter_get_command_name_candidates (unsigned char first)
950 {
951 #define DEF_CMD(STATES, FLAGS, NAME, FUNCTION) NAME,
952 #define UNIMPL_CMD(NAME, DESCRIPTION) NAME,
953   static const char *commands[] =
954     {
955 #include "language/command.def"
956       ""
957     };
958   static size_t n_commands = (sizeof commands / sizeof *commands) - 1;
959 #undef DEF_CMD
960 #undef UNIMPL_CMD
961
962   static bool inited;
963
964   static const char **cindex[UCHAR_MAX + 1];
965
966   if (!inited)
967     {
968       size_t i;
969
970       inited = true;
971
972       qsort (commands, n_commands, sizeof *commands, compare_commands);
973       for (i = 0; i < n_commands; i++)
974         {
975           unsigned char c = c_toupper (commands[i][0]);
976           if (cindex[c] == NULL)
977             cindex[c] = &commands[i];
978         }
979       for (i = 0; i <= UCHAR_MAX; i++)
980         if (cindex[i] == NULL)
981           cindex[i] = &commands[n_commands];
982     }
983
984   return cindex[c_toupper (first)];
985 }
986
987 static int
988 segmenter_detect_command_name__ (const char *input, size_t n, int ofs)
989 {
990   const char **commands;
991
992   input += ofs;
993   n -= ofs;
994   ofs = 0;
995   for (;;)
996     {
997       ucs4_t uc;
998       int mblen;
999
1000       if (ofs >= n)
1001         return -1;
1002
1003       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
1004       if (mblen < 0)
1005         return -1;
1006
1007       if (uc == '\n' || uc == '\0'
1008           || !(lex_uc_is_space (uc) || lex_uc_is_idn (uc) || uc == '-'))
1009         break;
1010
1011       ofs += mblen;
1012     }
1013   if (!ofs)
1014     return 0;
1015
1016   if (input[ofs - 1] == '.')
1017     ofs--;
1018
1019   for (commands = segmenter_get_command_name_candidates (input[0]);
1020        c_toupper (input[0]) == c_toupper ((*commands)[0]);
1021        commands++)
1022     {
1023       int missing_words;
1024       bool exact;
1025
1026       if (command_match (ss_cstr (*commands), ss_buffer (input, ofs),
1027                          &exact, &missing_words)
1028           && missing_words <= 0)
1029         return 1;
1030     }
1031
1032   return 0;
1033 }
1034
1035 static int
1036 is_start_of_string__ (const char *input, size_t n, int ofs)
1037 {
1038   int c;
1039
1040   c = input[ofs];
1041   if (c == 'x' || c == 'X' || c == 'u' || c == 'U')
1042     {
1043       if (ofs + 1 >= n)
1044         return -1;
1045
1046       return input[ofs + 1] == '\'' || input[ofs + 1] == '"';
1047     }
1048   else
1049     return c == '\'' || c == '"' || c == '\n';
1050 }
1051
1052 static int
1053 segmenter_parse_start_of_line__ (struct segmenter *s,
1054                                  const char *input, size_t n,
1055                                  enum segment_type *type)
1056 {
1057   ucs4_t uc;
1058   int mblen;
1059   int ofs;
1060
1061   assert (s->state == S_GENERAL);
1062   assert (s->substate & SS_START_OF_LINE);
1063
1064   mblen = segmenter_u8_to_uc__ (&uc, input, n);
1065   if (mblen < 0)
1066     return -1;
1067
1068   switch (uc)
1069     {
1070     case '+':
1071       ofs = skip_spaces_and_comments (input, n, 1);
1072       if (ofs < 0)
1073         return -1;
1074       else
1075         {
1076           int is_string = is_start_of_string__ (input, n, ofs);
1077           if (is_string < 0)
1078             return -1;
1079           else if (is_string)
1080             {
1081               /* This is punctuation that may separate pieces of a string. */
1082               *type = SEG_PUNCT;
1083               s->substate = 0;
1084               return 1;
1085             }
1086         }
1087       /* Fall through. */
1088
1089     case '-':
1090     case '.':
1091       *type = SEG_START_COMMAND;
1092       s->substate = SS_START_OF_COMMAND;
1093       return 1;
1094
1095     default:
1096       if (lex_uc_is_space (uc))
1097         {
1098           int eol = at_end_of_line (input, n, 0);
1099           if (eol < 0)
1100             return -1;
1101           else if (eol)
1102             {
1103               s->substate = SS_START_OF_COMMAND;
1104               *type = SEG_SEPARATE_COMMANDS;
1105               return 0;
1106             }
1107           break;
1108         }
1109
1110       if (s->mode == SEG_MODE_INTERACTIVE || s->substate & SS_START_OF_COMMAND)
1111         break;
1112       else if (s->mode == SEG_MODE_AUTO)
1113         {
1114           int cmd = segmenter_detect_command_name__ (input, n, 0);
1115           if (cmd < 0)
1116             return -1;
1117           else if (cmd == 0)
1118             break;
1119         }
1120       else
1121         assert (s->mode == SEG_MODE_BATCH);
1122
1123       s->substate = SS_START_OF_COMMAND;
1124       *type = SEG_START_COMMAND;
1125       return 0;
1126     }
1127
1128   s->substate = SS_START_OF_COMMAND;
1129   return segmenter_parse_mid_command__ (s, input, n, type);
1130 }
1131
1132 static int
1133 segmenter_parse_file_label__ (struct segmenter *s,
1134                               const char *input, size_t n,
1135                               enum segment_type *type)
1136 {
1137   struct segmenter sub;
1138   int ofs;
1139
1140   sub = *s;
1141   sub.state = S_GENERAL;
1142   ofs = segmenter_push (&sub, input, n, type);
1143
1144   if (ofs < 0)
1145     return -1;
1146   else if (*type == SEG_IDENTIFIER)
1147     {
1148       int result;
1149
1150       assert (lex_id_match (ss_cstr ("LABEL"),
1151                             ss_buffer ((char *) input, ofs)));
1152       result = segmenter_unquoted (input, n, ofs);
1153       if (result < 0)
1154         return -1;
1155       else
1156         {
1157           if (result)
1158             s->state = S_TITLE_1;
1159           else
1160             *s = sub;
1161           return ofs;
1162         }
1163     }
1164   else
1165     {
1166       s->substate = sub.substate;
1167       return ofs;
1168     }
1169 }
1170
1171 static int
1172 segmenter_subparse (struct segmenter *s,
1173                     const char *input, size_t n, enum segment_type *type)
1174 {
1175   struct segmenter sub;
1176   int ofs;
1177
1178   sub.mode = s->mode;
1179   sub.state = S_GENERAL;
1180   sub.substate = s->substate;
1181   ofs = segmenter_push (&sub, input, n, type);
1182   s->substate = sub.substate;
1183   return ofs;
1184 }
1185
1186 static int
1187 segmenter_parse_do_repeat_1__ (struct segmenter *s,
1188                                const char *input, size_t n,
1189                                enum segment_type *type)
1190 {
1191   int ofs = segmenter_subparse (s, input, n, type);
1192   if (ofs < 0)
1193     return -1;
1194
1195   if (*type == SEG_START_COMMAND || *type == SEG_SEPARATE_COMMANDS)
1196     s->state = S_DO_REPEAT_2;
1197   else if (*type == SEG_END_COMMAND)
1198     {
1199       s->state = S_DO_REPEAT_3;
1200       s->substate = 1;
1201     }
1202
1203   return ofs;
1204 }
1205
1206 static int
1207 segmenter_parse_do_repeat_2__ (struct segmenter *s,
1208                                const char *input, size_t n,
1209                                enum segment_type *type)
1210 {
1211   int ofs = segmenter_subparse (s, input, n, type);
1212   if (ofs < 0)
1213     return -1;
1214
1215   if (*type == SEG_NEWLINE)
1216     {
1217       s->state = S_DO_REPEAT_3;
1218       s->substate = 1;
1219     }
1220
1221   return ofs;
1222 }
1223
1224 static bool
1225 check_repeat_command (struct segmenter *s,
1226                       const char *input, size_t n)
1227 {
1228   int direction;
1229   char id[16];
1230   int ofs;
1231
1232   ofs = 0;
1233   if (input[ofs] == '+' || input[ofs] == '-')
1234     ofs++;
1235
1236   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1237   if (ofs < 0)
1238     return false;
1239   else if (lex_id_match (ss_cstr ("DO"), ss_cstr (id)))
1240     direction = 1;
1241   else if (lex_id_match (ss_cstr ("END"), ss_cstr (id)))
1242     direction = -1;
1243   else
1244     return true;
1245
1246   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1247   if (ofs < 0)
1248     return false;
1249
1250   if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
1251     s->substate += direction;
1252   return true;
1253 }
1254
1255 static int
1256 segmenter_parse_full_line__ (const char *input, size_t n,
1257                              enum segment_type *type)
1258 {
1259   const char *newline = memchr2 (input, '\n', '\0', n);
1260
1261   if (newline == NULL)
1262     return -1;
1263   else
1264     {
1265       int ofs = newline - input;
1266       if (*newline == '\0')
1267         {
1268           assert (ofs > 0);
1269           return ofs;
1270         }
1271       else if (ofs == 0 || (ofs == 1 && input[0] == '\r'))
1272         {
1273           *type = SEG_NEWLINE;
1274           return ofs + 1;
1275         }
1276       else
1277         return ofs - (input[ofs - 1] == '\r');
1278     }
1279 }
1280
1281 static int
1282 segmenter_parse_do_repeat_3__ (struct segmenter *s,
1283                                const char *input, size_t n,
1284                                enum segment_type *type)
1285 {
1286   int ofs;
1287
1288   ofs = segmenter_parse_full_line__ (input, n, type);
1289   if (ofs < 0 || input[ofs - 1] == '\n')
1290     return ofs;
1291   else if (!check_repeat_command (s, input, n))
1292     return -1;
1293   else if (s->substate == 0)
1294     {
1295       s->state = S_GENERAL;
1296       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1297       return segmenter_push (s, input, n, type);
1298     }
1299   else
1300     {
1301       *type = SEG_DO_REPEAT_COMMAND;
1302       return ofs;
1303     }
1304 }
1305
1306 static int
1307 segmenter_parse_begin_data_1__ (struct segmenter *s,
1308                                 const char *input, size_t n,
1309                                 enum segment_type *type)
1310 {
1311   int ofs = segmenter_subparse (s, input, n, type);
1312   if (ofs < 0)
1313     return -1;
1314
1315   if (*type == SEG_NEWLINE)
1316     s->state = S_BEGIN_DATA_2;
1317
1318   return ofs;
1319 }
1320
1321 static int
1322 segmenter_parse_begin_data_2__ (struct segmenter *s,
1323                                 const char *input, size_t n,
1324                                 enum segment_type *type)
1325 {
1326   int ofs = segmenter_subparse (s, input, n, type);
1327   if (ofs < 0)
1328     return -1;
1329
1330   if (*type == SEG_NEWLINE)
1331     s->state = S_BEGIN_DATA_3;
1332
1333   return ofs;
1334 }
1335
1336 static bool
1337 is_end_data (const char *input, size_t n)
1338 {
1339   const uint8_t *u_input = CHAR_CAST (const uint8_t *, input);
1340   bool endcmd;
1341   ucs4_t uc;
1342   int mblen;
1343   int ofs;
1344
1345   if (n < 3 || c_strncasecmp (input, "END", 3))
1346     return false;
1347
1348   ofs = 3;
1349   mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1350   if (!lex_uc_is_space (uc))
1351     return false;
1352   ofs += mblen;
1353
1354   if (n - ofs < 4 || c_strncasecmp (input + ofs, "DATA", 4))
1355     return false;
1356   ofs += 4;
1357
1358   endcmd = false;
1359   while (ofs < n)
1360     {
1361       mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1362       if (uc == '.')
1363         {
1364           if (endcmd)
1365             return false;
1366           endcmd = true;
1367         }
1368       else if (!lex_uc_is_space (uc))
1369         return false;
1370       ofs += mblen;
1371     }
1372
1373   return true;
1374 }
1375
1376 static int
1377 segmenter_parse_begin_data_3__ (struct segmenter *s,
1378                                 const char *input, size_t n,
1379                                 enum segment_type *type)
1380 {
1381   int ofs;
1382
1383   ofs = segmenter_parse_full_line__ (input, n, type);
1384   if (ofs < 0)
1385     return -1;
1386   else if (is_end_data (input, ofs))
1387     {
1388       s->state = S_GENERAL;
1389       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1390       return segmenter_push (s, input, n, type);
1391     }
1392   else
1393     {
1394       *type = SEG_INLINE_DATA;
1395       s->state = S_BEGIN_DATA_4;
1396       return input[ofs - 1] == '\n' ? 0 : ofs;
1397     }
1398 }
1399
1400 static int
1401 segmenter_parse_begin_data_4__ (struct segmenter *s,
1402                                 const char *input, size_t n,
1403                                 enum segment_type *type)
1404 {
1405   int ofs;
1406
1407   ofs = segmenter_parse_newline__ (input, n, type);
1408   if (ofs < 0)
1409     return -1;
1410
1411   s->state = S_BEGIN_DATA_3;
1412   return ofs;
1413 }
1414
1415 static int
1416 segmenter_parse_title_1__ (struct segmenter *s,
1417                            const char *input, size_t n,
1418                            enum segment_type *type)
1419 {
1420   int ofs;
1421
1422   ofs = skip_spaces (input, n, 0);
1423   if (ofs < 0)
1424     return -1;
1425   s->state = S_TITLE_2;
1426   *type = SEG_SPACES;
1427   return ofs;
1428 }
1429
1430 static int
1431 segmenter_parse_title_2__ (struct segmenter *s,
1432                            const char *input, size_t n,
1433                            enum segment_type *type)
1434 {
1435   int endcmd;
1436   int ofs;
1437
1438   endcmd = -1;
1439   ofs = 0;
1440   while (ofs < n)
1441     {
1442       ucs4_t uc;
1443       int mblen;
1444
1445       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
1446       if (mblen < 0)
1447         return -1;
1448
1449       switch (uc)
1450         {
1451         case '\n':
1452         case '\0':
1453           s->state = S_GENERAL;
1454           s->substate = 0;
1455           *type = SEG_UNQUOTED_STRING;
1456           return endcmd >= 0 ? endcmd : ofs;
1457
1458         case '.':
1459           endcmd = ofs;
1460           break;
1461
1462         default:
1463           if (!lex_uc_is_space (uc))
1464             endcmd = -1;
1465           break;
1466         }
1467
1468       ofs += mblen;
1469     }
1470
1471   return -1;
1472 }
1473
1474 /* Returns the name of segment TYPE as a string.  The caller must not modify
1475    or free the returned string.
1476
1477    This is useful only for debugging and testing. */
1478 const char *
1479 segment_type_to_string (enum segment_type type)
1480 {
1481   switch (type)
1482     {
1483 #define SEG_TYPE(NAME) case SEG_##NAME: return #NAME;
1484       SEG_TYPES
1485 #undef SEG_TYPE
1486     default:
1487       return "unknown segment type";
1488     }
1489 }
1490
1491 /* Initializes S as a segmenter with the given syntax MODE.
1492
1493    A segmenter does not contain any external references, so nothing needs to be
1494    done to destroy one.  For the same reason, segmenters may be copied with
1495    plain struct assignment (or memcpy). */
1496 void
1497 segmenter_init (struct segmenter *s, enum segmenter_mode mode)
1498 {
1499   s->state = S_SHBANG;
1500   s->substate = 0;
1501   s->mode = mode;
1502 }
1503
1504 /* Returns the mode passed to segmenter_init() for S. */
1505 enum segmenter_mode
1506 segmenter_get_mode (const struct segmenter *s)
1507 {
1508   return s->mode;
1509 }
1510
1511 /* Attempts to label a prefix of S's remaining input with a segment type.  The
1512    caller supplies the first N bytes of the remaining input as INPUT, which
1513    must be a UTF-8 encoded string.  The end of the input stream must be
1514    indicated by a null byte at the beginning of a line, that is, immediately
1515    following a new-line (or as the first byte of the input stream).
1516
1517    The input may contain '\n' or '\r\n' line ends in any combination.
1518
1519    If successful, returns the number of bytes in the segment at the beginning
1520    of INPUT (between 0 and N, inclusive) and stores the type of that segment
1521    into *TYPE.  The next call to segmenter_push() should not include those
1522    bytes as part of INPUT, because they have (figuratively) been consumed by
1523    the segmenter.
1524
1525    Failure occurs only if the segment type of the N bytes in INPUT cannot yet
1526    be determined.  In this case segmenter_push() returns -1.  The caller should
1527    obtain more input and then call segmenter_push() again with a larger N and
1528    repeat until the input is exhausted (which must be indicated as described
1529    above) or until a valid segment is returned.  segmenter_push() will never
1530    return -1 when the end of input is visible within INPUT.
1531
1532    The caller must not, in a sequence of calls, supply contradictory input.
1533    That is, bytes provided as part of INPUT in one call, but not consumed, must
1534    not be provided with *different* values on subsequent calls.  This is
1535    because segmenter_push() must often make decisions based on looking ahead
1536    beyond the bytes that it consumes. */
1537 int
1538 segmenter_push (struct segmenter *s, const char *input, size_t n,
1539                 enum segment_type *type)
1540 {
1541   if (n == 0)
1542     return -1;
1543
1544   if (input[0] == '\0')
1545     {
1546       *type = SEG_END;
1547       return 1;
1548     }
1549
1550   switch (s->state)
1551     {
1552     case S_SHBANG:
1553       return segmenter_parse_shbang__ (s, input, n, type);
1554
1555     case S_GENERAL:
1556       return (s->substate & SS_START_OF_LINE
1557               ? segmenter_parse_start_of_line__ (s, input, n, type)
1558               : segmenter_parse_mid_command__ (s, input, n, type));
1559
1560     case S_COMMENT_1:
1561       return segmenter_parse_comment_1__ (s, input, n, type);
1562     case S_COMMENT_2:
1563       return segmenter_parse_comment_2__ (s, input, n, type);
1564
1565     case S_DOCUMENT_1:
1566       return segmenter_parse_document_1__ (s, input, n, type);
1567     case S_DOCUMENT_2:
1568       return segmenter_parse_document_2__ (s, input, n, type);
1569     case S_DOCUMENT_3:
1570       return segmenter_parse_document_3__ (s, type);
1571
1572     case S_FILE_LABEL:
1573       return segmenter_parse_file_label__ (s, input, n, type);
1574
1575     case S_DO_REPEAT_1:
1576       return segmenter_parse_do_repeat_1__ (s, input, n, type);
1577     case S_DO_REPEAT_2:
1578       return segmenter_parse_do_repeat_2__ (s, input, n, type);
1579     case S_DO_REPEAT_3:
1580       return segmenter_parse_do_repeat_3__ (s, input, n, type);
1581
1582     case S_BEGIN_DATA_1:
1583       return segmenter_parse_begin_data_1__ (s, input, n, type);
1584     case S_BEGIN_DATA_2:
1585       return segmenter_parse_begin_data_2__ (s, input, n, type);
1586     case S_BEGIN_DATA_3:
1587       return segmenter_parse_begin_data_3__ (s, input, n, type);
1588     case S_BEGIN_DATA_4:
1589       return segmenter_parse_begin_data_4__ (s, input, n, type);
1590
1591     case S_TITLE_1:
1592       return segmenter_parse_title_1__ (s, input, n, type);
1593     case S_TITLE_2:
1594       return segmenter_parse_title_2__ (s, input, n, type);
1595     }
1596
1597   NOT_REACHED ();
1598 }
1599
1600 /* Returns the style of command prompt to display to an interactive user for
1601    input in S.  The return value is most accurate in mode SEG_MODE_INTERACTIVE
1602    and at the beginning of a line (that is, if segmenter_push() consumed as
1603    much as possible of the input up to a new-line).  */
1604 enum prompt_style
1605 segmenter_get_prompt (const struct segmenter *s)
1606 {
1607   switch (s->state)
1608     {
1609     case S_SHBANG:
1610       return PROMPT_FIRST;
1611
1612     case S_GENERAL:
1613       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1614
1615     case S_COMMENT_1:
1616     case S_COMMENT_2:
1617       return PROMPT_COMMENT;
1618
1619     case S_DOCUMENT_1:
1620     case S_DOCUMENT_2:
1621       return PROMPT_DOCUMENT;
1622     case S_DOCUMENT_3:
1623       return PROMPT_FIRST;
1624
1625     case S_FILE_LABEL:
1626       return PROMPT_LATER;
1627
1628     case S_DO_REPEAT_1:
1629     case S_DO_REPEAT_2:
1630       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1631     case S_DO_REPEAT_3:
1632       return PROMPT_DO_REPEAT;
1633
1634     case S_BEGIN_DATA_1:
1635       return PROMPT_FIRST;
1636     case S_BEGIN_DATA_2:
1637       return PROMPT_LATER;
1638     case S_BEGIN_DATA_3:
1639     case S_BEGIN_DATA_4:
1640       return PROMPT_DATA;
1641
1642     case S_TITLE_1:
1643     case S_TITLE_2:
1644       return PROMPT_FIRST;
1645     }
1646
1647   NOT_REACHED ();
1648 }