Mercurial > hg > xemacs-beta
view src/text.c @ 1964:ebb2b9148aad
[xemacs-hg @ 2004-03-22 09:22:24 by stephent]
XEmacs 21.5.17 "chayote" is released.
author | stephent |
---|---|
date | Mon, 22 Mar 2004 09:25:04 +0000 |
parents | a8d8f419b459 |
children | 04bc9d2f42c7 |
line wrap: on
line source
/* Buffer manipulation primitives for XEmacs. Copyright (C) 1995 Sun Microsystems, Inc. Copyright (C) 1995, 1996, 2000, 2001, 2002, 2003 Ben Wing. Copyright (C) 1999 Martin Buchholz. This file is part of XEmacs. XEmacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. XEmacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with XEmacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Synched up with: Not in FSF. */ /* Authorship: */ #include <config.h> #include "lisp.h" #include "buffer.h" #include "charset.h" #include "file-coding.h" #include "lstream.h" #include "profile.h" /************************************************************************/ /* long comments */ /************************************************************************/ /* ========================================================================== 1. Intro to Characters, Character Sets, and Encodings ========================================================================== A character (which is, BTW, a surprisingly complex concept) is, in a written representation of text, the most basic written unit that has a meaning of its own. It's comparable to a phoneme when analyzing words in spoken speech (for example, the sound of `t' in English, which in fact has different pronunciations in different words -- aspirated in `time', unaspirated in `stop', unreleased or even pronounced as a glottal stop in `button', etc. -- but logically is a single concept). Like a phoneme, a character is an abstract concept defined by its *meaning*. The character `lowercase f', for example, can always be used to represent the first letter in the word `fill', regardless of whether it's drawn upright or italic, whether the `fi' combination is drawn as a single ligature, whether there are serifs on the bottom of the vertical stroke, etc. (These different appearances of a single character are often called "graphs" or "glyphs".) Our concern when representing text is on representing the abstract characters, and not on their exact appearance. A character set (or "charset"), as we define it, is a set of characters, each with an associated number (or set of numbers -- see below), called a "code point". It's important to understand that a character is not defined by any number attached to it, but by its meaning. For example, ASCII and EBCDIC are two charsets containing exactly the same characters (lowercase and uppercase letters, numbers 0 through 9, particular punctuation marks) but with different numberings. The `comma' character in ASCII and EBCDIC, for instance, is the same character despite having a different numbering. Conversely, when comparing ASCII and JIS-Roman, which look the same except that the latter has a yen sign substituted for the backslash, we would say that the backslash and yen sign are *not* the same characters, despite having the same number (95) and despite the fact that all other characters are present in both charsets, with the same numbering. ASCII and JIS-Roman, then, do *not* have exactly the same characters in them (ASCII has a backslash character but no yen-sign character, and vice-versa for JIS-Roman), unlike ASCII and EBCDIC, even though the numberings in ASCII and JIS-Roman are closer. It's also important to distinguish between charsets and encodings. For a simple charset like ASCII, there is only one encoding normally used -- each character is represented by a single byte, with the same value as its code point. For more complicated charsets, however, things are not so obvious. Unicode version 2, for example, is a large charset with thousands of characters, each indexed by a 16-bit number, often represented in hex, e.g. 0x05D0 for the Hebrew letter "aleph". One obvious encoding uses two bytes per character (actually two encodings, depending on which of the two possible byte orderings is chosen). This encoding is convenient for internal processing of Unicode text; however, it's incompatible with ASCII, so a different encoding, e.g. UTF-8, is usually used for external text, for example files or e-mail. UTF-8 represents Unicode characters with one to three bytes (often extended to six bytes to handle characters with up to 31-bit indices). Unicode characters 00 to 7F (identical with ASCII) are directly represented with one byte, and other characters with two or more bytes, each in the range 80 to FF. In general, a single encoding may be able to represent more than one charset. See also man/lispref/mule.texi. ========================================================================== 2. Character Sets ========================================================================== A particular character in a charset is indexed using one or more "position codes", which are non-negative integers. The number of position codes needed to identify a particular character in a charset is called the "dimension" of the charset. In XEmacs/Mule, all charsets have 1 or 2 dimensions, and the size of all charsets (except for a few special cases) is either 94, 96, 94 by 94, or 96 by 96. The range of position codes used to index characters from any of these types of character sets is as follows: Charset type Position code 1 Position code 2 ------------------------------------------------------------ 94 33 - 126 N/A 96 32 - 127 N/A 94x94 33 - 126 33 - 126 96x96 32 - 127 32 - 127 Note that in the above cases position codes do not start at an expected value such as 0 or 1. The reason for this will become clear later. For example, Latin-1 is a 96-character charset, and JISX0208 (the Japanese national character set) is a 94x94-character charset. [Note that, although the ranges above define the *valid* position codes for a charset, some of the slots in a particular charset may in fact be empty. This is the case for JISX0208, for example, where (e.g.) all the slots whose first position code is in the range 118 - 127 are empty.] There are three charsets that do not follow the above rules. All of them have one dimension, and have ranges of position codes as follows: Charset name Position code 1 ------------------------------------ ASCII 0 - 127 Control-1 0 - 31 Composite 0 - some large number (The upper bound of the position code for composite characters has not yet been determined, but it will probably be at least 16,383). ASCII is the union of two subsidiary character sets: Printing-ASCII (the printing ASCII character set, consisting of position codes 33 - 126, like for a standard 94-character charset) and Control-ASCII (the non-printing characters that would appear in a binary file with codes 0 - 32 and 127). Control-1 contains the non-printing characters that would appear in a binary file with codes 128 - 159. Composite contains characters that are generated by overstriking one or more characters from other charsets. Note that some characters in ASCII, and all characters in Control-1, are "control" (non-printing) characters. These have no printed representation but instead control some other function of the printing (e.g. TAB or 8 moves the current character position to the next tab stop). All other characters in all charsets are "graphic" (printing) characters. When a binary file is read in, the bytes in the file are assigned to character sets as follows: Bytes Character set Range -------------------------------------------------- 0 - 127 ASCII 0 - 127 128 - 159 Control-1 0 - 31 160 - 255 Latin-1 32 - 127 This is a bit ad-hoc but gets the job done. ========================================================================== 3. Encodings ========================================================================== An "encoding" is a way of numerically representing characters from one or more character sets. If an encoding only encompasses one character set, then the position codes for the characters in that character set could be used directly. This is not possible, however, if more than one character set is to be used in the encoding. For example, the conversion detailed above between bytes in a binary file and characters is effectively an encoding that encompasses the three character sets ASCII, Control-1, and Latin-1 in a stream of 8-bit bytes. Thus, an encoding can be viewed as a way of encoding characters from a specified group of character sets using a stream of bytes, each of which contains a fixed number of bits (but not necessarily 8, as in the common usage of "byte"). Here are descriptions of a couple of common encodings: A. Japanese EUC (Extended Unix Code) This encompasses the character sets: - Printing-ASCII, - Katakana-JISX0201 (half-width katakana, the right half of JISX0201). - Japanese-JISX0208 - Japanese-JISX0212 It uses 8-bit bytes. Note that Printing-ASCII and Katakana-JISX0201 are 94-character charsets, while Japanese-JISX0208 is a 94x94-character charset. The encoding is as follows: Character set Representation (PC == position-code) ------------- -------------- Printing-ASCII PC1 Japanese-JISX0208 PC1 + 0x80 | PC2 + 0x80 Katakana-JISX0201 0x8E | PC1 + 0x80 B. JIS7 This encompasses the character sets: - Printing-ASCII - Latin-JISX0201 (the left half of JISX0201; this character set is very similar to Printing-ASCII and is a 94-character charset) - Japanese-JISX0208 - Katakana-JISX0201 It uses 7-bit bytes. Unlike Japanese EUC, this is a "modal" encoding, which means that there are multiple states that the encoding can be in, which affect how the bytes are to be interpreted. Special sequences of bytes (called "escape sequences") are used to change states. The encoding is as follows: Character set Representation ------------- -------------- Printing-ASCII PC1 Latin-JISX0201 PC1 Katakana-JISX0201 PC1 Japanese-JISX0208 PC1 | PC2 Escape sequence ASCII equivalent Meaning --------------- ---------------- ------- 0x1B 0x28 0x42 ESC ( B invoke Printing-ASCII 0x1B 0x28 0x4A ESC ( J invoke Latin-JISX0201 0x1B 0x28 0x49 ESC ( I invoke Katakana-JISX0201 0x1B 0x24 0x42 ESC $ B invoke Japanese-JISX0208 Initially, Printing-ASCII is invoked. ========================================================================== 4. Internal Mule Encodings ========================================================================== In XEmacs/Mule, each character set is assigned a unique number, called a "leading byte". This is used in the encodings of a character. Leading bytes are in the range 0x80 - 0xFF (except for ASCII, which has a leading byte of 0), although some leading bytes are reserved. Charsets whose leading byte is in the range 0x80 - 0x9F are called "official" and are used for built-in charsets. Other charsets are called "private" and have leading bytes in the range 0xA0 - 0xFF; these are user-defined charsets. More specifically: Character set Leading byte ------------- ------------ ASCII 0 (0x7F in arrays indexed by leading byte) Composite 0x8D Dimension-1 Official 0x80 - 0x8C/0x8D (0x8E is free) Control 0x8F Dimension-2 Official 0x90 - 0x99 (0x9A - 0x9D are free) Dimension-1 Private Marker 0x9E Dimension-2 Private Marker 0x9F Dimension-1 Private 0xA0 - 0xEF Dimension-2 Private 0xF0 - 0xFF There are two internal encodings for characters in XEmacs/Mule. One is called "string encoding" and is an 8-bit encoding that is used for representing characters in a buffer or string. It uses 1 to 4 bytes per character. The other is called "character encoding" and is a 19-bit encoding that is used for representing characters individually in a variable. (In the following descriptions, we'll ignore composite characters for the moment. We also give a general (structural) overview first, followed later by the exact details.) A. Internal String Encoding ASCII characters are encoded using their position code directly. Other characters are encoded using their leading byte followed by their position code(s) with the high bit set. Characters in private character sets have their leading byte prefixed with a "leading byte prefix", which is either 0x9E or 0x9F. (No character sets are ever assigned these leading bytes.) Specifically: Character set Encoding (PC == position-code) ------------- -------- (LB == leading-byte) ASCII PC1 | Control-1 LB | PC1 + 0xA0 Dimension-1 official LB | PC1 + 0x80 Dimension-1 private 0x9E | LB | PC1 + 0x80 Dimension-2 official LB | PC1 | PC2 + 0x80 Dimension-2 private 0x9F | LB | PC1 + 0x80 | PC2 + 0x80 The basic characteristic of this encoding is that the first byte of all characters is in the range 0x00 - 0x9F, and the second and following bytes of all characters is in the range 0xA0 - 0xFF. This means that it is impossible to get out of sync, or more specifically: 1. Given any byte position, the beginning of the character it is within can be determined in constant time. 2. Given any byte position at the beginning of a character, the beginning of the next character can be determined in constant time. 3. Given any byte position at the beginning of a character, the beginning of the previous character can be determined in constant time. 4. Textual searches can simply treat encoded strings as if they were encoded in a one-byte-per-character fashion rather than the actual multi-byte encoding. None of the standard non-modal encodings meet all of these conditions. For example, EUC satisfies only (2) and (3), while Shift-JIS and Big5 (not yet described) satisfy only (2). (All non-modal encodings must satisfy (2), in order to be unambiguous.) B. Internal Character Encoding One 19-bit word represents a single character. The word is separated into three fields: Bit number: 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 <------------> <------------------> <------------------> Field: 1 2 3 Note that fields 2 and 3 hold 7 bits each, while field 1 holds 5 bits. Character set Field 1 Field 2 Field 3 ------------- ------- ------- ------- ASCII 0 0 PC1 range: (00 - 7F) Control-1 0 1 PC1 range: (00 - 1F) Dimension-1 official 0 LB - 0x7F PC1 range: (01 - 0D) (20 - 7F) Dimension-1 private 0 LB - 0x80 PC1 range: (20 - 6F) (20 - 7F) Dimension-2 official LB - 0x8F PC1 PC2 range: (01 - 0A) (20 - 7F) (20 - 7F) Dimension-2 private LB - 0xE1 PC1 PC2 range: (0F - 1E) (20 - 7F) (20 - 7F) Composite 0x1F ? ? Note that character codes 0 - 255 are the same as the "binary encoding" described above. Most of the code in XEmacs knows nothing of the representation of a character other than that values 0 - 255 represent ASCII, Control 1, and Latin 1. WARNING WARNING WARNING: The Boyer-Moore code in search.c, and the code in search_buffer() that determines whether that code can be used, knows that "field 3" in a character always corresponds to the last byte in the textual representation of the character. (This is important because the Boyer-Moore algorithm works by looking at the last byte of the search string and &&#### finish this. ========================================================================== 5. Buffer Positions and Other Typedefs ========================================================================== A. Buffer Positions There are three possible ways to specify positions in a buffer. All of these are one-based: the beginning of the buffer is position or index 1, and 0 is not a valid position. As a "buffer position" (typedef Charbpos): This is an index specifying an offset in characters from the beginning of the buffer. Note that buffer positions are logically *between* characters, not on a character. The difference between two buffer positions specifies the number of characters between those positions. Buffer positions are the only kind of position externally visible to the user. As a "byte index" (typedef Bytebpos): This is an index over the bytes used to represent the characters in the buffer. If there is no Mule support, this is identical to a buffer position, because each character is represented using one byte. However, with Mule support, many characters require two or more bytes for their representation, and so a byte index may be greater than the corresponding buffer position. As a "memory index" (typedef Membpos): This is the byte index adjusted for the gap. For positions before the gap, this is identical to the byte index. For positions after the gap, this is the byte index plus the gap size. There are two possible memory indices for the gap position; the memory index at the beginning of the gap should always be used, except in code that deals with manipulating the gap, where both indices may be seen. The address of the character "at" (i.e. following) a particular position can be obtained from the formula buffer_start_address + memory_index(position) - 1 except in the case of characters at the gap position. B. Other Typedefs Ichar: ------ This typedef represents a single Emacs character, which can be ASCII, ISO-8859, or some extended character, as would typically be used for Kanji. Note that the representation of a character as an Ichar is *not* the same as the representation of that same character in a string; thus, you cannot do the standard C trick of passing a pointer to a character to a function that expects a string. An Ichar takes up 19 bits of representation and (for code compatibility and such) is compatible with an int. This representation is visible on the Lisp level. The important characteristics of the Ichar representation are -- values 0x00 - 0x7f represent ASCII. -- values 0x80 - 0xff represent the right half of ISO-8859-1. -- values 0x100 and up represent all other characters. This means that Ichar values are upwardly compatible with the standard 8-bit representation of ASCII/ISO-8859-1. Ibyte: ------ The data in a buffer or string is logically made up of Ibyte objects, where a Ibyte takes up the same amount of space as a char. (It is declared differently, though, to catch invalid usages.) Strings stored using Ibytes are said to be in "internal format". The important characteristics of internal format are -- ASCII characters are represented as a single Ibyte, in the range 0 - 0x7f. -- All other characters are represented as a Ibyte in the range 0x80 - 0x9f followed by one or more Ibytes in the range 0xa0 to 0xff. This leads to a number of desirable properties: -- Given the position of the beginning of a character, you can find the beginning of the next or previous character in constant time. -- When searching for a substring or an ASCII character within the string, you need merely use standard searching routines. Extbyte: -------- Strings that go in or out of Emacs are in "external format", typedef'ed as an array of char or a char *. There is more than one external format (JIS, EUC, etc.) but they all have similar properties. They are modal encodings, which is to say that the meaning of particular bytes is not fixed but depends on what "mode" the string is currently in (e.g. bytes in the range 0 - 0x7f might be interpreted as ASCII, or as Hiragana, or as 2-byte Kanji, depending on the current mode). The mode starts out in ASCII/ISO-8859-1 and is switched using escape sequences -- for example, in the JIS encoding, 'ESC $ B' switches to a mode where pairs of bytes in the range 0 - 0x7f are interpreted as Kanji characters. External-formatted data is generally desirable for passing data between programs because it is upwardly compatible with standard ASCII/ISO-8859-1 strings and may require less space than internal encodings such as the one described above. In addition, some encodings (e.g. JIS) keep all characters (except the ESC used to switch modes) in the printing ASCII range 0x20 - 0x7e, which results in a much higher probability that the data will avoid being garbled in transmission. Externally-formatted data is generally not very convenient to work with, however, and for this reason is usually converted to internal format before any work is done on the string. NOTE: filenames need to be in external format so that ISO-8859-1 characters come out correctly. Charcount: ---------- This typedef represents a count of characters, such as a character offset into a string or the number of characters between two positions in a buffer. The difference between two Charbpos's is a Charcount, and character positions in a string are represented using a Charcount. Bytecount: ---------- Similar to a Charcount but represents a count of bytes. The difference between two Bytebpos's is a Bytecount. C. Usage of the Various Representations Memory indices are used in low-level functions in insdel.c and for extent endpoints and marker positions. The reason for this is that this way, the extents and markers don't need to be updated for most insertions, which merely shrink the gap and don't move any characters around in memory. (The beginning-of-gap memory index simplifies insertions w.r.t. markers, because text usually gets inserted after markers. For extents, it is merely for consistency, because text can get inserted either before or after an extent's endpoint depending on the open/closedness of the endpoint.) Byte indices are used in other code that needs to be fast, such as the searching, redisplay, and extent-manipulation code. Buffer positions are used in all other code. This is because this representation is easiest to work with (especially since Lisp code always uses buffer positions), necessitates the fewest changes to existing code, and is the safest (e.g. if the text gets shifted underneath a buffer position, it will still point to a character; if text is shifted under a byte index, it might point to the middle of a character, which would be bad). Similarly, Charcounts are used in all code that deals with strings except for code that needs to be fast, which used Bytecounts. Strings are always passed around internally using internal format. Conversions between external format are performed at the time that the data goes in or out of Emacs. D. Working With the Various Representations We write things this way because it's very important the MAX_BYTEBPOS_GAP_SIZE_3 is a multiple of 3. (As it happens, 65535 is a multiple of 3, but this may not always be the case. #### unfinished ========================================================================== 6. Miscellaneous ========================================================================== A. Unicode Support Unicode support is very desirable. Currrently we know how to handle externally-encoded Unicode data in various encodings -- UTF-16, UTF-8, etc. However, we really need to represent Unicode characters internally as-is, rather than converting to some language-specific character set. For efficiency, we should represent Unicode characters using 3 bytes rather than 4. This means we need to find leading bytes for Unicode. Given that there are 65,536 characters in Unicode and we can attach 96x96 = 9,216 characters per leading byte, we need eight leading bytes for Unicode. We currently have four free (0x9A - 0x9D), and with a little bit of rearranging we can get five: ASCII doesn't really need to take up a leading byte. (We could just as well use 0x7F, with a little change to the functions that assume that 0x80 is the lowest leading byte.) This means we still need to dump three leading bytes and move them into private space. The CNS charsets are good candidates since they are rarely used, and JAPANESE_JISX0208_1978 is becoming less and less used and could also be dumped. B. Composite Characters Composite characters are characters constructed by overstriking two or more regular characters. 1) The old Mule implementation involves storing composite characters in a buffer as a tag followed by all of the actual characters used to make up the composite character. I think this is a bad idea; it greatly complicates code that wants to handle strings one character at a time because it has to deal with the possibility of great big ungainly characters. It's much more reasonable to simply store an index into a table of composite characters. 2) The current implementation only allows for 16,384 separate composite characters over the lifetime of the XEmacs process. This could become a potential problem if the user edited lots of different files that use composite characters. Due to FSF bogosity, increasing the number of allowable composite characters under Mule would decrease the number of possible faces that can exist. Mule already has shrunk this to 2048, and further shrinkage would become uncomfortable. No such problems exist in XEmacs. Composite characters could be represented as 0x8D C1 C2 C3, where each C[1-3] is in the range 0xA0 - 0xFF. This allows for slightly under 2^20 (one million) composite characters over the XEmacs process lifetime, and you only need to increase the size of a Mule character from 19 to 21 bits. Or you could use 0x8D C1 C2 C3 C4, allowing for about 85 million (slightly over 2^26) composite characters. */ /************************************************************************/ /* declarations */ /************************************************************************/ Eistring the_eistring_zero_init, the_eistring_malloc_zero_init; #define MAX_CHARBPOS_GAP_SIZE_3 (65535/3) #define MAX_BYTEBPOS_GAP_SIZE_3 (3 * MAX_CHARBPOS_GAP_SIZE_3) short three_to_one_table[1 + MAX_BYTEBPOS_GAP_SIZE_3]; #ifdef MULE /* Table of number of bytes in the string representation of a character indexed by the first byte of that representation. rep_bytes_by_first_byte(c) is more efficient than the equivalent canonical computation: XCHARSET_REP_BYTES (charset_by_leading_byte (c)) */ const Bytecount rep_bytes_by_first_byte[0xA0] = { /* 0x00 - 0x7f are for straight ASCII */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f are for Dimension-1 official charsets */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x90 - 0x9d are for Dimension-2 official charsets */ /* 0x9e is for Dimension-1 private charsets */ /* 0x9f is for Dimension-2 private charsets */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 }; #ifdef ENABLE_COMPOSITE_CHARS /* Hash tables for composite chars. One maps string representing composed chars to their equivalent chars; one goes the other way. */ Lisp_Object Vcomposite_char_char2string_hash_table; Lisp_Object Vcomposite_char_string2char_hash_table; static int composite_char_row_next; static int composite_char_col_next; #endif /* ENABLE_COMPOSITE_CHARS */ #endif /* MULE */ Lisp_Object QSin_char_byte_conversion; Lisp_Object QSin_internal_external_conversion; /************************************************************************/ /* qxestr***() functions */ /************************************************************************/ /* Most are inline functions in lisp.h */ int qxesprintf (Ibyte *buffer, const CIbyte *format, ...) { va_list args; int retval; va_start (args, format); retval = vsprintf ((char *) buffer, format, args); va_end (args); return retval; } /* strcasecmp() implementation from BSD */ static Ibyte strcasecmp_charmap[] = { 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 }; /* A version that works like generic strcasecmp() -- only collapsing case in ASCII A-Z/a-z. This is safe on Mule strings due to the current representation. This version was written by some Berkeley coder, favoring nanosecond improvements over clarity. In all other versions below, we use symmetrical algorithms that may sacrifice a few machine cycles but are MUCH MUCH clearer, which counts a lot more. */ int qxestrcasecmp (const Ibyte *s1, const Ibyte *s2) { Ibyte *cm = strcasecmp_charmap; while (cm[*s1] == cm[*s2++]) if (*s1++ == '\0') return (0); return (cm[*s1] - cm[*--s2]); } int ascii_strcasecmp (const Char_ASCII *s1, const Char_ASCII *s2) { return qxestrcasecmp ((const Ibyte *) s1, (const Ibyte *) s2); } int qxestrcasecmp_c (const Ibyte *s1, const Char_ASCII *s2) { return qxestrcasecmp (s1, (const Ibyte *) s2); } /* An internationalized version that collapses case in a general fashion. */ int qxestrcasecmp_i18n (const Ibyte *s1, const Ibyte *s2) { while (*s1 && *s2) { if (DOWNCASE (0, itext_ichar (s1)) != DOWNCASE (0, itext_ichar (s2))) break; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); } return (DOWNCASE (0, itext_ichar (s1)) - DOWNCASE (0, itext_ichar (s2))); } /* The only difference between these next two and qxememcasecmp()/qxememcasecmp_i18n() is that these two will stop if both strings are equal and less than LEN in length, while the mem...() versions would would run off the end. */ int qxestrncasecmp (const Ibyte *s1, const Ibyte *s2, Bytecount len) { Ibyte *cm = strcasecmp_charmap; while (len--) { int diff = cm[*s1] - cm[*s2]; if (diff != 0) return diff; if (!*s1) return 0; s1++, s2++; } return 0; } int ascii_strncasecmp (const Char_ASCII *s1, const Char_ASCII *s2, Bytecount len) { return qxestrncasecmp ((const Ibyte *) s1, (const Ibyte *) s2, len); } int qxestrncasecmp_c (const Ibyte *s1, const Char_ASCII *s2, Bytecount len) { return qxestrncasecmp (s1, (const Ibyte *) s2, len); } /* Compare LEN_FROM_S1 worth of characters from S1 with the same number of characters from S2, case insensitive. NOTE: Downcasing can convert characters from one length in bytes to another, so reversing S1 and S2 is *NOT* a symmetric operations! You must choose a length that agrees with S1. */ int qxestrncasecmp_i18n (const Ibyte *s1, const Ibyte *s2, Bytecount len_from_s1) { while (len_from_s1 > 0) { const Ibyte *old_s1 = s1; int diff = (DOWNCASE (0, itext_ichar (s1)) - DOWNCASE (0, itext_ichar (s2))); if (diff != 0) return diff; if (!*s1) return 0; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); len_from_s1 -= s1 - old_s1; } return 0; } int qxememcmp (const Ibyte *s1, const Ibyte *s2, Bytecount len) { return memcmp (s1, s2, len); } int qxememcmp4 (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2) { int retval = qxememcmp (s1, s2, min (len1, len2)); if (retval) return retval; return len1 - len2; } int qxememcasecmp (const Ibyte *s1, const Ibyte *s2, Bytecount len) { Ibyte *cm = strcasecmp_charmap; while (len--) { int diff = cm[*s1] - cm[*s2]; if (diff != 0) return diff; s1++, s2++; } return 0; } int qxememcasecmp4 (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2) { int retval = qxememcasecmp (s1, s2, min (len1, len2)); if (retval) return retval; return len1 - len2; } /* Do a character-by-character comparison, returning "which is greater" by comparing the Ichar values. (#### Should have option to compare Unicode points) */ int qxetextcmp (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2) { while (len1 > 0 && len2 > 0) { const Ibyte *old_s1 = s1; const Ibyte *old_s2 = s2; int diff = itext_ichar (s1) - itext_ichar (s2); if (diff != 0) return diff; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); len1 -= s1 - old_s1; len2 -= s2 - old_s2; } assert (len1 >= 0 && len2 >= 0); return len1 - len2; } int qxetextcmp_matching (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2, Charcount *matching) { *matching = 0; while (len1 > 0 && len2 > 0) { const Ibyte *old_s1 = s1; const Ibyte *old_s2 = s2; int diff = itext_ichar (s1) - itext_ichar (s2); if (diff != 0) return diff; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); len1 -= s1 - old_s1; len2 -= s2 - old_s2; (*matching)++; } assert (len1 >= 0 && len2 >= 0); return len1 - len2; } /* Do a character-by-character comparison, returning "which is greater" by comparing the Ichar values, case insensitively (by downcasing both first). (#### Should have option to compare Unicode points) In this case, both lengths must be specified becaused downcasing can convert characters from one length in bytes to another; therefore, two blocks of text of different length might be equal. If both compare equal up to the limit in length of one but not the other, the longer one is "greater". */ int qxetextcasecmp (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2) { while (len1 > 0 && len2 > 0) { const Ibyte *old_s1 = s1; const Ibyte *old_s2 = s2; int diff = (DOWNCASE (0, itext_ichar (s1)) - DOWNCASE (0, itext_ichar (s2))); if (diff != 0) return diff; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); len1 -= s1 - old_s1; len2 -= s2 - old_s2; } assert (len1 >= 0 && len2 >= 0); return len1 - len2; } /* Like qxetextcasecmp() but also return number of characters at beginning that match. */ int qxetextcasecmp_matching (const Ibyte *s1, Bytecount len1, const Ibyte *s2, Bytecount len2, Charcount *matching) { *matching = 0; while (len1 > 0 && len2 > 0) { const Ibyte *old_s1 = s1; const Ibyte *old_s2 = s2; int diff = (DOWNCASE (0, itext_ichar (s1)) - DOWNCASE (0, itext_ichar (s2))); if (diff != 0) return diff; INC_IBYTEPTR (s1); INC_IBYTEPTR (s2); len1 -= s1 - old_s1; len2 -= s2 - old_s2; (*matching)++; } assert (len1 >= 0 && len2 >= 0); return len1 - len2; } int lisp_strcasecmp (Lisp_Object s1, Lisp_Object s2) { Ibyte *cm = strcasecmp_charmap; Ibyte *p1 = XSTRING_DATA (s1); Ibyte *p2 = XSTRING_DATA (s2); Ibyte *e1 = p1 + XSTRING_LENGTH (s1); Ibyte *e2 = p2 + XSTRING_LENGTH (s2); /* again, we use a symmetric algorithm and favor clarity over nanosecond improvements. */ while (1) { /* if we reached the end of either string, compare lengths. do NOT compare the final null byte against anything, in case the other string also has a null byte at that position. */ if (p1 == e1 || p2 == e2) return e1 - e2; if (cm[*p1] != cm[*p2]) return cm[*p1] - cm[*p2]; p1++, p2++; } } int lisp_strcasecmp_i18n (Lisp_Object s1, Lisp_Object s2) { return qxetextcasecmp (XSTRING_DATA (s1), XSTRING_LENGTH (s1), XSTRING_DATA (s2), XSTRING_LENGTH (s2)); } /************************************************************************/ /* conversion between textual representations */ /************************************************************************/ /* NOTE: Does not reset the Dynarr. */ void convert_ibyte_string_into_ichar_dynarr (const Ibyte *str, Bytecount len, Ichar_dynarr *dyn) { const Ibyte *strend = str + len; while (str < strend) { Ichar ch = itext_ichar (str); Dynarr_add (dyn, ch); INC_IBYTEPTR (str); } } Charcount convert_ibyte_string_into_ichar_string (const Ibyte *str, Bytecount len, Ichar *arr) { const Ibyte *strend = str + len; Charcount newlen = 0; while (str < strend) { Ichar ch = itext_ichar (str); arr[newlen++] = ch; INC_IBYTEPTR (str); } return newlen; } /* Convert an array of Ichars into the equivalent string representation. Store into the given Ibyte dynarr. Does not reset the dynarr. Does not add a terminating zero. */ void convert_ichar_string_into_ibyte_dynarr (Ichar *arr, int nels, Ibyte_dynarr *dyn) { Ibyte str[MAX_ICHAR_LEN]; int i; for (i = 0; i < nels; i++) { Bytecount len = set_itext_ichar (str, arr[i]); Dynarr_add_many (dyn, str, len); } } /* Convert an array of Ichars into the equivalent string representation. Malloc the space needed for this and return it. If LEN_OUT is not a NULL pointer, store into LEN_OUT the number of Ibytes in the malloc()ed string. Note that the actual number of Ibytes allocated is one more than this: the returned string is zero-terminated. */ Ibyte * convert_ichar_string_into_malloced_string (Ichar *arr, int nels, Bytecount *len_out) { /* Damn zero-termination. */ Ibyte *str = (Ibyte *) ALLOCA (nels * MAX_ICHAR_LEN + 1); Ibyte *strorig = str; Bytecount len; int i; for (i = 0; i < nels; i++) str += set_itext_ichar (str, arr[i]); *str = '\0'; len = str - strorig; str = (Ibyte *) xmalloc (1 + len); memcpy (str, strorig, 1 + len); if (len_out) *len_out = len; return str; } #define COPY_TEXT_BETWEEN_FORMATS(srcfmt, dstfmt) \ do \ { \ if (dst) \ { \ Ibyte *dstend = dst + dstlen; \ Ibyte *dstp = dst; \ const Ibyte *srcend = src + srclen; \ const Ibyte *srcp = src; \ \ while (srcp < srcend) \ { \ Ichar ch = itext_ichar_fmt (srcp, srcfmt, srcobj); \ Bytecount len = ichar_len_fmt (ch, dstfmt); \ \ if (dstp + len <= dstend) \ { \ set_itext_ichar_fmt (dstp, ch, dstfmt, dstobj); \ dstp += len; \ } \ else \ break; \ INC_IBYTEPTR_FMT (srcp, srcfmt); \ } \ text_checking_assert (srcp <= srcend); \ if (src_used) \ *src_used = srcp - src; \ return dstp - dst; \ } \ else \ { \ const Ibyte *srcend = src + srclen; \ const Ibyte *srcp = src; \ Bytecount total = 0; \ \ while (srcp < srcend) \ { \ total += ichar_len_fmt (itext_ichar_fmt (srcp, srcfmt, \ srcobj), dstfmt); \ INC_IBYTEPTR_FMT (srcp, srcfmt); \ } \ text_checking_assert (srcp == srcend); \ if (src_used) \ *src_used = srcp - src; \ return total; \ } \ } \ while (0) /* Copy as much text from SRC/SRCLEN to DST/DSTLEN as will fit, converting from SRCFMT/SRCOBJ to DSTFMT/DSTOBJ. Return number of bytes stored into DST as return value, and number of bytes copied from SRC through SRC_USED (if not NULL). If DST is NULL, don't actually store anything and just return the size needed to store all the text. Will not copy partial characters into DST. */ Bytecount copy_text_between_formats (const Ibyte *src, Bytecount srclen, Internal_Format srcfmt, Lisp_Object srcobj, Ibyte *dst, Bytecount dstlen, Internal_Format dstfmt, Lisp_Object dstobj, Bytecount *src_used) { if (srcfmt == dstfmt && objects_have_same_internal_representation (srcobj, dstobj)) { if (dst) { srclen = min (srclen, dstlen); srclen = validate_ibyte_string_backward (src, srclen); memcpy (dst, src, srclen); if (src_used) *src_used = srclen; return srclen; } else return srclen; } /* Everything before the final else statement is an optimization. The inner loops inside COPY_TEXT_BETWEEN_FORMATS() have a number of calls to *_fmt(), each of which has a switch statement in it. By using constants as the FMT argument, these switch statements will be optimized out of existence. */ #define ELSE_FORMATS(fmt1, fmt2) \ else if (srcfmt == fmt1 && dstfmt == fmt2) \ COPY_TEXT_BETWEEN_FORMATS (fmt1, fmt2) ELSE_FORMATS (FORMAT_DEFAULT, FORMAT_8_BIT_FIXED); ELSE_FORMATS (FORMAT_8_BIT_FIXED, FORMAT_DEFAULT); ELSE_FORMATS (FORMAT_DEFAULT, FORMAT_32_BIT_FIXED); ELSE_FORMATS (FORMAT_32_BIT_FIXED, FORMAT_DEFAULT); else COPY_TEXT_BETWEEN_FORMATS (srcfmt, dstfmt); #undef ELSE_FORMATS } /* Copy as much buffer text in BUF, starting at POS, of length LEN, as will fit into DST/DSTLEN, converting to DSTFMT. Return number of bytes stored into DST as return value, and number of bytes copied from BUF through SRC_USED (if not NULL). If DST is NULL, don't actually store anything and just return the size needed to store all the text. */ Bytecount copy_buffer_text_out (struct buffer *buf, Bytebpos pos, Bytecount len, Ibyte *dst, Bytecount dstlen, Internal_Format dstfmt, Lisp_Object dstobj, Bytecount *src_used) { Bytecount dst_used = 0; if (src_used) *src_used = 0; { BUFFER_TEXT_LOOP (buf, pos, len, runptr, runlen) { Bytecount the_src_used, the_dst_used; the_dst_used = copy_text_between_formats (runptr, runlen, BUF_FORMAT (buf), wrap_buffer (buf), dst, dstlen, dstfmt, dstobj, &the_src_used); dst_used += the_dst_used; if (src_used) *src_used += the_src_used; if (dst) { dst += the_dst_used; dstlen -= the_dst_used; /* Stop if we didn't use all of the source text. Also stop if the destination is full. We need the first test because there might be a couple bytes left in the destination, but not enough to fit a full character. The first test will in fact catch the vast majority of cases where the destination is empty, too -- but in case the destination holds *exactly* the run length, we put in the second check. (It shouldn't really matter though -- next time through we'll just get a 0.) */ if (the_src_used < runlen || !dstlen) break; } } } return dst_used; } /************************************************************************/ /* charset properties of strings */ /************************************************************************/ void find_charsets_in_ibyte_string (unsigned char *charsets, const Ibyte *str, Bytecount len) { #ifndef MULE /* Telescope this. */ charsets[0] = 1; #else const Ibyte *strend = str + len; memset (charsets, 0, NUM_LEADING_BYTES); /* #### SJT doesn't like this. */ if (len == 0) { charsets[XCHARSET_LEADING_BYTE (Vcharset_ascii) - MIN_LEADING_BYTE] = 1; return; } while (str < strend) { charsets[ichar_leading_byte (itext_ichar (str)) - MIN_LEADING_BYTE] = 1; INC_IBYTEPTR (str); } #endif } void find_charsets_in_ichar_string (unsigned char *charsets, const Ichar *str, Charcount len) { #ifndef MULE /* Telescope this. */ charsets[0] = 1; #else int i; memset (charsets, 0, NUM_LEADING_BYTES); /* #### SJT doesn't like this. */ if (len == 0) { charsets[XCHARSET_LEADING_BYTE (Vcharset_ascii) - MIN_LEADING_BYTE] = 1; return; } for (i = 0; i < len; i++) { charsets[ichar_leading_byte (str[i]) - MIN_LEADING_BYTE] = 1; } #endif } int ibyte_string_displayed_columns (const Ibyte *str, Bytecount len) { int cols = 0; const Ibyte *end = str + len; while (str < end) { #ifdef MULE Ichar ch = itext_ichar (str); cols += XCHARSET_COLUMNS (ichar_charset (ch)); #else cols++; #endif INC_IBYTEPTR (str); } return cols; } int ichar_string_displayed_columns (const Ichar *str, Charcount len) { #ifdef MULE int cols = 0; int i; for (i = 0; i < len; i++) cols += XCHARSET_COLUMNS (ichar_charset (str[i])); return cols; #else /* not MULE */ return len; #endif } Charcount ibyte_string_nonascii_chars (const Ibyte *str, Bytecount len) { #ifdef MULE const Ibyte *end = str + len; Charcount retval = 0; while (str < end) { if (!byte_ascii_p (*str)) retval++; INC_IBYTEPTR (str); } return retval; #else return 0; #endif } /***************************************************************************/ /* Eistring helper functions */ /***************************************************************************/ int eistr_casefiddle_1 (Ibyte *olddata, Bytecount len, Ibyte *newdata, int downp) { Ibyte *endp = olddata + len; Ibyte *newp = newdata; int changedp = 0; while (olddata < endp) { Ichar c = itext_ichar (olddata); Ichar newc; if (downp) newc = DOWNCASE (0, c); else newc = UPCASE (0, c); if (c != newc) changedp = 1; newp += set_itext_ichar (newp, newc); INC_IBYTEPTR (olddata); } *newp = '\0'; return changedp ? newp - newdata : 0; } int eifind_large_enough_buffer (int oldbufsize, int needed_size) { while (oldbufsize < needed_size) { oldbufsize = oldbufsize * 3 / 2; oldbufsize = max (oldbufsize, 32); } return oldbufsize; } void eito_malloc_1 (Eistring *ei) { if (ei->mallocp_) return; ei->mallocp_ = 1; if (ei->data_) { Ibyte *newdata; ei->max_size_allocated_ = eifind_large_enough_buffer (0, ei->bytelen_ + 1); newdata = (Ibyte *) xmalloc (ei->max_size_allocated_); memcpy (newdata, ei->data_, ei->bytelen_ + 1); ei->data_ = newdata; } if (ei->extdata_) { Extbyte *newdata = (Extbyte *) xmalloc (ei->extlen_ + 2); memcpy (newdata, ei->extdata_, ei->extlen_); /* Double null-terminate in case of Unicode data */ newdata[ei->extlen_] = '\0'; newdata[ei->extlen_ + 1] = '\0'; ei->extdata_ = newdata; } } int eicmp_1 (Eistring *ei, Bytecount off, Charcount charoff, Bytecount len, Charcount charlen, const Ibyte *data, const Eistring *ei2, int is_c, int fold_case) { assert ((off < 0) != (charoff < 0)); if (off < 0) { off = charcount_to_bytecount (ei->data_, charoff); if (charlen < 0) len = -1; else len = charcount_to_bytecount (ei->data_ + off, charlen); } if (len < 0) len = ei->bytelen_ - off; assert (off >= 0 && off <= ei->bytelen_); assert (len >= 0 && off + len <= ei->bytelen_); assert ((data == 0) != (ei == 0)); assert ((is_c != 0) == (data != 0)); assert (fold_case >= 0 && fold_case <= 2); { Bytecount dstlen; const Ibyte *src = ei->data_, *dst; if (data) { dst = data; dstlen = qxestrlen (data); } else { dst = ei2->data_; dstlen = ei2->bytelen_; } if (is_c) EI_ASSERT_ASCII ((Char_ASCII *) dst, dstlen); return (fold_case == 0 ? qxememcmp4 (src, len, dst, dstlen) : fold_case == 1 ? qxememcasecmp4 (src, len, dst, dstlen) : qxetextcasecmp (src, len, dst, dstlen)); } } Ibyte * eicpyout_malloc_fmt (Eistring *eistr, Bytecount *len_out, Internal_Format fmt, Lisp_Object object) { Ibyte *ptr; assert (fmt == FORMAT_DEFAULT); ptr = xnew_array (Ibyte, eistr->bytelen_ + 1); if (len_out) *len_out = eistr->bytelen_; memcpy (ptr, eistr->data_, eistr->bytelen_ + 1); return ptr; } /************************************************************************/ /* Charcount/Bytecount conversion */ /************************************************************************/ /* Optimization. Do it. Live it. Love it. */ #ifdef MULE /* Skip as many ASCII bytes as possible in the memory block [PTR, END). Return pointer to the first non-ASCII byte. optimized for long stretches of ASCII. */ inline static const Ibyte * skip_ascii (const Ibyte *ptr, const Ibyte *end) { #ifdef EFFICIENT_INT_128_BIT # define STRIDE_TYPE INT_128_BIT # define HIGH_BIT_MASK \ MAKE_128_BIT_UNSIGNED_CONSTANT (0x80808080808080808080808080808080) #elif defined (EFFICIENT_INT_64_BIT) # define STRIDE_TYPE INT_64_BIT # define HIGH_BIT_MASK MAKE_64_BIT_UNSIGNED_CONSTANT (0x8080808080808080) #else # define STRIDE_TYPE INT_32_BIT # define HIGH_BIT_MASK MAKE_32_BIT_UNSIGNED_CONSTANT (0x80808080) #endif #define ALIGN_BITS ((EMACS_UINT) (ALIGNOF (STRIDE_TYPE) - 1)) #define ALIGN_MASK (~ ALIGN_BITS) #define ALIGNED(ptr) ((((EMACS_UINT) ptr) & ALIGN_BITS) == 0) #define STRIDE sizeof (STRIDE_TYPE) const unsigned STRIDE_TYPE *ascii_end; /* Need to do in 3 sections -- before alignment start, aligned chunk, after alignment end. */ while (!ALIGNED (ptr)) { if (ptr == end || !byte_ascii_p (*ptr)) return ptr; ptr++; } ascii_end = (const unsigned STRIDE_TYPE *) ptr; /* This loop screams, because we can detect ASCII characters 4 or 8 at a time. */ while ((const Ibyte *) ascii_end + STRIDE <= end && !(*ascii_end & HIGH_BIT_MASK)) ascii_end++; ptr = (Ibyte *) ascii_end; while (ptr < end && byte_ascii_p (*ptr)) ptr++; return ptr; } /* Function equivalents of bytecount_to_charcount/charcount_to_bytecount. These work on strings of all sizes but are more efficient than a simple loop on large strings and probably less efficient on sufficiently small strings. */ Charcount bytecount_to_charcount_fun (const Ibyte *ptr, Bytecount len) { Charcount count = 0; const Ibyte *end = ptr + len; while (1) { const Ibyte *newptr = skip_ascii (ptr, end); count += newptr - ptr; ptr = newptr; if (ptr == end) break; { /* Optimize for successive characters from the same charset */ Ibyte leading_byte = *ptr; int bytes = rep_bytes_by_first_byte (leading_byte); while (ptr < end && *ptr == leading_byte) ptr += bytes, count++; } } /* Bomb out if the specified substring ends in the middle of a character. Note that we might have already gotten a core dump above from an invalid reference, but at least we will get no farther than here. This also catches len < 0. */ text_checking_assert (ptr == end); return count; } Bytecount charcount_to_bytecount_fun (const Ibyte *ptr, Charcount len) { const Ibyte *newptr = ptr; while (1) { const Ibyte *newnewptr = skip_ascii (newptr, newptr + len); len -= newnewptr - newptr; newptr = newnewptr; if (!len) break; { /* Optimize for successive characters from the same charset */ Ibyte leading_byte = *newptr; int bytes = rep_bytes_by_first_byte (leading_byte); while (len > 0 && *newptr == leading_byte) newptr += bytes, len--; } } return newptr - ptr; } /* The next two functions are the actual meat behind the charbpos-to-bytebpos and bytebpos-to-charbpos conversions. Currently the method they use is fairly unsophisticated; see buffer.h. Note that charbpos_to_bytebpos_func() is probably the most-called function in all of XEmacs. Therefore, it must be FAST FAST FAST. This is the reason why so much of the code is duplicated. Similar considerations apply to bytebpos_to_charbpos_func(), although less so because the function is not called so often. #### At some point this should use a more sophisticated method; see buffer.h. */ static int not_very_random_number; Bytebpos charbpos_to_bytebpos_func (struct buffer *buf, Charbpos x) { Charbpos bufmin; Charbpos bufmax; Bytebpos bytmin; Bytebpos bytmax; int size; int forward_p; Bytebpos retval; int diff_so_far; int add_to_cache = 0; PROFILE_DECLARE (); /* Check for some cached positions, for speed. */ if (x == BUF_PT (buf)) return BYTE_BUF_PT (buf); if (x == BUF_ZV (buf)) return BYTE_BUF_ZV (buf); if (x == BUF_BEGV (buf)) return BYTE_BUF_BEGV (buf); PROFILE_RECORD_ENTERING_SECTION (QSin_char_byte_conversion); bufmin = buf->text->mule_bufmin; bufmax = buf->text->mule_bufmax; bytmin = buf->text->mule_bytmin; bytmax = buf->text->mule_bytmax; size = (1 << buf->text->mule_shifter) + !!buf->text->mule_three_p; /* The basic idea here is that we shift the "known region" up or down until it overlaps the specified position. We do this by moving the upper bound of the known region up one character at a time, and moving the lower bound of the known region up as necessary when the size of the character just seen changes. We optimize this, however, by first shifting the known region to one of the cached points if it's close by. (We don't check BEG or Z, even though they're cached; most of the time these will be the same as BEGV and ZV, and when they're not, they're not likely to be used.) */ if (x > bufmax) { Charbpos diffmax = x - bufmax; Charbpos diffpt = x - BUF_PT (buf); Charbpos diffzv = BUF_ZV (buf) - x; /* #### This value could stand some more exploration. */ Charcount heuristic_hack = (bufmax - bufmin) >> 2; /* Check if the position is closer to PT or ZV than to the end of the known region. */ if (diffpt < 0) diffpt = -diffpt; if (diffzv < 0) diffzv = -diffzv; /* But also implement a heuristic that favors the known region over PT or ZV. The reason for this is that switching to PT or ZV will wipe out the knowledge in the known region, which might be annoying if the known region is large and PT or ZV is not that much closer than the end of the known region. */ diffzv += heuristic_hack; diffpt += heuristic_hack; if (diffpt < diffmax && diffpt <= diffzv) { bufmax = bufmin = BUF_PT (buf); bytmax = bytmin = BYTE_BUF_PT (buf); /* We set the size to 1 even though it doesn't really matter because the new known region contains no characters. We do this because this is the most likely size of the characters around the new known region, and we avoid potential yuckiness that is done when size == 3. */ size = 1; } if (diffzv < diffmax) { bufmax = bufmin = BUF_ZV (buf); bytmax = bytmin = BYTE_BUF_ZV (buf); size = 1; } } #ifdef ERROR_CHECK_TEXT else if (x >= bufmin) abort (); #endif else { Charbpos diffmin = bufmin - x; Charbpos diffpt = BUF_PT (buf) - x; Charbpos diffbegv = x - BUF_BEGV (buf); /* #### This value could stand some more exploration. */ Charcount heuristic_hack = (bufmax - bufmin) >> 2; if (diffpt < 0) diffpt = -diffpt; if (diffbegv < 0) diffbegv = -diffbegv; /* But also implement a heuristic that favors the known region -- see above. */ diffbegv += heuristic_hack; diffpt += heuristic_hack; if (diffpt < diffmin && diffpt <= diffbegv) { bufmax = bufmin = BUF_PT (buf); bytmax = bytmin = BYTE_BUF_PT (buf); /* We set the size to 1 even though it doesn't really matter because the new known region contains no characters. We do this because this is the most likely size of the characters around the new known region, and we avoid potential yuckiness that is done when size == 3. */ size = 1; } if (diffbegv < diffmin) { bufmax = bufmin = BUF_BEGV (buf); bytmax = bytmin = BYTE_BUF_BEGV (buf); size = 1; } } diff_so_far = x > bufmax ? x - bufmax : bufmin - x; if (diff_so_far > 50) { /* If we have to move more than a certain amount, then look into our cache. */ int minval = INT_MAX; int found = 0; int i; add_to_cache = 1; /* I considered keeping the positions ordered. This would speed up this loop, but updating the cache would take longer, so it doesn't seem like it would really matter. */ for (i = 0; i < 16; i++) { int diff = buf->text->mule_charbpos_cache[i] - x; if (diff < 0) diff = -diff; if (diff < minval) { minval = diff; found = i; } } if (minval < diff_so_far) { bufmax = bufmin = buf->text->mule_charbpos_cache[found]; bytmax = bytmin = buf->text->mule_bytebpos_cache[found]; size = 1; } } /* It's conceivable that the caching above could lead to X being the same as one of the range edges. */ if (x >= bufmax) { Bytebpos newmax; Bytecount newsize; forward_p = 1; while (x > bufmax) { newmax = bytmax; INC_BYTEBPOS (buf, newmax); newsize = newmax - bytmax; if (newsize != size) { bufmin = bufmax; bytmin = bytmax; size = newsize; } bytmax = newmax; bufmax++; } retval = bytmax; /* #### Should go past the found location to reduce the number of times that this function is called */ } else /* x < bufmin */ { Bytebpos newmin; Bytecount newsize; forward_p = 0; while (x < bufmin) { newmin = bytmin; DEC_BYTEBPOS (buf, newmin); newsize = bytmin - newmin; if (newsize != size) { bufmax = bufmin; bytmax = bytmin; size = newsize; } bytmin = newmin; bufmin--; } retval = bytmin; /* #### Should go past the found location to reduce the number of times that this function is called */ } /* If size is three, than we have to max sure that the range we discovered isn't too large, because we use a fixed-length table to divide by 3. */ if (size == 3) { int gap = bytmax - bytmin; buf->text->mule_three_p = 1; buf->text->mule_shifter = 1; if (gap > MAX_BYTEBPOS_GAP_SIZE_3) { if (forward_p) { bytmin = bytmax - MAX_BYTEBPOS_GAP_SIZE_3; bufmin = bufmax - MAX_CHARBPOS_GAP_SIZE_3; } else { bytmax = bytmin + MAX_BYTEBPOS_GAP_SIZE_3; bufmax = bufmin + MAX_CHARBPOS_GAP_SIZE_3; } } } else { buf->text->mule_three_p = 0; if (size == 4) buf->text->mule_shifter = 2; else buf->text->mule_shifter = size - 1; } buf->text->mule_bufmin = bufmin; buf->text->mule_bufmax = bufmax; buf->text->mule_bytmin = bytmin; buf->text->mule_bytmax = bytmax; if (add_to_cache) { int replace_loc; /* We throw away a "random" cached value and replace it with the new value. It doesn't actually have to be very random at all, just evenly distributed. #### It would be better to use a least-recently-used algorithm or something that tries to space things out, but I'm not sure it's worth it to go to the trouble of maintaining that. */ not_very_random_number += 621; replace_loc = not_very_random_number & 15; buf->text->mule_charbpos_cache[replace_loc] = x; buf->text->mule_bytebpos_cache[replace_loc] = retval; } PROFILE_RECORD_EXITING_SECTION (QSin_char_byte_conversion); return retval; } /* The logic in this function is almost identical to the logic in the previous function. */ Charbpos bytebpos_to_charbpos_func (struct buffer *buf, Bytebpos x) { Charbpos bufmin; Charbpos bufmax; Bytebpos bytmin; Bytebpos bytmax; int size; int forward_p; Charbpos retval; int diff_so_far; int add_to_cache = 0; PROFILE_DECLARE (); /* Check for some cached positions, for speed. */ if (x == BYTE_BUF_PT (buf)) return BUF_PT (buf); if (x == BYTE_BUF_ZV (buf)) return BUF_ZV (buf); if (x == BYTE_BUF_BEGV (buf)) return BUF_BEGV (buf); PROFILE_RECORD_ENTERING_SECTION (QSin_char_byte_conversion); bufmin = buf->text->mule_bufmin; bufmax = buf->text->mule_bufmax; bytmin = buf->text->mule_bytmin; bytmax = buf->text->mule_bytmax; size = (1 << buf->text->mule_shifter) + !!buf->text->mule_three_p; /* The basic idea here is that we shift the "known region" up or down until it overlaps the specified position. We do this by moving the upper bound of the known region up one character at a time, and moving the lower bound of the known region up as necessary when the size of the character just seen changes. We optimize this, however, by first shifting the known region to one of the cached points if it's close by. (We don't check BYTE_BEG or BYTE_Z, even though they're cached; most of the time these will be the same as BYTE_BEGV and BYTE_ZV, and when they're not, they're not likely to be used.) */ if (x > bytmax) { Bytebpos diffmax = x - bytmax; Bytebpos diffpt = x - BYTE_BUF_PT (buf); Bytebpos diffzv = BYTE_BUF_ZV (buf) - x; /* #### This value could stand some more exploration. */ Bytecount heuristic_hack = (bytmax - bytmin) >> 2; /* Check if the position is closer to PT or ZV than to the end of the known region. */ if (diffpt < 0) diffpt = -diffpt; if (diffzv < 0) diffzv = -diffzv; /* But also implement a heuristic that favors the known region over BYTE_PT or BYTE_ZV. The reason for this is that switching to BYTE_PT or BYTE_ZV will wipe out the knowledge in the known region, which might be annoying if the known region is large and BYTE_PT or BYTE_ZV is not that much closer than the end of the known region. */ diffzv += heuristic_hack; diffpt += heuristic_hack; if (diffpt < diffmax && diffpt <= diffzv) { bufmax = bufmin = BUF_PT (buf); bytmax = bytmin = BYTE_BUF_PT (buf); /* We set the size to 1 even though it doesn't really matter because the new known region contains no characters. We do this because this is the most likely size of the characters around the new known region, and we avoid potential yuckiness that is done when size == 3. */ size = 1; } if (diffzv < diffmax) { bufmax = bufmin = BUF_ZV (buf); bytmax = bytmin = BYTE_BUF_ZV (buf); size = 1; } } #ifdef ERROR_CHECK_TEXT else if (x >= bytmin) abort (); #endif else { Bytebpos diffmin = bytmin - x; Bytebpos diffpt = BYTE_BUF_PT (buf) - x; Bytebpos diffbegv = x - BYTE_BUF_BEGV (buf); /* #### This value could stand some more exploration. */ Bytecount heuristic_hack = (bytmax - bytmin) >> 2; if (diffpt < 0) diffpt = -diffpt; if (diffbegv < 0) diffbegv = -diffbegv; /* But also implement a heuristic that favors the known region -- see above. */ diffbegv += heuristic_hack; diffpt += heuristic_hack; if (diffpt < diffmin && diffpt <= diffbegv) { bufmax = bufmin = BUF_PT (buf); bytmax = bytmin = BYTE_BUF_PT (buf); /* We set the size to 1 even though it doesn't really matter because the new known region contains no characters. We do this because this is the most likely size of the characters around the new known region, and we avoid potential yuckiness that is done when size == 3. */ size = 1; } if (diffbegv < diffmin) { bufmax = bufmin = BUF_BEGV (buf); bytmax = bytmin = BYTE_BUF_BEGV (buf); size = 1; } } diff_so_far = x > bytmax ? x - bytmax : bytmin - x; if (diff_so_far > 50) { /* If we have to move more than a certain amount, then look into our cache. */ int minval = INT_MAX; int found = 0; int i; add_to_cache = 1; /* I considered keeping the positions ordered. This would speed up this loop, but updating the cache would take longer, so it doesn't seem like it would really matter. */ for (i = 0; i < 16; i++) { int diff = buf->text->mule_bytebpos_cache[i] - x; if (diff < 0) diff = -diff; if (diff < minval) { minval = diff; found = i; } } if (minval < diff_so_far) { bufmax = bufmin = buf->text->mule_charbpos_cache[found]; bytmax = bytmin = buf->text->mule_bytebpos_cache[found]; size = 1; } } /* It's conceivable that the caching above could lead to X being the same as one of the range edges. */ if (x >= bytmax) { Bytebpos newmax; Bytecount newsize; forward_p = 1; while (x > bytmax) { newmax = bytmax; INC_BYTEBPOS (buf, newmax); newsize = newmax - bytmax; if (newsize != size) { bufmin = bufmax; bytmin = bytmax; size = newsize; } bytmax = newmax; bufmax++; } retval = bufmax; /* #### Should go past the found location to reduce the number of times that this function is called */ } else /* x <= bytmin */ { Bytebpos newmin; Bytecount newsize; forward_p = 0; while (x < bytmin) { newmin = bytmin; DEC_BYTEBPOS (buf, newmin); newsize = bytmin - newmin; if (newsize != size) { bufmax = bufmin; bytmax = bytmin; size = newsize; } bytmin = newmin; bufmin--; } retval = bufmin; /* #### Should go past the found location to reduce the number of times that this function is called */ } /* If size is three, than we have to max sure that the range we discovered isn't too large, because we use a fixed-length table to divide by 3. */ if (size == 3) { int gap = bytmax - bytmin; buf->text->mule_three_p = 1; buf->text->mule_shifter = 1; if (gap > MAX_BYTEBPOS_GAP_SIZE_3) { if (forward_p) { bytmin = bytmax - MAX_BYTEBPOS_GAP_SIZE_3; bufmin = bufmax - MAX_CHARBPOS_GAP_SIZE_3; } else { bytmax = bytmin + MAX_BYTEBPOS_GAP_SIZE_3; bufmax = bufmin + MAX_CHARBPOS_GAP_SIZE_3; } } } else { buf->text->mule_three_p = 0; if (size == 4) buf->text->mule_shifter = 2; else buf->text->mule_shifter = size - 1; } buf->text->mule_bufmin = bufmin; buf->text->mule_bufmax = bufmax; buf->text->mule_bytmin = bytmin; buf->text->mule_bytmax = bytmax; if (add_to_cache) { int replace_loc; /* We throw away a "random" cached value and replace it with the new value. It doesn't actually have to be very random at all, just evenly distributed. #### It would be better to use a least-recently-used algorithm or something that tries to space things out, but I'm not sure it's worth it to go to the trouble of maintaining that. */ not_very_random_number += 621; replace_loc = not_very_random_number & 15; buf->text->mule_charbpos_cache[replace_loc] = retval; buf->text->mule_bytebpos_cache[replace_loc] = x; } PROFILE_RECORD_EXITING_SECTION (QSin_char_byte_conversion); return retval; } /* Text of length BYTELENGTH and CHARLENGTH (in different units) was inserted at charbpos START. */ void buffer_mule_signal_inserted_region (struct buffer *buf, Charbpos start, Bytecount bytelength, Charcount charlength) { int size = (1 << buf->text->mule_shifter) + !!buf->text->mule_three_p; int i; /* Adjust the cache of known positions. */ for (i = 0; i < 16; i++) { if (buf->text->mule_charbpos_cache[i] > start) { buf->text->mule_charbpos_cache[i] += charlength; buf->text->mule_bytebpos_cache[i] += bytelength; } } if (start >= buf->text->mule_bufmax) return; /* The insertion is either before the known region, in which case it shoves it forward; or within the known region, in which case it shoves the end forward. (But it may make the known region inconsistent, so we may have to shorten it.) */ if (start <= buf->text->mule_bufmin) { buf->text->mule_bufmin += charlength; buf->text->mule_bufmax += charlength; buf->text->mule_bytmin += bytelength; buf->text->mule_bytmax += bytelength; } else { Charbpos end = start + charlength; /* the insertion point divides the known region in two. Keep the longer half, at least, and expand into the inserted chunk as much as possible. */ if (start - buf->text->mule_bufmin > buf->text->mule_bufmax - start) { Bytebpos bytestart = (buf->text->mule_bytmin + size * (start - buf->text->mule_bufmin)); Bytebpos bytenew; while (start < end) { bytenew = bytestart; INC_BYTEBPOS (buf, bytenew); if (bytenew - bytestart != size) break; start++; bytestart = bytenew; } if (start != end) { buf->text->mule_bufmax = start; buf->text->mule_bytmax = bytestart; } else { buf->text->mule_bufmax += charlength; buf->text->mule_bytmax += bytelength; } } else { Bytebpos byteend = (buf->text->mule_bytmin + size * (start - buf->text->mule_bufmin) + bytelength); Bytebpos bytenew; buf->text->mule_bufmax += charlength; buf->text->mule_bytmax += bytelength; while (end > start) { bytenew = byteend; DEC_BYTEBPOS (buf, bytenew); if (byteend - bytenew != size) break; end--; byteend = bytenew; } if (start != end) { buf->text->mule_bufmin = end; buf->text->mule_bytmin = byteend; } } } } /* Text from START to END (equivalent in Bytebpos's: from BYTE_START to BYTE_END) was deleted. */ void buffer_mule_signal_deleted_region (struct buffer *buf, Charbpos start, Charbpos end, Bytebpos byte_start, Bytebpos byte_end) { int i; /* Adjust the cache of known positions. */ for (i = 0; i < 16; i++) { /* After the end; gets shoved backward */ if (buf->text->mule_charbpos_cache[i] > end) { buf->text->mule_charbpos_cache[i] -= end - start; buf->text->mule_bytebpos_cache[i] -= byte_end - byte_start; } /* In the range; moves to start of range */ else if (buf->text->mule_charbpos_cache[i] > start) { buf->text->mule_charbpos_cache[i] = start; buf->text->mule_bytebpos_cache[i] = byte_start; } } /* We don't care about any text after the end of the known region. */ end = min (end, buf->text->mule_bufmax); byte_end = min (byte_end, buf->text->mule_bytmax); if (start >= end) return; /* The end of the known region offsets by the total amount of deletion, since it's all before it. */ buf->text->mule_bufmax -= end - start; buf->text->mule_bytmax -= byte_end - byte_start; /* Now we don't care about any text after the start of the known region. */ end = min (end, buf->text->mule_bufmin); byte_end = min (byte_end, buf->text->mule_bytmin); if (start < end) { buf->text->mule_bufmin -= end - start; buf->text->mule_bytmin -= byte_end - byte_start; } } #endif /* MULE */ /************************************************************************/ /* verifying buffer and string positions */ /************************************************************************/ /* Functions below are tagged with either _byte or _char indicating whether they return byte or character positions. For a buffer, a character position is a "Charbpos" and a byte position is a "Bytebpos". For strings, these are sometimes typed using "Charcount" and "Bytecount". */ /* Flags for the functions below are: GB_ALLOW_PAST_ACCESSIBLE Allow positions to range over the entire buffer (BUF_BEG to BUF_Z), rather than just the accessible portion (BUF_BEGV to BUF_ZV). For strings, this flag has no effect. GB_COERCE_RANGE If the position is outside the allowable range, return the lower or upper bound of the range, whichever is closer to the specified position. GB_NO_ERROR_IF_BAD If the position is outside the allowable range, return -1. GB_NEGATIVE_FROM_END If a value is negative, treat it as an offset from the end. Only applies to strings. The following additional flags apply only to the functions that return ranges: GB_ALLOW_NIL Either or both positions can be nil. If FROM is nil, FROM_OUT will contain the lower bound of the allowed range. If TO is nil, TO_OUT will contain the upper bound of the allowed range. GB_CHECK_ORDER FROM must contain the lower bound and TO the upper bound of the range. If the positions are reversed, an error is signalled. The following is a combination flag: GB_HISTORICAL_STRING_BEHAVIOR Equivalent to (GB_NEGATIVE_FROM_END | GB_ALLOW_NIL). */ /* Return a buffer position stored in a Lisp_Object. Full error-checking is done on the position. Flags can be specified to control the behavior of out-of-range values. The default behavior is to require that the position is within the accessible part of the buffer (BEGV and ZV), and to signal an error if the position is out of range. */ Charbpos get_buffer_pos_char (struct buffer *b, Lisp_Object pos, unsigned int flags) { /* Does not GC */ Charbpos ind; Charbpos min_allowed, max_allowed; CHECK_INT_COERCE_MARKER (pos); ind = XINT (pos); min_allowed = flags & GB_ALLOW_PAST_ACCESSIBLE ? BUF_BEG (b) : BUF_BEGV (b); max_allowed = flags & GB_ALLOW_PAST_ACCESSIBLE ? BUF_Z (b) : BUF_ZV (b); if (ind < min_allowed || ind > max_allowed) { if (flags & GB_COERCE_RANGE) ind = ind < min_allowed ? min_allowed : max_allowed; else if (flags & GB_NO_ERROR_IF_BAD) ind = -1; else { Lisp_Object buffer = wrap_buffer (b); args_out_of_range (buffer, pos); } } return ind; } Bytebpos get_buffer_pos_byte (struct buffer *b, Lisp_Object pos, unsigned int flags) { Charbpos bpos = get_buffer_pos_char (b, pos, flags); if (bpos < 0) /* could happen with GB_NO_ERROR_IF_BAD */ return -1; return charbpos_to_bytebpos (b, bpos); } /* Return a pair of buffer positions representing a range of text, taken from a pair of Lisp_Objects. Full error-checking is done on the positions. Flags can be specified to control the behavior of out-of-range values. The default behavior is to allow the range bounds to be specified in either order (however, FROM_OUT will always be the lower bound of the range and TO_OUT the upper bound),to require that the positions are within the accessible part of the buffer (BEGV and ZV), and to signal an error if the positions are out of range. */ void get_buffer_range_char (struct buffer *b, Lisp_Object from, Lisp_Object to, Charbpos *from_out, Charbpos *to_out, unsigned int flags) { /* Does not GC */ Charbpos min_allowed, max_allowed; min_allowed = (flags & GB_ALLOW_PAST_ACCESSIBLE) ? BUF_BEG (b) : BUF_BEGV (b); max_allowed = (flags & GB_ALLOW_PAST_ACCESSIBLE) ? BUF_Z (b) : BUF_ZV (b); if (NILP (from) && (flags & GB_ALLOW_NIL)) *from_out = min_allowed; else *from_out = get_buffer_pos_char (b, from, flags | GB_NO_ERROR_IF_BAD); if (NILP (to) && (flags & GB_ALLOW_NIL)) *to_out = max_allowed; else *to_out = get_buffer_pos_char (b, to, flags | GB_NO_ERROR_IF_BAD); if ((*from_out < 0 || *to_out < 0) && !(flags & GB_NO_ERROR_IF_BAD)) { Lisp_Object buffer = wrap_buffer (b); args_out_of_range_3 (buffer, from, to); } if (*from_out >= 0 && *to_out >= 0 && *from_out > *to_out) { if (flags & GB_CHECK_ORDER) invalid_argument_2 ("start greater than end", from, to); else { Charbpos temp = *from_out; *from_out = *to_out; *to_out = temp; } } } void get_buffer_range_byte (struct buffer *b, Lisp_Object from, Lisp_Object to, Bytebpos *from_out, Bytebpos *to_out, unsigned int flags) { Charbpos s, e; get_buffer_range_char (b, from, to, &s, &e, flags); if (s >= 0) *from_out = charbpos_to_bytebpos (b, s); else /* could happen with GB_NO_ERROR_IF_BAD */ *from_out = -1; if (e >= 0) *to_out = charbpos_to_bytebpos (b, e); else *to_out = -1; } static Charcount get_string_pos_char_1 (Lisp_Object string, Lisp_Object pos, unsigned int flags, Charcount known_length) { Charcount ccpos; Charcount min_allowed = 0; Charcount max_allowed = known_length; /* Computation of KNOWN_LENGTH is potentially expensive so we pass it in. */ CHECK_INT (pos); ccpos = XINT (pos); if (ccpos < 0 && flags & GB_NEGATIVE_FROM_END) ccpos += max_allowed; if (ccpos < min_allowed || ccpos > max_allowed) { if (flags & GB_COERCE_RANGE) ccpos = ccpos < min_allowed ? min_allowed : max_allowed; else if (flags & GB_NO_ERROR_IF_BAD) ccpos = -1; else args_out_of_range (string, pos); } return ccpos; } Charcount get_string_pos_char (Lisp_Object string, Lisp_Object pos, unsigned int flags) { return get_string_pos_char_1 (string, pos, flags, string_char_length (string)); } Bytecount get_string_pos_byte (Lisp_Object string, Lisp_Object pos, unsigned int flags) { Charcount ccpos = get_string_pos_char (string, pos, flags); if (ccpos < 0) /* could happen with GB_NO_ERROR_IF_BAD */ return -1; return string_index_char_to_byte (string, ccpos); } void get_string_range_char (Lisp_Object string, Lisp_Object from, Lisp_Object to, Charcount *from_out, Charcount *to_out, unsigned int flags) { Charcount min_allowed = 0; Charcount max_allowed = string_char_length (string); if (NILP (from) && (flags & GB_ALLOW_NIL)) *from_out = min_allowed; else *from_out = get_string_pos_char_1 (string, from, flags | GB_NO_ERROR_IF_BAD, max_allowed); if (NILP (to) && (flags & GB_ALLOW_NIL)) *to_out = max_allowed; else *to_out = get_string_pos_char_1 (string, to, flags | GB_NO_ERROR_IF_BAD, max_allowed); if ((*from_out < 0 || *to_out < 0) && !(flags & GB_NO_ERROR_IF_BAD)) args_out_of_range_3 (string, from, to); if (*from_out >= 0 && *to_out >= 0 && *from_out > *to_out) { if (flags & GB_CHECK_ORDER) invalid_argument_2 ("start greater than end", from, to); else { Charbpos temp = *from_out; *from_out = *to_out; *to_out = temp; } } } void get_string_range_byte (Lisp_Object string, Lisp_Object from, Lisp_Object to, Bytecount *from_out, Bytecount *to_out, unsigned int flags) { Charcount s, e; get_string_range_char (string, from, to, &s, &e, flags); if (s >= 0) *from_out = string_index_char_to_byte (string, s); else /* could happen with GB_NO_ERROR_IF_BAD */ *from_out = -1; if (e >= 0) *to_out = string_index_char_to_byte (string, e); else *to_out = -1; } Charxpos get_buffer_or_string_pos_char (Lisp_Object object, Lisp_Object pos, unsigned int flags) { return STRINGP (object) ? get_string_pos_char (object, pos, flags) : get_buffer_pos_char (XBUFFER (object), pos, flags); } Bytexpos get_buffer_or_string_pos_byte (Lisp_Object object, Lisp_Object pos, unsigned int flags) { return STRINGP (object) ? get_string_pos_byte (object, pos, flags) : get_buffer_pos_byte (XBUFFER (object), pos, flags); } void get_buffer_or_string_range_char (Lisp_Object object, Lisp_Object from, Lisp_Object to, Charxpos *from_out, Charxpos *to_out, unsigned int flags) { if (STRINGP (object)) get_string_range_char (object, from, to, from_out, to_out, flags); else get_buffer_range_char (XBUFFER (object), from, to, from_out, to_out, flags); } void get_buffer_or_string_range_byte (Lisp_Object object, Lisp_Object from, Lisp_Object to, Bytexpos *from_out, Bytexpos *to_out, unsigned int flags) { if (STRINGP (object)) get_string_range_byte (object, from, to, from_out, to_out, flags); else get_buffer_range_byte (XBUFFER (object), from, to, from_out, to_out, flags); } Charxpos buffer_or_string_accessible_begin_char (Lisp_Object object) { return STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object)); } Charxpos buffer_or_string_accessible_end_char (Lisp_Object object) { return STRINGP (object) ? string_char_length (object) : BUF_ZV (XBUFFER (object)); } Bytexpos buffer_or_string_accessible_begin_byte (Lisp_Object object) { return STRINGP (object) ? 0 : BYTE_BUF_BEGV (XBUFFER (object)); } Bytexpos buffer_or_string_accessible_end_byte (Lisp_Object object) { return STRINGP (object) ? XSTRING_LENGTH (object) : BYTE_BUF_ZV (XBUFFER (object)); } Charxpos buffer_or_string_absolute_begin_char (Lisp_Object object) { return STRINGP (object) ? 0 : BUF_BEG (XBUFFER (object)); } Charxpos buffer_or_string_absolute_end_char (Lisp_Object object) { return STRINGP (object) ? string_char_length (object) : BUF_Z (XBUFFER (object)); } Bytexpos buffer_or_string_absolute_begin_byte (Lisp_Object object) { return STRINGP (object) ? 0 : BYTE_BUF_BEG (XBUFFER (object)); } Bytexpos buffer_or_string_absolute_end_byte (Lisp_Object object) { return STRINGP (object) ? XSTRING_LENGTH (object) : BYTE_BUF_Z (XBUFFER (object)); } Charbpos charbpos_clip_to_bounds (Charbpos lower, Charbpos num, Charbpos upper) { return (num < lower ? lower : num > upper ? upper : num); } Bytebpos bytebpos_clip_to_bounds (Bytebpos lower, Bytebpos num, Bytebpos upper) { return (num < lower ? lower : num > upper ? upper : num); } Charxpos charxpos_clip_to_bounds (Charxpos lower, Charxpos num, Charxpos upper) { return (num < lower ? lower : num > upper ? upper : num); } Bytexpos bytexpos_clip_to_bounds (Bytexpos lower, Bytexpos num, Bytexpos upper) { return (num < lower ? lower : num > upper ? upper : num); } /* These could be implemented in terms of the get_buffer_or_string() functions above, but those are complicated and handle lots of weird cases stemming from uncertain external input. */ Charxpos buffer_or_string_clip_to_accessible_char (Lisp_Object object, Charxpos pos) { return (charxpos_clip_to_bounds (pos, buffer_or_string_accessible_begin_char (object), buffer_or_string_accessible_end_char (object))); } Bytexpos buffer_or_string_clip_to_accessible_byte (Lisp_Object object, Bytexpos pos) { return (bytexpos_clip_to_bounds (pos, buffer_or_string_accessible_begin_byte (object), buffer_or_string_accessible_end_byte (object))); } Charxpos buffer_or_string_clip_to_absolute_char (Lisp_Object object, Charxpos pos) { return (charxpos_clip_to_bounds (pos, buffer_or_string_absolute_begin_char (object), buffer_or_string_absolute_end_char (object))); } Bytexpos buffer_or_string_clip_to_absolute_byte (Lisp_Object object, Bytexpos pos) { return (bytexpos_clip_to_bounds (pos, buffer_or_string_absolute_begin_byte (object), buffer_or_string_absolute_end_byte (object))); } /************************************************************************/ /* Implement TO_EXTERNAL_FORMAT, TO_INTERNAL_FORMAT */ /************************************************************************/ typedef struct { Dynarr_declare (Ibyte_dynarr *); } Ibyte_dynarr_dynarr; typedef struct { Dynarr_declare (Extbyte_dynarr *); } Extbyte_dynarr_dynarr; static Extbyte_dynarr_dynarr *conversion_out_dynarr_list; static Ibyte_dynarr_dynarr *conversion_in_dynarr_list; static int dfc_convert_to_external_format_in_use; static int dfc_convert_to_internal_format_in_use; void dfc_convert_to_external_format (dfc_conversion_type source_type, dfc_conversion_data *source, Lisp_Object coding_system, dfc_conversion_type sink_type, dfc_conversion_data *sink) { /* It's guaranteed that many callers are not prepared for GC here, esp. given that this code conversion occurs in many very hidden places. */ int count; Extbyte_dynarr *conversion_out_dynarr; PROFILE_DECLARE (); PROFILE_RECORD_ENTERING_SECTION (QSin_internal_external_conversion); count = begin_gc_forbidden (); type_checking_assert (((source_type == DFC_TYPE_DATA) || (source_type == DFC_TYPE_LISP_LSTREAM && LSTREAMP (source->lisp_object)) || (source_type == DFC_TYPE_LISP_STRING && STRINGP (source->lisp_object))) && ((sink_type == DFC_TYPE_DATA) || (sink_type == DFC_TYPE_LISP_LSTREAM && LSTREAMP (source->lisp_object)))); if (Dynarr_length (conversion_out_dynarr_list) <= dfc_convert_to_external_format_in_use) Dynarr_add (conversion_out_dynarr_list, Dynarr_new (Extbyte)); conversion_out_dynarr = Dynarr_at (conversion_out_dynarr_list, dfc_convert_to_external_format_in_use); Dynarr_reset (conversion_out_dynarr); internal_bind_int (&dfc_convert_to_external_format_in_use, dfc_convert_to_external_format_in_use + 1); coding_system = get_coding_system_for_text_file (coding_system, 0); /* Here we optimize in the case where the coding system does no conversion. However, we don't want to optimize in case the source or sink is an lstream, since writing to an lstream can cause a garbage collection, and this could be problematic if the source is a lisp string. */ if (source_type != DFC_TYPE_LISP_LSTREAM && sink_type != DFC_TYPE_LISP_LSTREAM && coding_system_is_binary (coding_system)) { const Ibyte *ptr; Bytecount len; if (source_type == DFC_TYPE_LISP_STRING) { ptr = XSTRING_DATA (source->lisp_object); len = XSTRING_LENGTH (source->lisp_object); } else { ptr = (Ibyte *) source->data.ptr; len = source->data.len; } #ifdef MULE { const Ibyte *end; for (end = ptr + len; ptr < end;) { Ibyte c = (byte_ascii_p (*ptr)) ? *ptr : (*ptr == LEADING_BYTE_CONTROL_1) ? (*(ptr+1) - 0x20) : (*ptr == LEADING_BYTE_LATIN_ISO8859_1) ? (*(ptr+1)) : '~'; Dynarr_add (conversion_out_dynarr, (Extbyte) c); INC_IBYTEPTR (ptr); } text_checking_assert (ptr == end); } #else Dynarr_add_many (conversion_out_dynarr, ptr, len); #endif } #ifdef WIN32_ANY /* Optimize the common case involving Unicode where only ASCII is involved */ else if (source_type != DFC_TYPE_LISP_LSTREAM && sink_type != DFC_TYPE_LISP_LSTREAM && dfc_coding_system_is_unicode (coding_system)) { const Ibyte *ptr, *p; Bytecount len; const Ibyte *end; if (source_type == DFC_TYPE_LISP_STRING) { ptr = XSTRING_DATA (source->lisp_object); len = XSTRING_LENGTH (source->lisp_object); } else { ptr = (Ibyte *) source->data.ptr; len = source->data.len; } end = ptr + len; for (p = ptr; p < end; p++) { if (!byte_ascii_p (*p)) goto the_hard_way; } for (p = ptr; p < end; p++) { Dynarr_add (conversion_out_dynarr, (Extbyte) (*p)); Dynarr_add (conversion_out_dynarr, (Extbyte) '\0'); } } #endif /* WIN32_ANY */ else { Lisp_Object streams_to_delete[3]; int delete_count; Lisp_Object instream, outstream; Lstream *reader, *writer; #ifdef WIN32_ANY the_hard_way: #endif /* WIN32_ANY */ delete_count = 0; if (source_type == DFC_TYPE_LISP_LSTREAM) instream = source->lisp_object; else if (source_type == DFC_TYPE_DATA) streams_to_delete[delete_count++] = instream = make_fixed_buffer_input_stream (source->data.ptr, source->data.len); else { type_checking_assert (source_type == DFC_TYPE_LISP_STRING); streams_to_delete[delete_count++] = instream = /* This will GCPRO the Lisp string */ make_lisp_string_input_stream (source->lisp_object, 0, -1); } if (sink_type == DFC_TYPE_LISP_LSTREAM) outstream = sink->lisp_object; else { type_checking_assert (sink_type == DFC_TYPE_DATA); streams_to_delete[delete_count++] = outstream = make_dynarr_output_stream ((unsigned_char_dynarr *) conversion_out_dynarr); } streams_to_delete[delete_count++] = outstream = make_coding_output_stream (XLSTREAM (outstream), coding_system, CODING_ENCODE, 0); reader = XLSTREAM (instream); writer = XLSTREAM (outstream); /* decoding_stream will gc-protect outstream */ { struct gcpro gcpro1, gcpro2; GCPRO2 (instream, outstream); while (1) { Bytecount size_in_bytes; char tempbuf[1024]; /* some random amount */ size_in_bytes = Lstream_read (reader, tempbuf, sizeof (tempbuf)); if (size_in_bytes == 0) break; else if (size_in_bytes < 0) signal_error (Qtext_conversion_error, "Error converting to external format", Qunbound); if (Lstream_write (writer, tempbuf, size_in_bytes) < 0) signal_error (Qtext_conversion_error, "Error converting to external format", Qunbound); } /* Closing writer will close any stream at the other end of writer. */ Lstream_close (writer); Lstream_close (reader); UNGCPRO; } /* The idea is that this function will create no garbage. */ while (delete_count) Lstream_delete (XLSTREAM (streams_to_delete [--delete_count])); } unbind_to (count); if (sink_type != DFC_TYPE_LISP_LSTREAM) { sink->data.len = Dynarr_length (conversion_out_dynarr); /* double zero-extend because we may be dealing with Unicode data */ Dynarr_add (conversion_out_dynarr, '\0'); Dynarr_add (conversion_out_dynarr, '\0'); sink->data.ptr = Dynarr_atp (conversion_out_dynarr, 0); } PROFILE_RECORD_EXITING_SECTION (QSin_internal_external_conversion); } void dfc_convert_to_internal_format (dfc_conversion_type source_type, dfc_conversion_data *source, Lisp_Object coding_system, dfc_conversion_type sink_type, dfc_conversion_data *sink) { /* It's guaranteed that many callers are not prepared for GC here, esp. given that this code conversion occurs in many very hidden places. */ int count; Ibyte_dynarr *conversion_in_dynarr; PROFILE_DECLARE (); PROFILE_RECORD_ENTERING_SECTION (QSin_internal_external_conversion); count = begin_gc_forbidden (); type_checking_assert ((source_type == DFC_TYPE_DATA || source_type == DFC_TYPE_LISP_LSTREAM) && (sink_type == DFC_TYPE_DATA || sink_type == DFC_TYPE_LISP_LSTREAM)); if (Dynarr_length (conversion_in_dynarr_list) <= dfc_convert_to_internal_format_in_use) Dynarr_add (conversion_in_dynarr_list, Dynarr_new (Ibyte)); conversion_in_dynarr = Dynarr_at (conversion_in_dynarr_list, dfc_convert_to_internal_format_in_use); Dynarr_reset (conversion_in_dynarr); internal_bind_int (&dfc_convert_to_internal_format_in_use, dfc_convert_to_internal_format_in_use + 1); coding_system = get_coding_system_for_text_file (coding_system, 1); if (source_type != DFC_TYPE_LISP_LSTREAM && sink_type != DFC_TYPE_LISP_LSTREAM && coding_system_is_binary (coding_system)) { #ifdef MULE const Ibyte *ptr = (const Ibyte *) source->data.ptr; Bytecount len = source->data.len; const Ibyte *end = ptr + len; for (; ptr < end; ptr++) { Ibyte c = *ptr; if (byte_ascii_p (c)) Dynarr_add (conversion_in_dynarr, c); else if (byte_c1_p (c)) { Dynarr_add (conversion_in_dynarr, LEADING_BYTE_CONTROL_1); Dynarr_add (conversion_in_dynarr, c + 0x20); } else { Dynarr_add (conversion_in_dynarr, LEADING_BYTE_LATIN_ISO8859_1); Dynarr_add (conversion_in_dynarr, c); } } #else Dynarr_add_many (conversion_in_dynarr, source->data.ptr, source->data.len); #endif } #ifdef WIN32_ANY /* Optimize the common case involving Unicode where only ASCII/Latin-1 is involved */ else if (source_type != DFC_TYPE_LISP_LSTREAM && sink_type != DFC_TYPE_LISP_LSTREAM && dfc_coding_system_is_unicode (coding_system)) { const Ibyte *ptr = (const Ibyte *) source->data.ptr + 1; Bytecount len = source->data.len; const Ibyte *end = ptr + len; if (len & 1) goto the_hard_way; for (; ptr < end; ptr += 2) { if (*ptr) goto the_hard_way; } ptr = (const Ibyte *) source->data.ptr; end = ptr + len; for (; ptr < end; ptr += 2) { Ibyte c = *ptr; if (byte_ascii_p (c)) Dynarr_add (conversion_in_dynarr, c); #ifdef MULE else if (byte_c1_p (c)) { Dynarr_add (conversion_in_dynarr, LEADING_BYTE_CONTROL_1); Dynarr_add (conversion_in_dynarr, c + 0x20); } else { Dynarr_add (conversion_in_dynarr, LEADING_BYTE_LATIN_ISO8859_1); Dynarr_add (conversion_in_dynarr, c); } #endif /* MULE */ } } #endif /* WIN32_ANY */ else { Lisp_Object streams_to_delete[3]; int delete_count; Lisp_Object instream, outstream; Lstream *reader, *writer; #ifdef WIN32_ANY the_hard_way: #endif /* WIN32_ANY */ delete_count = 0; if (source_type == DFC_TYPE_LISP_LSTREAM) instream = source->lisp_object; else { type_checking_assert (source_type == DFC_TYPE_DATA); streams_to_delete[delete_count++] = instream = make_fixed_buffer_input_stream (source->data.ptr, source->data.len); } if (sink_type == DFC_TYPE_LISP_LSTREAM) outstream = sink->lisp_object; else { type_checking_assert (sink_type == DFC_TYPE_DATA); streams_to_delete[delete_count++] = outstream = make_dynarr_output_stream ((unsigned_char_dynarr *) conversion_in_dynarr); } streams_to_delete[delete_count++] = outstream = make_coding_output_stream (XLSTREAM (outstream), coding_system, CODING_DECODE, 0); reader = XLSTREAM (instream); writer = XLSTREAM (outstream); { struct gcpro gcpro1, gcpro2; /* outstream will gc-protect its sink stream, if necessary */ GCPRO2 (instream, outstream); while (1) { Bytecount size_in_bytes; char tempbuf[1024]; /* some random amount */ size_in_bytes = Lstream_read (reader, tempbuf, sizeof (tempbuf)); if (size_in_bytes == 0) break; else if (size_in_bytes < 0) signal_error (Qtext_conversion_error, "Error converting to internal format", Qunbound); if (Lstream_write (writer, tempbuf, size_in_bytes) < 0) signal_error (Qtext_conversion_error, "Error converting to internal format", Qunbound); } /* Closing writer will close any stream at the other end of writer. */ Lstream_close (writer); Lstream_close (reader); UNGCPRO; } /* The idea is that this function will create no garbage. */ while (delete_count) Lstream_delete (XLSTREAM (streams_to_delete [--delete_count])); } unbind_to (count); if (sink_type != DFC_TYPE_LISP_LSTREAM) { sink->data.len = Dynarr_length (conversion_in_dynarr); Dynarr_add (conversion_in_dynarr, '\0'); /* remember to NUL-terminate! */ /* The macros don't currently distinguish between internal and external sinks, and allocate and copy two extra bytes in both cases. So we add a second zero, just like for external data (in that case, because we may be converting to Unicode). */ Dynarr_add (conversion_in_dynarr, '\0'); sink->data.ptr = Dynarr_atp (conversion_in_dynarr, 0); } PROFILE_RECORD_EXITING_SECTION (QSin_internal_external_conversion); } /* ----------------------------------------------------------------------- */ /* New-style DFC converters (data is returned rather than stored into var) */ /* ----------------------------------------------------------------------- */ /* We handle here the cases where SRC is a Lisp_Object, internal data (sized or unsized), or external data (sized or unsized), and return type is unsized alloca() or malloc() data. If the return type is a Lisp_Object, use build_ext_string() for unsized external data, make_ext_string() for sized external data. If the return type needs to be sized data, use the *_TO_SIZED_*() macros, and for other more complicated cases, use the original TO_*_FORMAT() macros. */ static void new_dfc_convert_now_damn_it (const void *src, Bytecount src_size, enum new_dfc_src_type type, void **dst, Bytecount *dst_size, Lisp_Object codesys) { /* #### In the case of alloca(), it would be a bit more efficient, for small strings, to use static Dynarr's like are used internally in TO_*_FORMAT(), or some other way of avoiding malloc() followed by free(). I doubt it really matters, though. */ switch (type) { case DFC_EXTERNAL: TO_INTERNAL_FORMAT (C_STRING, src, MALLOC, (*dst, *dst_size), codesys); break; case DFC_SIZED_EXTERNAL: TO_INTERNAL_FORMAT (DATA, (src, src_size), MALLOC, (*dst, *dst_size), codesys); break; case DFC_INTERNAL: TO_EXTERNAL_FORMAT (C_STRING, src, MALLOC, (*dst, *dst_size), codesys); break; case DFC_SIZED_INTERNAL: TO_EXTERNAL_FORMAT (DATA, (src, src_size), MALLOC, (*dst, *dst_size), codesys); break; case DFC_LISP_STRING: TO_EXTERNAL_FORMAT (LISP_STRING, VOID_TO_LISP (src), MALLOC, (*dst, *dst_size), codesys); break; default: abort (); } } void * new_dfc_convert_malloc (const void *src, Bytecount src_size, enum new_dfc_src_type type, Lisp_Object codesys) { void *dst; Bytecount dst_size; new_dfc_convert_now_damn_it (src, src_size, type, &dst, &dst_size, codesys); return dst; } /* For alloca(), things are trickier because the calling function needs to allocate. This means that the caller needs to do the following: (a) invoke us to do the conversion, remember the data and return the size. (b) alloca() the proper size. (c) invoke us again to copy the data. We need to handle the possibility of two or more invocations of the converter in the same expression. In such cases it's conceivable that the evaluation of the sub-expressions will be overlapping (e.g. one size function called, then the other one called, then the copy functions called). To handle this, we keep a list of active data, indexed by the src expression. (We use the stringize operator to avoid evaluating the expression multiple times.) If the caller uses the exact same src expression twice in two converter calls in the same subexpression, we will lose, but at least we can check for this and abort(). We could conceivably try to index on other parameters as well, but there is not really any point. */ typedef struct { const char *srctext; void *dst; Bytecount dst_size; } dfc_e2c_vals; typedef struct { Dynarr_declare (dfc_e2c_vals); } dfc_e2c_vals_dynarr; static dfc_e2c_vals_dynarr *active_dfc_e2c; static int find_pos_of_existing_active_dfc_e2c (const char *srctext) { dfc_e2c_vals *vals = NULL; int i; for (i = 0; i < Dynarr_length (active_dfc_e2c); i++) { vals = Dynarr_atp (active_dfc_e2c, i); if (vals->srctext == srctext) return i; } return -1; } void * new_dfc_convert_alloca (const char *srctext, void *alloca_data) { dfc_e2c_vals *vals; int i = find_pos_of_existing_active_dfc_e2c (srctext); assert (i >= 0); vals = Dynarr_atp (active_dfc_e2c, i); assert (alloca_data); memcpy (alloca_data, vals->dst, vals->dst_size + 2); xfree (vals->dst, void *); Dynarr_delete (active_dfc_e2c, i); return alloca_data; } Bytecount new_dfc_convert_size (const char *srctext, const void *src, Bytecount src_size, enum new_dfc_src_type type, Lisp_Object codesys) { dfc_e2c_vals vals; assert (find_pos_of_existing_active_dfc_e2c (srctext) < 0); vals.srctext = srctext; new_dfc_convert_now_damn_it (src, src_size, type, &vals.dst, &vals.dst_size, codesys); Dynarr_add (active_dfc_e2c, vals); /* The size is always + 2 because we have double zero-termination at the end of all data (for Unicode-correctness). */ return vals.dst_size + 2; } /************************************************************************/ /* Basic Ichar functions */ /************************************************************************/ #ifdef MULE /* Convert a non-ASCII Mule character C into a one-character Mule-encoded string in STR. Returns the number of bytes stored. Do not call this directly. Use the macro set_itext_ichar() instead. */ Bytecount non_ascii_set_itext_ichar (Ibyte *str, Ichar c) { Ibyte *p; Ibyte lb; int c1, c2; Lisp_Object charset; p = str; BREAKUP_ICHAR (c, charset, c1, c2); lb = ichar_leading_byte (c); if (leading_byte_private_p (lb)) *p++ = private_leading_byte_prefix (lb); *p++ = lb; if (EQ (charset, Vcharset_control_1)) c1 += 0x20; *p++ = c1 | 0x80; if (c2) *p++ = c2 | 0x80; return (p - str); } /* Return the first character from a Mule-encoded string in STR, assuming it's non-ASCII. Do not call this directly. Use the macro itext_ichar() instead. */ Ichar non_ascii_itext_ichar (const Ibyte *str) { Ibyte i0 = *str, i1, i2 = 0; Lisp_Object charset; if (i0 == LEADING_BYTE_CONTROL_1) return (Ichar) (*++str - 0x20); if (leading_byte_prefix_p (i0)) i0 = *++str; i1 = *++str & 0x7F; charset = charset_by_leading_byte (i0); if (XCHARSET_DIMENSION (charset) == 2) i2 = *++str & 0x7F; return make_ichar (charset, i1, i2); } /* Return whether CH is a valid Ichar, assuming it's non-ASCII. Do not call this directly. Use the macro valid_ichar_p() instead. */ int non_ascii_valid_ichar_p (Ichar ch) { int f1, f2, f3; /* Must have only lowest 19 bits set */ if (ch & ~0x7FFFF) return 0; f1 = ichar_field1 (ch); f2 = ichar_field2 (ch); f3 = ichar_field3 (ch); if (f1 == 0) { /* dimension-1 char */ Lisp_Object charset; /* leading byte must be correct */ if (f2 < MIN_ICHAR_FIELD2_OFFICIAL || (f2 > MAX_ICHAR_FIELD2_OFFICIAL && f2 < MIN_ICHAR_FIELD2_PRIVATE) || f2 > MAX_ICHAR_FIELD2_PRIVATE) return 0; /* octet not out of range */ if (f3 < 0x20) return 0; /* charset exists */ /* NOTE: This takes advantage of the fact that FIELD2_TO_OFFICIAL_LEADING_BYTE and FIELD2_TO_PRIVATE_LEADING_BYTE are the same. */ charset = charset_by_leading_byte (f2 + FIELD2_TO_OFFICIAL_LEADING_BYTE); if (EQ (charset, Qnil)) return 0; /* check range as per size (94 or 96) of charset */ return ((f3 > 0x20 && f3 < 0x7f) || XCHARSET_CHARS (charset) == 96); } else { /* dimension-2 char */ Lisp_Object charset; /* leading byte must be correct */ if (f1 < MIN_ICHAR_FIELD1_OFFICIAL || (f1 > MAX_ICHAR_FIELD1_OFFICIAL && f1 < MIN_ICHAR_FIELD1_PRIVATE) || f1 > MAX_ICHAR_FIELD1_PRIVATE) return 0; /* octets not out of range */ if (f2 < 0x20 || f3 < 0x20) return 0; #ifdef ENABLE_COMPOSITE_CHARS if (f1 + FIELD1_TO_OFFICIAL_LEADING_BYTE == LEADING_BYTE_COMPOSITE) { if (UNBOUNDP (Fgethash (make_int (ch), Vcomposite_char_char2string_hash_table, Qunbound))) return 0; return 1; } #endif /* ENABLE_COMPOSITE_CHARS */ /* charset exists */ if (f1 <= MAX_ICHAR_FIELD1_OFFICIAL) charset = charset_by_leading_byte (f1 + FIELD1_TO_OFFICIAL_LEADING_BYTE); else charset = charset_by_leading_byte (f1 + FIELD1_TO_PRIVATE_LEADING_BYTE); if (EQ (charset, Qnil)) return 0; /* check range as per size (94x94 or 96x96) of charset */ return ((f2 != 0x20 && f2 != 0x7F && f3 != 0x20 && f3 != 0x7F) || XCHARSET_CHARS (charset) == 96); } } /* Copy the character pointed to by SRC into DST. Do not call this directly. Use the macro itext_copy_ichar() instead. Return the number of bytes copied. */ Bytecount non_ascii_itext_copy_ichar (const Ibyte *src, Ibyte *dst) { Bytecount bytes = rep_bytes_by_first_byte (*src); Bytecount i; for (i = bytes; i; i--, dst++, src++) *dst = *src; return bytes; } #endif /* MULE */ /************************************************************************/ /* streams of Ichars */ /************************************************************************/ #ifdef MULE /* Treat a stream as a stream of Ichar's rather than a stream of bytes. The functions below are not meant to be called directly; use the macros in insdel.h. */ Ichar Lstream_get_ichar_1 (Lstream *stream, int ch) { Ibyte str[MAX_ICHAR_LEN]; Ibyte *strptr = str; Bytecount bytes; str[0] = (Ibyte) ch; for (bytes = rep_bytes_by_first_byte (ch) - 1; bytes; bytes--) { int c = Lstream_getc (stream); text_checking_assert (c >= 0); *++strptr = (Ibyte) c; } return itext_ichar (str); } int Lstream_fput_ichar (Lstream *stream, Ichar ch) { Ibyte str[MAX_ICHAR_LEN]; Bytecount len = set_itext_ichar (str, ch); return Lstream_write (stream, str, len); } void Lstream_funget_ichar (Lstream *stream, Ichar ch) { Ibyte str[MAX_ICHAR_LEN]; Bytecount len = set_itext_ichar (str, ch); Lstream_unread (stream, str, len); } #endif /* MULE */ /************************************************************************/ /* Lisp primitives for working with characters */ /************************************************************************/ DEFUN ("make-char", Fmake_char, 2, 3, 0, /* Make a character from CHARSET and octets ARG1 and ARG2. ARG2 is required only for characters from two-dimensional charsets. Each octet should be in the range 32 through 127 for a 96 or 96x96 charset and 33 through 126 for a 94 or 94x94 charset. (Most charsets are either 96 or 94x94.) Note that this is 32 more than the values typically given for 94x94 charsets. When two octets are required, the order is "standard" -- the same as appears in ISO-2022 encodings, reference tables, etc. \(Note the following non-obvious result: Computerized translation tables often encode the two octets as the high and low bytes, respectively, of a hex short, while when there's only one octet, it goes in the low byte. When decoding such a value, you need to treat the two cases differently when calling make-char: One is (make-char CHARSET HIGH LOW), the other is (make-char CHARSET LOW).) For example, (make-char 'latin-iso8859-2 185) or (make-char 'latin-iso8859-2 57) will return the Latin 2 character s with caron. As another example, the Japanese character for "kawa" (stream), which looks something like this: | | | | | | | | | | | / | appears in the Unicode Standard (version 2.0) on page 7-287 with the following values (see also page 7-4): U 5DDD (Unicode) G 0-2008 (GB 2312-80) J 0-3278 (JIS X 0208-1990) K 0-8425 (KS C 5601-1987) B A474 (Big Five) C 1-4455 (CNS 11643-1986 (1st plane)) A 213C34 (ANSI Z39.64-1989) These are equivalent to: \(make-char 'chinese-gb2312 52 40) \(make-char 'japanese-jisx0208 64 110) \(make-char 'korean-ksc5601 116 57) \(make-char 'chinese-cns11643-1 76 87) \(decode-big5-char '(164 . 116)) \(All codes above are two decimal numbers except for Big Five and ANSI Z39.64, which we don't support. We add 32 to each of the decimal numbers. Big Five is split in a rather hackish fashion into two charsets, `big5-1' and `big5-2', due to its excessive size -- 94x157, with the first codepoint in the range 0xA1 to 0xFE and the second in the range 0x40 to 0x7E or 0xA1 to 0xFE. `decode-big5-char' is used to generate the char from its codes, and `encode-big5-char' extracts the codes.) When compiled without MULE, this function does not do much, but it's provided for compatibility. In this case, the following CHARSET symbols are allowed: `ascii' -- ARG1 should be in the range 0 through 127. `control-1' -- ARG1 should be in the range 128 through 159. else -- ARG1 is coerced to be between 0 and 255, and then the high bit is set. `int-to-char of the resulting ARG1' is returned, and ARG2 is always ignored. */ (charset, arg1, arg2)) { #ifdef MULE Lisp_Charset *cs; int a1, a2; int lowlim, highlim; charset = Fget_charset (charset); cs = XCHARSET (charset); get_charset_limits (charset, &lowlim, &highlim); CHECK_INT (arg1); /* It is useful (and safe, according to Olivier Galibert) to strip the 8th bit off ARG1 and ARG2 because it allows programmers to write (make-char 'latin-iso8859-2 CODE) where code is the actual Latin 2 code of the character. */ a1 = XINT (arg1) & 0x7f; if (a1 < lowlim || a1 > highlim) args_out_of_range_3 (arg1, make_int (lowlim), make_int (highlim)); if (CHARSET_DIMENSION (cs) == 1) { if (!NILP (arg2)) invalid_argument ("Charset is of dimension one; second octet must be nil", arg2); return make_char (make_ichar (charset, a1, 0)); } CHECK_INT (arg2); a2 = XINT (arg2) & 0x7f; if (a2 < lowlim || a2 > highlim) args_out_of_range_3 (arg2, make_int (lowlim), make_int (highlim)); return make_char (make_ichar (charset, a1, a2)); #else int a1; int lowlim, highlim; if (EQ (charset, Qascii)) lowlim = 0, highlim = 127; else if (EQ (charset, Qcontrol_1)) lowlim = 0, highlim = 31; else lowlim = 0, highlim = 127; CHECK_INT (arg1); /* It is useful (and safe, according to Olivier Galibert) to strip the 8th bit off ARG1 and ARG2 because it allows programmers to write (make-char 'latin-iso8859-2 CODE) where code is the actual Latin 2 code of the character. */ a1 = XINT (arg1) & 0x7f; if (a1 < lowlim || a1 > highlim) args_out_of_range_3 (arg1, make_int (lowlim), make_int (highlim)); if (EQ (charset, Qascii)) return make_char (a1); return make_char (a1 + 128); #endif /* MULE */ } #ifdef MULE DEFUN ("char-charset", Fchar_charset, 1, 1, 0, /* Return the character set of char CH. */ (ch)) { CHECK_CHAR_COERCE_INT (ch); return XCHARSET_NAME (charset_by_leading_byte (ichar_leading_byte (XCHAR (ch)))); } DEFUN ("char-octet", Fchar_octet, 1, 2, 0, /* Return the octet numbered N (should be 0 or 1) of char CH. N defaults to 0 if omitted. */ (ch, n)) { Lisp_Object charset; int octet0, octet1; CHECK_CHAR_COERCE_INT (ch); BREAKUP_ICHAR (XCHAR (ch), charset, octet0, octet1); if (NILP (n) || EQ (n, Qzero)) return make_int (octet0); else if (EQ (n, make_int (1))) return make_int (octet1); else invalid_constant ("Octet number must be 0 or 1", n); } DEFUN ("split-char", Fsplit_char, 1, 1, 0, /* Return list of charset and one or two position-codes of CHAR. */ (character)) { /* This function can GC */ struct gcpro gcpro1, gcpro2; Lisp_Object charset = Qnil; Lisp_Object rc = Qnil; int c1, c2; GCPRO2 (charset, rc); CHECK_CHAR_COERCE_INT (character); BREAKUP_ICHAR (XCHAR (character), charset, c1, c2); if (XCHARSET_DIMENSION (Fget_charset (charset)) == 2) { rc = list3 (XCHARSET_NAME (charset), make_int (c1), make_int (c2)); } else { rc = list2 (XCHARSET_NAME (charset), make_int (c1)); } UNGCPRO; return rc; } #endif /* MULE */ /************************************************************************/ /* composite character functions */ /************************************************************************/ #ifdef ENABLE_COMPOSITE_CHARS Ichar lookup_composite_char (Ibyte *str, int len) { Lisp_Object lispstr = make_string (str, len); Lisp_Object ch = Fgethash (lispstr, Vcomposite_char_string2char_hash_table, Qunbound); Ichar emch; if (UNBOUNDP (ch)) { if (composite_char_row_next >= 128) invalid_operation ("No more composite chars available", lispstr); emch = make_ichar (Vcharset_composite, composite_char_row_next, composite_char_col_next); Fputhash (make_char (emch), lispstr, Vcomposite_char_char2string_hash_table); Fputhash (lispstr, make_char (emch), Vcomposite_char_string2char_hash_table); composite_char_col_next++; if (composite_char_col_next >= 128) { composite_char_col_next = 32; composite_char_row_next++; } } else emch = XCHAR (ch); return emch; } Lisp_Object composite_char_string (Ichar ch) { Lisp_Object str = Fgethash (make_char (ch), Vcomposite_char_char2string_hash_table, Qunbound); assert (!UNBOUNDP (str)); return str; } DEFUN ("make-composite-char", Fmake_composite_char, 1, 1, 0, /* Convert a string into a single composite character. The character is the result of overstriking all the characters in the string. */ (string)) { CHECK_STRING (string); return make_char (lookup_composite_char (XSTRING_DATA (string), XSTRING_LENGTH (string))); } DEFUN ("composite-char-string", Fcomposite_char_string, 1, 1, 0, /* Return a string of the characters comprising a composite character. */ (ch)) { Ichar emch; CHECK_CHAR (ch); emch = XCHAR (ch); if (ichar_leading_byte (emch) != LEADING_BYTE_COMPOSITE) invalid_argument ("Must be composite char", ch); return composite_char_string (emch); } #endif /* ENABLE_COMPOSITE_CHARS */ /************************************************************************/ /* initialization */ /************************************************************************/ void reinit_eistring_early (void) { the_eistring_malloc_zero_init = the_eistring_zero_init; the_eistring_malloc_zero_init.mallocp_ = 1; } void init_eistring_once_early (void) { reinit_eistring_early (); } void syms_of_text (void) { DEFSUBR (Fmake_char); #ifdef MULE DEFSUBR (Fchar_charset); DEFSUBR (Fchar_octet); DEFSUBR (Fsplit_char); #ifdef ENABLE_COMPOSITE_CHARS DEFSUBR (Fmake_composite_char); DEFSUBR (Fcomposite_char_string); #endif #endif /* MULE */ } void reinit_vars_of_text (void) { int i; conversion_in_dynarr_list = Dynarr_new2 (Ibyte_dynarr_dynarr, Ibyte_dynarr *); conversion_out_dynarr_list = Dynarr_new2 (Extbyte_dynarr_dynarr, Extbyte_dynarr *); active_dfc_e2c = Dynarr_new (dfc_e2c_vals); for (i = 0; i <= MAX_BYTEBPOS_GAP_SIZE_3; i++) three_to_one_table[i] = i / 3; } void vars_of_text (void) { reinit_vars_of_text (); QSin_char_byte_conversion = build_msg_string ("(in char-byte conversion)"); staticpro (&QSin_char_byte_conversion); QSin_internal_external_conversion = build_msg_string ("(in internal-external conversion)"); staticpro (&QSin_internal_external_conversion); #ifdef ENABLE_COMPOSITE_CHARS /* #### not dumped properly */ composite_char_row_next = 32; composite_char_col_next = 32; Vcomposite_char_string2char_hash_table = make_lisp_hash_table (500, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL); Vcomposite_char_char2string_hash_table = make_lisp_hash_table (500, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ); staticpro (&Vcomposite_char_string2char_hash_table); staticpro (&Vcomposite_char_char2string_hash_table); #endif /* ENABLE_COMPOSITE_CHARS */ }