From: Paul Eggert Date: Sat, 6 Jul 2013 02:40:50 +0000 (-0700) Subject: Use emacs_open more consistently when opening files. X-Git-Tag: emacs-24.3.90~173^2^2~42^2~45^2~387^2~1915 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=406af475be236b874e3633b68999f6a099d47587;p=emacs.git Use emacs_open more consistently when opening files. This handles EINTR more consistently now, and makes it easier to introduce other uniform changes to file descriptor handling. * src/systdio.h: New file. * src/buffer.c (mmap_init): * cygw32.c (chdir_to_default_directory): * dispnew.c (Fopen_termscript): * emacs.c (Fdaemon_initialized): * fileio.c (Fdo_auto_save): * image.c (slurp_file, png_load_body, jpeg_load_body): * keyboard.c (Fopen_dribble_file): * lread.c (Fload): * print.c (Fredirect_debugging_output): * sysdep.c (get_up_time, procfs_ttyname, procfs_get_total_memory): * termcap.c (tgetent): * unexaix.c, unexcoff.c (unexec, adjust_lnnoptrs): * unexcw.c, unexelf.c, unexhp9k800.c, unexmacosx.c (unexec): * w32term.c (w32_initialize) [CYGWIN]: * xfaces.c (Fx_load_color_file): Use emacs_open instead of plain open, and emacs_fopen instead of plain fopen. * dispnew.c, fileio.c, image.c, keyboard.c, lread.c, print.c, sysdep.c: * xfaces.c: Include sysstdio.h rather than stdio.h, for emacs_fopen. * callproc.c (default_output_mode): New constant. (Fcall_process): Use it to call emacs_open instead of plain creat. * dispnew.c (Fopen_termscript): Fix minor race in opening termscript. * sysdep.c (emacs_open): Add commentary and don't call file name "path". (emacs_fopen): New function. * unexaix.c, unexcoff.c, unexelf.c, unexhp9k800.c, unexmacosx.c: Include , for emacs_open. * unexelf.c (fatal): Remove decl; not needed with included. --- diff --git a/src/ChangeLog b/src/ChangeLog index d42f5733ce6..0465bdcaf2e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,37 @@ 2013-07-05 Paul Eggert + Use emacs_open more consistently when opening files. + This handles EINTR more consistently now, and makes it easier + to introduce other uniform changes to file descriptor handling. + * src/systdio.h: New file. + * src/buffer.c (mmap_init): + * cygw32.c (chdir_to_default_directory): + * dispnew.c (Fopen_termscript): + * emacs.c (Fdaemon_initialized): + * fileio.c (Fdo_auto_save): + * image.c (slurp_file, png_load_body, jpeg_load_body): + * keyboard.c (Fopen_dribble_file): + * lread.c (Fload): + * print.c (Fredirect_debugging_output): + * sysdep.c (get_up_time, procfs_ttyname, procfs_get_total_memory): + * termcap.c (tgetent): + * unexaix.c, unexcoff.c (unexec, adjust_lnnoptrs): + * unexcw.c, unexelf.c, unexhp9k800.c, unexmacosx.c (unexec): + * w32term.c (w32_initialize) [CYGWIN]: + * xfaces.c (Fx_load_color_file): + Use emacs_open instead of plain open, and emacs_fopen instead of + plain fopen. + * dispnew.c, fileio.c, image.c, keyboard.c, lread.c, print.c, sysdep.c: + * xfaces.c: Include sysstdio.h rather than stdio.h, for emacs_fopen. + * callproc.c (default_output_mode): New constant. + (Fcall_process): Use it to call emacs_open instead of plain creat. + * dispnew.c (Fopen_termscript): Fix minor race in opening termscript. + * sysdep.c (emacs_open): Add commentary and don't call file name "path". + (emacs_fopen): New function. + * unexaix.c, unexcoff.c, unexelf.c, unexhp9k800.c, unexmacosx.c: + Include , for emacs_open. + * unexelf.c (fatal): Remove decl; not needed with included. + Remove duplicate #include directives. * alloc.c [GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES]: * xfaces.c: diff --git a/src/buffer.c b/src/buffer.c index 64b1bce9b00..94104ef535c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4726,7 +4726,7 @@ mmap_init (void) if (mmap_fd <= 0) { /* No anonymous mmap -- we need the file descriptor. */ - mmap_fd = open ("/dev/zero", O_RDONLY); + mmap_fd = emacs_open ("/dev/zero", O_RDONLY, 0); if (mmap_fd == -1) fatal ("Cannot open /dev/zero: %s", emacs_strerror (errno)); } diff --git a/src/callproc.c b/src/callproc.c index f0aa8222342..6de8113dc14 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -186,6 +186,12 @@ call_process_cleanup (Lisp_Object arg) return Qnil; } +#ifdef DOS_NT +static mode_t const default_output_mode = S_IREAD | S_IWRITE; +#else +static mode_t const default_output_mode = 0666; +#endif + DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0, doc: /* Call PROGRAM synchronously in separate process. The remaining arguments are optional. @@ -407,13 +413,9 @@ usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) * if (STRINGP (output_file)) { -#ifdef DOS_NT fd_output = emacs_open (SSDATA (output_file), - O_WRONLY | O_TRUNC | O_CREAT | O_TEXT, - S_IREAD | S_IWRITE); -#else /* not DOS_NT */ - fd_output = creat (SSDATA (output_file), 0666); -#endif /* not DOS_NT */ + O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, + default_output_mode); if (fd_output < 0) { output_file = DECODE_FILE (output_file); @@ -492,7 +494,8 @@ usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) * strcat (tempfile, "/"); strcat (tempfile, "detmp.XXX"); mktemp (tempfile); - outfilefd = creat (tempfile, S_IREAD | S_IWRITE); + outfilefd = emacs_open (tempfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IREAD | S_IWRITE); if (outfilefd < 0) { emacs_close (filefd); report_file_error ("Opening process output file", @@ -535,15 +538,9 @@ usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) * if (NILP (error_file)) fd_error = emacs_open (NULL_DEVICE, O_WRONLY, 0); else if (STRINGP (error_file)) - { -#ifdef DOS_NT - fd_error = emacs_open (SSDATA (error_file), - O_WRONLY | O_TRUNC | O_CREAT | O_TEXT, - S_IREAD | S_IWRITE); -#else /* not DOS_NT */ - fd_error = creat (SSDATA (error_file), 0666); -#endif /* not DOS_NT */ - } + fd_error = emacs_open (SSDATA (error_file), + O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, + default_output_mode); if (fd_error < 0) { diff --git a/src/cygw32.c b/src/cygw32.c index a7dbdaed615..bbc3a49fd88 100644 --- a/src/cygw32.c +++ b/src/cygw32.c @@ -35,7 +35,7 @@ static void chdir_to_default_directory () { Lisp_Object new_cwd; - int old_cwd_fd = open (".", O_RDONLY | O_DIRECTORY); + int old_cwd_fd = emacs_open (".", O_RDONLY | O_DIRECTORY, 0); if (old_cwd_fd == -1) error ("could not open current directory: %s", strerror (errno)); diff --git a/src/dispnew.c b/src/dispnew.c index b4ca654a8f7..31b8a1310ea 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -22,7 +22,7 @@ along with GNU Emacs. If not, see . */ #define DISPEXTERN_INLINE EXTERN_INLINE -#include +#include "sysstdio.h" #include #include "lisp.h" @@ -5605,17 +5605,17 @@ FILE = nil means just close any termscript file currently open. */) tty = CURTTY (); if (tty->termscript != 0) - { - block_input (); - fclose (tty->termscript); - unblock_input (); - } - tty->termscript = 0; + { + block_input (); + fclose (tty->termscript); + tty->termscript = 0; + unblock_input (); + } if (! NILP (file)) { file = Fexpand_file_name (file, Qnil); - tty->termscript = fopen (SSDATA (file), "w"); + tty->termscript = emacs_fopen (SSDATA (file), "w"); if (tty->termscript == 0) report_file_error ("Opening termscript", Fcons (file, Qnil)); } diff --git a/src/emacs.c b/src/emacs.c index 4b709527ff0..6ba01d1a443 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2236,7 +2236,7 @@ from the parent process and its tty file descriptors. */) error ("This function can only be called after loading the init files"); /* Get rid of stdin, stdout and stderr. */ - nfd = open ("/dev/null", O_RDWR); + nfd = emacs_open ("/dev/null", O_RDWR, 0); err |= nfd < 0; err |= dup2 (nfd, 0) < 0; err |= dup2 (nfd, 1) < 0; diff --git a/src/fileio.c b/src/fileio.c index 0ba7f85da2c..89ae89e1613 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -20,7 +20,7 @@ along with GNU Emacs. If not, see . */ #include #include #include -#include +#include "sysstdio.h" #include #include #include @@ -5618,7 +5618,7 @@ A non-nil CURRENT-ONLY argument means save only current buffer. */) UNGCPRO; } - stream = fopen (SSDATA (listfile), "w"); + stream = emacs_fopen (SSDATA (listfile), "w"); } record_unwind_protect (do_auto_save_unwind, diff --git a/src/image.c b/src/image.c index a3e103f9559..7ecd59d27b4 100644 --- a/src/image.c +++ b/src/image.c @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include -#include +#include "sysstdio.h" #include #ifdef HAVE_PNG @@ -2274,7 +2274,7 @@ x_find_image_file (Lisp_Object file) static unsigned char * slurp_file (char *file, ptrdiff_t *size) { - FILE *fp = fopen (file, "rb"); + FILE *fp = emacs_fopen (file, "rb"); unsigned char *buf = NULL; struct stat st; @@ -5723,7 +5723,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) } /* Open the image file. */ - fp = fopen (SSDATA (file), "rb"); + fp = emacs_fopen (SSDATA (file), "rb"); if (!fp) { image_error ("Cannot open image file `%s'", file, Qnil); @@ -6484,7 +6484,7 @@ jpeg_load_body (struct frame *f, struct image *img, return 0; } - fp = fopen (SSDATA (file), "rb"); + fp = emacs_fopen (SSDATA (file), "rb"); if (fp == NULL) { image_error ("Cannot open `%s'", file, Qnil); diff --git a/src/keyboard.c b/src/keyboard.c index e6094a939b9..39535f5cd71 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -23,7 +23,7 @@ along with GNU Emacs. If not, see . */ #define BLOCKINPUT_INLINE EXTERN_INLINE #define KEYBOARD_INLINE EXTERN_INLINE -#include +#include "sysstdio.h" #include "lisp.h" #include "termchar.h" @@ -10129,7 +10129,7 @@ The file will be closed when Emacs exits. */) if (!NILP (file)) { file = Fexpand_file_name (file, Qnil); - dribble = fopen (SSDATA (file), "w"); + dribble = emacs_fopen (SSDATA (file), "w"); if (dribble == 0) report_file_error ("Opening dribble", Fcons (file, Qnil)); } diff --git a/src/lread.c b/src/lread.c index 5f7b2cbdbe2..06badce12be 100644 --- a/src/lread.c +++ b/src/lread.c @@ -20,7 +20,7 @@ along with GNU Emacs. If not, see . */ #include -#include +#include "sysstdio.h" #include #include #include @@ -1297,7 +1297,7 @@ Return t if the file exists and loads successfully. */) if (fd >= 0) { emacs_close (fd); - stream = fopen (SSDATA (efound), fmode); + stream = emacs_fopen (SSDATA (efound), fmode); } else stream = NULL; diff --git a/src/print.c b/src/print.c index b1eec4c48d4..a16f3b32f46 100644 --- a/src/print.c +++ b/src/print.c @@ -20,7 +20,7 @@ along with GNU Emacs. If not, see . */ #include -#include +#include "sysstdio.h" #include "lisp.h" #include "character.h" @@ -765,7 +765,7 @@ append to existing target file. */) { file = Fexpand_file_name (file, Qnil); initial_stderr_stream = stderr; - stderr = fopen (SSDATA (file), NILP (append) ? "w" : "a"); + stderr = emacs_fopen (SSDATA (file), NILP (append) ? "w" : "a"); if (stderr == NULL) { stderr = initial_stderr_stream; diff --git a/src/sysdep.c b/src/sysdep.c index 11b2dbfd617..91e941a2050 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -22,7 +22,7 @@ along with GNU Emacs. If not, see . */ #define SYSTIME_INLINE EXTERN_INLINE #include -#include +#include "sysstdio.h" #ifdef HAVE_PWD_H #include #include @@ -2151,15 +2151,29 @@ emacs_abort (void) } #endif +/* Open FILE for Emacs use, using open flags OFLAG and mode MODE. + Do not fail merely because the open was interrupted by a signal. + Allow the user to quit. */ + int -emacs_open (const char *path, int oflag, int mode) +emacs_open (const char *file, int oflags, int mode) { - register int rtnval; + int fd; + while ((fd = open (file, oflags, mode)) < 0 && errno == EINTR) + QUIT; + return fd; +} - while ((rtnval = open (path, oflag, mode)) == -1 - && (errno == EINTR)) +/* Open FILE as a stream for Emacs use, with mode MODE. + Act like emacs_open with respect to threads, signals, and quits. */ + +FILE * +emacs_fopen (char const *file, char const *mode) +{ + FILE *fp; + while (! (fp = fopen (file, mode)) && errno == EINTR) QUIT; - return (rtnval); + return fp; } int @@ -2637,7 +2651,7 @@ get_up_time (void) EMACS_TIME up = make_emacs_time (0, 0); block_input (); - fup = fopen ("/proc/uptime", "r"); + fup = emacs_fopen ("/proc/uptime", "r"); if (fup) { @@ -2682,7 +2696,7 @@ procfs_ttyname (int rdev) char name[PATH_MAX]; block_input (); - fdev = fopen ("/proc/tty/drivers", "r"); + fdev = emacs_fopen ("/proc/tty/drivers", "r"); if (fdev) { @@ -2724,7 +2738,7 @@ procfs_get_total_memory (void) unsigned long retval = 2 * 1024 * 1024; /* default: 2GB */ block_input (); - fmem = fopen ("/proc/meminfo", "r"); + fmem = emacs_fopen ("/proc/meminfo", "r"); if (fmem) { diff --git a/src/sysstdio.h b/src/sysstdio.h new file mode 100644 index 00000000000..e9dfb696059 --- /dev/null +++ b/src/sysstdio.h @@ -0,0 +1,2 @@ +#include +extern FILE *emacs_fopen (char const *, char const *); diff --git a/src/term.c b/src/term.c index 0bcef55947a..39d143564c6 100644 --- a/src/term.c +++ b/src/term.c @@ -2479,7 +2479,7 @@ term_mouse_moveto (int x, int y) const char *name; int fd; name = (const char *) ttyname (0); - fd = open (name, O_WRONLY); + fd = emacs_open (name, O_WRONLY, 0); SOME_FUNCTION (x, y, fd); close (fd); last_mouse_x = x; diff --git a/src/termcap.c b/src/termcap.c index 99bbfce27f5..7256eef9e81 100644 --- a/src/termcap.c +++ b/src/termcap.c @@ -427,11 +427,7 @@ tgetent (char *bp, const char *name) /* Here we know we must search a file and termcap_name has its name. */ -#ifdef MSDOS - fd = open (termcap_name, O_RDONLY|O_TEXT, 0); -#else - fd = open (termcap_name, O_RDONLY, 0); -#endif + fd = emacs_open (termcap_name, O_RDONLY | O_TEXT, 0); if (fd < 0) return -1; diff --git a/src/unexaix.c b/src/unexaix.c index 44a824b8c12..204f6cf4ad3 100644 --- a/src/unexaix.c +++ b/src/unexaix.c @@ -42,6 +42,7 @@ what you give them. Help stamp out software-hoarding! */ #include #include "unexec.h" +#include "lisp.h" #define PERROR(file) report_error (file, new) #include @@ -132,11 +133,11 @@ unexec (const char *new_name, const char *a_name) { int new = -1, a_out = -1; - if (a_name && (a_out = open (a_name, O_RDONLY)) < 0) + if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0) { PERROR (a_name); } - if ((new = creat (new_name, 0666)) < 0) + if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { PERROR (new_name); } @@ -503,7 +504,7 @@ adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name) if (!lnnoptr || !f_hdr.f_symptr) return 0; - if ((new = open (new_name, O_RDWR)) < 0) + if ((new = emacs_open (new_name, O_RDWR, 0)) < 0) { PERROR (new_name); return -1; diff --git a/src/unexcoff.c b/src/unexcoff.c index 2e662a34145..e79821251ba 100644 --- a/src/unexcoff.c +++ b/src/unexcoff.c @@ -52,6 +52,7 @@ along with GNU Emacs. If not, see . */ #include #include "unexec.h" +#include "lisp.h" #define PERROR(file) report_error (file, new) @@ -486,7 +487,7 @@ adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name) #ifdef MSDOS if ((new = writedesc) < 0) #else - if ((new = open (new_name, O_RDWR)) < 0) + if ((new = emacs_open (new_name, O_RDWR, 0)) < 0) #endif { PERROR (new_name); @@ -525,11 +526,11 @@ unexec (const char *new_name, const char *a_name) { int new = -1, a_out = -1; - if (a_name && (a_out = open (a_name, O_RDONLY)) < 0) + if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0) { PERROR (a_name); } - if ((new = creat (new_name, 0666)) < 0) + if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { PERROR (new_name); } diff --git a/src/unexcw.c b/src/unexcw.c index 1290c28d245..0312a7328fb 100644 --- a/src/unexcw.c +++ b/src/unexcw.c @@ -20,8 +20,8 @@ along with GNU Emacs. If not, see . */ #include #include "unexec.h" +#include "lisp.h" -#include #include #include #include @@ -298,9 +298,9 @@ unexec (const char *outfile, const char *infile) infile = add_exe_suffix_if_necessary (infile, infile_buffer); outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer); - fd_in = open (infile, O_RDONLY | O_BINARY); + fd_in = emacs_open (infile, O_RDONLY | O_BINARY, 0); assert (fd_in >= 0); - fd_out = open (outfile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0755); + fd_out = emacs_open (outfile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0755); assert (fd_out >= 0); for (;;) { diff --git a/src/unexelf.c b/src/unexelf.c index 4e50bb86367..28847157e40 100644 --- a/src/unexelf.c +++ b/src/unexelf.c @@ -386,9 +386,8 @@ temacs: Instead we read the whole file, modify it, and write it out. */ #include -#include - -extern _Noreturn void fatal (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2); +#include "unexec.h" +#include "lisp.h" #include #include @@ -672,7 +671,7 @@ unexec (const char *new_name, const char *old_name) /* Open the old file, allocate a buffer of the right size, and read in the file contents. */ - old_file = open (old_name, O_RDONLY); + old_file = emacs_open (old_name, O_RDONLY, 0); if (old_file < 0) fatal ("Can't open %s for reading: %s", old_name, strerror (errno)); @@ -681,7 +680,7 @@ unexec (const char *new_name, const char *old_name) fatal ("Can't fstat (%s): %s", old_name, strerror (errno)); #if MAP_ANON == 0 - mmap_fd = open ("/dev/zero", O_RDONLY); + mmap_fd = emacs_open ("/dev/zero", O_RDONLY, 0); if (mmap_fd < 0) fatal ("Can't open /dev/zero for reading: %s", strerror (errno)); #endif @@ -801,7 +800,7 @@ unexec (const char *new_name, const char *old_name) the image of the new file. Set pointers to various interesting objects. */ - new_file = open (new_name, O_RDWR | O_CREAT, 0666); + new_file = emacs_open (new_name, O_RDWR | O_CREAT, 0666); if (new_file < 0) fatal ("Can't creat (%s): %s", new_name, strerror (errno)); diff --git a/src/unexhp9k800.c b/src/unexhp9k800.c index ce65faffd4e..0f6eb87ee01 100644 --- a/src/unexhp9k800.c +++ b/src/unexhp9k800.c @@ -51,6 +51,7 @@ #include #include "unexec.h" +#include "lisp.h" #include #include @@ -268,10 +269,10 @@ unexec (const char *new_name, /* name of the new a.out file to be created * intact. NOT implemented. */ /* Open the input and output a.out files */ - old = open (old_name, O_RDONLY); + old = emacs_open (old_name, O_RDONLY, 0); if (old < 0) { perror (old_name); exit (1); } - new = open (new_name, O_CREAT|O_RDWR|O_TRUNC, 0777); + new = emacs_open (new_name, O_CREAT | O_RDWR | O_TRUNC, 0777); if (new < 0) { perror (new_name); exit (1); } diff --git a/src/unexmacosx.c b/src/unexmacosx.c index 8d4e636fa5c..2bc6de177eb 100644 --- a/src/unexmacosx.c +++ b/src/unexmacosx.c @@ -97,6 +97,7 @@ along with GNU Emacs. If not, see . */ #undef free #include "unexec.h" +#include "lisp.h" #include #include @@ -1322,13 +1323,13 @@ unexec (const char *outfile, const char *infile) unexec_error ("Unexec from a dumped executable is not supported."); pagesize = getpagesize (); - infd = open (infile, O_RDONLY, 0); + infd = emacs_open (infile, O_RDONLY, 0); if (infd < 0) { unexec_error ("cannot open input file `%s'", infile); } - outfd = open (outfile, O_WRONLY | O_TRUNC | O_CREAT, 0755); + outfd = emacs_open (outfile, O_WRONLY | O_TRUNC | O_CREAT, 0755); if (outfd < 0) { close (infd); diff --git a/src/w32term.c b/src/w32term.c index fcd5886d5c9..c9951ca1d52 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -6621,7 +6621,7 @@ w32_initialize (void) } #ifdef CYGWIN - if ((w32_message_fd = open ("/dev/windows", O_RDWR | O_CLOEXEC)) == -1) + if ((w32_message_fd = emacs_open ("/dev/windows", O_RDWR, 0)) == -1) fatal ("opening /dev/windows: %s", strerror (errno)); #endif /* CYGWIN */ diff --git a/src/xfaces.c b/src/xfaces.c index 0c627d7f9ee..4b42cb7dc40 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -200,7 +200,7 @@ along with GNU Emacs. If not, see . */ used to fill in unspecified attributes of the default face. */ #include -#include +#include "sysstdio.h" #include #include @@ -6290,7 +6290,7 @@ where R,G,B are numbers between 0 and 255 and name is an arbitrary string. */) CHECK_STRING (filename); abspath = Fexpand_file_name (filename, Qnil); - fp = fopen (SSDATA (abspath), "rt"); + fp = emacs_fopen (SSDATA (abspath), "rt"); if (fp) { char buf[512];