*** empty log message ***
[emacs.git] / lib-src / make-docfile.c
blobdf557e7edb4b0c3e43a2af9223f4632f6a729344
1 /* Generate doc-string file for GNU Emacs from source files.
2 Copyright (C) 1985, 86, 92, 93, 94, 97, 1999 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs 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 2, or (at your option)
9 any later version.
11 GNU Emacs 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.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* The arguments given to this program are all the C and Lisp source files
22 of GNU Emacs. .elc and .el and .c files are allowed.
23 A .o file can also be specified; the .c file it was made from is used.
24 This helps the makefile pass the correct list of files.
26 The results, which go to standard output or to a file
27 specified with -a or -o (-a to append, -o to start from nothing),
28 are entries containing function or variable names and their documentation.
29 Each entry starts with a ^_ character.
30 Then comes F for a function or V for a variable.
31 Then comes the function or variable name, terminated with a newline.
32 Then comes the documentation for that function or variable.
35 #define NO_SHORTNAMES /* Tell config not to load remap.h */
36 #include <config.h>
38 /* defined to be emacs_main, sys_fopen, etc. in config.h */
39 #undef main
40 #undef fopen
41 #undef chdir
43 #include <stdio.h>
44 #ifdef MSDOS
45 #include <fcntl.h>
46 #endif /* MSDOS */
47 #ifdef WINDOWSNT
48 #include <stdlib.h>
49 #include <fcntl.h>
50 #include <direct.h>
51 #endif /* WINDOWSNT */
53 #ifdef DOS_NT
54 #define READ_TEXT "rt"
55 #define READ_BINARY "rb"
56 #else /* not DOS_NT */
57 #define READ_TEXT "r"
58 #define READ_BINARY "r"
59 #endif /* not DOS_NT */
61 int scan_file ();
62 int scan_lisp_file ();
63 int scan_c_file ();
65 #ifdef MSDOS
66 /* s/msdos.h defines this as sys_chdir, but we're not linking with the
67 file where that function is defined. */
68 #undef chdir
69 #endif
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif
75 /* Stdio stream for output to the DOC file. */
76 FILE *outfile;
78 /* Name this program was invoked with. */
79 char *progname;
81 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
83 /* VARARGS1 */
84 void
85 error (s1, s2)
86 char *s1, *s2;
88 fprintf (stderr, "%s: ", progname);
89 fprintf (stderr, s1, s2);
90 fprintf (stderr, "\n");
93 /* Print error message and exit. */
95 /* VARARGS1 */
96 void
97 fatal (s1, s2)
98 char *s1, *s2;
100 error (s1, s2);
101 exit (1);
104 /* Like malloc but get fatal error if memory is exhausted. */
106 long *
107 xmalloc (size)
108 unsigned int size;
110 long *result = (long *) malloc (size);
111 if (result == NULL)
112 fatal ("virtual memory exhausted", 0);
113 return result;
117 main (argc, argv)
118 int argc;
119 char **argv;
121 int i;
122 int err_count = 0;
123 int first_infile;
125 progname = argv[0];
127 outfile = stdout;
129 /* Don't put CRs in the DOC file. */
130 #ifdef MSDOS
131 _fmode = O_BINARY;
132 #if 0 /* Suspicion is that this causes hanging.
133 So instead we require people to use -o on MSDOS. */
134 (stdout)->_flag &= ~_IOTEXT;
135 _setmode (fileno (stdout), O_BINARY);
136 #endif
137 outfile = 0;
138 #endif /* MSDOS */
139 #ifdef WINDOWSNT
140 _fmode = O_BINARY;
141 _setmode (fileno (stdout), O_BINARY);
142 #endif /* WINDOWSNT */
144 /* If first two args are -o FILE, output to FILE. */
145 i = 1;
146 if (argc > i + 1 && !strcmp (argv[i], "-o"))
148 outfile = fopen (argv[i + 1], "w");
149 i += 2;
151 if (argc > i + 1 && !strcmp (argv[i], "-a"))
153 outfile = fopen (argv[i + 1], "a");
154 i += 2;
156 if (argc > i + 1 && !strcmp (argv[i], "-d"))
158 chdir (argv[i + 1]);
159 i += 2;
162 if (outfile == 0)
163 fatal ("No output file specified", "");
165 first_infile = i;
166 for (; i < argc; i++)
168 int j;
169 /* Don't process one file twice. */
170 for (j = first_infile; j < i; j++)
171 if (! strcmp (argv[i], argv[j]))
172 break;
173 if (j == i)
174 err_count += scan_file (argv[i]);
176 #ifndef VMS
177 exit (err_count > 0);
178 #endif /* VMS */
179 return err_count > 0;
182 /* Read file FILENAME and output its doc strings to outfile. */
183 /* Return 1 if file is not found, 0 if it is found. */
186 scan_file (filename)
187 char *filename;
189 int len = strlen (filename);
190 if (len > 4 && !strcmp (filename + len - 4, ".elc"))
191 return scan_lisp_file (filename, READ_BINARY);
192 else if (len > 3 && !strcmp (filename + len - 3, ".el"))
193 return scan_lisp_file (filename, READ_TEXT);
194 else
195 return scan_c_file (filename, READ_TEXT);
198 char buf[128];
200 /* Skip a C string from INFILE,
201 and return the character that follows the closing ".
202 If printflag is positive, output string contents to outfile.
203 If it is negative, store contents in buf.
204 Convert escape sequences \n and \t to newline and tab;
205 discard \ followed by newline. */
208 read_c_string (infile, printflag)
209 FILE *infile;
210 int printflag;
212 register int c;
213 char *p = buf;
215 c = getc (infile);
216 while (c != EOF)
218 while (c != '"' && c != EOF)
220 if (c == '\\')
222 c = getc (infile);
223 if (c == '\n' || c == '\r')
225 c = getc (infile);
226 continue;
228 if (c == 'n')
229 c = '\n';
230 if (c == 't')
231 c = '\t';
233 if (printflag > 0)
234 putc (c, outfile);
235 else if (printflag < 0)
236 *p++ = c;
237 c = getc (infile);
239 c = getc (infile);
240 if (c != '"')
241 break;
242 /* If we had a "", concatenate the two strings. */
243 c = getc (infile);
246 if (printflag < 0)
247 *p = 0;
249 return c;
252 /* Write to file OUT the argument names of function FUNC, whose text is in BUF.
253 MINARGS and MAXARGS are the minimum and maximum number of arguments. */
255 void
256 write_c_args (out, func, buf, minargs, maxargs)
257 FILE *out;
258 char *func, *buf;
259 int minargs, maxargs;
261 register char *p;
262 int in_ident = 0;
263 int just_spaced = 0;
264 int need_space = 1;
266 fprintf (out, "(%s", func);
268 if (*buf == '(')
269 ++buf;
271 for (p = buf; *p; p++)
273 char c = *p;
274 int ident_start = 0;
276 /* Notice when we start printing a new identifier. */
277 if ((('A' <= c && c <= 'Z')
278 || ('a' <= c && c <= 'z')
279 || ('0' <= c && c <= '9')
280 || c == '_')
281 != in_ident)
283 if (!in_ident)
285 in_ident = 1;
286 ident_start = 1;
288 if (need_space)
289 putc (' ', out);
291 if (minargs == 0 && maxargs > 0)
292 fprintf (out, "&optional ");
293 just_spaced = 1;
295 minargs--;
296 maxargs--;
298 else
299 in_ident = 0;
302 /* Print the C argument list as it would appear in lisp:
303 print underscores as hyphens, and print commas as spaces.
304 Collapse adjacent spaces into one. */
305 if (c == '_') c = '-';
306 if (c == ',') c = ' ';
308 /* In C code, `default' is a reserved word, so we spell it
309 `defalt'; unmangle that here. */
310 if (ident_start
311 && strncmp (p, "defalt", 6) == 0
312 && ! (('A' <= p[6] && p[6] <= 'Z')
313 || ('a' <= p[6] && p[6] <= 'z')
314 || ('0' <= p[6] && p[6] <= '9')
315 || p[6] == '_'))
317 fprintf (out, "DEFAULT");
318 p += 5;
319 in_ident = 0;
320 just_spaced = 0;
322 else if (c != ' ' || ! just_spaced)
324 if (c >= 'a' && c <= 'z')
325 /* Upcase the letter. */
326 c += 'A' - 'a';
327 putc (c, out);
330 just_spaced = (c == ' ');
331 need_space = 0;
335 /* Read through a c file. If a .o file is named,
336 the corresponding .c file is read instead.
337 Looks for DEFUN constructs such as are defined in ../src/lisp.h.
338 Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED. */
341 scan_c_file (filename, mode)
342 char *filename, *mode;
344 FILE *infile;
345 register int c;
346 register int commas;
347 register int defunflag;
348 register int defvarperbufferflag;
349 register int defvarflag;
350 int minargs, maxargs;
351 int extension = filename[strlen (filename) - 1];
353 if (extension == 'o')
354 filename[strlen (filename) - 1] = 'c';
356 infile = fopen (filename, mode);
358 /* No error if non-ex input file */
359 if (infile == NULL)
361 perror (filename);
362 return 0;
365 /* Reset extension to be able to detect duplicate files. */
366 filename[strlen (filename) - 1] = extension;
368 c = '\n';
369 while (!feof (infile))
371 if (c != '\n' && c != '\r')
373 c = getc (infile);
374 continue;
376 c = getc (infile);
377 if (c == ' ')
379 while (c == ' ')
380 c = getc (infile);
381 if (c != 'D')
382 continue;
383 c = getc (infile);
384 if (c != 'E')
385 continue;
386 c = getc (infile);
387 if (c != 'F')
388 continue;
389 c = getc (infile);
390 if (c != 'V')
391 continue;
392 c = getc (infile);
393 if (c != 'A')
394 continue;
395 c = getc (infile);
396 if (c != 'R')
397 continue;
398 c = getc (infile);
399 if (c != '_')
400 continue;
402 defvarflag = 1;
403 defunflag = 0;
405 c = getc (infile);
406 defvarperbufferflag = (c == 'P');
408 c = getc (infile);
410 else if (c == 'D')
412 c = getc (infile);
413 if (c != 'E')
414 continue;
415 c = getc (infile);
416 if (c != 'F')
417 continue;
418 c = getc (infile);
419 defunflag = c == 'U';
420 defvarflag = 0;
422 else continue;
424 while (c != '(')
426 if (c < 0)
427 goto eof;
428 c = getc (infile);
431 c = getc (infile);
432 if (c != '"')
433 continue;
434 c = read_c_string (infile, -1);
436 if (defunflag)
437 commas = 5;
438 else if (defvarperbufferflag)
439 commas = 2;
440 else if (defvarflag)
441 commas = 1;
442 else /* For DEFSIMPLE and DEFPRED */
443 commas = 2;
445 while (commas)
447 if (c == ',')
449 commas--;
450 if (defunflag && (commas == 1 || commas == 2))
453 c = getc (infile);
454 while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
455 if (c < 0)
456 goto eof;
457 ungetc (c, infile);
458 if (commas == 2) /* pick up minargs */
459 fscanf (infile, "%d", &minargs);
460 else /* pick up maxargs */
461 if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
462 maxargs = -1;
463 else
464 fscanf (infile, "%d", &maxargs);
467 if (c < 0)
468 goto eof;
469 c = getc (infile);
471 while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
472 c = getc (infile);
473 if (c == '"')
474 c = read_c_string (infile, 0);
475 while (c != ',')
476 c = getc (infile);
477 c = getc (infile);
478 while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
479 c = getc (infile);
481 if (c == '"')
483 putc (037, outfile);
484 putc (defvarflag ? 'V' : 'F', outfile);
485 fprintf (outfile, "%s\n", buf);
486 c = read_c_string (infile, 1);
488 /* If this is a defun, find the arguments and print them. If
489 this function takes MANY or UNEVALLED args, then the C source
490 won't give the names of the arguments, so we shouldn't bother
491 trying to find them. */
492 if (defunflag && maxargs != -1)
494 char argbuf[1024], *p = argbuf;
495 while (c != ')')
497 if (c < 0)
498 goto eof;
499 c = getc (infile);
501 /* Skip into arguments. */
502 while (c != '(')
504 if (c < 0)
505 goto eof;
506 c = getc (infile);
508 /* Copy arguments into ARGBUF. */
509 *p++ = c;
511 *p++ = c = getc (infile);
512 while (c != ')');
513 *p = '\0';
514 /* Output them. */
515 fprintf (outfile, "\n\n");
516 write_c_args (outfile, buf, argbuf, minargs, maxargs);
520 eof:
521 fclose (infile);
522 return 0;
525 /* Read a file of Lisp code, compiled or interpreted.
526 Looks for
527 (defun NAME ARGS DOCSTRING ...)
528 (defmacro NAME ARGS DOCSTRING ...)
529 (defsubst NAME ARGS DOCSTRING ...)
530 (autoload (quote NAME) FILE DOCSTRING ...)
531 (defvar NAME VALUE DOCSTRING)
532 (defconst NAME VALUE DOCSTRING)
533 (fset (quote NAME) (make-byte-code ... DOCSTRING ...))
534 (fset (quote NAME) #[... DOCSTRING ...])
535 (defalias (quote NAME) #[... DOCSTRING ...])
536 (custom-declare-variable (quote NAME) VALUE DOCSTRING ...)
537 starting in column zero.
538 (quote NAME) may appear as 'NAME as well.
540 We also look for #@LENGTH CONTENTS^_ at the beginning of the line.
541 When we find that, we save it for the following defining-form,
542 and we use that instead of reading a doc string within that defining-form.
544 For defvar, defconst, and fset we skip to the docstring with a kludgy
545 formatting convention: all docstrings must appear on the same line as the
546 initial open-paren (the one in column zero) and must contain a backslash
547 and a newline immediately after the initial double-quote. No newlines
548 must appear between the beginning of the form and the first double-quote.
549 For defun, defmacro, and autoload, we know how to skip over the
550 arglist, but the doc string must still have a backslash and newline
551 immediately after the double quote.
552 The only source files that must follow this convention are preloaded
553 uncompiled ones like loaddefs.el and bindings.el; aside
554 from that, it is always the .elc file that we look at, and they are no
555 problem because byte-compiler output follows this convention.
556 The NAME and DOCSTRING are output.
557 NAME is preceded by `F' for a function or `V' for a variable.
558 An entry is output only if DOCSTRING has \ newline just after the opening "
561 void
562 skip_white (infile)
563 FILE *infile;
565 char c = ' ';
566 while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
567 c = getc (infile);
568 ungetc (c, infile);
571 void
572 read_lisp_symbol (infile, buffer)
573 FILE *infile;
574 char *buffer;
576 char c;
577 char *fillp = buffer;
579 skip_white (infile);
580 while (1)
582 c = getc (infile);
583 if (c == '\\')
584 *(++fillp) = getc (infile);
585 else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')')
587 ungetc (c, infile);
588 *fillp = 0;
589 break;
591 else
592 *fillp++ = c;
595 if (! buffer[0])
596 fprintf (stderr, "## expected a symbol, got '%c'\n", c);
598 skip_white (infile);
602 scan_lisp_file (filename, mode)
603 char *filename, *mode;
605 FILE *infile;
606 register int c;
607 char *saved_string = 0;
609 infile = fopen (filename, mode);
610 if (infile == NULL)
612 perror (filename);
613 return 0; /* No error */
616 c = '\n';
617 while (!feof (infile))
619 char buffer[BUFSIZ];
620 char type;
622 /* If not at end of line, skip till we get to one. */
623 if (c != '\n' && c != '\r')
625 c = getc (infile);
626 continue;
628 /* Skip the line break. */
629 while (c == '\n' || c == '\r')
630 c = getc (infile);
631 /* Detect a dynamic doc string and save it for the next expression. */
632 if (c == '#')
634 c = getc (infile);
635 if (c == '@')
637 int length = 0;
638 int i;
640 /* Read the length. */
641 while ((c = getc (infile),
642 c >= '0' && c <= '9'))
644 length *= 10;
645 length += c - '0';
648 /* The next character is a space that is counted in the length
649 but not part of the doc string.
650 We already read it, so just ignore it. */
651 length--;
653 /* Read in the contents. */
654 if (saved_string != 0)
655 free (saved_string);
656 saved_string = (char *) malloc (length);
657 for (i = 0; i < length; i++)
658 saved_string[i] = getc (infile);
659 /* The last character is a ^_.
660 That is needed in the .elc file
661 but it is redundant in DOC. So get rid of it here. */
662 saved_string[length - 1] = 0;
663 /* Skip the line break. */
664 while (c == '\n' && c == '\r')
665 c = getc (infile);
666 /* Skip the following line. */
667 while (c != '\n' && c != '\r')
668 c = getc (infile);
670 continue;
673 if (c != '(')
674 continue;
676 read_lisp_symbol (infile, buffer);
678 if (! strcmp (buffer, "defun")
679 || ! strcmp (buffer, "defmacro")
680 || ! strcmp (buffer, "defsubst"))
682 type = 'F';
683 read_lisp_symbol (infile, buffer);
685 /* Skip the arguments: either "nil" or a list in parens */
687 c = getc (infile);
688 if (c == 'n') /* nil */
690 if ((c = getc (infile)) != 'i'
691 || (c = getc (infile)) != 'l')
693 fprintf (stderr, "## unparsable arglist in %s (%s)\n",
694 buffer, filename);
695 continue;
698 else if (c != '(')
700 fprintf (stderr, "## unparsable arglist in %s (%s)\n",
701 buffer, filename);
702 continue;
704 else
705 while (c != ')')
706 c = getc (infile);
707 skip_white (infile);
709 /* If the next three characters aren't `dquote bslash newline'
710 then we're not reading a docstring.
712 if ((c = getc (infile)) != '"'
713 || (c = getc (infile)) != '\\'
714 || ((c = getc (infile)) != '\n' && c != '\r'))
716 #ifdef DEBUG
717 fprintf (stderr, "## non-docstring in %s (%s)\n",
718 buffer, filename);
719 #endif
720 continue;
724 else if (! strcmp (buffer, "defvar")
725 || ! strcmp (buffer, "defconst"))
727 char c1 = 0, c2 = 0;
728 type = 'V';
729 read_lisp_symbol (infile, buffer);
731 if (saved_string == 0)
734 /* Skip until the end of line; remember two previous chars. */
735 while (c != '\n' && c != '\r' && c >= 0)
737 c2 = c1;
738 c1 = c;
739 c = getc (infile);
742 /* If two previous characters were " and \,
743 this is a doc string. Otherwise, there is none. */
744 if (c2 != '"' || c1 != '\\')
746 #ifdef DEBUG
747 fprintf (stderr, "## non-docstring in %s (%s)\n",
748 buffer, filename);
749 #endif
750 continue;
755 else if (! strcmp (buffer, "custom-declare-variable"))
757 char c1 = 0, c2 = 0;
758 type = 'V';
760 c = getc (infile);
761 if (c == '\'')
762 read_lisp_symbol (infile, buffer);
763 else
765 if (c != '(')
767 fprintf (stderr,
768 "## unparsable name in custom-declare-variable in %s\n",
769 filename);
770 continue;
772 read_lisp_symbol (infile, buffer);
773 if (strcmp (buffer, "quote"))
775 fprintf (stderr,
776 "## unparsable name in custom-declare-variable in %s\n",
777 filename);
778 continue;
780 read_lisp_symbol (infile, buffer);
781 c = getc (infile);
782 if (c != ')')
784 fprintf (stderr,
785 "## unparsable quoted name in custom-declare-variable in %s\n",
786 filename);
787 continue;
791 if (saved_string == 0)
793 /* Skip to end of line; remember the two previous chars. */
794 while (c != '\n' && c != '\r' && c >= 0)
796 c2 = c1;
797 c1 = c;
798 c = getc (infile);
801 /* If two previous characters were " and \,
802 this is a doc string. Otherwise, there is none. */
803 if (c2 != '"' || c1 != '\\')
805 #ifdef DEBUG
806 fprintf (stderr, "## non-docstring in %s (%s)\n",
807 buffer, filename);
808 #endif
809 continue;
814 else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias"))
816 char c1 = 0, c2 = 0;
817 type = 'F';
819 c = getc (infile);
820 if (c == '\'')
821 read_lisp_symbol (infile, buffer);
822 else
824 if (c != '(')
826 fprintf (stderr, "## unparsable name in fset in %s\n",
827 filename);
828 continue;
830 read_lisp_symbol (infile, buffer);
831 if (strcmp (buffer, "quote"))
833 fprintf (stderr, "## unparsable name in fset in %s\n",
834 filename);
835 continue;
837 read_lisp_symbol (infile, buffer);
838 c = getc (infile);
839 if (c != ')')
841 fprintf (stderr,
842 "## unparsable quoted name in fset in %s\n",
843 filename);
844 continue;
848 if (saved_string == 0)
850 /* Skip to end of line; remember the two previous chars. */
851 while (c != '\n' && c != '\r' && c >= 0)
853 c2 = c1;
854 c1 = c;
855 c = getc (infile);
858 /* If two previous characters were " and \,
859 this is a doc string. Otherwise, there is none. */
860 if (c2 != '"' || c1 != '\\')
862 #ifdef DEBUG
863 fprintf (stderr, "## non-docstring in %s (%s)\n",
864 buffer, filename);
865 #endif
866 continue;
871 else if (! strcmp (buffer, "autoload"))
873 type = 'F';
874 c = getc (infile);
875 if (c == '\'')
876 read_lisp_symbol (infile, buffer);
877 else
879 if (c != '(')
881 fprintf (stderr, "## unparsable name in autoload in %s\n",
882 filename);
883 continue;
885 read_lisp_symbol (infile, buffer);
886 if (strcmp (buffer, "quote"))
888 fprintf (stderr, "## unparsable name in autoload in %s\n",
889 filename);
890 continue;
892 read_lisp_symbol (infile, buffer);
893 c = getc (infile);
894 if (c != ')')
896 fprintf (stderr,
897 "## unparsable quoted name in autoload in %s\n",
898 filename);
899 continue;
902 skip_white (infile);
903 if ((c = getc (infile)) != '\"')
905 fprintf (stderr, "## autoload of %s unparsable (%s)\n",
906 buffer, filename);
907 continue;
909 read_c_string (infile, 0);
910 skip_white (infile);
912 if (saved_string == 0)
914 /* If the next three characters aren't `dquote bslash newline'
915 then we're not reading a docstring. */
916 if ((c = getc (infile)) != '"'
917 || (c = getc (infile)) != '\\'
918 || ((c = getc (infile)) != '\n' && c != '\r'))
920 #ifdef DEBUG
921 fprintf (stderr, "## non-docstring in %s (%s)\n",
922 buffer, filename);
923 #endif
924 continue;
929 #ifdef DEBUG
930 else if (! strcmp (buffer, "if")
931 || ! strcmp (buffer, "byte-code"))
933 #endif
935 else
937 #ifdef DEBUG
938 fprintf (stderr, "## unrecognised top-level form, %s (%s)\n",
939 buffer, filename);
940 #endif
941 continue;
944 /* At this point, we should either use the previous
945 dynamic doc string in saved_string
946 or gobble a doc string from the input file.
948 In the latter case, the opening quote (and leading
949 backslash-newline) have already been read. */
951 putc (037, outfile);
952 putc (type, outfile);
953 fprintf (outfile, "%s\n", buffer);
954 if (saved_string)
956 fputs (saved_string, outfile);
957 /* Don't use one dynamic doc string twice. */
958 free (saved_string);
959 saved_string = 0;
961 else
962 read_c_string (infile, 1);
964 fclose (infile);
965 return 0;