1
2/*
3
4   Test to see if a particular fix should be applied to a header file.
5
6   Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
7
8= = = = = = = = = = = = = = = = = = = = = = = = =
9
10NOTE TO DEVELOPERS
11
12The routines you write here must work closely with fixincl.c.
13
14Here are the rules:
15
161.  Every test procedure name must be suffixed with "_fix".
17    These routines will be referenced from inclhack.def, sans the suffix.
18
192.  Use the "FIX_PROC_HEAD()" macro _with_ the "_fix" suffix
20    (I cannot use the ## magic from ANSI C) for defining your entry point.
21
223.  Put your test name into the FIXUP_TABLE.
23
244.  Do not read anything from stdin.  It is closed.
25
265.  Write to stderr only in the event of a reportable error
27    In such an event, call "exit (EXIT_FAILURE)".
28
296.  You have access to the fixDescList entry for the fix in question.
30    This may be useful, for example, if there are interesting strings
31    or pre-compiled regular expressions stored there.
32
33= = = = = = = = = = = = = = = = = = = = = = = = =
34
35This file is part of GNU CC.
36
37GNU CC is free software; you can redistribute it and/or modify
38it under the terms of the GNU General Public License as published by
39the Free Software Foundation; either version 2, or (at your option)
40any later version.
41
42GNU CC is distributed in the hope that it will be useful,
43but WITHOUT ANY WARRANTY; without even the implied warranty of
44MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45GNU General Public License for more details.
46
47You should have received a copy of the GNU General Public License
48along with GNU CC; see the file COPYING.  If not, write to
49the Free Software Foundation, 59 Temple Place - Suite 330,
50Boston, MA 02111-1307, USA.  */
51
52#include "fixlib.h"
53#define    GTYPE_SE_CT 1
54
55#ifdef SEPARATE_FIX_PROC
56#include "fixincl.x"
57#endif
58
59tSCC zNeedsArg[] = "fixincl error:  `%s' needs %s argument (c_fix_arg[%d])\n";
60
61typedef void t_fix_proc PARAMS ((const char *, const char *, tFixDesc *));
62typedef struct {
63    const char*  fix_name;
64    t_fix_proc*  fix_proc;
65} fix_entry_t;
66
67#define FIXUP_TABLE \
68  _FT_( "char_macro_def",   char_macro_def_fix ) \
69  _FT_( "char_macro_use",   char_macro_use_fix ) \
70  _FT_( "format",           format_fix )         \
71  _FT_( "machine_name",     machine_name_fix )   \
72  _FT_( "wrap",             wrap_fix )           \
73  _FT_( "gnu_type",         gnu_type_fix )
74
75
76#define FIX_PROC_HEAD( fix )                    \
77static void fix PARAMS ((const char *, const char *, tFixDesc *)); /* avoid warning */      \
78static void fix ( filname, text, p_fixd )       \
79    const char* filname;                        \
80    const char* text;                           \
81    tFixDesc* p_fixd;
82
83#ifdef NEED_PRINT_QUOTE
84/*
85 *  Skip over a quoted string.  Single quote strings may
86 *  contain multiple characters if the first character is
87 *  a backslash.  Especially a backslash followed by octal digits.
88 *  We are not doing a correctness syntax check here.
89 */
90static char*
91print_quote( q, text )
92  char  q;
93  char* text;
94{
95  fputc( q, stdout );
96
97  for (;;)
98    {
99      char ch = *(text++);
100      fputc( ch, stdout );
101
102      switch (ch)
103        {
104        case '\\':
105          if (*text == NUL)
106            goto quote_done;
107
108          fputc( *(text++), stdout );
109          break;
110
111        case '"':
112        case '\'':
113          if (ch != q)
114            break;
115          /*FALLTHROUGH*/
116
117        case '\n':
118        case NUL:
119          goto quote_done;
120        }
121    } quote_done:;
122
123  return text;
124}
125#endif /* NEED_PRINT_QUOTE */
126
127
128/*
129 *  Emit the GNU standard type wrapped up in such a way that
130 *  this thing can be encountered countless times during a compile
131 *  and not cause even a warning.
132 */
133static const char *emit_gnu_type PARAMS ((const char *, regmatch_t *));
134static const char*
135emit_gnu_type ( text, rm )
136  const char* text;
137  regmatch_t* rm;
138{
139  char z_TYPE[ 64 ];
140  char z_type[ 64 ];
141
142  fwrite (text, rm[0].rm_so, 1, stdout);
143
144  {
145    const char* ps = text   + rm[1].rm_so;
146    const char* pe = text   + rm[1].rm_eo;
147    char* pd = z_type;
148    char* pD = z_TYPE;
149
150    while (ps < pe)
151      *(pD++) = TOUPPER( *(pd++) = *(ps++) );
152
153    *pD = *pd = NUL;
154  }
155
156  /*
157   *  Now print out the reformed typedef,
158   *  with a C++ guard for WCHAR
159   */
160  {
161    tSCC z_fmt[] = "\
162#if !defined(_GCC_%s_T)%s\n\
163#define _GCC_%s_T\n\
164typedef __%s_TYPE__ %s_t;\n\
165#endif\n";
166
167    const char *const pz_guard = (strcmp (z_type, "wchar") == 0)
168                           ? " && ! defined(__cplusplus)" : "";
169
170    printf (z_fmt, z_TYPE, pz_guard, z_TYPE, z_TYPE, z_type);
171  }
172
173  return text += rm[0].rm_eo;
174}
175
176
177/*
178 *  Copy the `format' string to std out, replacing `%n' expressions
179 *  with the matched text from a regular expression evaluation.
180 *  Doubled '%' characters will be replaced with a single copy.
181 *  '%' characters in other contexts and all other characters are
182 *  copied out verbatim.
183 */
184static void format_write PARAMS ((tCC *, tCC *, regmatch_t[]));
185static void
186format_write (format, text, av)
187     tCC* format;
188     tCC* text;
189     regmatch_t av[];
190{
191  int c;
192
193  while ((c = (unsigned)*(format++)) != NUL) {
194
195    if (c != '%')
196      {
197        putchar(c);
198        continue;
199      }
200
201    c = (unsigned)*(format++);
202
203    /*
204     *  IF the character following a '%' is not a digit,
205     *  THEN we will always emit a '%' and we may or may
206     *  not emit the following character.  We will end on
207     *  a NUL and we will emit only one of a pair of '%'.
208     */
209    if (! ISDIGIT ( c ))
210      {
211        putchar( '%' );
212        switch (c) {
213        case NUL:
214          return;
215        case '%':
216          break;
217        default:
218          putchar(c);
219        }
220      }
221
222    /*
223     *  Emit the matched subexpression numbered 'c'.
224     *  IF, of course, there was such a match...
225     */
226    else {
227      regmatch_t*  pRM = av + (c - (unsigned)'0');
228      size_t len;
229
230      if (pRM->rm_so < 0)
231        continue;
232
233      len = pRM->rm_eo - pRM->rm_so;
234      if (len > 0)
235        fwrite(text + pRM->rm_so, len, 1, stdout);
236    }
237  }
238}
239
240
241/*
242 *  Search for multiple copies of a regular expression.  Each block
243 *  of matched text is replaced with the format string, as described
244 *  above in `format_write'.
245 */
246FIX_PROC_HEAD( format_fix )
247{
248  tCC*  pz_pat = p_fixd->patch_args[2];
249  tCC*  pz_fmt = p_fixd->patch_args[1];
250  regex_t re;
251  regmatch_t rm[10];
252  IGNORE_ARG(filname);
253
254  /*
255   *  We must have a format
256   */
257  if (pz_fmt == (tCC*)NULL)
258    {
259      fprintf( stderr, zNeedsArg, p_fixd->fix_name, "replacement format", 0 );
260      exit (EXIT_BROKEN);
261    }
262
263  /*
264   *  IF we don't have a search text, then go find the first
265   *  regular expression among the tests.
266   */
267  if (pz_pat == (tCC*)NULL)
268    {
269      tTestDesc* pTD = p_fixd->p_test_desc;
270      int        ct  = p_fixd->test_ct;
271      for (;;)
272        {
273          if (ct-- <= 0)
274            {
275              fprintf( stderr, zNeedsArg, p_fixd->fix_name, "search text", 1 );
276              exit (EXIT_BROKEN);
277            }
278
279          if (pTD->type == TT_EGREP)
280            {
281              pz_pat = pTD->pz_test_text;
282              break;
283            }
284
285          pTD++;
286        }
287    }
288
289  /*
290   *  Replace every copy of the text we find
291   */
292  compile_re (pz_pat, &re, 1, "format search-text", "format_fix" );
293  while (regexec (&re, text, 10, rm, 0) == 0)
294    {
295      fwrite( text, rm[0].rm_so, 1, stdout );
296      format_write( pz_fmt, text, rm );
297      text += rm[0].rm_eo;
298    }
299
300  /*
301   *  Dump out the rest of the file
302   */
303  fputs (text, stdout);
304}
305
306
307/* Scan the input file for all occurrences of text like this:
308
309   #define TIOCCONS _IO(T, 12)
310
311   and change them to read like this:
312
313   #define TIOCCONS _IO('T', 12)
314
315   which is the required syntax per the C standard.  (The definition of
316   _IO also has to be tweaked - see below.)  'IO' is actually whatever you
317   provide as the `c_fix_arg' argument.  */
318
319FIX_PROC_HEAD( char_macro_use_fix )
320{
321  /* This regexp looks for a traditional-syntax #define (# in column 1)
322     of an object-like macro.  */
323  static const char pat[] =
324    "^#[ \t]*define[ \t]+[_A-Za-z][_A-Za-z0-9]*[ \t]+";
325  static regex_t re;
326
327  const char* str = p_fixd->patch_args[1];
328  regmatch_t rm[1];
329  const char *p, *limit;
330  size_t len;
331  IGNORE_ARG(filname);
332
333  if (str == NULL)
334    {
335      fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0);
336      exit (EXIT_BROKEN);
337    }
338
339  len = strlen (str);
340  compile_re (pat, &re, 1, "macro pattern", "char_macro_use_fix");
341
342  for (p = text;
343       regexec (&re, p, 1, rm, 0) == 0;
344       p = limit + 1)
345    {
346      /* p + rm[0].rm_eo is the first character of the macro replacement.
347	 Find the end of the macro replacement, and the STR we were
348	 sent to look for within the replacement.  */
349      p += rm[0].rm_eo;
350      limit = p - 1;
351      do
352	{
353	  limit = strchr (limit + 1, '\n');
354	  if (!limit)
355	    goto done;
356	}
357      while (limit[-1] == '\\');
358
359      do
360	{
361	  if (*p == str[0] && !strncmp (p+1, str+1, len-1))
362	    goto found;
363	}
364      while (++p < limit - len);
365      /* Hit end of line.  */
366      continue;
367
368    found:
369      /* Found STR on this line.  If the macro needs fixing,
370	 the next few chars will be whitespace or uppercase,
371	 then an open paren, then a single letter.  */
372      while ((ISSPACE (*p) || ISUPPER (*p)) && p < limit) p++;
373      if (*p++ != '(')
374	continue;
375      if (!ISALPHA (*p))
376	continue;
377      if (ISIDNUM (p[1]))
378	continue;
379
380      /* Splat all preceding text into the output buffer,
381	 quote the character at p, then proceed.  */
382      fwrite (text, 1, p - text, stdout);
383      putchar ('\'');
384      putchar (*p);
385      putchar ('\'');
386      text = p + 1;
387    }
388 done:
389  fputs (text, stdout);
390}
391
392
393/* Scan the input file for all occurrences of text like this:
394
395   #define xxxIOxx(x, y) (....'x'<<16....)
396
397   and change them to read like this:
398
399   #define xxxIOxx(x, y) (....x<<16....)
400
401   which is the required syntax per the C standard.  (The uses of _IO
402   also has to be tweaked - see above.)  'IO' is actually whatever
403   you provide as the `c_fix_arg' argument.  */
404FIX_PROC_HEAD( char_macro_def_fix )
405{
406  /* This regexp looks for any traditional-syntax #define (# in column 1).  */
407  static const char pat[] =
408    "^#[ \t]*define[ \t]+";
409  static regex_t re;
410
411  const char* str = p_fixd->patch_args[1];
412  regmatch_t rm[1];
413  const char *p, *limit;
414  char arg;
415  size_t len;
416  IGNORE_ARG(filname);
417
418  if (str == NULL)
419    {
420      fprintf (stderr, zNeedsArg, p_fixd->fix_name, "ioctl type", 0);
421      exit (EXIT_BROKEN);
422    }
423
424  len = strlen (str);
425  compile_re (pat, &re, 1, "macro pattern", "fix_char_macro_defines");
426
427  for (p = text;
428       regexec (&re, p, 1, rm, 0) == 0;
429       p = limit + 1)
430    {
431      /* p + rm[0].rm_eo is the first character of the macro name.
432	 Find the end of the macro replacement, and the STR we were
433	 sent to look for within the name.  */
434      p += rm[0].rm_eo;
435      limit = p - 1;
436      do
437	{
438	  limit = strchr (limit + 1, '\n');
439	  if (!limit)
440	    goto done;
441	}
442      while (limit[-1] == '\\');
443
444      do
445	{
446	  if (*p == str[0] && !strncmp (p+1, str+1, len-1))
447	    goto found;
448	  p++;
449	}
450      while (ISIDNUM (*p));
451      /* Hit end of macro name without finding the string.  */
452      continue;
453
454    found:
455      /* Found STR in this macro name.  If the macro needs fixing,
456	 there may be a few uppercase letters, then there will be an
457	 open paren with _no_ intervening whitespace, and then a
458	 single letter.  */
459      while (ISUPPER (*p) && p < limit) p++;
460      if (*p++ != '(')
461	continue;
462      if (!ISALPHA (*p))
463	continue;
464      if (ISIDNUM (p[1]))
465	continue;
466
467      /* The character at P is the one to look for in the following
468	 text.  */
469      arg = *p;
470      p += 2;
471
472      while (p < limit)
473	{
474	  if (p[-1] == '\'' && p[0] == arg && p[1] == '\'')
475	    {
476	      /* Remove the quotes from this use of ARG.  */
477	      p--;
478	      fwrite (text, 1, p - text, stdout);
479	      putchar (arg);
480	      p += 3;
481	      text = p;
482	    }
483	  else
484	    p++;
485	}
486    }
487 done:
488  fputs (text, stdout);
489}
490
491/* Fix for machine name #ifdefs that are not in the namespace reserved
492   by the C standard.  They won't be defined if compiling with -ansi,
493   and the headers will break.  We go to some trouble to only change
494   #ifdefs where the macro is defined by GCC in non-ansi mode; this
495   minimizes the number of headers touched.  */
496
497#define SCRATCHSZ 64   /* hopefully long enough */
498
499FIX_PROC_HEAD( machine_name_fix )
500{
501#ifndef MN_NAME_PAT
502  fputs( "The target machine has no needed machine name fixes\n", stderr );
503#else
504  regmatch_t match[2];
505  const char *line, *base, *limit, *p, *q;
506  regex_t *label_re, *name_re;
507  char scratch[SCRATCHSZ];
508  size_t len;
509  IGNORE_ARG(filname);
510  IGNORE_ARG(p_fixd);
511
512  mn_get_regexps (&label_re, &name_re, "machine_name_fix");
513
514  scratch[0] = '_';
515  scratch[1] = '_';
516
517  for (base = text;
518       regexec (label_re, base, 2, match, 0) == 0;
519       base = limit)
520    {
521      base += match[0].rm_eo;
522      /* We're looking at an #if or #ifdef.  Scan forward for the
523         next non-escaped newline.  */
524      line = limit = base;
525      do
526        {
527          limit++;
528          limit = strchr (limit, '\n');
529          if (!limit)
530            goto done;
531        }
532      while (limit[-1] == '\\');
533
534      /* If the 'name_pat' matches in between base and limit, we have
535         a bogon.  It is not worth the hassle of excluding comments
536         because comments on #if/#ifdef lines are rare, and strings on
537         such lines are illegal.
538
539         REG_NOTBOL means 'base' is not at the beginning of a line, which
540         shouldn't matter since the name_re has no ^ anchor, but let's
541         be accurate anyway.  */
542
543      for (;;)
544        {
545        again:
546          if (base == limit)
547            break;
548
549          if (regexec (name_re, base, 1, match, REG_NOTBOL))
550            goto done;  /* No remaining match in this file */
551
552          /* Match; is it on the line?  */
553          if (match[0].rm_eo > limit - base)
554            break;
555
556          p = base + match[0].rm_so;
557          base += match[0].rm_eo;
558
559          /* One more test: if on the same line we have the same string
560             with the appropriate underscores, then leave it alone.
561             We want exactly two leading and trailing underscores.  */
562          if (*p == '_')
563            {
564              len = base - p - ((*base == '_') ? 2 : 1);
565              q = p + 1;
566            }
567          else
568            {
569              len = base - p - ((*base == '_') ? 1 : 0);
570              q = p;
571            }
572          if (len + 4 > SCRATCHSZ)
573            abort ();
574          memcpy (&scratch[2], q, len);
575          len += 2;
576          scratch[len++] = '_';
577          scratch[len++] = '_';
578
579          for (q = line; q <= limit - len; q++)
580            if (*q == '_' && !strncmp (q, scratch, len))
581              goto again;
582
583          fwrite (text, 1, p - text, stdout);
584          fwrite (scratch, 1, len, stdout);
585
586          text = base;
587        }
588    }
589 done:
590#endif
591  fputs (text, stdout);
592}
593
594
595FIX_PROC_HEAD( wrap_fix )
596{
597  tSCC   z_no_wrap_pat[] = "^#if.*__need_";
598  static regex_t no_wrapping_re; /* assume zeroed data */
599
600  tCC*   pz_name = NULL;
601
602  if (no_wrapping_re.allocated == 0)
603    compile_re( z_no_wrap_pat, &no_wrapping_re, 0, "no-wrap pattern",
604                "wrap-fix" );
605
606  /*
607   *  IF we do *not* match the no-wrap re, then we have a double negative.
608   *  A double negative means YES.
609   */
610  if (regexec( &no_wrapping_re, text, 0, NULL, 0 ) != 0)
611    {
612      /*
613       *  A single file can get wrapped more than once by different fixes.
614       *  A single fix can wrap multiple files.  Therefore, guard with
615       *  *both* the fix name and the file name.
616       */
617      size_t ln = strlen( filname ) + strlen( p_fixd->fix_name ) + 14;
618      char*  pz = xmalloc( ln );
619      pz_name = pz;
620      sprintf( pz, "FIXINC_WRAP_%s-%s", filname, p_fixd->fix_name );
621
622      for (pz += 12; 1; pz++) {
623        char ch = *pz;
624
625        if (ch == NUL)
626          break;
627
628        if (! ISALNUM( ch )) {
629          *pz = '_';
630        }
631        else {
632          *pz = TOUPPER( ch );
633        }
634      }
635
636      printf( "#ifndef %s\n", pz_name );
637      printf( "#define %s 1\n\n", pz_name );
638    }
639
640  if (p_fixd->patch_args[1] == (tCC*)NULL)
641    fputs( text, stdout );
642
643  else {
644    fputs( p_fixd->patch_args[1], stdout );
645    fputs( text, stdout );
646    if (p_fixd->patch_args[2] != (tCC*)NULL)
647      fputs( p_fixd->patch_args[2], stdout );
648  }
649
650  if (pz_name != NULL) {
651    printf( "\n#endif  /* %s */\n", pz_name );
652    free( (void*)pz_name );
653  }
654}
655
656
657/*
658 *  Search for multiple copies of a regular expression.  Each block
659 *  of matched text is replaced with the format string, as described
660 *  above in `format_write'.
661 */
662FIX_PROC_HEAD( gnu_type_fix )
663{
664  const char* pz_pat;
665  regex_t    re;
666  regmatch_t rm[GTYPE_SE_CT+1];
667  IGNORE_ARG(filname);
668
669  {
670    tTestDesc* pTD = p_fixd->p_test_desc;
671    int        ct  = p_fixd->test_ct;
672    for (;;)
673      {
674        if (ct-- <= 0)
675          {
676            fprintf (stderr, zNeedsArg, p_fixd->fix_name, "search text", 1);
677            exit (EXIT_BROKEN);
678          }
679
680        if (pTD->type == TT_EGREP)
681          {
682            pz_pat = pTD->pz_test_text;
683            break;
684          }
685
686        pTD++;
687      }
688  }
689
690  compile_re (pz_pat, &re, 1, "gnu type typedef", "gnu_type_fix");
691
692  while (regexec (&re, text, GTYPE_SE_CT+1, rm, 0) == 0)
693    {
694      text = emit_gnu_type (text, rm);
695    }
696
697  /*
698   *  Dump out the rest of the file
699   */
700  fputs (text, stdout);
701}
702
703
704/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
705
706     test for fix selector
707
708     THIS IS THE ONLY EXPORTED ROUTINE
709
710*/
711void
712apply_fix( p_fixd, filname )
713  tFixDesc* p_fixd;
714  tCC* filname;
715{
716#define _FT_(n,p) { n, p },
717  static fix_entry_t fix_table[] = { FIXUP_TABLE { NULL, NULL }};
718#undef _FT_
719#define FIX_TABLE_CT (ARRAY_SIZE (fix_table)-1)
720
721  tCC* fixname = p_fixd->patch_args[0];
722  char* buf;
723  int ct = FIX_TABLE_CT;
724  fix_entry_t* pfe = fix_table;
725
726  for (;;)
727    {
728      if (strcmp (pfe->fix_name, fixname) == 0)
729        break;
730      if (--ct <= 0)
731        {
732          fprintf (stderr, "fixincl error:  the `%s' fix is unknown\n",
733                   fixname );
734          exit (EXIT_BROKEN);
735        }
736      pfe++;
737    }
738
739  buf = load_file_data (stdin);
740  (*pfe->fix_proc)( filname, buf, p_fixd );
741}
742
743#ifdef SEPARATE_FIX_PROC
744tSCC z_usage[] =
745"USAGE: applyfix <fix-name> <file-to-fix> <file-source> <file-destination>\n";
746tSCC z_reopen[] =
747"FS error %d (%s) reopening %s as std%s\n";
748
749int
750main( argc, argv )
751  int     argc;
752  char**  argv;
753{
754  tFixDesc* pFix;
755  char* pz_tmptmp;
756  char* pz_tmp_base;
757  char* pz_tmp_dot;
758
759  if (argc != 5)
760    {
761    usage_failure:
762      fputs (z_usage, stderr);
763      return EXIT_FAILURE;
764    }
765
766  {
767    char* pz = argv[1];
768    long  idx;
769
770    if (! ISDIGIT ( *pz ))
771      goto usage_failure;
772
773    idx = strtol (pz, &pz, 10);
774    if ((*pz != NUL) || ((unsigned)idx >= FIX_COUNT))
775      goto usage_failure;
776    pFix = fixDescList + idx;
777  }
778
779  if (freopen (argv[3], "r", stdin) != stdin)
780    {
781      fprintf (stderr, z_reopen, errno, strerror( errno ), argv[3], "in");
782      return EXIT_FAILURE;
783    }
784
785  pz_tmptmp = (char*)xmalloc( strlen( argv[4] ) + 5 );
786  strcpy( pz_tmptmp, argv[4] );
787
788  /* Don't lose because "12345678" and "12345678X" map to the same
789     file under DOS restricted 8+3 file namespace.  Note that DOS
790     doesn't allow more than one dot in the trunk of a file name.  */
791  pz_tmp_base = basename( pz_tmptmp );
792  pz_tmp_dot = strchr( pz_tmp_base, '.' );
793  if (pathconf( pz_tmptmp, _PC_NAME_MAX ) <= 12	/* is this DOS or Windows9X? */
794      && pz_tmp_dot != (char*)NULL)
795    strcpy (pz_tmp_dot+1, "X"); /* nuke the original extension */
796  else
797    strcat (pz_tmptmp, ".X");
798  if (freopen (pz_tmptmp, "w", stdout) != stdout)
799    {
800      fprintf (stderr, z_reopen, errno, strerror( errno ), pz_tmptmp, "out");
801      return EXIT_FAILURE;
802    }
803
804  apply_fix (pFix, argv[1]);
805  fclose (stdout);
806  fclose (stdin);
807  unlink (argv[4]);
808  if (rename (pz_tmptmp, argv[4]) != 0)
809    {
810      fprintf (stderr, "error %d (%s) renaming %s to %s\n", errno,
811               strerror( errno ), pz_tmptmp, argv[4]);
812      return EXIT_FAILURE;
813    }
814
815  return EXIT_SUCCESS;
816}
817#endif
818