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