X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/cadf50ff82951ce01ca4a57e5388c8cfe5b54d02..0087ade67a7c8e31b32579a353381fb00b53a112:/src/fileio.c diff --git a/src/fileio.c b/src/fileio.c index 8aa87038cf..7b3084c7aa 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1,5 +1,5 @@ /* File IO for GNU Emacs. - Copyright (C) 1985,86,87,88,93,94,95,96,97,1998 Free Software Foundation, Inc. + Copyright (C) 1985,86,87,88,93,94,95,96,97,98,1999 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -65,8 +65,6 @@ Boston, MA 02111-1307, USA. */ extern int errno; #endif -extern char *strerror (); - #ifdef APOLLO #include #endif @@ -141,6 +139,9 @@ extern char *strerror (); #endif #endif +#include "commands.h" +extern int use_dialog_box; + #ifndef O_WRONLY #define O_WRONLY 1 #endif @@ -149,6 +150,10 @@ extern char *strerror (); #define O_RDONLY 0 #endif +#ifndef S_ISLNK +# define lstat stat +#endif + #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) @@ -206,6 +211,10 @@ Lisp_Object Vdirectory_sep_char; extern Lisp_Object Vuser_login_name; +#ifdef WINDOWSNT +extern Lisp_Object Vw32_get_true_file_attributes; +#endif + extern int minibuf_level; extern int minibuffer_auto_raise; @@ -221,7 +230,7 @@ static Lisp_Object Vinhibit_file_name_handlers; static Lisp_Object Vinhibit_file_name_operation; Lisp_Object Qfile_error, Qfile_already_exists, Qfile_date_error; - +Lisp_Object Qexcl; Lisp_Object Qfile_name_history; Lisp_Object Qcar_less_than_car; @@ -236,24 +245,34 @@ report_file_error (string, data) Lisp_Object data; { Lisp_Object errstring; + int errorno = errno; - errstring = build_string (strerror (errno)); - - /* System error messages are capitalized. Downcase the initial - unless it is followed by a slash. */ - if (XSTRING (errstring)->data[1] != '/') - XSTRING (errstring)->data[0] = DOWNCASE (XSTRING (errstring)->data[0]); + synchronize_system_messages_locale (); + errstring = code_convert_string_norecord (build_string (strerror (errorno)), + Vlocale_coding_system, 0); while (1) - Fsignal (Qfile_error, - Fcons (build_string (string), Fcons (errstring, data))); + switch (errorno) + { + case EEXIST: + Fsignal (Qfile_already_exists, Fcons (errstring, data)); + break; + default: + /* System error messages are capitalized. Downcase the initial + unless it is followed by a slash. */ + if (XSTRING (errstring)->data[1] != '/') + XSTRING (errstring)->data[0] = DOWNCASE (XSTRING (errstring)->data[0]); + + Fsignal (Qfile_error, + Fcons (build_string (string), Fcons (errstring, data))); + } } Lisp_Object close_file_unwind (fd) Lisp_Object fd; { - close (XFASTINT (fd)); + emacs_close (XFASTINT (fd)); return Qnil; } @@ -322,19 +341,19 @@ use the standard functions without calling themselves recursively.") inhibited_handlers = Qnil; for (chain = Vfile_name_handler_alist; CONSP (chain); - chain = XCONS (chain)->cdr) + chain = XCDR (chain)) { Lisp_Object elt; - elt = XCONS (chain)->car; + elt = XCAR (chain); if (CONSP (elt)) { Lisp_Object string; - string = XCONS (elt)->car; + string = XCAR (elt); if (STRINGP (string) && fast_string_match (string, filename) >= 0) { Lisp_Object handler, tem; - handler = XCONS (elt)->cdr; + handler = XCDR (elt); tem = Fmemq (handler, inhibited_handlers); if (NILP (tem)) return handler; @@ -824,7 +843,11 @@ so there is no danger of generating a name being used by another process.\n\ \n\ In addition, this function makes an attempt to choose a name\n\ which has no existing file. To make this work,\n\ -PREFIX should be an absolute file name.") +PREFIX should be an absolute file name.\n\ +\n\ +There is a race condition between calling `make-temp-name' and creating the\n\ +file which opens all kinds of security holes. For that reason, you should\n\ +probably use `make-temp-file' instead.") (prefix) Lisp_Object prefix; { @@ -2107,7 +2130,7 @@ duplicates what `expand-file-name' does.") xnm = p; #ifdef DOS_NT else if (IS_DRIVE (p[0]) && p[1] == ':' - && p > nm && IS_DIRECTORY_SEP (p[-1])) + && p > xnm && IS_DIRECTORY_SEP (p[-1])) xnm = p; #endif @@ -2261,7 +2284,7 @@ A prefix arg makes KEEP-TIME non-nil.") else if (stat (XSTRING (encoded_newname)->data, &out_st) < 0) out_st.st_mode = 0; - ifd = open (XSTRING (encoded_file)->data, O_RDONLY); + ifd = emacs_open (XSTRING (encoded_file)->data, O_RDONLY, 0); if (ifd < 0) report_file_error ("Opening input file", Fcons (file, Qnil)); @@ -2313,13 +2336,13 @@ A prefix arg makes KEEP-TIME non-nil.") immediate_quit = 1; QUIT; - while ((n = read (ifd, buf, sizeof buf)) > 0) - if (write (ofd, buf, n) != n) + while ((n = emacs_read (ifd, buf, sizeof buf)) > 0) + if (emacs_write (ofd, buf, n) != n) report_file_error ("I/O error", Fcons (newname, Qnil)); immediate_quit = 0; /* Closing the output clobbers the file times on some systems. */ - if (close (ofd) < 0) + if (emacs_close (ofd) < 0) report_file_error ("I/O error", Fcons (newname, Qnil)); if (input_file_statable_p) @@ -2349,7 +2372,7 @@ A prefix arg makes KEEP-TIME non-nil.") #endif /* MSDOS */ } - close (ifd); + emacs_close (ifd); /* Discard the unwind protects. */ specpdl_ptr = specpdl + count; @@ -2888,10 +2911,10 @@ See also `file-exists-p' and `file-attributes'.") if (S_ISFIFO (statbuf.st_mode)) flags |= O_NONBLOCK; #endif - desc = open (XSTRING (absname)->data, flags); + desc = emacs_open (XSTRING (absname)->data, flags, 0); if (desc < 0) return Qnil; - close (desc); + emacs_close (desc); return Qt; #endif /* not DOS_NT */ } @@ -2947,6 +2970,7 @@ If there is no error, we return nil.") int fd; CHECK_STRING (filename, 0); + CHECK_STRING (string, 1); /* If the file name has special constructs in it, call the corresponding file handler. */ @@ -2956,10 +2980,10 @@ If there is no error, we return nil.") encoded_filename = ENCODE_FILE (filename); - fd = open (XSTRING (encoded_filename)->data, O_RDONLY); + fd = emacs_open (XSTRING (encoded_filename)->data, O_RDONLY, 0); if (fd < 0) report_file_error (XSTRING (string)->data, Fcons (filename, Qnil)); - close (fd); + emacs_close (fd); return Qnil; } @@ -3015,7 +3039,9 @@ Otherwise returns nil.") } DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, - "Return t if FILENAME names an existing directory.") + "Return t if FILENAME names an existing directory.\n\ +Symbolic links to directories count as directories.\n\ +See `file-symlink-p' to distinguish symlinks.") (filename) Lisp_Object filename; { @@ -3091,9 +3117,25 @@ This is the sort of file that holds an ordinary stream of data bytes.") absname = ENCODE_FILE (absname); +#ifdef WINDOWSNT + { + int result; + Lisp_Object tem = Vw32_get_true_file_attributes; + + /* Tell stat to use expensive method to get accurate info. */ + Vw32_get_true_file_attributes = Qt; + result = stat (XSTRING (absname)->data, &st); + Vw32_get_true_file_attributes = tem; + + if (result < 0) + return Qnil; + return (st.st_mode & S_IFMT) == S_IFREG ? Qt : Qnil; + } +#else if (stat (XSTRING (absname)->data, &st) < 0) return Qnil; return (st.st_mode & S_IFMT) == S_IFREG ? Qt : Qnil; +#endif } DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0, @@ -3246,6 +3288,26 @@ Lisp_Object Qfind_buffer_file_type; #define READ_BUF_SIZE (64 << 10) #endif +/* This function is called when a function bound to + Vset_auto_coding_function causes some error. At that time, a text + of a file has already been inserted in the current buffer, but, + markers has not yet been adjusted. Thus we must adjust markers + here. We are sure that the buffer was empty before the text of the + file was inserted. */ + +static Lisp_Object +set_auto_coding_unwind (multibyte) + Lisp_Object multibyte; +{ + int inserted = Z_BYTE - BEG_BYTE; + + if (!NILP (multibyte)) + inserted = multibyte_chars_in_text (GPT_ADDR - inserted, inserted); + adjust_after_insert (PT, PT_BYTE, Z, Z_BYTE, inserted); + + return Qnil; +} + DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents, 1, 5, 0, "Insert contents of file FILENAME after point.\n\ @@ -3266,10 +3328,11 @@ the whole thing because (1) it preserves some marker positions\n\ and (2) it puts less data in the undo list.\n\ When REPLACE is non-nil, the value is the number of characters actually read,\n\ which is often less than the number of characters to be read.\n\ +\n\ This does code conversion according to the value of\n\ - `coding-system-for-read' or `file-coding-system-alist',\n\ - and sets the variable `last-coding-system-used' to the coding system\n\ - actually used.") +`coding-system-for-read' or `file-coding-system-alist',\n\ +and sets the variable `last-coding-system-used' to the coding system\n\ +actually used.") (filename, visit, beg, end, replace) Lisp_Object filename, visit, beg, end, replace; { @@ -3289,6 +3352,7 @@ This does code conversion according to the value of\n\ unsigned char buffer[1 << 14]; int replace_handled = 0; int set_coding_system = 0; + int coding_system_decided = 0; if (current_buffer->base_buffer && ! NILP (visit)) error ("Cannot do file visiting in an indirect buffer"); @@ -3312,6 +3376,8 @@ This does code conversion according to the value of\n\ { val = call6 (handler, Qinsert_file_contents, filename, visit, beg, end, replace); + if (CONSP (val) && CONSP (XCDR (val))) + inserted = XINT (XCAR (XCDR (val))); goto handled; } @@ -3320,21 +3386,33 @@ This does code conversion according to the value of\n\ fd = -1; +#ifdef WINDOWSNT + { + Lisp_Object tem = Vw32_get_true_file_attributes; + + /* Tell stat to use expensive method to get accurate info. */ + Vw32_get_true_file_attributes = Qt; + total = stat (XSTRING (filename)->data, &st); + Vw32_get_true_file_attributes = tem; + } + if (total < 0) +#else #ifndef APOLLO if (stat (XSTRING (filename)->data, &st) < 0) #else - if ((fd = open (XSTRING (filename)->data, O_RDONLY)) < 0 + if ((fd = emacs_open (XSTRING (filename)->data, O_RDONLY, 0)) < 0 || fstat (fd, &st) < 0) #endif /* not APOLLO */ +#endif /* WINDOWSNT */ { - if (fd >= 0) close (fd); + if (fd >= 0) emacs_close (fd); badopen: if (NILP (visit)) report_file_error ("Opening input file", Fcons (orig_filename, Qnil)); st.st_mtime = -1; how_much = 0; if (!NILP (Vcoding_system_for_read)) - current_buffer->buffer_file_coding_system = Vcoding_system_for_read; + Fset (Qbuffer_file_coding_system, Vcoding_system_for_read); goto notfound; } @@ -3357,7 +3435,7 @@ This does code conversion according to the value of\n\ #endif if (fd < 0) - if ((fd = open (XSTRING (filename)->data, O_RDONLY)) < 0) + if ((fd = emacs_open (XSTRING (filename)->data, O_RDONLY, 0)) < 0) goto badopen; /* Replacement should preserve point as it preserves markers. */ @@ -3370,6 +3448,9 @@ This does code conversion according to the value of\n\ if (! not_regular && st.st_size < 0) error ("File size is negative"); + /* Prevent redisplay optimizations. */ + current_buffer->clip_changed = 1; + if (!NILP (beg) || !NILP (end)) if (!NILP (visit)) error ("Attempt to visit less than an entire file"); @@ -3386,163 +3467,113 @@ This does code conversion according to the value of\n\ if (! not_regular) { XSETINT (end, st.st_size); - if (XINT (end) != st.st_size) + + /* Arithmetic overflow can occur if an Emacs integer cannot + represent the file size, or if the calculations below + overflow. The calculations below double the file size + twice, so check that it can be multiplied by 4 safely. */ + if (XINT (end) != st.st_size + || ((int) st.st_size * 4) / 4 != st.st_size) error ("Maximum buffer size exceeded"); } } - /* Decide the coding-system of the file. */ - { - Lisp_Object val; - val = Qnil; - - if (!NILP (Vcoding_system_for_read)) - val = Vcoding_system_for_read; - else if (! NILP (replace)) - /* In REPLACE mode, we can use the same coding system - that was used to visit the file. */ - val = current_buffer->buffer_file_coding_system; - else if (! not_regular) - { - /* Don't try looking inside a file for a coding system specification - if it is not seekable. */ - if (! NILP (Vset_auto_coding_function)) - { - /* Find a coding system specified in the heading two lines - or in the tailing several lines of the file. We assume - that the 1K-byte and 3K-byte for heading and tailing - respectively are sufficient fot this purpose. */ - int nread; - int beginning_of_end, end_of_beginning; - - if (st.st_size <= (1024 * 4)) - { - nread = read (fd, read_buf, 1024 * 4); - end_of_beginning = nread; - beginning_of_end = 0; - } - else - { - nread = read (fd, read_buf, 1024); - end_of_beginning = nread; - beginning_of_end = nread; - if (nread >= 0) - { - if (lseek (fd, st.st_size - (1024 * 3), 0) < 0) - report_file_error ("Setting file position", - Fcons (orig_filename, Qnil)); - nread += read (fd, read_buf + nread, 1024 * 3); - } - } + if (BEG < Z) + { + /* Decide the coding system to use for reading the file now + because we can't use an optimized method for handling + `coding:' tag if the current buffer is not empty. */ + Lisp_Object val; + val = Qnil; - if (nread < 0) - error ("IO error reading %s: %s", - XSTRING (orig_filename)->data, strerror (errno)); - else if (nread > 0) - { - int i; - int possible_spec = 0; - unsigned char *p, *p1; - Lisp_Object tem; - unsigned char *copy = (unsigned char *) alloca (nread + 1); - - /* Make a copy of the contents of read_buf in COPY, - and convert it to lower case so we can compare - more efficiently. */ - bcopy (read_buf, copy, nread); - for (i = 0; i < nread; i++) - copy[i] = DOWNCASE (copy[i]); - /* Ensure various comparisons fail at end of data. */ - copy[nread] = 0; - - /* Now test quickly whether the file contains a -*- line. */ - p = copy; - while (*p != '\n' && p - copy < end_of_beginning) - p++; - if (copy[0] == '#' && copy[1] == '!') - while (*p != '\n' && p - copy < end_of_beginning) - p++; - p1 = copy; - while (p - p1 >= 3) - { - if (p1[0] == '-' && p1[1] == '*' && p1[2] == '-') - { - while (p - p1 >= 7) - { - if (! bcmp ("coding:", p1, 7)) - { - possible_spec = 1; - goto win; - } - p1++; - } - break; - } - p1++; - } + if (!NILP (Vcoding_system_for_read)) + val = Vcoding_system_for_read; + else if (! NILP (replace)) + /* In REPLACE mode, we can use the same coding system + that was used to visit the file. */ + val = current_buffer->buffer_file_coding_system; + else + { + /* Don't try looking inside a file for a coding system + specification if it is not seekable. */ + if (! not_regular && ! NILP (Vset_auto_coding_function)) + { + /* Find a coding system specified in the heading two + lines or in the tailing several lines of the file. + We assume that the 1K-byte and 3K-byte for heading + and tailing respectively are sufficient for this + purpose. */ + int how_many, nread; + + if (st.st_size <= (1024 * 4)) + nread = emacs_read (fd, read_buf, 1024 * 4); + else + { + nread = emacs_read (fd, read_buf, 1024); + if (nread >= 0) + { + if (lseek (fd, st.st_size - (1024 * 3), 0) < 0) + report_file_error ("Setting file position", + Fcons (orig_filename, Qnil)); + nread += emacs_read (fd, read_buf + nread, 1024 * 3); + } + } - /* Test quickly whether the file - contains a local variables list. */ - p = ©[nread - 1]; - p1 = ©[beginning_of_end]; - while (p > p1) - { - if (p[0] == '\n' && p[1] == '\f') - break; - p--; - } - p1 = ©[nread]; - while (p1 - p >= 16) - { - if (! bcmp ("local variables:", p, 16)) - { - possible_spec = 1; - break; - } - p++; - } - win: + if (nread < 0) + error ("IO error reading %s: %s", + XSTRING (orig_filename)->data, emacs_strerror (errno)); + else if (nread > 0) + { + int count = specpdl_ptr - specpdl; + struct buffer *prev = current_buffer; + + record_unwind_protect (Fset_buffer, Fcurrent_buffer ()); + temp_output_buffer_setup (" *code-converting-work*"); + set_buffer_internal (XBUFFER (Vstandard_output)); + current_buffer->enable_multibyte_characters = Qnil; + insert_1_both (read_buf, nread, nread, 0, 0, 0); + TEMP_SET_PT_BOTH (BEG, BEG_BYTE); + val = call2 (Vset_auto_coding_function, + filename, make_number (nread)); + set_buffer_internal (prev); + /* Discard the unwind protect for recovering the + current buffer. */ + specpdl_ptr--; + + /* Rewind the file for the actual read done later. */ + if (lseek (fd, 0, 0) < 0) + report_file_error ("Setting file position", + Fcons (orig_filename, Qnil)); + } + } - if (possible_spec) - { - /* Always make this a unibyte string - because we have not yet decoded it. */ - tem = make_unibyte_string (read_buf, nread); - val = call1 (Vset_auto_coding_function, tem); - } + if (NILP (val)) + { + /* If we have not yet decided a coding system, check + file-coding-system-alist. */ + Lisp_Object args[6], coding_systems; + + args[0] = Qinsert_file_contents, args[1] = orig_filename; + args[2] = visit, args[3] = beg, args[4] = end, args[5] = replace; + coding_systems = Ffind_operation_coding_system (6, args); + if (CONSP (coding_systems)) + val = XCAR (coding_systems); + } + } - /* Rewind the file for the actual read done later. */ - if (lseek (fd, 0, 0) < 0) - report_file_error ("Setting file position", - Fcons (orig_filename, Qnil)); - } - } - if (NILP (val)) - { - Lisp_Object args[6], coding_systems; + setup_coding_system (Fcheck_coding_system (val), &coding); - args[0] = Qinsert_file_contents, args[1] = orig_filename; - args[2] = visit, args[3] = beg, args[4] = end, args[5] = replace; - coding_systems = Ffind_operation_coding_system (6, args); - if (CONSP (coding_systems)) - val = XCONS (coding_systems)->car; - } - } + if (NILP (current_buffer->enable_multibyte_characters) + && ! NILP (val)) + /* We must suppress all character code conversion except for + end-of-line conversion. */ + setup_raw_text_coding_system (&coding); - if (NILP (Vcoding_system_for_read) - && NILP (current_buffer->enable_multibyte_characters)) - { - /* We must suppress all text conversion except for end-of-line - conversion. */ - struct coding_system coding_temp; + coding_system_decided = 1; + } - setup_coding_system (Fcheck_coding_system (val), &coding_temp); - setup_coding_system (Qraw_text, &coding); - coding.eol_type = coding_temp.eol_type; - } - else - setup_coding_system (Fcheck_coding_system (val), &coding); - } + /* Ensure we always set Vlast_coding_system_used. */ + set_coding_system = 1; /* If requested, replace the accessible part of the buffer with the file contents. Avoid replacing text at the @@ -3559,6 +3590,7 @@ This does code conversion according to the value of\n\ But if we discover the need for conversion, we give up on this method and let the following if-statement handle the replace job. */ if (!NILP (replace) + && BEGV < ZV && ! CODING_REQUIRE_DECODING (&coding) && (coding.eol_type == CODING_EOL_UNDECIDED || coding.eol_type == CODING_EOL_LF)) @@ -3589,10 +3621,10 @@ This does code conversion according to the value of\n\ { int nread, bufpos; - nread = read (fd, buffer, sizeof buffer); + nread = emacs_read (fd, buffer, sizeof buffer); if (nread < 0) error ("IO error reading %s: %s", - XSTRING (orig_filename)->data, strerror (errno)); + XSTRING (orig_filename)->data, emacs_strerror (errno)); else if (nread == 0) break; @@ -3631,7 +3663,7 @@ This does code conversion according to the value of\n\ there's no need to replace anything. */ if (same_at_start - BEGV_BYTE == XINT (end)) { - close (fd); + emacs_close (fd); specpdl_ptr--; /* Truncate the buffer to the size of the file. */ del_range_1 (same_at_start, same_at_end, 0); @@ -3660,10 +3692,10 @@ This does code conversion according to the value of\n\ total_read = 0; while (total_read < trial) { - nread = read (fd, buffer + total_read, trial - total_read); + nread = emacs_read (fd, buffer + total_read, trial - total_read); if (nread <= 0) error ("IO error reading %s: %s", - XSTRING (orig_filename)->data, strerror (errno)); + XSTRING (orig_filename)->data, emacs_strerror (errno)); total_read += nread; } /* Scan this bufferful from the end, comparing with @@ -3697,7 +3729,14 @@ This does code conversion according to the value of\n\ /* We win! We can handle REPLACE the optimized way. */ - /* Extends the end of non-matching text area to multibyte + /* Extend the start of non-matching text area to multibyte + character boundary. */ + if (! NILP (current_buffer->enable_multibyte_characters)) + while (same_at_start > BEGV_BYTE + && ! CHAR_HEAD_P (FETCH_BYTE (same_at_start))) + same_at_start--; + + /* Extend the end of non-matching text area to multibyte character boundary. */ if (! NILP (current_buffer->enable_multibyte_characters)) while (same_at_end < ZV_BYTE @@ -3737,7 +3776,7 @@ This does code conversion according to the value of\n\ is needed, in a simple way that needs a lot of memory. The preceding if-statement handles the case of no conversion in a more optimized way. */ - if (!NILP (replace) && ! replace_handled) + if (!NILP (replace) && ! replace_handled && BEGV < ZV) { int same_at_start = BEGV_BYTE; int same_at_end = ZV_BYTE; @@ -3753,7 +3792,7 @@ This does code conversion according to the value of\n\ if (lseek (fd, XINT (beg), 0) < 0) { - free (conversion_buffer); + xfree (conversion_buffer); report_file_error ("Setting file position", Fcons (orig_filename, Qnil)); } @@ -3773,7 +3812,7 @@ This does code conversion according to the value of\n\ /* Allow quitting out of the actual I/O. */ immediate_quit = 1; QUIT; - this = read (fd, destination, trytry); + this = emacs_read (fd, destination, trytry); immediate_quit = 0; if (this < 0 || this + unprocessed == 0) @@ -3822,11 +3861,11 @@ This does code conversion according to the value of\n\ if (how_much < 0) { - free (conversion_buffer); + xfree (conversion_buffer); if (how_much == -1) error ("IO error reading %s: %s", - XSTRING (orig_filename)->data, strerror (errno)); + XSTRING (orig_filename)->data, emacs_strerror (errno)); else if (how_much == -2) error ("maximum buffer size exceeded"); } @@ -3844,14 +3883,22 @@ This does code conversion according to the value of\n\ if (bufpos == inserted) { - free (conversion_buffer); - close (fd); + xfree (conversion_buffer); + emacs_close (fd); specpdl_ptr--; /* Truncate the buffer to the size of the file. */ - del_range_1 (same_at_start, same_at_end, 0); + del_range_byte (same_at_start, same_at_end, 0); + inserted = 0; goto handled; } + /* Extend the start of non-matching text area to multibyte + character boundary. */ + if (! NILP (current_buffer->enable_multibyte_characters)) + while (same_at_start > BEGV_BYTE + && ! CHAR_HEAD_P (FETCH_BYTE (same_at_start))) + same_at_start--; + /* Scan this bufferful from the end, comparing with the Emacs buffer. */ bufpos = inserted; @@ -3862,6 +3909,13 @@ This does code conversion according to the value of\n\ && FETCH_BYTE (same_at_end - 1) == conversion_buffer[bufpos - 1]) same_at_end--, bufpos--; + /* Extend the end of non-matching text area to multibyte + character boundary. */ + if (! NILP (current_buffer->enable_multibyte_characters)) + while (same_at_end < ZV_BYTE + && ! CHAR_HEAD_P (FETCH_BYTE (same_at_end))) + same_at_end++; + /* Don't try to reuse the same piece of text twice. */ overlap = same_at_start - BEGV_BYTE - (same_at_end + inserted - ZV_BYTE); if (overlap > 0) @@ -3876,21 +3930,26 @@ This does code conversion according to the value of\n\ and update INSERTED to equal the number of bytes we are taking from the file. */ inserted -= (Z_BYTE - same_at_end) + (same_at_start - BEG_BYTE); - del_range_byte (same_at_start, same_at_end, 0); + if (same_at_end != same_at_start) - SET_PT_BOTH (GPT, GPT_BYTE); + { + del_range_byte (same_at_start, same_at_end, 0); + temp = GPT; + same_at_start = GPT_BYTE; + } else { - /* Insert from the file at the proper position. */ temp = BYTE_TO_CHAR (same_at_start); - SET_PT_BOTH (temp, same_at_start); } - + /* Insert from the file at the proper position. */ + SET_PT_BOTH (temp, same_at_start); insert_1 (conversion_buffer + same_at_start - BEG_BYTE, inserted, 0, 0, 0); + /* Set `inserted' to the number of inserted characters. */ + inserted = PT - temp; free (conversion_buffer); - close (fd); + emacs_close (fd); specpdl_ptr--; goto handled; @@ -3947,7 +4006,8 @@ This does code conversion according to the value of\n\ /* Allow quitting out of the actual I/O. */ immediate_quit = 1; QUIT; - this = read (fd, BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1, trytry); + this = emacs_read (fd, BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1, + trytry); immediate_quit = 0; if (this <= 0) @@ -3978,16 +4038,81 @@ This does code conversion according to the value of\n\ /* Put an anchor to ensure multi-byte form ends at gap. */ *GPT_ADDR = 0; - close (fd); + emacs_close (fd); /* Discard the unwind protect for closing the file. */ specpdl_ptr--; if (how_much < 0) error ("IO error reading %s: %s", - XSTRING (orig_filename)->data, strerror (errno)); + XSTRING (orig_filename)->data, emacs_strerror (errno)); - if (inserted > 0) + if (! coding_system_decided) + { + /* The coding system is not yet decided. Decide it by an + optimized method for handling `coding:' tag. */ + Lisp_Object val; + val = Qnil; + + if (!NILP (Vcoding_system_for_read)) + val = Vcoding_system_for_read; + else + { + if (inserted > 0 && ! NILP (Vset_auto_coding_function)) + { + /* Since we are sure that the current buffer was + empty before the insertion, we can toggle + enable-multibyte-characters directly here without + taking care of marker adjustment and byte + combining problem. */ + Lisp_Object prev_multibyte; + int count = specpdl_ptr - specpdl; + + prev_multibyte = current_buffer->enable_multibyte_characters; + current_buffer->enable_multibyte_characters = Qnil; + record_unwind_protect (set_auto_coding_unwind, + prev_multibyte); + val = call2 (Vset_auto_coding_function, + filename, make_number (inserted)); + /* Discard the unwind protect for recovering the + error of Vset_auto_coding_function. */ + specpdl_ptr--; + current_buffer->enable_multibyte_characters = prev_multibyte; + TEMP_SET_PT_BOTH (BEG, BEG_BYTE); + } + + if (NILP (val)) + { + /* If the coding system is not yet decided, check + file-coding-system-alist. */ + Lisp_Object args[6], coding_systems; + + args[0] = Qinsert_file_contents, args[1] = orig_filename; + args[2] = visit, args[3] = beg, args[4] = end, args[5] = Qnil; + coding_systems = Ffind_operation_coding_system (6, args); + if (CONSP (coding_systems)) + val = XCAR (coding_systems); + } + } + + /* The following kludgy code is to avoid some compiler bug. + We can't simply do + setup_coding_system (val, &coding); + on some system. */ + { + struct coding_system temp_coding; + setup_coding_system (val, &temp_coding); + bcopy (&temp_coding, &coding, sizeof coding); + } + + if (NILP (current_buffer->enable_multibyte_characters) + && ! NILP (val)) + /* We must suppress all character code conversion except for + end-of-line conversion. */ + setup_raw_text_coding_system (&coding); + } + + if (inserted > 0 || coding.type == coding_type_ccl) { if (CODING_MAY_REQUIRE_DECODING (&coding)) { @@ -4015,21 +4140,19 @@ This does code conversion according to the value of\n\ else adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted, inserted); + } #ifdef DOS_NT - /* Use the conversion type to determine buffer-file-type - (find-buffer-file-type is now used to help determine the - conversion). */ - if ((coding.eol_type == CODING_EOL_UNDECIDED - || coding.eol_type == CODING_EOL_LF) - && ! CODING_REQUIRE_DECODING (&coding)) - current_buffer->buffer_file_type = Qt; - else - current_buffer->buffer_file_type = Qnil; + /* Use the conversion type to determine buffer-file-type + (find-buffer-file-type is now used to help determine the + conversion). */ + if ((coding.eol_type == CODING_EOL_UNDECIDED + || coding.eol_type == CODING_EOL_LF) + && ! CODING_REQUIRE_DECODING (&coding)) + current_buffer->buffer_file_type = Qt; + else + current_buffer->buffer_file_type = Qnil; #endif - } - - set_coding_system = 1; notfound: handled: @@ -4154,12 +4277,22 @@ If VISIT is neither t nor nil nor a string,\n\ that means do not print the \"Wrote file\" message.\n\ The optional sixth arg LOCKNAME, if non-nil, specifies the name to\n\ use for locking and unlocking, overriding FILENAME and VISIT.\n\ -The optional seventh arg CONFIRM, if non-nil, says ask for confirmation\n\ - before overwriting an existing file.\n\ +The optional seventh arg MUSTBENEW, if non-nil, insists on a check\n\ + for an existing file with the same name. If MUSTBENEW is `excl',\n\ + that means to get an error if the file already exists; never overwrite.\n\ + If MUSTBENEW is neither nil nor `excl', that means ask for\n\ + confirmation before overwriting, but do go ahead and overwrite the file\n\ + if the user confirms.\n\ Kludgy feature: if START is a string, then that string is written\n\ -to the file, instead of any buffer contents, and END is ignored.") - (start, end, filename, append, visit, lockname, confirm) - Lisp_Object start, end, filename, append, visit, lockname, confirm; +to the file, instead of any buffer contents, and END is ignored.\n\ +\n\ +This does code conversion according to the value of\n\ +`coding-system-for-write', `buffer-file-coding-system', or\n\ +`file-coding-system-alist', and sets the variable\n\ +`last-coding-system-used' to the coding system actually used.") + + (start, end, filename, append, visit, lockname, mustbenew) + Lisp_Object start, end, filename, append, visit, lockname, mustbenew; { register int desc; int failure; @@ -4200,7 +4333,7 @@ to the file, instead of any buffer contents, and END is ignored.") val = Qnil; else if (!NILP (Vcoding_system_for_write)) val = Vcoding_system_for_write; - else if (NILP (current_buffer->enable_multibyte_characters)) + else { /* If the variable `buffer-file-coding-system' is set locally, it means that the file was read with some kind of code @@ -4211,38 +4344,72 @@ to the file, instead of any buffer contents, and END is ignored.") If it is not set locally, we anyway have to convert EOL format if the default value of `buffer-file-coding-system' tells that it is not Unix-like (LF only) format. */ + int using_default_coding = 0; + int force_raw_text = 0; + val = current_buffer->buffer_file_coding_system; - if (NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil))) + if (NILP (val) + || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil))) + { + val = Qnil; + if (NILP (current_buffer->enable_multibyte_characters)) + force_raw_text = 1; + } + + if (NILP (val)) + { + /* Check file-coding-system-alist. */ + Lisp_Object args[7], coding_systems; + + args[0] = Qwrite_region; args[1] = start; args[2] = end; + args[3] = filename; args[4] = append; args[5] = visit; + args[6] = lockname; + coding_systems = Ffind_operation_coding_system (7, args); + if (CONSP (coding_systems) && !NILP (XCDR (coding_systems))) + val = XCDR (coding_systems); + } + + if (NILP (val) + && !NILP (current_buffer->buffer_file_coding_system)) { - struct coding_system coding_temp; + /* If we still have not decided a coding system, use the + default value of buffer-file-coding-system. */ + val = current_buffer->buffer_file_coding_system; + using_default_coding = 1; + } + + if (!force_raw_text + && !NILP (Ffboundp (Vselect_safe_coding_system_function))) + /* Confirm that VAL can surely encode the current region. */ + val = call3 (Vselect_safe_coding_system_function, start, end, val); - setup_coding_system (Fcheck_coding_system (val), &coding_temp); - if (coding_temp.eol_type == CODING_EOL_CRLF - || coding_temp.eol_type == CODING_EOL_CR) + setup_coding_system (Fcheck_coding_system (val), &coding); + if (coding.eol_type == CODING_EOL_UNDECIDED + && !using_default_coding) + { + if (! EQ (default_buffer_file_coding.symbol, + buffer_defaults.buffer_file_coding_system)) + setup_coding_system (buffer_defaults.buffer_file_coding_system, + &default_buffer_file_coding); + if (default_buffer_file_coding.eol_type != CODING_EOL_UNDECIDED) { - setup_coding_system (Qraw_text, &coding); - coding.eol_type = coding_temp.eol_type; - goto done_setup_coding; + Lisp_Object subsidiaries; + + coding.eol_type = default_buffer_file_coding.eol_type; + subsidiaries = Fget (coding.symbol, Qeol_type); + if (VECTORP (subsidiaries) + && XVECTOR (subsidiaries)->size == 3) + coding.symbol + = XVECTOR (subsidiaries)->contents[coding.eol_type]; } - val = Qnil; } + + if (force_raw_text) + setup_raw_text_coding_system (&coding); + goto done_setup_coding; } - else - { - Lisp_Object args[7], coding_systems; - - args[0] = Qwrite_region; args[1] = start; args[2] = end; - args[3] = filename; args[4] = append; args[5] = visit; - args[6] = lockname; - coding_systems = Ffind_operation_coding_system (7, args); - val = (CONSP (coding_systems) && !NILP (XCONS (coding_systems)->cdr) - ? XCONS (coding_systems)->cdr - : current_buffer->buffer_file_coding_system); - /* Confirm that VAL can surely encode the current region. */ - if (!NILP (Ffboundp (Vselect_safe_coding_system_function))) - val = call3 (Vselect_safe_coding_system_function, start, end, val); - } - setup_coding_system (Fcheck_coding_system (val), &coding); + + setup_coding_system (Fcheck_coding_system (val), &coding); done_setup_coding: if (!STRINGP (start) && !NILP (current_buffer->selective_display)) @@ -4253,7 +4420,7 @@ to the file, instead of any buffer contents, and END is ignored.") filename = Fexpand_file_name (filename, Qnil); - if (! NILP (confirm)) + if (! NILP (mustbenew) && mustbenew != Qexcl) barf_or_query_if_file_exists (filename, "overwrite", 1, 0, 1); if (STRINGP (visit)) @@ -4333,9 +4500,9 @@ to the file, instead of any buffer contents, and END is ignored.") desc = -1; if (!NILP (append)) #ifdef DOS_NT - desc = open (fn, O_WRONLY | buffer_file_type); + desc = emacs_open (fn, O_WRONLY | buffer_file_type, 0); #else /* not DOS_NT */ - desc = open (fn, O_WRONLY); + desc = emacs_open (fn, O_WRONLY, 0); #endif /* not DOS_NT */ if (desc < 0 && (NILP (append) || errno == ENOENT)) @@ -4343,7 +4510,7 @@ to the file, instead of any buffer contents, and END is ignored.") if (auto_saving) /* Overwrite any previous version of autosave file */ { vms_truncate (fn); /* if fn exists, truncate to zero length */ - desc = open (fn, O_RDWR); + desc = emacs_open (fn, O_RDWR, 0); if (desc < 0) desc = creat_copy_attrs (STRINGP (current_buffer->filename) ? XSTRING (current_buffer->filename)->data : 0, @@ -4376,7 +4543,7 @@ to the file, instead of any buffer contents, and END is ignored.") /* We can't make a new version; try to truncate and rewrite existing version if any. */ vms_truncate (fn); - desc = open (fn, O_RDWR); + desc = emacs_open (fn, O_RDWR, 0); } #endif } @@ -4386,11 +4553,14 @@ to the file, instead of any buffer contents, and END is ignored.") } #else /* not VMS */ #ifdef DOS_NT - desc = open (fn, - O_WRONLY | O_TRUNC | O_CREAT | buffer_file_type, - S_IREAD | S_IWRITE); + desc = emacs_open (fn, + O_WRONLY | O_TRUNC | O_CREAT | buffer_file_type + | (mustbenew == Qexcl ? O_EXCL : 0), + S_IREAD | S_IWRITE); #else /* not DOS_NT */ - desc = creat (fn, auto_saving ? auto_save_mode_bits : 0666); + desc = emacs_open (fn, O_WRONLY | O_TRUNC | O_CREAT + | (mustbenew == Qexcl ? O_EXCL : 0), + auto_saving ? auto_save_mode_bits : 0666); #endif /* not DOS_NT */ #endif /* not VMS */ @@ -4408,7 +4578,7 @@ to the file, instead of any buffer contents, and END is ignored.") record_unwind_protect (close_file_unwind, make_number (desc)); - if (!NILP (append)) + if (!NILP (append) && !NILP (Ffile_regular_p (filename))) if (lseek (desc, 0, 2) < 0) { #ifdef CLASH_DETECTION @@ -4535,7 +4705,7 @@ to the file, instead of any buffer contents, and END is ignored.") #endif /* NFS can report a write failure now. */ - if (close (desc) < 0) + if (emacs_close (desc) < 0) failure = 1, save_errno = errno; #ifdef VMS @@ -4569,7 +4739,7 @@ to the file, instead of any buffer contents, and END is ignored.") if (failure) error ("IO error writing %s: %s", XSTRING (filename)->data, - strerror (save_errno)); + emacs_strerror (save_errno)); if (visiting) { @@ -4755,13 +4925,22 @@ e_write (desc, addr, nbytes, coding) now it is handled within encode_coding. */ while (1) { - encode_coding (coding, addr, buf, nbytes, WRITE_BUF_SIZE); + int result; + + result = encode_coding (coding, addr, buf, nbytes, WRITE_BUF_SIZE); nbytes -= coding->consumed, addr += coding->consumed; if (coding->produced > 0) { - coding->produced -= write (desc, buf, coding->produced); + coding->produced -= emacs_write (desc, buf, coding->produced); if (coding->produced) return -1; } + if (result == CODING_FINISH_INSUFFICIENT_SRC) + { + /* The source text ends by an incomplete multibyte form. + There's no way other than write it out as is. */ + nbytes -= emacs_write (desc, addr, nbytes); + if (nbytes) return -1; + } if (nbytes <= 0) break; } @@ -4908,8 +5087,8 @@ do_auto_save_unwind (stream) /* used as unwind-protect function */ { auto_saving = 0; if (!NILP (stream)) - fclose ((FILE *) (XFASTINT (XCONS (stream)->car) << 16 - | XFASTINT (XCONS (stream)->cdr))); + fclose ((FILE *) (XFASTINT (XCAR (stream)) << 16 + | XFASTINT (XCDR (stream)))); return Qnil; } @@ -4937,9 +5116,6 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") struct buffer *old = current_buffer, *b; Lisp_Object tail, buf; int auto_saved = 0; - char *omessage = echo_area_glyphs; - int omessage_length = echo_area_glyphs_length; - int oldmultibyte = message_enable_multibyte; int do_handled_files; Lisp_Object oquit; FILE *stream; @@ -4947,7 +5123,8 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") int count = specpdl_ptr - specpdl; int *ptr; int orig_minibuffer_auto_raise = minibuffer_auto_raise; - + int message_p = push_message (); + /* Ordinarily don't quit within this function, but don't make it impossible to quit (in case we get hung in I/O). */ oquit = Vquit_flag; @@ -4972,8 +5149,8 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") /* Arrange to close that file whether or not we get an error. Also reset auto_saving to 0. */ lispstream = Fcons (Qnil, Qnil); - XSETFASTINT (XCONS (lispstream)->car, (EMACS_UINT)stream >> 16); - XSETFASTINT (XCONS (lispstream)->cdr, (EMACS_UINT)stream & 0xffff); + XSETFASTINT (XCAR (lispstream), (EMACS_UINT)stream >> 16); + XSETFASTINT (XCDR (lispstream), (EMACS_UINT)stream & 0xffff); } else lispstream = Qnil; @@ -4996,9 +5173,9 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") autosave perfectly ordinary files because it couldn't handle some ange-ftp'd file. */ for (do_handled_files = 0; do_handled_files < 2; do_handled_files++) - for (tail = Vbuffer_alist; GC_CONSP (tail); tail = XCONS (tail)->cdr) + for (tail = Vbuffer_alist; GC_CONSP (tail); tail = XCDR (tail)) { - buf = XCONS (XCONS (tail)->car)->cdr; + buf = XCDR (XCAR (tail)); b = XBUFFER (buf); /* Record all the buffers that have auto save mode @@ -5091,10 +5268,10 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") if (auto_saved && NILP (no_message)) { - if (omessage) + if (message_p) { sit_for (1, 0, 0, 0, 0); - message2 (omessage, omessage_length, oldmultibyte); + restore_message (); } else message1 ("Auto-saving...done"); @@ -5102,6 +5279,7 @@ A non-nil CURRENT-ONLY argument means save only current buffer.") Vquit_flag = oquit; + pop_message (); unbind_to (count, Qnil); return Qnil; } @@ -5260,7 +5438,7 @@ DIR defaults to current buffer's directory default.") (prompt, dir, default_filename, mustmatch, initial) Lisp_Object prompt, dir, default_filename, mustmatch, initial; { - Lisp_Object val, insdef, insdef1, tem; + Lisp_Object val, insdef, tem; struct gcpro gcpro1, gcpro2; register char *homedir; int replace_in_history = 0; @@ -5292,6 +5470,22 @@ DIR defaults to current buffer's directory default.") STRING_BYTES (XSTRING (dir)) - strlen (homedir) + 1); XSTRING (dir)->data[0] = '~'; } + /* Likewise for default_filename. */ + if (homedir != 0 + && STRINGP (default_filename) + && !strncmp (homedir, XSTRING (default_filename)->data, strlen (homedir)) + && IS_DIRECTORY_SEP (XSTRING (default_filename)->data[strlen (homedir)])) + { + default_filename + = make_string (XSTRING (default_filename)->data + strlen (homedir) - 1, + STRING_BYTES (XSTRING (default_filename)) - strlen (homedir) + 1); + XSTRING (default_filename)->data[0] = '~'; + } + if (!NILP (default_filename)) + { + CHECK_STRING (default_filename, 3); + default_filename = double_dollars (default_filename); + } if (insert_default_directory && STRINGP (dir)) { @@ -5304,31 +5498,41 @@ DIR defaults to current buffer's directory default.") args[1] = initial; insdef = Fconcat (2, args); pos = make_number (XSTRING (double_dollars (dir))->size); - insdef1 = Fcons (double_dollars (insdef), pos); + insdef = Fcons (double_dollars (insdef), pos); } else - insdef1 = double_dollars (insdef); + insdef = double_dollars (insdef); } else if (STRINGP (initial)) - { - insdef = initial; - insdef1 = Fcons (double_dollars (insdef), make_number (0)); - } + insdef = Fcons (double_dollars (initial), make_number (0)); else - insdef = Qnil, insdef1 = Qnil; + insdef = Qnil; -#ifdef VMS count = specpdl_ptr - specpdl; +#ifdef VMS specbind (intern ("completion-ignore-case"), Qt); #endif + specbind (intern ("minibuffer-completing-file-name"), Qt); + GCPRO2 (insdef, default_filename); - val = Fcompleting_read (prompt, intern ("read-file-name-internal"), - dir, mustmatch, insdef1, - Qfile_name_history, default_filename, Qnil); + +#ifdef USE_MOTIF + if ((NILP (last_nonmenu_event) || CONSP (last_nonmenu_event)) + && use_dialog_box + && have_menus_p ()) + { + val = Fx_file_dialog (prompt, dir, default_filename, mustmatch); + add_to_history = 1; + } + else +#endif + val = Fcompleting_read (prompt, intern ("read-file-name-internal"), + dir, mustmatch, insdef, + Qfile_name_history, default_filename, Qnil); tem = Fsymbol_value (Qfile_name_history); - if (CONSP (tem) && EQ (XCONS (tem)->car, val)) + if (CONSP (tem) && EQ (XCAR (tem), val)) replace_in_history = 1; /* If Fcompleting_read returned the inserted default string itself @@ -5346,15 +5550,12 @@ DIR defaults to current buffer's directory default.") val = build_string (""); } -#ifdef VMS unbind_to (count, Qnil); -#endif - UNGCPRO; if (NILP (val)) error ("No file name specified"); - tem = Fstring_equal (val, insdef); + tem = Fstring_equal (val, CONSP (insdef) ? XCAR (insdef) : insdef); if (!NILP (tem) && !NILP (default_filename)) val = default_filename; @@ -5370,18 +5571,29 @@ DIR defaults to current buffer's directory default.") if (replace_in_history) /* Replace what Fcompleting_read added to the history with what we will actually return. */ - XCONS (Fsymbol_value (Qfile_name_history))->car = val; + XCAR (Fsymbol_value (Qfile_name_history)) = double_dollars (val); else if (add_to_history) { /* Add the value to the history--but not if it matches the last value already there. */ + Lisp_Object val1 = double_dollars (val); tem = Fsymbol_value (Qfile_name_history); - if (! CONSP (tem) || NILP (Fequal (XCONS (tem)->car, val))) + if (! CONSP (tem) || NILP (Fequal (XCAR (tem), val1))) Fset (Qfile_name_history, - Fcons (val, tem)); + Fcons (val1, tem)); } + return val; } + + +void +init_fileio_once () +{ + /* Must be set before any path manipulation is performed. */ + XSETFASTINT (Vdirectory_sep_char, '/'); +} + void syms_of_fileio () @@ -5458,6 +5670,8 @@ syms_of_fileio () staticpro (&Qfile_already_exists); Qfile_date_error = intern ("file-date-error"); staticpro (&Qfile_date_error); + Qexcl = intern ("excl"); + staticpro (&Qexcl); #ifdef DOS_NT Qfind_buffer_file_type = intern ("find-buffer-file-type"); @@ -5527,7 +5741,6 @@ The value should be either ?/ or ?\\ (any other value is treated as ?\\).\n\ This variable affects the built-in functions only on Windows,\n\ on other platforms, it is initialized so that Lisp code can find out\n\ what the normal separator is."); - XSETFASTINT (Vdirectory_sep_char, '/'); DEFVAR_LISP ("file-name-handler-alist", &Vfile_name_handler_alist, "*Alist of elements (REGEXP . HANDLER) for file names handled specially.\n\ @@ -5547,9 +5760,11 @@ for its argument."); DEFVAR_LISP ("set-auto-coding-function", &Vset_auto_coding_function, "If non-nil, a function to call to decide a coding system of file.\n\ -One argument is passed to this function: the string of concatination\n\ -or the heading 1K-byte and the tailing 3K-byte of a file to be read.\n\ -This function should return a coding system to decode the file contents\n\ +Two arguments are passed to this function: the file name\n\ +and the length of a file contents following the point.\n\ +This function should return a coding system to decode the file contents.\n\ +It should check the file name against `auto-coding-alist'.\n\ +If no coding system is decided, it should check a coding system\n\ specified in the heading lines with the format:\n\ -*- ... coding: CODING-SYSTEM; ... -*-\n\ or local variable spec of the tailing lines with `coding:' tag.");