packaging: remove old options to find hidden files
[platform/upstream/findutils.git] / lib / regexprops.c
1 /* regexprops.c -- document the properties of the regular expressions
2    understood by gnulib.
3
4    Copyright 2005, 2007, 2010, 2011 Free Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21 /*
22   The output of this program is included in the GNU findutils source
23   distribution.  The copying conditions for that file are generated
24   by the copying() function below.
25 */
26
27 /* Written by James Youngman, <jay@gnu.org>. */
28
29 /* config.h must be included first. */
30 #include <config.h>
31
32 /* system headers */
33 #include <errno.h>
34 #include <regex.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 /* gnulib headers */
40 #include "progname.h"
41
42 /* find headers */
43 #include "regextype.h"
44
45 static void
46 output (const char *s, int escape)
47 {
48   (void) escape;
49
50   fputs (s, stdout);
51 }
52
53
54 static void
55 newline (void)
56 {
57   output ("\n", 0);
58 }
59
60 static void
61 content (const char *s)
62 {
63   output (s, 1);
64 }
65
66 static void
67 literal (const char *s)
68 {
69   output (s, 0);
70 }
71
72 static void
73 directive (const char *s)
74 {
75   output (s, 0);
76 }
77
78 static void
79 comment (const char *s)
80 {
81   directive ("@c ");
82   literal (s);
83   newline ();
84 }
85
86 static void
87 enum_item (const char *s)
88 {
89   newline ();
90   directive ("@item ");
91   literal (s);
92   newline ();
93 }
94
95 static void
96 begin_subsection (const char *name,
97                   const char *next,
98                   const char *prev,
99                   const char *up)
100 {
101   (void) next;
102   (void) prev;
103   (void) up;
104
105   newline ();
106
107   directive ("@node ");
108   content (name);
109   content (" regular expression syntax");
110   newline ();
111
112   directive ("@subsection ");
113   output ("@samp{", 0);
114   content (name);
115   output ("}", 0);
116   content (" regular expression syntax");
117   newline ();
118 }
119
120 static void
121 begintable_markup (char const *markup)
122 {
123   newline ();
124   directive ("@table ");
125   literal (markup);
126   newline ();
127 }
128
129 static void
130 endtable ()
131 {
132   newline ();
133   directive ("@end table");
134   newline ();
135 }
136
137 static void
138 beginenum ()
139 {
140   newline ();
141   directive ("@enumerate");
142   newline ();
143 }
144
145 static void
146 endenum ()
147 {
148   newline ();
149   directive ("@end enumerate");
150   newline ();
151 }
152
153 static void
154 newpara ()
155 {
156   content ("\n\n");
157 }
158
159
160 static void
161 describe_regex_syntax (int options)
162 {
163   newpara ();
164   content ("The character @samp{.} matches any single character");
165   if ( (options & RE_DOT_NEWLINE)  == 0 )
166     {
167       content (" except newline");
168     }
169   if (options & RE_DOT_NOT_NULL)
170     {
171       if ( (options & RE_DOT_NEWLINE)  == 0 )
172         content (" and");
173       else
174         content (" except");
175
176       content (" the null character");
177     }
178   content (".  ");
179   newpara ();
180
181   if (!(options & RE_LIMITED_OPS))
182     {
183       begintable_markup ("@samp");
184       if (options & RE_BK_PLUS_QM)
185         {
186           enum_item ("\\+");
187           content ("indicates that the regular expression should match one"
188                    " or more occurrences of the previous atom or regexp.  ");
189           enum_item ("\\?");
190           content ("indicates that the regular expression should match zero"
191                    " or one occurrence of the previous atom or regexp.  ");
192           enum_item ("+ and ? ");
193           content ("match themselves.  ");
194         }
195       else
196         {
197           enum_item ("+");
198           content ("indicates that the regular expression should match one"
199                    " or more occurrences of the previous atom or regexp.  ");
200           enum_item ("?");
201           content ("indicates that the regular expression should match zero"
202                    " or one occurrence of the previous atom or regexp.  ");
203           enum_item ("\\+");
204           literal ("matches a @samp{+}");
205           enum_item ("\\?");
206           literal ("matches a @samp{?}.  ");
207         }
208       endtable ();
209     }
210
211   newpara ();
212
213   content ("Bracket expressions are used to match ranges of characters.  ");
214   literal ("Bracket expressions where the range is backward, for example @samp{[z-a]}, are ");
215   if (options & RE_NO_EMPTY_RANGES)
216     content ("invalid");
217   else
218     content ("ignored");
219   content (".  ");
220
221   if (options &  RE_BACKSLASH_ESCAPE_IN_LISTS)
222     literal ("Within square brackets, @samp{\\} can be used to quote "
223              "the following character.  ");
224   else
225     literal ("Within square brackets, @samp{\\} is taken literally.  ");
226
227   if (options & RE_CHAR_CLASSES)
228     content ("Character classes are supported; for example "
229              "@samp{[[:digit:]]} will match a single decimal digit.  ");
230   else
231     literal ("Character classes are not supported, so for example "
232              "you would need to use @samp{[0-9]} "
233              "instead of @samp{[[:digit:]]}.  ");
234
235   if (options & RE_HAT_LISTS_NOT_NEWLINE)
236     {
237       literal ("Non-matching lists @samp{[^@dots{}]} do not ever match newline.  ");
238     }
239   newpara ();
240   if (options & RE_NO_GNU_OPS)
241     {
242       content ("GNU extensions are not supported and so "
243                "@samp{\\w}, @samp{\\W}, @samp{\\<}, @samp{\\>}, @samp{\\b}, @samp{\\B}, @samp{\\`}, and @samp{\\'} "
244                "match "
245                "@samp{w}, @samp{W}, @samp{<}, @samp{>}, @samp{b}, @samp{B}, @samp{`}, and @samp{'} respectively.  ");
246     }
247   else
248     {
249       content ("GNU extensions are supported:");
250       beginenum ();
251       enum_item ("@samp{\\w} matches a character within a word");
252       enum_item ("@samp{\\W} matches a character which is not within a word");
253       enum_item ("@samp{\\<} matches the beginning of a word");
254       enum_item ("@samp{\\>} matches the end of a word");
255       enum_item ("@samp{\\b} matches a word boundary");
256       enum_item ("@samp{\\B} matches characters which are not a word boundary");
257       enum_item ("@samp{\\`} matches the beginning of the whole input");
258       enum_item ("@samp{\\'} matches the end of the whole input");
259       endenum ();
260     }
261
262   newpara ();
263
264
265   if (options & RE_NO_BK_PARENS)
266     {
267       literal ("Grouping is performed with parentheses @samp{()}.  ");
268
269       if (options & RE_UNMATCHED_RIGHT_PAREN_ORD)
270         literal ("An unmatched @samp{)} matches just itself.  ");
271     }
272   else
273     {
274       literal ("Grouping is performed with backslashes followed by parentheses @samp{\\(}, @samp{\\)}.  ");
275     }
276
277   if (options & RE_NO_BK_REFS)
278     {
279       content ("A backslash followed by a digit matches that digit.  ");
280     }
281   else
282     {
283       literal ("A backslash followed by a digit acts as a back-reference and matches the same thing as the previous grouped expression indicated by that number.  For example @samp{\\2} matches the second group expression.  The order of group expressions is determined by the position of their opening parenthesis ");
284       if (options & RE_NO_BK_PARENS)
285         literal ("@samp{(}");
286       else
287         literal ("@samp{\\(}");
288       content (".  ");
289     }
290
291
292   newpara ();
293   if (!(options & RE_LIMITED_OPS))
294     {
295       if (options & RE_NO_BK_VBAR)
296         literal ("The alternation operator is @samp{|}.  ");
297       else
298         literal ("The alternation operator is @samp{\\|}. ");
299     }
300   newpara ();
301
302   if (options & RE_CONTEXT_INDEP_ANCHORS)
303     {
304       literal ("The characters @samp{^} and @samp{$} always represent the beginning and end of a string respectively, except within square brackets.  Within brackets, @samp{^} can be used to invert the membership of the character class being specified.  ");
305     }
306   else
307     {
308       literal ("The character @samp{^} only represents the beginning of a string when it appears:");
309       beginenum ();
310       enum_item ("\nAt the beginning of a regular expression");
311       enum_item ("After an open-group, signified by ");
312       if (options & RE_NO_BK_PARENS)
313         {
314           literal ("@samp{(}");
315         }
316       else
317         {
318           literal ("@samp{\\(}");
319         }
320       newline ();
321       if (!(options & RE_LIMITED_OPS))
322         {
323           if (options & RE_NEWLINE_ALT)
324             enum_item ("After a newline");
325
326           if (options & RE_NO_BK_VBAR )
327             enum_item ("After the alternation operator @samp{|}");
328           else
329             enum_item ("After the alternation operator @samp{\\|}");
330         }
331       endenum ();
332
333       newpara ();
334       literal ("The character @samp{$} only represents the end of a string when it appears:");
335       beginenum ();
336       enum_item ("At the end of a regular expression");
337       enum_item ("Before a close-group, signified by ");
338       if (options & RE_NO_BK_PARENS)
339         {
340           literal ("@samp{)}");
341         }
342       else
343         {
344           literal ("@samp{\\)}");
345         }
346       if (!(options & RE_LIMITED_OPS))
347         {
348           if (options & RE_NEWLINE_ALT)
349             enum_item ("Before a newline");
350
351           if (options & RE_NO_BK_VBAR)
352             enum_item ("Before the alternation operator @samp{|}");
353           else
354             enum_item ("Before the alternation operator @samp{\\|}");
355         }
356       endenum ();
357     }
358   newpara ();
359   if (!(options & RE_LIMITED_OPS) )
360     {
361       if ((options & RE_CONTEXT_INDEP_OPS)
362           && !(options & RE_CONTEXT_INVALID_OPS))
363         {
364           literal ("The characters @samp{*}, @samp{+} and @samp{?} are special anywhere in a regular expression.  ");
365         }
366       else
367         {
368           if (options & RE_BK_PLUS_QM)
369             literal ("@samp{\\*}, @samp{\\+} and @samp{\\?} ");
370           else
371             literal ("@samp{*}, @samp{+} and @samp{?} ");
372
373           if (options & RE_CONTEXT_INVALID_OPS)
374             {
375               content ("are special at any point in a regular expression except the following places, where they are not allowed:");
376             }
377           else
378             {
379               content ("are special at any point in a regular expression except:");
380             }
381
382           beginenum ();
383           enum_item ("At the beginning of a regular expression");
384           enum_item ("After an open-group, signified by ");
385           if (options & RE_NO_BK_PARENS)
386             {
387               literal ("@samp{(}");
388             }
389           else
390             {
391               literal ("@samp{\\(}");
392             }
393           if (!(options & RE_LIMITED_OPS))
394             {
395               if (options & RE_NEWLINE_ALT)
396                 enum_item ("After a newline");
397
398               if (options & RE_NO_BK_VBAR)
399                 enum_item ("After the alternation operator @samp{|}");
400               else
401                 enum_item ("After the alternation operator @samp{\\|}");
402             }
403           endenum ();
404         }
405     }
406
407
408   newpara ();
409   if (options & RE_INTERVALS)
410     {
411       if (options & RE_NO_BK_BRACES)
412         {
413           literal ("Intervals are specified by @samp{@{} and @samp{@}}.  ");
414           if (options & RE_INVALID_INTERVAL_ORD)
415             {
416               literal ("Invalid intervals are treated as literals, for example @samp{a@{1} is treated as @samp{a\\@{1}");
417             }
418           else
419             {
420               literal ("Invalid intervals such as @samp{a@{1z} are not accepted.  ");
421             }
422         }
423       else
424         {
425           literal ("Intervals are specified by @samp{\\@{} and @samp{\\@}}.  ");
426           if (options & RE_INVALID_INTERVAL_ORD)
427             {
428               literal ("Invalid intervals are treated as literals, for example @samp{a\\@{1} is treated as @samp{a@{1}");
429             }
430           else
431             {
432               literal ("Invalid intervals such as @samp{a\\@{1z} are not accepted.  ");
433             }
434         }
435
436     }
437
438   newpara ();
439   if (options & RE_NO_POSIX_BACKTRACKING)
440     {
441       content ("Matching succeeds as soon as the whole pattern is matched, meaning that the result may not be the longest possible match.  ");
442     }
443   else
444     {
445       content ("The longest possible match is returned; this applies to the regular expression as a whole and (subject to this constraint) to subexpressions within groups.  ");
446     }
447   newpara ();
448 }
449
450
451 static void
452 copying (void)
453 {
454   static const char *copy_para[]=
455     {
456       "Copyright (C) 1994, 1996, 1998, 2000, 2001, 2003, 2004, 2005, 2006,"
457       ,"2007, 2009, 2010, 2011 Free Software Foundation, Inc."
458       ,""
459       ,"Permission is granted to copy, distribute and/or modify this document"
460       ,"under the terms of the GNU Free Documentation License, Version 1.3 or"
461       ,"any later version published by the Free Software Foundation; with no"
462       ,"Invariant Sections, with no Front-Cover Texts, and with no Back-Cover"
463       ,"Texts.  A copy of the license is included in the ``GNU Free"
464       ,"Documentation License'' file as part of this distribution."
465       ""
466       ,NULL
467     };
468   const char **s = copy_para;
469   while (*s)
470     comment (*s++);
471 }
472
473 static int
474 ignore (int ix, const unsigned int context)
475 {
476   return 0 == (get_regex_type_context (ix) & context);
477 }
478
479 static void
480 menu (unsigned int context)
481 {
482   int i, options;
483   const char *name;
484
485   output ("@menu\n", 0);
486   for (i=0;
487        options = get_regex_type_flags (i),
488          name=get_regex_type_name (i);
489        ++i)
490     {
491       if (!ignore (i, context))
492         {
493           output ("* ", 0);
494           output (name, 0);
495           content (" regular expression syntax");
496           output ("::", 0);
497           newline ();
498         }
499     }
500   output ("@end menu\n", 0);
501 }
502
503
504
505 static const char *
506 get_next (unsigned int ix, unsigned int context)
507 {
508   const char *next;
509   while (get_regex_type_name (ix))
510     {
511       if (!ignore (ix, context))
512         {
513           next = get_regex_type_name (ix);
514           if (NULL == next)
515             return "";
516           else
517             return next;
518         }
519       ++ix;
520     }
521   return "";
522 }
523
524
525 static void
526 describe_all (const char *contextname,
527               unsigned int context,
528               const char *up)
529 {
530   const char *name, *next, *previous;
531   int options;
532   int i, parent;
533
534   copying ();
535   newline ();
536   literal ("@c this regular expression description is for: ");
537   literal (contextname);
538   newline ();
539   newline ();
540   menu (context);
541
542   previous = "";
543
544   for (i=0;
545        options = get_regex_type_flags (i),
546          name=get_regex_type_name (i);
547        ++i)
548     {
549       if (ignore (i, context))
550         {
551           fprintf (stderr,
552                    "Skipping regexp type %s for context %s\n",
553                    name, contextname);
554           name = previous;
555           continue;
556         }
557
558       next = get_next (i+1, context);
559       if (NULL == next)
560         next = "";
561       begin_subsection (name, next, previous, up);
562       parent = get_regex_type_synonym (i);
563       if (parent >= 0)
564         {
565           content ("This is a synonym for ");
566           content (get_regex_type_name (parent));
567           content (".");
568         }
569       else
570         {
571           describe_regex_syntax (options);
572         }
573       previous = name;
574     }
575 }
576
577
578
579 int
580 main (int argc, char *argv[])
581 {
582   const char *up = "";
583   unsigned int context = CONTEXT_ALL;
584   const char *contextname = "all";
585
586   if (argc)
587     set_program_name (argv[0]);
588   else
589     set_program_name ("regexprops");
590
591   if (argc > 1)
592     {
593       up = argv[1];
594     }
595   if (argc > 2)
596     {
597       contextname = argv[2];
598       if (0 == strcmp (contextname, "findutils"))
599         context = CONTEXT_FINDUTILS;
600       else if (0 == strcmp (contextname, "generic"))
601         context = CONTEXT_GENERIC;
602       else if (0 == strcmp (contextname, "all"))
603         context = CONTEXT_ALL;
604       else
605         {
606           fprintf (stderr, "Unexpected context %s",
607                    contextname);
608           return 1;
609         }
610     }
611
612   describe_all (contextname, context, up);
613   return 0;
614 }