9c889b64226e726e88917e98f5024b5509022059
[pspp-builds.git] / src / casefile.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "casefile.h"
22 #include <assert.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include "alloc.h"
30 #include "error.h"
31 #include "misc.h"
32 #include "var.h"
33 #include "workspace.h"
34
35 #define IO_BUF_SIZE 8192
36
37 /* A casefile is a sequentially accessible array of immutable
38    cases.  It may be stored in memory or on disk as workspace
39    allows.  Cases may be appended to the end of the file.  Cases
40    may be read sequentially starting from the beginning of the
41    file.  Once any cases have been read, no more cases may be
42    appended.  The entire file is discarded at once. */
43
44 /* A casefile. */
45 struct casefile 
46   {
47     /* Basic data. */
48     struct casefile *next, *prev;       /* Next, prev in global list. */
49     size_t case_size;                   /* Case size in bytes. */
50     size_t case_list_size;              /* Bytes to allocate for case_lists. */
51     unsigned long case_cnt;             /* Number of cases stored. */
52     enum { MEMORY, DISK } storage;      /* Where cases are stored. */
53     enum { WRITE, READ } mode;          /* Is writing or reading allowed? */
54     struct casereader *readers;         /* List of our readers. */
55
56     /* Memory storage. */
57     struct case_list *head, *tail;      /* Beginning, end of list of cases. */
58
59     /* Disk storage. */
60     int fd;                             /* File descriptor, -1 if none. */
61     char *filename;                     /* Filename. */
62     unsigned char *buffer;              /* I/O buffer, NULL if none. */
63     size_t buffer_used;                 /* Number of bytes used in buffer. */
64     size_t buffer_size;                 /* Buffer size in bytes. */
65   };
66
67 /* For reading out the casing in a casefile. */
68 struct casereader 
69   {
70     struct casereader *next, *prev;     /* Next, prev in casefile's list. */
71     struct casefile *cf;                /* Our casefile. */
72     unsigned long case_idx;             /* Case number of current case. */
73
74     /* Memory storage. */
75     struct case_list *cur;              /* Current case. */
76
77     /* Disk storage. */
78     int fd;                             /* File descriptor. */
79     unsigned char *buffer;              /* I/O buffer. */
80     size_t buffer_pos;                  /* Byte offset of buffer position. */
81   };
82
83 struct casefile *casefiles;
84
85 static void register_atexit (void);
86 static void exit_handler (void);
87
88 static void reader_open_file (struct casereader *reader);
89 static void write_case_to_disk (struct casefile *cf, const struct ccase *c);
90 static void flush_buffer (struct casefile *cf);
91 static void fill_buffer (struct casereader *reader);
92
93 static int safe_open (const char *filename, int flags);
94 static int safe_close (int fd);
95 static int full_read (int fd, char *buffer, size_t size);
96 static int full_write (int fd, const char *buffer, size_t size);
97
98 /* Creates and returns a casefile to store cases of CASE_SIZE bytes each. */
99 struct casefile *
100 casefile_create (size_t case_size) 
101 {
102   struct casefile *cf = xmalloc (sizeof *cf);
103   cf->next = casefiles;
104   cf->prev = NULL;
105   if (cf->next != NULL)
106     cf->next->prev = cf;
107   casefiles = cf;
108   cf->case_size = case_size;
109   cf->case_list_size = sizeof *cf->head + case_size - sizeof *cf->head->c.data;
110   cf->case_cnt = 0;
111   cf->storage = MEMORY;
112   cf->mode = WRITE;
113   cf->readers = NULL;
114   cf->head = cf->tail = NULL;
115   cf->fd = -1;
116   cf->filename = NULL;
117   cf->buffer = NULL;
118   cf->buffer_size = ROUND_UP (case_size, IO_BUF_SIZE);
119   if (case_size > 0 && cf->buffer_size % case_size > 512)
120     cf->buffer_size = case_size;
121   cf->buffer_used = 0;
122   register_atexit ();
123   return cf;
124 }
125
126 /* Destroys casefile CF. */
127 void
128 casefile_destroy (struct casefile *cf) 
129 {
130   if (cf != NULL) 
131     {
132       struct case_list *iter, *next;
133
134       if (cf->next != NULL)
135         cf->next->prev = cf->prev;
136       if (cf->prev != NULL)
137         cf->prev->next = cf->next;
138       if (casefiles == cf)
139         casefiles = cf->next;
140
141       while (cf->readers != NULL) 
142         casereader_destroy (cf->readers);
143
144       for (iter = cf->head; iter != NULL; iter = next) 
145         {
146           next = iter->next;
147           workspace_free (iter, cf->case_list_size);
148         }
149
150       if (cf->fd != -1)
151         safe_close (cf->fd);
152           
153       if (cf->filename != NULL && remove (cf->filename) == -1) 
154         msg (ME, _("%s: Removing temporary file: %s."),
155              cf->filename, strerror (errno));
156
157       free (cf->buffer);
158
159       free (cf);
160     }
161 }
162
163 /* Returns nonzero only if casefile CF is stored in memory (instead of on
164    disk). */
165 int
166 casefile_in_core (const struct casefile *cf) 
167 {
168   assert (cf != NULL);
169
170   return cf->storage == MEMORY;
171 }
172
173 /* Returns the number of bytes in a case for CF. */
174 size_t
175 casefile_get_case_size (const struct casefile *cf) 
176 {
177   assert (cf != NULL);
178
179   return cf->case_size;
180 }
181
182 /* Returns the number of cases in casefile CF. */
183 unsigned long
184 casefile_get_case_cnt (const struct casefile *cf) 
185 {
186   assert (cf != NULL);
187
188   return cf->case_cnt;
189 }
190
191 /* Appends case C to casefile CF.  Not valid after any reader for CF has been
192    created. */
193 void
194 casefile_append (struct casefile *cf, const struct ccase *c) 
195 {
196   assert (cf != NULL);
197   assert (c != NULL);
198   assert (cf->mode == WRITE);
199
200   cf->case_cnt++;
201
202   /* Try memory first. */
203   if (cf->storage == MEMORY) 
204     {
205       struct case_list *new_case;
206
207       new_case = workspace_malloc (cf->case_list_size);
208       if (new_case != NULL) 
209         {
210           memcpy (&new_case->c, c, cf->case_size);
211           new_case->next = NULL;
212           if (cf->head != NULL)
213             cf->tail->next = new_case;
214           else
215             cf->head = new_case;
216           cf->tail = new_case;
217         }
218       else
219         {
220           casefile_to_disk (cf);
221           assert (cf->storage == DISK);
222           write_case_to_disk (cf, c);
223         }
224     }
225   else
226     write_case_to_disk (cf, c);
227 }
228
229 /* Writes case C to casefile CF's disk buffer, first flushing the buffer to
230    disk if it would otherwise overflow. */
231 static void
232 write_case_to_disk (struct casefile *cf, const struct ccase *c) 
233 {
234   memcpy (cf->buffer + cf->buffer_used, c->data, cf->case_size);
235   cf->buffer_used += cf->case_size;
236   if (cf->buffer_used + cf->case_size > cf->buffer_size)
237     flush_buffer (cf);
238
239 }
240
241 static void
242 flush_buffer (struct casefile *cf) 
243 {
244   if (cf->buffer_used > 0) 
245     {
246       if (!full_write (cf->fd, cf->buffer, cf->buffer_size)) 
247         msg (FE, _("Error writing temporary file: %s."), strerror (errno));
248
249       cf->buffer_used = 0;
250     } 
251 }
252
253 /* Creates a temporary file and stores its name in *FILENAME and
254    a file descriptor for it in *FD.  Returns success.  Caller is
255    responsible for freeing *FILENAME. */
256 static int
257 make_temp_file (int *fd, char **filename)
258 {
259   const char *parent_dir;
260
261   assert (filename != NULL);
262   assert (fd != NULL);
263
264   if (getenv ("TMPDIR") != NULL)
265     parent_dir = getenv ("TMPDIR");
266   else
267     parent_dir = P_tmpdir;
268
269   *filename = xmalloc (strlen (parent_dir) + 32);
270   sprintf (*filename, "%s%cpsppXXXXXX", parent_dir, DIR_SEPARATOR);
271   *fd = mkstemp (*filename);
272   if (*fd < 0)
273     {
274       msg (FE, _("%s: Creating temporary file: %s."),
275            *filename, strerror (errno));
276       free (*filename);
277       *filename = NULL;
278       return 0;
279     }
280   return 1;
281 }
282
283 /* If CF is currently stored in memory, writes it to disk.  Readers, if any,
284    retain their current positions. */
285 void
286 casefile_to_disk (struct casefile *cf) 
287 {
288   struct case_list *iter, *next;
289   struct casereader *reader;
290   
291   assert (cf != NULL);
292   
293   if (cf->storage == MEMORY)
294     {
295       assert (cf->filename == NULL);
296       assert (cf->fd == -1);
297       assert (cf->buffer_used == 0);
298
299       cf->storage = DISK;
300       if (!make_temp_file (&cf->fd, &cf->filename))
301         err_failure ();
302 #if HAVE_POSIX_FADVISE
303       posix_fadvise (cf->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
304 #endif
305       cf->buffer = xmalloc (cf->buffer_size);
306       memset (cf->buffer, 0, cf->buffer_size);
307
308       for (iter = cf->head; iter != NULL; iter = next) 
309         {
310           next = iter->next;
311           write_case_to_disk (cf, &iter->c);
312           workspace_free (iter, cf->case_list_size);
313         }
314       flush_buffer (cf);
315       cf->head = cf->tail = NULL;
316
317       for (reader = cf->readers; reader != NULL; reader = reader->next)
318         reader_open_file (reader);
319     }
320 }
321
322 /* Merges lists A and B into a single list, which is returned.  Cases are
323    compared according to comparison function COMPARE, which receives auxiliary
324    data AUX. */
325 static struct case_list *
326 merge (struct case_list *a, struct case_list *b,
327        int (*compare) (const struct ccase *,
328                        const struct ccase *, void *aux),
329        void *aux) 
330 {
331   struct case_list head;
332   struct case_list *tail = &head;
333
334   while (a != NULL && b != NULL)
335     if (compare (&a->c, &b->c, aux) < 0) 
336       {
337         tail->next = a;
338         tail = a;
339         a = a->next;
340       }
341     else 
342       {
343         tail->next = b;
344         tail = b;
345         b = b->next;
346       }
347
348   tail->next = a == NULL ? b : a;
349
350   return head.next;
351 }
352
353 /* Sorts the list beginning at FIRST, returning the new first case.  Cases are
354    compared according to comparison function COMPARE, which receives auxiliary
355    data AUX. */
356 static struct case_list *
357 merge_sort (struct case_list *first,
358             int (*compare) (const struct ccase *,
359                             const struct ccase *, void *aux),
360             void *aux) 
361 {
362   /* FIXME: we should use a "natural" merge sort to take
363      advantage of the natural order of the data. */
364   struct case_list *middle, *last, *tmp;
365
366   /* A list of zero or one elements is already sorted. */
367   if (first == NULL || first->next == NULL)
368     return first;
369
370   middle = first;
371   last = first->next;
372   while (last != NULL && last->next != NULL) 
373     {
374       middle = middle->next;
375       last = last->next->next;
376     }
377   tmp = middle;
378   middle = middle->next;
379   tmp->next = NULL;
380   return merge (merge_sort (first, compare, aux),
381                 merge_sort (middle, compare, aux),
382                 compare, aux);
383 }
384
385 /* Tries to sort casefile CF according to comparison function
386    COMPARE, which is passes auxiliary data AUX.  If successful,
387    returns nonzero.  Currently only sorting of in-memory
388    casefiles is implemented. */
389 int
390 casefile_sort (struct casefile *cf,
391                int (*compare) (const struct ccase *,
392                                const struct ccase *, void *aux),
393                void *aux)
394 {
395   assert (cf != NULL);
396   assert (compare != NULL);
397
398   cf->mode = WRITE;
399
400   if (cf->case_cnt < 2)
401     return 1;
402   else if (cf->storage == DISK)
403     return 0;
404   else 
405     {
406       cf->head = cf->tail = merge_sort (cf->head, compare, aux);
407       while (cf->tail->next != NULL)
408         cf->tail = cf->tail->next;
409
410       return 1; 
411     }
412 }
413
414 /* Creates and returns a casereader for CF.  A casereader can be used to
415    sequentially read the cases in a casefile. */
416 struct casereader *
417 casefile_get_reader (const struct casefile *cf_) 
418 {
419   struct casefile *cf = (struct casefile *) cf_;
420   struct casereader *reader;
421
422   /* Flush the buffer to disk if it's not empty. */
423   if (cf->mode == WRITE && cf->storage == DISK)
424     flush_buffer (cf);
425   
426   cf->mode = READ;
427
428   reader = xmalloc (sizeof *reader);
429   reader->cf = cf;
430   reader->next = cf->readers;
431   if (cf->readers != NULL)
432     reader->next->prev = reader;
433   reader->prev = NULL;
434   cf->readers = reader;
435   reader->case_idx = 0;
436   reader->cur = NULL;
437   reader->fd = -1;
438   reader->buffer = NULL;
439   reader->buffer_pos = 0;
440
441   if (reader->cf->storage == MEMORY) 
442     reader->cur = cf->head;
443   else
444     reader_open_file (reader);
445
446   return reader;
447 }
448
449 /* Opens a disk file for READER and seeks to the current position as indicated
450    by case_idx.  Normally the current position is the beginning of the file,
451    but casefile_to_disk may cause the file to be opened at a different
452    position. */
453 static void
454 reader_open_file (struct casereader *reader) 
455 {
456   size_t buffer_case_cnt;
457   off_t file_ofs;
458
459   if (reader->case_idx >= reader->cf->case_cnt)
460     return;
461
462   if (reader->cf->fd != -1) 
463     {
464       reader->fd = reader->cf->fd;
465       reader->cf->fd = -1;
466     }
467   else 
468     {
469       reader->fd = safe_open (reader->cf->filename, O_RDONLY);
470       if (reader->fd < 0)
471         msg (FE, _("%s: Opening temporary file: %s."),
472              reader->cf->filename, strerror (errno));
473     }
474
475   if (reader->cf->buffer != NULL) 
476     {
477       reader->buffer = reader->cf->buffer;
478       reader->cf->buffer = NULL; 
479     }
480   else 
481     {
482       reader->buffer = xmalloc (reader->cf->buffer_size);
483       memset (reader->buffer, 0, reader->cf->buffer_size); 
484     }
485
486   if (reader->cf->case_size != 0) 
487     {
488       buffer_case_cnt = reader->cf->buffer_size / reader->cf->case_size;
489       file_ofs = ((off_t) reader->case_idx
490                   / buffer_case_cnt * reader->cf->buffer_size);
491       reader->buffer_pos = (reader->case_idx % buffer_case_cnt
492                             * reader->cf->case_size);
493     }
494   else 
495     file_ofs = 0;
496 #if HAVE_POSIX_FADVISE
497   posix_fadvise (reader->fd, file_ofs, 0, POSIX_FADV_SEQUENTIAL);
498 #endif
499   if (lseek (reader->fd, file_ofs, SEEK_SET) != file_ofs)
500     msg (FE, _("%s: Seeking temporary file: %s."),
501          reader->cf->filename, strerror (errno));
502
503   if (reader->cf->case_cnt > 0 && reader->cf->case_size > 0)
504     fill_buffer (reader);
505 }
506
507 /* Fills READER's buffer by reading a block from disk. */
508 static void
509 fill_buffer (struct casereader *reader)
510 {
511   int retval = full_read (reader->fd, reader->buffer, reader->cf->buffer_size);
512   if (retval < 0)
513     msg (FE, _("%s: Reading temporary file: %s."),
514          reader->cf->filename, strerror (errno));
515   else if (retval != reader->cf->buffer_size)
516     msg (FE, _("%s: Temporary file ended unexpectedly."),
517          reader->cf->filename); 
518 }
519
520 int
521 casereader_read (struct casereader *reader, const struct ccase **c) 
522 {
523   assert (reader != NULL);
524   
525   if (reader->case_idx >= reader->cf->case_cnt) 
526     {
527       *c = NULL;
528       return 0;
529     }
530
531   reader->case_idx++;
532   if (reader->cf->storage == MEMORY) 
533     {
534       assert (reader->cur != NULL);
535       *c = &reader->cur->c;
536       reader->cur = reader->cur->next;
537       return 1;
538     }
539   else 
540     {
541       if (reader->buffer_pos + reader->cf->case_size > reader->cf->buffer_size)
542         {
543           fill_buffer (reader);
544           reader->buffer_pos = 0;
545         }
546
547       *c = (struct ccase *) (reader->buffer + reader->buffer_pos);
548       reader->buffer_pos += reader->cf->case_size;
549       return 1;
550     }
551 }
552
553 void
554 casereader_destroy (struct casereader *reader)
555 {
556   assert (reader != NULL);
557
558   if (reader->next != NULL)
559     reader->next->prev = reader->prev;
560   if (reader->prev != NULL)
561     reader->prev->next = reader->next;
562   if (reader->cf->readers == reader)
563     reader->cf->readers = reader->next;
564
565   if (reader->cf->buffer == NULL)
566     reader->cf->buffer = reader->buffer;
567   else
568     free (reader->buffer);
569
570   if (reader->cf->fd == -1)
571     reader->cf->fd = reader->fd;
572   else
573     safe_close (reader->fd);
574
575   free (reader);
576 }
577
578 static int
579 safe_open (const char *filename, int flags) 
580 {
581   int fd;
582
583   do 
584     {
585       fd = open (filename, flags);
586     }
587   while (fd == -1 && errno == EINTR);
588
589   return fd;
590 }
591
592 static int safe_close (int fd) 
593 {
594   int retval;
595
596   do 
597     {
598       retval = close (fd);
599     }
600   while (retval == -1 && errno == EINTR);
601
602   return retval;
603 }
604
605 static int
606 full_read (int fd, char *buffer, size_t size) 
607 {
608   size_t bytes_read = 0;
609   
610   while (bytes_read < size)
611     {
612       int retval = read (fd, buffer + bytes_read, size - bytes_read);
613       if (retval > 0) 
614         bytes_read += retval; 
615       else if (retval == 0) 
616         return bytes_read;
617       else if (errno != EINTR)
618         return -1;
619     }
620
621   return bytes_read;
622 }
623
624 static int
625 full_write (int fd, const char *buffer, size_t size) 
626 {
627   size_t bytes_written = 0;
628   
629   while (bytes_written < size)
630     {
631       int retval = write (fd, buffer + bytes_written, size - bytes_written);
632       if (retval >= 0) 
633         bytes_written += retval; 
634       else if (errno != EINTR)
635         return -1;
636     }
637
638   return bytes_written;
639 }
640
641 static void
642 register_atexit (void) 
643 {
644   static int registered = 0;
645   if (!registered) 
646     {
647       registered = 1;
648       atexit (exit_handler);
649     }
650 }
651
652 static void
653 exit_handler (void) 
654 {
655   while (casefiles != NULL)
656     casefile_destroy (casefiles);
657 }
658 \f
659 #include <stdarg.h>
660 #include "command.h"
661 #include "random.h"
662 #include "lexer.h"
663
664 static void test_casefile (int pattern, size_t case_size, size_t case_cnt);
665 static struct ccase *get_random_case (size_t case_size, size_t case_idx);
666 static void write_random_case (struct casefile *cf, size_t case_idx);
667 static void read_and_verify_random_case (struct casefile *cf,
668                                          struct casereader *reader,
669                                          size_t case_idx);
670 static void fail_test (const char *message, ...);
671
672 int
673 cmd_debug_casefile (void) 
674 {
675   static const size_t sizes[] =
676     {
677       1, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17, 31, 55, 73,
678       100, 137, 257, 521, 1031, 2053
679     };
680   int size_max;
681   int case_max;
682   int pattern;
683
684   size_max = sizeof sizes / sizeof *sizes;
685   if (lex_match_id ("SMALL")) 
686     {
687       size_max -= 4;
688       case_max = 511; 
689     }
690   else
691     case_max = 4095;
692   if (token != '.')
693     return lex_end_of_command ();
694     
695   for (pattern = 0; pattern < 5; pattern++) 
696     {
697       const size_t *size;
698
699       for (size = sizes; size < sizes + size_max; size++) 
700         {
701           size_t case_cnt;
702
703           for (case_cnt = 0; case_cnt <= case_max;
704                case_cnt = (case_cnt * 2) + 1)
705             test_casefile (pattern, *size * sizeof (union value), case_cnt);
706         }
707     }
708   printf ("Casefile tests succeeded.\n");
709   return CMD_SUCCESS;
710 }
711
712 static void
713 test_casefile (int pattern, size_t case_size, size_t case_cnt) 
714 {
715   int zero = 0;
716   struct casefile *cf;
717   struct casereader *r1, *r2;
718   const struct ccase *c;
719   struct rng *rng;
720   size_t i, j;
721
722   rng = rng_create ();
723   rng_seed (rng, &zero, sizeof zero);
724   cf = casefile_create (case_size);
725   for (i = 0; i < case_cnt; i++)
726     write_random_case (cf, i);
727   r1 = casefile_get_reader (cf);
728   r2 = casefile_get_reader (cf);
729   switch (pattern) 
730     {
731     case 0:
732       for (i = 0; i < case_cnt; i++) 
733         {
734           read_and_verify_random_case (cf, r1, i);
735           read_and_verify_random_case (cf, r2, i);
736         } 
737       break;
738     case 1:
739       for (i = 0; i < case_cnt; i++)
740         read_and_verify_random_case (cf, r1, i);
741       for (i = 0; i < case_cnt; i++) 
742         read_and_verify_random_case (cf, r2, i);
743       break;
744     case 2:
745     case 3:
746     case 4:
747       for (i = j = 0; i < case_cnt; i++) 
748         {
749           read_and_verify_random_case (cf, r1, i);
750           if (rng_get_int (rng) % pattern == 0)
751             read_and_verify_random_case (cf, r2, j++);
752           if (i == case_cnt / 2)
753             casefile_to_disk (cf);
754         }
755       for (; j < case_cnt; j++) 
756         read_and_verify_random_case (cf, r2, j);
757       break;
758     }
759   if (casereader_read (r1, &c))
760     fail_test ("Casereader 1 not at end of file.");
761   if (casereader_read (r2, &c))
762     fail_test ("Casereader 2 not at end of file.");
763   if (pattern != 1)
764     casereader_destroy (r1);
765   if (pattern != 2)
766     casereader_destroy (r2);
767   casefile_destroy (cf);
768   rng_destroy (rng);
769 }
770
771 static struct ccase *
772 get_random_case (size_t case_size, size_t case_idx) 
773 {
774   struct ccase *c = xmalloc (case_size);
775   memset (c, case_idx % 257, case_size);
776   return c;
777 }
778
779 static void
780 write_random_case (struct casefile *cf, size_t case_idx) 
781 {
782   struct ccase *c = get_random_case (casefile_get_case_size (cf), case_idx);
783   casefile_append (cf, c);
784   free (c);
785 }
786
787 static void
788 read_and_verify_random_case (struct casefile *cf,
789                              struct casereader *reader, size_t case_idx) 
790 {
791   const struct ccase *read_case;
792   struct ccase *expected_case;
793   size_t case_size;
794
795   case_size = casefile_get_case_size (cf);
796   expected_case = get_random_case (case_size, case_idx);
797   if (!casereader_read (reader, &read_case)) 
798     fail_test ("Premature end of casefile.");
799   if (memcmp (read_case, expected_case, case_size))
800     fail_test ("Case %lu fails comparison.", (unsigned long) case_idx);
801   free (expected_case);
802 }
803
804 static void
805 fail_test (const char *message, ...) 
806 {
807   va_list args;
808
809   va_start (args, message);
810   vprintf (message, args);
811   putchar ('\n');
812   va_end (args);
813   
814   exit (1);
815 }