diff options
| author | Akinori Ito <aito@eie.yz.yamagata-u.ac.jp> | 2001-11-08 05:14:08 +0000 | 
|---|---|---|
| committer | Akinori Ito <aito@eie.yz.yamagata-u.ac.jp> | 2001-11-08 05:14:08 +0000 | 
| commit | 68a07bf03b7624c9924065cce9ffa45497225834 (patch) | |
| tree | c2adb06a909a8594445e4a3f8587c4bad46e3ecd /gc/cord | |
| download | w3m-68a07bf03b7624c9924065cce9ffa45497225834.tar.gz w3m-68a07bf03b7624c9924065cce9ffa45497225834.zip | |
Initial revision
Diffstat (limited to 'gc/cord')
| -rw-r--r-- | gc/cord/README | 31 | ||||
| -rwxr-xr-x | gc/cord/SCOPTIONS.amiga | 14 | ||||
| -rw-r--r-- | gc/cord/SMakefile.amiga | 20 | ||||
| -rw-r--r-- | gc/cord/cord.h | 327 | ||||
| -rw-r--r-- | gc/cord/cordbscs.c | 915 | ||||
| -rw-r--r-- | gc/cord/cordprnt.c | 390 | ||||
| -rw-r--r-- | gc/cord/cordtest.c | 228 | ||||
| -rw-r--r-- | gc/cord/cordxtra.c | 621 | ||||
| -rw-r--r-- | gc/cord/de.c | 603 | ||||
| -rw-r--r-- | gc/cord/de_cmds.h | 33 | ||||
| -rwxr-xr-x | gc/cord/de_win.ICO | bin | 0 -> 766 bytes | |||
| -rw-r--r-- | gc/cord/de_win.RC | 78 | ||||
| -rw-r--r-- | gc/cord/de_win.c | 366 | ||||
| -rw-r--r-- | gc/cord/de_win.h | 103 | ||||
| -rw-r--r-- | gc/cord/ec.h | 70 | ||||
| -rw-r--r-- | gc/cord/gc.h | 754 | ||||
| -rw-r--r-- | gc/cord/private/cord_pos.h | 118 | 
17 files changed, 4671 insertions, 0 deletions
| diff --git a/gc/cord/README b/gc/cord/README new file mode 100644 index 0000000..6210145 --- /dev/null +++ b/gc/cord/README @@ -0,0 +1,31 @@ +Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose,  provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +Please send bug reports to Hans-J. Boehm (boehm@sgi.com). + +This is a string packages that uses a tree-based representation. +See cord.h for a description of the functions provided.  Ec.h describes +"extensible cords", which are essentially output streams that write +to a cord.  These allow for efficient construction of cords without +requiring a bound on the size of a cord. + +de.c is a very dumb text editor that illustrates the use of cords. +It maintains a list of file versions.  Each version is simply a +cord representing the file contents.  Nonetheless, standard +editing operations are efficient, even on very large files. +(Its 3 line "user manual" can be obtained by invoking it without +arguments.  Note that ^R^N and ^R^P move the cursor by +almost a screen.  It does not understand tabs, which will show +up as highlighred "I"s.  Use the UNIX "expand" program first.) +To build the editor, type "make cord/de" in the gc directory. + +This package assumes an ANSI C compiler such as gcc.  It will +not compile with an old-style K&R compiler. diff --git a/gc/cord/SCOPTIONS.amiga b/gc/cord/SCOPTIONS.amiga new file mode 100755 index 0000000..2a09197 --- /dev/null +++ b/gc/cord/SCOPTIONS.amiga @@ -0,0 +1,14 @@ +MATH=STANDARD +CPU=68030 +NOSTACKCHECK +OPTIMIZE +VERBOSE +NOVERSION +NOICONS +OPTIMIZERTIME +INCLUDEDIR=/ +DEFINE AMIGA +LIBRARY=cord.lib +LIBRARY=/gc.lib +IGNORE=100 +IGNORE=161 diff --git a/gc/cord/SMakefile.amiga b/gc/cord/SMakefile.amiga new file mode 100644 index 0000000..5aef131 --- /dev/null +++ b/gc/cord/SMakefile.amiga @@ -0,0 +1,20 @@ +# Makefile for cord.lib +# Michel Schinz 1994/07/20 + +OBJS = cordbscs.o cordprnt.o cordxtra.o + +all: cord.lib cordtest + +cordbscs.o: cordbscs.c +cordprnt.o: cordprnt.c +cordxtra.o: cordxtra.c +cordtest.o: cordtest.c + +cord.lib: $(OBJS) +	oml cord.lib r $(OBJS) + +cordtest: cordtest.o cord.lib +	sc cordtest.o link + +clean: +	delete cord.lib cordtest \#?.o \#?.lnk diff --git a/gc/cord/cord.h b/gc/cord/cord.h new file mode 100644 index 0000000..584112f --- /dev/null +++ b/gc/cord/cord.h @@ -0,0 +1,327 @@ +/*  + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* Boehm, October 5, 1995 4:20 pm PDT */ +  +/* + * Cords are immutable character strings.  A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments.  (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords.  They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + *   concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + *   performs editing operations by producing a new cord representing + *   the file after echa character change (and keeping the old ones in an + *   edit history) + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord.  + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type fo a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i;  CORD_FOR(i, cord) {  ... CORD_pos_fetch(i) ... } - + *    examine each character in a cord.  CORD_pos_fetch(i) is the char. + * CORD_fetch(int i) - Retrieve i'th character (slowly). + * CORD_cmp(cord1, cord2) - compare two cords. + * CORD_from_file(FILE * f) - turn a read-only file into a cord. + * CORD_to_char_star(cord) - convert to C string. + *   (Non-NULL C constant strings are cords.) + * CORD_printf (etc.) - cord version of printf. Use %r for cords. + */ +# ifndef CORD_H + +# define CORD_H +# include <stddef.h> +# include <stdio.h> +/* Cords have type const char *.  This is cheating quite a bit, and not	*/ +/* 100% portable.  But it means that nonempty character string		*/ +/* constants may be used as cords directly, provided the string is	*/ +/* never modified in place.  The empty cord is represented by, and	*/ +/* can be written as, 0.						*/ + +typedef const char * CORD; + +/* An empty cord is always represented as nil 	*/ +# define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords.  If the arguments are C strings, they may 	*/ +/* not be subsequently altered.						*/ +CORD CORD_cat(CORD x, CORD y); + +/* Concatenate a cord and a C string with known length.  Except for the	*/ +/* empty string case, this is a special case of CORD_cat.  Since the	*/ +/* length is known, it can be faster.					*/ +/* The string y is shared with the resulting CORD.  Hence it should	*/ +/* not be altered by the caller.					*/ +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny); + +/* Compute the length of a cord */ +size_t CORD_len(CORD x); + +/* Cords may be represented by functions defining the ith character */ +typedef char (* CORD_fn)(size_t i, void * client_data); + +/* Turn a functional description into a cord. 	*/ +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len); + +/* Return the substring (subcord really) of x with length at most n,	*/ +/* starting at position i.  (The initial character has position 0.)	*/ +CORD CORD_substr(CORD x, size_t i, size_t n); + +/* Return the argument, but rebalanced to allow more efficient   	*/ +/* character retrieval, substring operations, and comparisons.		*/ +/* This is useful only for cords that were built using repeated 	*/ +/* concatenation.  Guarantees log time access to the result, unless	*/ +/* x was obtained through a large number of repeated substring ops	*/ +/* or the embedded functional descriptions take longer to evaluate.	*/ +/* May reallocate significant parts of the cord.  The argument is not	*/ +/* modified; only the result is balanced.				*/ +CORD CORD_balance(CORD x); + +/* The following traverse a cord by applying a function to each 	*/ +/* character.  This is occasionally appropriate, especially where	*/ +/* speed is crucial.  But, since C doesn't have nested functions,	*/ +/* clients of this sort of traversal are clumsy to write.  Consider	*/ +/* the functions that operate on cord positions instead.		*/ + +/* Function to iteratively apply to individual characters in cord.	*/ +typedef int (* CORD_iter_fn)(char c, void * client_data); + +/* Function to apply to substrings of a cord.  Each substring is a 	*/ +/* a C character string, not a general cord.				*/ +typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data); +# define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order,		*/ +/* starting at position i. If						*/ +/* f2 is not CORD_NO_FN, then multiple calls to f1 may be replaced by	*/ +/* a single call to f2.  The parameter f2 is provided only to allow	*/ +/* some optimization by the client.  This terminates when the right	*/ +/* end of this string is reached, or when f1 or f2 return != 0.  In the	*/ +/* latter case CORD_iter returns != 0.  Otherwise it returns 0.		*/ +/* The specified value of i must be < CORD_len(x).			*/ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, +	       CORD_batched_iter_fn f2, void * client_data); + +/* A simpler version that starts at 0, and without f2:	*/ +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data); +# define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning.	No provisions for	*/ +/* CORD_batched_iter_fn.						*/ +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data); + +/* A simpler version that starts at the end:	*/ +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data); + +/* Functions that operate on cord positions.  The easy way to traverse	*/ +/* cords.  A cord position is logically a pair consisting of a cord	*/ +/* and an index into that cord.  But it is much faster to retrieve a	*/ +/* charcter based on a position than on an index.  Unfortunately,	*/ +/* positions are big (order of a few 100 bytes), so allocate them with	*/ +/* caution.								*/ +/* Things in cord_pos.h should be treated as opaque, except as		*/ +/* described below.  Also note that					*/ +/* CORD_pos_fetch, CORD_next and CORD_prev have both macro and function	*/ +/* definitions.  The former may evaluate their argument more than once. */ +# include "private/cord_pos.h" + +/* +	Visible definitions from above: +	 +	typedef <OPAQUE but fairly big> CORD_pos[1]; +	 +	* Extract the cord from a position: +	CORD CORD_pos_to_cord(CORD_pos p); +	 +	* Extract the current index from a position: +	size_t CORD_pos_to_index(CORD_pos p); +	 +	* Fetch the character located at the given position: +	char CORD_pos_fetch(CORD_pos p); +	 +	* Initialize the position to refer to the given cord and index. +	* Note that this is the most expensive function on positions: +	void CORD_set_pos(CORD_pos p, CORD x, size_t i); +	 +	* Advance the position to the next character. +	* P must be initialized and valid. +	* Invalidates p if past end: +	void CORD_next(CORD_pos p); +	 +	* Move the position to the preceding character. +	* P must be initialized and valid. +	* Invalidates p if past beginning: +	void CORD_prev(CORD_pos p); +	 +	* Is the position valid, i.e. inside the cord? +	int CORD_pos_valid(CORD_pos p); +*/ +# define CORD_FOR(pos, cord) \ +    for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + +			 +/* An out of memory handler to call.  May be supplied by client.	*/ +/* Must not return.							*/ +extern void (* CORD_oom_fn)(void); + +/* Dump the representation of x to stdout in an implementation defined	*/ +/* manner.  Intended for debugging only.				*/ +void CORD_dump(CORD x); + +/* The following could easily be implemented by the client.  They are	*/ +/* provided in cordxtra.c for convenience.				*/ + +/* Concatenate a character to the end of a cord.	*/ +CORD CORD_cat_char(CORD x, char c); + +/* Concatenate n cords.	*/ +CORD CORD_catn(int n, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1)  	*/ +char CORD_fetch(CORD x, size_t i); + +/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y	*/ +int CORD_cmp(CORD x, CORD y); + +/* A generalization that takes both starting positions for the 		*/ +/* comparison, and a limit on the number of characters to be compared.	*/ +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len); + +/* Find the first occurrence of s in x at position start or later.	*/ +/* Return the position of the first character of s in x, or		*/ +/* CORD_NOT_FOUND if there is none.					*/ +size_t CORD_str(CORD x, size_t start, CORD s); + +/* Return a cord consisting of i copies of (possibly NUL) c.  Dangerous	*/ +/* in conjunction with CORD_to_char_star.				*/ +/* The resulting representation takes constant space, independent of i.	*/ +CORD CORD_chars(char c, size_t i); +# define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord.  The file must be seekable.  Its contents	*/ +/* must remain constant.  The file may be accessed as an immediate	*/ +/* result of this call and/or as a result of subsequent accesses to 	*/ +/* the cord.  Short files are likely to be immediately read, but	*/ +/* long files are likely to be read on demand, possibly relying on 	*/ +/* stdio for buffering.							*/ +/* We must have exclusive access to the descriptor f, i.e. we may	*/ +/* read it at any time, and expect the file pointer to be		*/ +/* where we left it.  Normally this should be invoked as		*/ +/* CORD_from_file(fopen(...))						*/ +/* CORD_from_file arranges to close the file descriptor when it is no	*/ +/* longer needed (e.g. when the result becomes inaccessible).		*/  +/* The file f must be such that ftell reflects the actual character	*/ +/* position in the file, i.e. the number of characters that can be 	*/ +/* or were read with fread.  On UNIX systems this is always true.  On	*/ +/* MS Windows systems, f must be opened in binary mode.			*/ +CORD CORD_from_file(FILE * f); + +/* Equivalent to the above, except that the entire file will be read	*/ +/* and the file pointer will be closed immediately.			*/ +/* The binary mode restriction from above does not apply.		*/ +CORD CORD_from_file_eager(FILE * f); + +/* Equivalent to the above, except that the file will be read on demand.*/ +/* The binary mode restriction applies.					*/ +CORD CORD_from_file_lazy(FILE * f); + +/* Turn a cord into a C string.	The result shares no structure with	*/ +/* x, and is thus modifiable.						*/ +char * CORD_to_char_star(CORD x); + +/* Turn a C string into a CORD.  The C string is copied, and so may	*/ +/* subsequently be modified.						*/ +CORD CORD_from_char_star(const char *s); + +/* Identical to the above, but the result may share structure with	*/ +/* the argument and is thus not modifiable.				*/ +const char * CORD_to_const_char_star(CORD x);  + +/* Write a cord to a file, starting at the current position.  No	*/ +/* trailing NULs are newlines are added.				*/ +/* Returns EOF if a write error occurs, 1 otherwise.			*/ +int CORD_put(CORD x, FILE * f); + +/* "Not found" result for the following two functions.			*/ +# define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr.  Returns the position (an integer, not	*/ +/* a pointer) of the first occurrence of (char) c inside x at position 	*/ +/* i or later. The value i must be < CORD_len(x).			*/ +size_t CORD_chr(CORD x, size_t i, int c); + +/* A vague analog of strrchr.  Returns index of the last occurrence	*/ +/* of (char) c inside x at position i or earlier. The value i		*/ +/* must be < CORD_len(x).						*/ +size_t CORD_rchr(CORD x, size_t i, int c); + + +/* The following are also not primitive, but are implemented in 	*/ +/* cordprnt.c.  They provide functionality similar to the ANSI C	*/ +/* functions with corresponding names, but with the following		*/ +/* additions and changes:						*/ +/* 1. A %r conversion specification specifies a CORD argument.  Field	*/ +/*    width, precision, etc. have the same semantics as for %s.		*/ +/*    (Note that %c,%C, and %S were already taken.)			*/ +/* 2. The format string is represented as a CORD.		        */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st	*/ 	/*    argument.	Unlike their ANSI C versions, there is no need to guess	*/ +/*    the correct buffer size.						*/ +/* 4. Most of the conversions are implement through the native 		*/ +/*    vsprintf.  Hence they are usually no faster, and 			*/ +/*    idiosyncracies of the native printf are preserved.  However,	*/ +/*    CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied;	*/ +/*    the result shares the original structure.  This may make them	*/ +/*    very efficient in some unusual applications.			*/ +/*    The format string is copied.					*/ +/* All functions return the number of characters generated or -1 on	*/ +/* error.  This complies with the ANSI standard, but is inconsistent	*/ +/* with some older implementations of sprintf.				*/ + +/* The implementation of these is probably less portable than the rest	*/ +/* of this package.							*/ + +#ifndef CORD_NO_IO + +#include <stdarg.h> + +int CORD_sprintf(CORD * out, CORD format, ...); +int CORD_vsprintf(CORD * out, CORD format, va_list args); +int CORD_fprintf(FILE * f, CORD format, ...); +int CORD_vfprintf(FILE * f, CORD format, va_list args); +int CORD_printf(CORD format, ...); +int CORD_vprintf(CORD format, va_list args); + +#endif /* CORD_NO_IO */ + +# endif /* CORD_H */ diff --git a/gc/cord/cordbscs.c b/gc/cord/cordbscs.c new file mode 100644 index 0000000..9fc894d --- /dev/null +++ b/gc/cord/cordbscs.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* Boehm, October 3, 1994 5:19 pm PDT */ +# include "gc.h" +# include "cord.h" +# include <stdlib.h> +# include <stdio.h> +# include <string.h> + +/* An implementation of the cord primitives.  These are the only 	*/ +/* Functions that understand the representation.  We perform only	*/ +/* minimal checks on arguments to these functions.  Out of bounds	*/ +/* arguments to the iteration functions may result in client functions	*/ +/* invoked on garbage data.  In most cases, client functions should be	*/ +/* programmed defensively enough that this does not result in memory	*/ +/* smashes.								*/  + +typedef void (* oom_fn)(void); + +oom_fn CORD_oom_fn = (oom_fn) 0; + +# define OUT_OF_MEMORY {  if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ +			  ABORT("Out of memory\n"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +typedef unsigned long word; + +typedef union { +    struct Concatenation { +    	char null; +	char header; +	char depth;	/* concatenation nesting depth. */ +	unsigned char left_len; +			/* Length of left child if it is sufficiently	*/ +			/* short; 0 otherwise.				*/ +#	    define MAX_LEFT_LEN 255 +	word len; +	CORD left;	/* length(left) > 0	*/ +	CORD right;	/* length(right) > 0	*/ +    } concatenation; +    struct Function { +	char null; +	char header; +	char depth;	/* always 0	*/ +	char left_len;	/* always 0	*/ +	word len; +	CORD_fn fn; +	void * client_data; +    } function; +    struct Generic { +    	char null; +	char header; +	char depth; +	char left_len; +	word len; +    } generic; +    char string[1]; +} CordRep; + +# define CONCAT_HDR 1 +	 +# define FN_HDR 4 +# define SUBSTR_HDR 6 +	/* Substring nodes are a special case of function nodes.  	*/ +	/* The client_data field is known to point to a substr_args	*/ +	/* structure, and the function is either CORD_apply_access_fn 	*/ +	/* or CORD_index_access_fn.					*/ + +/* The following may be applied only to function and concatenation nodes: */ +#define IS_CONCATENATION(s)  (((CordRep *)s)->generic.header == CONCAT_HDR) + +#define IS_FUNCTION(s)  ((((CordRep *)s)->generic.header & FN_HDR) != 0) + +#define IS_SUBSTR(s) (((CordRep *)s)->generic.header == SUBSTR_HDR) + +#define LEN(s) (((CordRep *)s) -> generic.len) +#define DEPTH(s) (((CordRep *)s) -> generic.depth) +#define GEN_LEN(s) (CORD_IS_STRING(s) ? strlen(s) : LEN(s)) + +#define LEFT_LEN(c) ((c) -> left_len != 0? \ +				(c) -> left_len \ +				: (CORD_IS_STRING((c) -> left) ? \ +					(c) -> len - GEN_LEN((c) -> right) \ +					: LEN((c) -> left))) + +#define SHORT_LIMIT (sizeof(CordRep) - 1) +	/* Cords shorter than this are C strings */ + + +/* Dump the internal representation of x to stdout, with initial 	*/ +/* indentation level n.							*/ +void CORD_dump_inner(CORD x, unsigned n) +{ +    register size_t i; +     +    for (i = 0; i < (size_t)n; i++) { +        fputs("  ", stdout); +    } +    if (x == 0) { +      	fputs("NIL\n", stdout); +    } else if (CORD_IS_STRING(x)) { +        for (i = 0; i <= SHORT_LIMIT; i++) { +            if (x[i] == '\0') break; +            putchar(x[i]); +        } +        if (x[i] != '\0') fputs("...", stdout); +        putchar('\n'); +    } else if (IS_CONCATENATION(x)) { +        register struct Concatenation * conc = +        			&(((CordRep *)x) -> concatenation); +        printf("Concatenation: %p (len: %d, depth: %d)\n", +               x, (int)(conc -> len), (int)(conc -> depth)); +        CORD_dump_inner(conc -> left, n+1); +        CORD_dump_inner(conc -> right, n+1); +    } else /* function */{ +        register struct Function * func = +        			&(((CordRep *)x) -> function); +        if (IS_SUBSTR(x)) printf("(Substring) "); +        printf("Function: %p (len: %d): ", x, (int)(func -> len)); +        for (i = 0; i < 20 && i < func -> len; i++) { +            putchar((*(func -> fn))(i, func -> client_data)); +        } +        if (i < func -> len) fputs("...", stdout); +        putchar('\n'); +    } +} + +/* Dump the internal representation of x to stdout	*/ +void CORD_dump(CORD x) +{ +    CORD_dump_inner(x, 0); +    fflush(stdout); +} + +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) +{ +    register size_t result_len; +    register size_t lenx; +    register int depth; +     +    if (x == CORD_EMPTY) return(y); +    if (leny == 0) return(x); +    if (CORD_IS_STRING(x)) { +        lenx = strlen(x); +        result_len = lenx + leny; +        if (result_len <= SHORT_LIMIT) { +            register char * result = GC_MALLOC_ATOMIC(result_len+1); +         +            if (result == 0) OUT_OF_MEMORY; +            memcpy(result, x, lenx); +            memcpy(result + lenx, y, leny); +            result[result_len] = '\0'; +            return((CORD) result); +        } else { +            depth = 1; +        } +    } else { +    	register CORD right; +    	register CORD left; +    	register char * new_right; +    	register size_t right_len; +    	 +    	lenx = LEN(x); +    	 +        if (leny <= SHORT_LIMIT/2 +    	    && IS_CONCATENATION(x) +            && CORD_IS_STRING(right = ((CordRep *)x) -> concatenation.right)) { +            /* Merge y into right part of x. */ +            if (!CORD_IS_STRING(left = ((CordRep *)x) -> concatenation.left)) { +            	right_len = lenx - LEN(left); +            } else if (((CordRep *)x) -> concatenation.left_len != 0) { +                right_len = lenx - ((CordRep *)x) -> concatenation.left_len; +            } else { +            	right_len = strlen(right); +            } +            result_len = right_len + leny;  /* length of new_right */ +            if (result_len <= SHORT_LIMIT) { +            	new_right = GC_MALLOC_ATOMIC(result_len + 1); +            	memcpy(new_right, right, right_len); +            	memcpy(new_right + right_len, y, leny); +            	new_right[result_len] = '\0'; +            	y = new_right; +            	leny = result_len; +            	x = left; +            	lenx -= right_len; +            	/* Now fall through to concatenate the two pieces: */ +            } +            if (CORD_IS_STRING(x)) { +                depth = 1; +            } else { +                depth = DEPTH(x) + 1; +            } +        } else { +            depth = DEPTH(x) + 1; +        } +        result_len = lenx + leny; +    } +    { +      /* The general case; lenx, result_len is known: */ +    	register struct Concatenation * result; +    	 +    	result = GC_NEW(struct Concatenation); +    	if (result == 0) OUT_OF_MEMORY; +    	result->header = CONCAT_HDR; +    	result->depth = depth; +    	if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; +    	result->len = result_len; +    	result->left = x; +    	result->right = y; +    	if (depth > MAX_DEPTH) { +    	    return(CORD_balance((CORD)result)); +    	} else { +    	    return((CORD) result); +    	} +    } +} + + +CORD CORD_cat(CORD x, CORD y) +{ +    register size_t result_len; +    register int depth; +    register size_t lenx; +     +    if (x == CORD_EMPTY) return(y); +    if (y == CORD_EMPTY) return(x); +    if (CORD_IS_STRING(y)) { +        return(CORD_cat_char_star(x, y, strlen(y))); +    } else if (CORD_IS_STRING(x)) { +        lenx = strlen(x); +        depth = DEPTH(y) + 1; +    } else { +        register int depthy = DEPTH(y); +         +        lenx = LEN(x); +        depth = DEPTH(x) + 1; +        if (depthy >= depth) depth = depthy + 1; +    } +    result_len = lenx + LEN(y); +    { +    	register struct Concatenation * result; +    	 +    	result = GC_NEW(struct Concatenation); +    	if (result == 0) OUT_OF_MEMORY; +    	result->header = CONCAT_HDR; +    	result->depth = depth; +    	if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; +    	result->len = result_len; +    	result->left = x; +    	result->right = y; +    	return((CORD) result); +    } +} + + + +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) +{ +    if (len <= 0) return(0); +    if (len <= SHORT_LIMIT) { +        register char * result; +        register size_t i; +        char buf[SHORT_LIMIT+1]; +        register char c; +         +        for (i = 0; i < len; i++) { +            c = (*fn)(i, client_data); +            if (c == '\0') goto gen_case; +            buf[i] = c; +        } +        buf[i] = '\0'; +        result = GC_MALLOC_ATOMIC(len+1); +        if (result == 0) OUT_OF_MEMORY; +        strcpy(result, buf); +        result[len] = '\0'; +        return((CORD) result); +    } +  gen_case: +    { +    	register struct Function * result; +    	 +    	result = GC_NEW(struct Function); +    	if (result == 0) OUT_OF_MEMORY; +    	result->header = FN_HDR; +    	/* depth is already 0 */ +    	result->len = len; +    	result->fn = fn; +    	result->client_data = client_data; +    	return((CORD) result); +    } +} + +size_t CORD_len(CORD x) +{ +    if (x == 0) { +     	return(0); +    } else { +	return(GEN_LEN(x)); +    } +} + +struct substr_args { +    CordRep * sa_cord; +    size_t sa_index; +}; + +char CORD_index_access_fn(size_t i, void * client_data) +{ +    register struct substr_args *descr = (struct substr_args *)client_data; +     +    return(((char *)(descr->sa_cord))[i + descr->sa_index]); +} + +char CORD_apply_access_fn(size_t i, void * client_data) +{ +    register struct substr_args *descr = (struct substr_args *)client_data; +    register struct Function * fn_cord = &(descr->sa_cord->function); +     +    return((*(fn_cord->fn))(i + descr->sa_index, fn_cord->client_data)); +} + +/* A version of CORD_substr that simply returns a function node, thus	*/ +/* postponing its work.	The fourth argument is a function that may	*/ +/* be used for efficient access to the ith character.			*/ +/* Assumes i >= 0 and i + n < length(x).				*/ +CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) +{ +    register struct substr_args * sa = GC_NEW(struct substr_args); +    CORD result; +     +    if (sa == 0) OUT_OF_MEMORY; +    sa->sa_cord = (CordRep *)x; +    sa->sa_index = i; +    result = CORD_from_fn(f, (void *)sa, n); +    ((CordRep *)result) -> function.header = SUBSTR_HDR; +    return (result); +} + +# define SUBSTR_LIMIT (10 * SHORT_LIMIT) +	/* Substrings of function nodes and flat strings shorter than 	*/ +	/* this are flat strings.  Othewise we use a functional 	*/ +	/* representation, which is significantly slower to access.	*/ + +/* A version of CORD_substr that assumes i >= 0, n > 0, and i + n < length(x).*/ +CORD CORD_substr_checked(CORD x, size_t i, size_t n) +{ +    if (CORD_IS_STRING(x)) { +        if (n > SUBSTR_LIMIT) { +            return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); +        } else { +            register char * result = GC_MALLOC_ATOMIC(n+1); +             +            if (result == 0) OUT_OF_MEMORY; +            strncpy(result, x+i, n); +            result[n] = '\0'; +            return(result); +        } +    } else if (IS_CONCATENATION(x)) { +    	register struct Concatenation * conc +    			= &(((CordRep *)x) -> concatenation); +    	register size_t left_len; +    	register size_t right_len; +    	 +    	left_len = LEFT_LEN(conc); +    	right_len = conc -> len - left_len; +    	if (i >= left_len) { +    	    if (n == right_len) return(conc -> right); +    	    return(CORD_substr_checked(conc -> right, i - left_len, n)); +    	} else if (i+n <= left_len) { +    	    if (n == left_len) return(conc -> left); +    	    return(CORD_substr_checked(conc -> left, i, n)); +    	} else { +    	    /* Need at least one character from each side. */ +    	    register CORD left_part; +    	    register CORD right_part; +    	    register size_t left_part_len = left_len - i; +     	 +    	    if (i == 0) { +    	        left_part = conc -> left; +    	    } else { +    	        left_part = CORD_substr_checked(conc -> left, i, left_part_len); +    	    } +    	    if (i + n == right_len + left_len) { +    	         right_part = conc -> right; +    	    } else { +    	         right_part = CORD_substr_checked(conc -> right, 0, +    	    				          n - left_part_len); +    	    } +    	    return(CORD_cat(left_part, right_part)); +    	} +    } else /* function */ { +        if (n > SUBSTR_LIMIT) { +            if (IS_SUBSTR(x)) { +            	/* Avoid nesting substring nodes.	*/ +            	register struct Function * f = &(((CordRep *)x) -> function); +            	register struct substr_args *descr = +            			(struct substr_args *)(f -> client_data); +            	 +            	return(CORD_substr_closure((CORD)descr->sa_cord, +            				   i + descr->sa_index, +            				   n, f -> fn)); +            } else { +                return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); +            } +        } else { +            char * result; +            register struct Function * f = &(((CordRep *)x) -> function); +            char buf[SUBSTR_LIMIT+1]; +            register char * p = buf; +            register char c; +            register int j; +            register int lim = i + n; +             +            for (j = i; j < lim; j++) { +            	c = (*(f -> fn))(j, f -> client_data); +            	if (c == '\0') { +            	    return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); +            	} +            	*p++ = c; +            } +            *p = '\0'; +            result = GC_MALLOC_ATOMIC(n+1); +            if (result == 0) OUT_OF_MEMORY; +            strcpy(result, buf); +            return(result); +        } +    } +} + +CORD CORD_substr(CORD x, size_t i, size_t n) +{ +    register size_t len = CORD_len(x); +     +    if (i >= len || n <= 0) return(0); +    	/* n < 0 is impossible in a correct C implementation, but	*/ +    	/* quite possible  under SunOS 4.X.				*/ +    if (i + n > len) n = len - i; +#   ifndef __STDC__ +      if (i < 0) ABORT("CORD_substr: second arg. negative"); +    	/* Possible only if both client and C implementation are buggy.	*/ +    	/* But empirically this happens frequently.			*/ +#   endif +    return(CORD_substr_checked(x, i, n)); +} + +/* See cord.h for definition.  We assume i is in range.	*/ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, +			 CORD_batched_iter_fn f2, void * client_data) +{ +    if (x == 0) return(0); +    if (CORD_IS_STRING(x)) { +    	register const char *p = x+i; +    	 +    	if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); +        if (f2 != CORD_NO_FN) { +            return((*f2)(p, client_data)); +        } else { +	    while (*p) { +                if ((*f1)(*p, client_data)) return(1); +                p++; +	    } +	    return(0); +        } +    } else if (IS_CONCATENATION(x)) { +    	register struct Concatenation * conc +    			= &(((CordRep *)x) -> concatenation); +    	 +    	 +    	if (i > 0) { +    	    register size_t left_len = LEFT_LEN(conc); +    	     +    	    if (i >= left_len) { +    	        return(CORD_iter5(conc -> right, i - left_len, f1, f2, +    	        		  client_data)); +    	    } +    	} +    	if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { +    	    return(1); +    	} +    	return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); +    } else /* function */ { +        register struct Function * f = &(((CordRep *)x) -> function); +        register size_t j; +        register size_t lim = f -> len; +         +        for (j = i; j < lim; j++) { +            if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { +                return(1); +            } +        } +        return(0); +    } +} +			 +#undef CORD_iter +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data) +{ +    return(CORD_iter5(x, 0, f1, CORD_NO_FN, client_data)); +} + +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) +{ +    if (x == 0) return(0); +    if (CORD_IS_STRING(x)) { +	register const char *p = x + i; +	register char c; +                +	for(;;) { +	    c = *p; +	    if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); +            if ((*f1)(c, client_data)) return(1); +	    if (p == x) break; +            p--; +	} +	return(0); +    } else if (IS_CONCATENATION(x)) { +    	register struct Concatenation * conc +    			= &(((CordRep *)x) -> concatenation); +    	register CORD left_part = conc -> left; +    	register size_t left_len; +    	 +    	left_len = LEFT_LEN(conc); +    	if (i >= left_len) { +    	    if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { +    	    	return(1); +    	    } +    	    return(CORD_riter4(left_part, left_len - 1, f1, client_data)); +    	} else { +    	    return(CORD_riter4(left_part, i, f1, client_data)); +    	} +    } else /* function */ { +        register struct Function * f = &(((CordRep *)x) -> function); +        register size_t j; +         +        for (j = i; ; j--) { +            if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { +                return(1); +            } +            if (j == 0) return(0); +        } +    } +} + +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) +{ +    return(CORD_riter4(x, CORD_len(x) - 1, f1, client_data)); +} + +/* + * The following functions are concerned with balancing cords. + * Strategy: + * Scan the cord from left to right, keeping the cord scanned so far + * as a forest of balanced trees of exponentialy decreasing length. + * When a new subtree needs to be added to the forest, we concatenate all + * shorter ones to the new tree in the appropriate order, and then insert + * the result into the forest. + * Crucial invariants: + * 1. The concatenation of the forest (in decreasing order) with the + *     unscanned part of the rope is equal to the rope being balanced. + * 2. All trees in the forest are balanced. + * 3. forest[i] has depth at most i. + */ + +typedef struct { +    CORD c; +    size_t len;		/* Actual length of c 	*/ +} ForestElement; + +static size_t min_len [ MAX_DEPTH ]; + +static int min_len_init = 0; + +int CORD_max_len; + +typedef ForestElement Forest [ MAX_DEPTH ]; +			/* forest[i].len >= fib(i+1)	        */ +			/* The string is the concatenation	*/ +			/* of the forest in order of DECREASING */ +			/* indices.				*/ + +void CORD_init_min_len() +{ +    register int i; +    register size_t last, previous, current; +         +    min_len[0] = previous = 1; +    min_len[1] = last = 2; +    for (i = 2; i < MAX_DEPTH; i++) { +    	current = last + previous; +    	if (current < last) /* overflow */ current = last; +    	min_len[i] = current; +    	previous = last; +    	last = current; +    } +    CORD_max_len = last - 1; +    min_len_init = 1; +} + + +void CORD_init_forest(ForestElement * forest, size_t max_len) +{ +    register int i; +     +    for (i = 0; i < MAX_DEPTH; i++) { +    	forest[i].c = 0; +    	if (min_len[i] > max_len) return; +    } +    ABORT("Cord too long"); +} + +/* Add a leaf to the appropriate level in the forest, cleaning		*/ +/* out lower levels as necessary.					*/ +/* Also works if x is a balanced tree of concatenations; however	*/ +/* in this case an extra concatenation node may be inserted above x;	*/ +/* This node should not be counted in the statement of the invariants.	*/ +void CORD_add_forest(ForestElement * forest, CORD x, size_t len) +{ +    register int i = 0; +    register CORD sum = CORD_EMPTY; +    register size_t sum_len = 0; +     +    while (len > min_len[i + 1]) { +    	if (forest[i].c != 0) { +    	    sum = CORD_cat(forest[i].c, sum); +    	    sum_len += forest[i].len; +    	    forest[i].c = 0; +    	} +        i++; +    } +    /* Sum has depth at most 1 greter than what would be required 	*/ +    /* for balance.							*/ +    sum = CORD_cat(sum, x); +    sum_len += len; +    /* If x was a leaf, then sum is now balanced.  To see this		*/ +    /* consider the two cases in which forest[i-1] either is or is 	*/ +    /* not empty.							*/ +    while (sum_len >= min_len[i]) { +    	if (forest[i].c != 0) { +    	    sum = CORD_cat(forest[i].c, sum); +    	    sum_len += forest[i].len; +    	    /* This is again balanced, since sum was balanced, and has	*/ +    	    /* allowable depth that differs from i by at most 1.	*/ +    	    forest[i].c = 0; +    	} +        i++; +    } +    i--; +    forest[i].c = sum; +    forest[i].len = sum_len; +} + +CORD CORD_concat_forest(ForestElement * forest, size_t expected_len) +{ +    register int i = 0; +    CORD sum = 0; +    size_t sum_len = 0; +     +    while (sum_len != expected_len) { +    	if (forest[i].c != 0) { +    	    sum = CORD_cat(forest[i].c, sum); +    	    sum_len += forest[i].len; +    	} +        i++; +    } +    return(sum); +} + +/* Insert the frontier of x into forest.  Balanced subtrees are	*/ +/* treated as leaves.  This potentially adds one to the depth	*/ +/* of the final tree.						*/ +void CORD_balance_insert(CORD x, size_t len, ForestElement * forest) +{ +    register int depth; +     +    if (CORD_IS_STRING(x)) { +        CORD_add_forest(forest, x, len); +    } else if (IS_CONCATENATION(x) +               && ((depth = DEPTH(x)) >= MAX_DEPTH +                   || len < min_len[depth])) { +    	register struct Concatenation * conc +    			= &(((CordRep *)x) -> concatenation); +    	size_t left_len = LEFT_LEN(conc); +    	 +    	CORD_balance_insert(conc -> left, left_len, forest); +    	CORD_balance_insert(conc -> right, len - left_len, forest); +    } else /* function or balanced */ { +    	CORD_add_forest(forest, x, len); +    } +} + + +CORD CORD_balance(CORD x) +{ +    Forest forest; +    register size_t len; +     +    if (x == 0) return(0); +    if (CORD_IS_STRING(x)) return(x); +    if (!min_len_init) CORD_init_min_len(); +    len = LEN(x); +    CORD_init_forest(forest, len); +    CORD_balance_insert(x, len, forest); +    return(CORD_concat_forest(forest, len)); +} + + +/* Position primitives	*/ + +/* Private routines to deal with the hard cases only: */ + +/* P contains a prefix of the  path to cur_pos.	Extend it to a full	*/ +/* path and set up leaf info.						*/ +/* Return 0 if past the end of cord, 1 o.w.				*/ +void CORD__extend_path(register CORD_pos p) +{ +     register struct CORD_pe * current_pe = &(p[0].path[p[0].path_len]); +     register CORD top = current_pe -> pe_cord; +     register size_t pos = p[0].cur_pos; +     register size_t top_pos = current_pe -> pe_start_pos; +     register size_t top_len = GEN_LEN(top); +      +     /* Fill in the rest of the path. */ +       while(!CORD_IS_STRING(top) && IS_CONCATENATION(top)) { +     	 register struct Concatenation * conc = +     	 		&(((CordRep *)top) -> concatenation); +     	 register size_t left_len; +     	  +     	 left_len = LEFT_LEN(conc); +     	 current_pe++; +     	 if (pos >= top_pos + left_len) { +     	     current_pe -> pe_cord = top = conc -> right; +     	     current_pe -> pe_start_pos = top_pos = top_pos + left_len; +     	     top_len -= left_len; +     	 } else { +     	     current_pe -> pe_cord = top = conc -> left; +     	     current_pe -> pe_start_pos = top_pos; +     	     top_len = left_len; +     	 } +     	 p[0].path_len++; +       } +     /* Fill in leaf description for fast access. */ +       if (CORD_IS_STRING(top)) { +         p[0].cur_leaf = top; +         p[0].cur_start = top_pos; +         p[0].cur_end = top_pos + top_len; +       } else { +         p[0].cur_end = 0; +       } +       if (pos >= top_pos + top_len) p[0].path_len = CORD_POS_INVALID; +} + +char CORD__pos_fetch(register CORD_pos p) +{ +    /* Leaf is a function node */ +    struct CORD_pe * pe = &((p)[0].path[(p)[0].path_len]); +    CORD leaf = pe -> pe_cord; +    register struct Function * f = &(((CordRep *)leaf) -> function); +     +    if (!IS_FUNCTION(leaf)) ABORT("CORD_pos_fetch: bad leaf"); +    return ((*(f -> fn))(p[0].cur_pos - pe -> pe_start_pos, f -> client_data)); +} + +void CORD__next(register CORD_pos p) +{ +    register size_t cur_pos = p[0].cur_pos + 1; +    register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); +    register CORD leaf = current_pe -> pe_cord; +     +    /* Leaf is not a string or we're at end of leaf */ +    p[0].cur_pos = cur_pos; +    if (!CORD_IS_STRING(leaf)) { +    	/* Function leaf	*/ +    	register struct Function * f = &(((CordRep *)leaf) -> function); +    	register size_t start_pos = current_pe -> pe_start_pos; +    	register size_t end_pos = start_pos + f -> len; +    	 +    	if (cur_pos < end_pos) { +    	  /* Fill cache and return. */ +    	    register size_t i; +    	    register size_t limit = cur_pos + FUNCTION_BUF_SZ; +    	    register CORD_fn fn = f -> fn; +    	    register void * client_data = f -> client_data; +    	     +    	    if (limit > end_pos) { +    	        limit = end_pos; +    	    } +    	    for (i = cur_pos; i < limit; i++) { +    	        p[0].function_buf[i - cur_pos] = +    	        	(*fn)(i - start_pos, client_data); +    	    } +    	    p[0].cur_start = cur_pos; +    	    p[0].cur_leaf = p[0].function_buf; +    	    p[0].cur_end = limit; +    	    return; +    	} +    } +    /* End of leaf	*/ +    /* Pop the stack until we find two concatenation nodes with the 	*/ +    /* same start position: this implies we were in left part.		*/ +    { +    	while (p[0].path_len > 0 +    	       && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { +    	    p[0].path_len--; +    	    current_pe--; +    	} +    	if (p[0].path_len == 0) { +	    p[0].path_len = CORD_POS_INVALID; +            return; +	} +    } +    p[0].path_len--; +    CORD__extend_path(p); +} + +void CORD__prev(register CORD_pos p) +{ +    register struct CORD_pe * pe = &(p[0].path[p[0].path_len]); +     +    if (p[0].cur_pos == 0) { +        p[0].path_len = CORD_POS_INVALID; +        return; +    } +    p[0].cur_pos--; +    if (p[0].cur_pos >= pe -> pe_start_pos) return; +     +    /* Beginning of leaf	*/ +     +    /* Pop the stack until we find two concatenation nodes with the 	*/ +    /* different start position: this implies we were in right part.	*/ +    { +    	register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); +    	 +    	while (p[0].path_len > 0 +    	       && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { +    	    p[0].path_len--; +    	    current_pe--; +    	} +    } +    p[0].path_len--; +    CORD__extend_path(p); +} + +#undef CORD_pos_fetch +#undef CORD_next +#undef CORD_prev +#undef CORD_pos_to_index +#undef CORD_pos_to_cord +#undef CORD_pos_valid + +char CORD_pos_fetch(register CORD_pos p) +{ +    if (p[0].cur_start <= p[0].cur_pos && p[0].cur_pos < p[0].cur_end) { +    	return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); +    } else { +        return(CORD__pos_fetch(p)); +    } +} + +void CORD_next(CORD_pos p) +{ +    if (p[0].cur_pos < p[0].cur_end - 1) { +    	p[0].cur_pos++; +    } else { +    	CORD__next(p); +    } +} + +void CORD_prev(CORD_pos p) +{ +    if (p[0].cur_end != 0 && p[0].cur_pos > p[0].cur_start) { +    	p[0].cur_pos--; +    } else { +    	CORD__prev(p); +    } +} + +size_t CORD_pos_to_index(CORD_pos p) +{ +    return(p[0].cur_pos); +} + +CORD CORD_pos_to_cord(CORD_pos p) +{ +    return(p[0].path[0].pe_cord); +} + +int CORD_pos_valid(CORD_pos p) +{ +    return(p[0].path_len != CORD_POS_INVALID); +} + +void CORD_set_pos(CORD_pos p, CORD x, size_t i) +{ +    if (x == CORD_EMPTY) { +    	p[0].path_len = CORD_POS_INVALID; +    	return; +    } +    p[0].path[0].pe_cord = x; +    p[0].path[0].pe_start_pos = 0; +    p[0].path_len = 0; +    p[0].cur_pos = i; +    CORD__extend_path(p); +} diff --git a/gc/cord/cordprnt.c b/gc/cord/cordprnt.c new file mode 100644 index 0000000..9c8cc87 --- /dev/null +++ b/gc/cord/cordprnt.c @@ -0,0 +1,390 @@ +/*  + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An sprintf implementation that understands cords.  This is probably	*/ +/* not terribly portable.  It assumes an ANSI stdarg.h.  It further	*/ +/* assumes that I can make copies of va_list variables, and read 	*/ +/* arguments repeatedly by applyting va_arg to the copies.  This	*/ +/* could be avoided at some performance cost.				*/ +/* We also assume that unsigned and signed integers of various kinds	*/ +/* have the same sizes, and can be cast back and forth.			*/ +/* We assume that void * and char * have the same size.			*/ +/* All this cruft is needed because we want to rely on the underlying	*/ +/* sprintf implementation whenever possible.				*/ +/* Boehm, September 21, 1995 6:00 pm PDT */ + +#include "cord.h" +#include "ec.h" +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "gc.h" + +#define CONV_SPEC_LEN 50	/* Maximum length of a single	*/ +				/* conversion specification.	*/ +#define CONV_RESULT_LEN 50	/* Maximum length of any 	*/ +				/* conversion with default	*/ +				/* width and prec.		*/ + + +static int ec_len(CORD_ec x) +{ +    return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); +} + +/* Possible nonumeric precision values.	*/ +# define NONE -1 +# define VARIABLE -2 +/* Copy the conversion specification from CORD_pos into the buffer buf	*/ +/* Return negative on error.						*/ +/* Source initially points one past the leading %.			*/ +/* It is left pointing at the conversion type.				*/ +/* Assign field width and precision to *width and *prec.		*/ +/* If width or prec is *, VARIABLE is assigned.				*/ +/* Set *left to 1 if left adjustment flag is present.			*/ +/* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to	*/ +/* -1 if 'h' is present.						*/ +static int extract_conv_spec(CORD_pos source, char *buf, +			     int * width, int *prec, int *left, int * long_arg) +{ +    register int result = 0; +    register int current_number = 0; +    register int saw_period = 0; +    register int saw_number; +    register int chars_so_far = 0; +    register char current; +     +    *width = NONE; +    buf[chars_so_far++] = '%'; +    while(CORD_pos_valid(source)) { +        if (chars_so_far >= CONV_SPEC_LEN) return(-1); +        current = CORD_pos_fetch(source); +        buf[chars_so_far++] = current; +        switch(current) { +	  case '*': +	    saw_number = 1; +	    current_number = VARIABLE; +	    break; +          case '0': +            if (!saw_number) { +                /* Zero fill flag; ignore */ +                break; +            } /* otherwise fall through: */ +          case '1': +	  case '2': +	  case '3': +	  case '4': +	  case '5': +          case '6': +	  case '7': +	  case '8': +	  case '9': +	    saw_number = 1; +	    current_number *= 10; +	    current_number += current - '0'; +	    break; +	  case '.': +	    saw_period = 1; +	    if(saw_number) { +	        *width = current_number; +	        saw_number = 0; +	    } +	    current_number = 0; +	    break; +	  case 'l': +	  case 'L': +	    *long_arg = 1; +	    current_number = 0; +	    break; +	  case 'h': +	    *long_arg = -1; +	    current_number = 0; +	    break; +	  case ' ': +	  case '+': +	  case '#': +	    current_number = 0; +	    break; +	  case '-': +	    *left = 1; +	    current_number = 0; +	    break; +	  case 'd': +	  case 'i': +	  case 'o': +	  case 'u': +	  case 'x': +	  case 'X': +	  case 'f': +	  case 'e': +	  case 'E': +	  case 'g': +	  case 'G': +	  case 'c': +	  case 'C': +	  case 's': +	  case 'S': +	  case 'p': +	  case 'n': +	  case 'r': +	    goto done;           +          default: +            return(-1); +        } +        CORD_next(source); +    } +    return(-1); +  done: +    if (saw_number) { +    	if (saw_period) { +    	    *prec = current_number; +    	} else { +    	    *prec = NONE; +    	    *width = current_number; +    	} +    } else { +    	*prec = NONE; +    } +    buf[chars_so_far] = '\0'; +    return(result); +} + +int CORD_vsprintf(CORD * out, CORD format, va_list args) +{ +    CORD_ec result; +    register int count; +    register char current; +    CORD_pos pos; +    char conv_spec[CONV_SPEC_LEN + 1]; +     +    CORD_ec_init(result); +    for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { +       	current = CORD_pos_fetch(pos); +       	if (current == '%') { +            CORD_next(pos); +            if (!CORD_pos_valid(pos)) return(-1); +            current = CORD_pos_fetch(pos); +            if (current == '%') { +               	CORD_ec_append(result, current); +            } else { +             	int width, prec; +             	int left_adj = 0; +             	int long_arg = 0; +		CORD arg; +		size_t len; +                +              	if (extract_conv_spec(pos, conv_spec, +              			      &width, &prec, +              			      &left_adj, &long_arg) < 0) { +              	    return(-1); +              	} +              	current = CORD_pos_fetch(pos); +            	switch(current) { +            	    case 'n': +            	    	/* Assign length to next arg */ +            	    	if (long_arg == 0) { +            	    	    int * pos_ptr; +            	    	    pos_ptr = va_arg(args, int *); +            	    	    *pos_ptr = ec_len(result); +            	    	} else if (long_arg > 0) { +            	    	    long * pos_ptr; +            	    	    pos_ptr = va_arg(args, long *); +            	    	    *pos_ptr = ec_len(result); +            	    	} else { +            	    	    short * pos_ptr; +            	    	    pos_ptr = va_arg(args, short *); +            	    	    *pos_ptr = ec_len(result); +            	    	} +            	    	goto done; +            	    case 'r': +            	    	/* Append cord and any padding	*/ +            	    	if (width == VARIABLE) width = va_arg(args, int); +            	    	if (prec == VARIABLE) prec = va_arg(args, int); +			arg = va_arg(args, CORD); +			len = CORD_len(arg); +			if (prec != NONE && len > prec) { +			  if (prec < 0) return(-1); +			  arg = CORD_substr(arg, 0, prec); +			  len = prec; +			} +			if (width != NONE && len < width) { +			  char * blanks = GC_MALLOC_ATOMIC(width-len+1); + +			  memset(blanks, ' ', width-len); +			  blanks[width-len] = '\0'; +			  if (left_adj) { +			    arg = CORD_cat(arg, blanks); +			  } else { +			    arg = CORD_cat(blanks, arg); +			  } +			} +			CORD_ec_append_cord(result, arg); +            	    	goto done; +		    case 'c': +			if (width == NONE && prec == NONE) { +			    register char c; + +			    c = va_arg(args, char); +			    CORD_ec_append(result, c); +			    goto done; +			} +			break; +		    case 's': +		        if (width == NONE && prec == NONE) { +			    char * str = va_arg(args, char *); +			    register char c; + +			    while (c = *str++) { +			        CORD_ec_append(result, c); +			    } +			    goto done; +			} +			break; +            	    default: +            	        break; +            	} +            	/* Use standard sprintf to perform conversion */ +            	{ +            	    register char * buf; +            	    va_list vsprintf_args = args; +            	    	/* The above does not appear to be sanctioned	*/ +            	    	/* by the ANSI C standard.			*/ +            	    int max_size = 0; +            	    int res; +            	    	 +            	    if (width == VARIABLE) width = va_arg(args, int); +            	    if (prec == VARIABLE) prec = va_arg(args, int); +            	    if (width != NONE) max_size = width; +            	    if (prec != NONE && prec > max_size) max_size = prec; +            	    max_size += CONV_RESULT_LEN; +            	    if (max_size >= CORD_BUFSZ) { +            	        buf = GC_MALLOC_ATOMIC(max_size + 1); +            	    } else { +            	        if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) +            	            < max_size) { +            	            CORD_ec_flush_buf(result); +            	        } +            	        buf = result[0].ec_bufptr; +            	    } +            	    switch(current) { +            	        case 'd': +            	        case 'i': +            	        case 'o': +            	        case 'u': +            	        case 'x': +            	        case 'X': +            	        case 'c': +            	            if (long_arg <= 0) { +            	              (void) va_arg(args, int); +            	            } else if (long_arg > 0) { +            	              (void) va_arg(args, long); +            	            } +            	            break; +            	        case 's': +            	        case 'p': +            	            (void) va_arg(args, char *); +            	            break; +            	        case 'f': +            	        case 'e': +            	        case 'E': +            	        case 'g': +            	        case 'G': +            	            (void) va_arg(args, double); +            	            break; +            	        default: +            	            return(-1); +            	    } +            	    res = vsprintf(buf, conv_spec, vsprintf_args); +            	    len = (size_t)res; +            	    if ((char *)(GC_word)res == buf) { +            	    	/* old style vsprintf */ +            	    	len = strlen(buf); +            	    } else if (res < 0) { +            	        return(-1); +            	    } +            	    if (buf != result[0].ec_bufptr) { +            	        register char c; + +			while (c = *buf++) { +			    CORD_ec_append(result, c); +		        } +		    } else { +		        result[0].ec_bufptr = buf + len; +		    } +            	} +              done:; +            } +        } else { +            CORD_ec_append(result, current); +        } +    } +    count = ec_len(result); +    *out = CORD_balance(CORD_ec_to_cord(result)); +    return(count); +} + +int CORD_sprintf(CORD * out, CORD format, ...) +{ +    va_list args; +    int result; +     +    va_start(args, format); +    result = CORD_vsprintf(out, format, args); +    va_end(args); +    return(result); +} + +int CORD_fprintf(FILE * f, CORD format, ...) +{ +    va_list args; +    int result; +    CORD out; +     +    va_start(args, format); +    result = CORD_vsprintf(&out, format, args); +    va_end(args); +    if (result > 0) CORD_put(out, f); +    return(result); +} + +int CORD_vfprintf(FILE * f, CORD format, va_list args) +{ +    int result; +    CORD out; +     +    result = CORD_vsprintf(&out, format, args); +    if (result > 0) CORD_put(out, f); +    return(result); +} + +int CORD_printf(CORD format, ...) +{ +    va_list args; +    int result; +    CORD out; +     +    va_start(args, format); +    result = CORD_vsprintf(&out, format, args); +    va_end(args); +    if (result > 0) CORD_put(out, stdout); +    return(result); +} + +int CORD_vprintf(CORD format, va_list args) +{ +    int result; +    CORD out; +     +    result = CORD_vsprintf(&out, format, args); +    if (result > 0) CORD_put(out, stdout); +    return(result); +} diff --git a/gc/cord/cordtest.c b/gc/cord/cordtest.c new file mode 100644 index 0000000..d11d7dd --- /dev/null +++ b/gc/cord/cordtest.c @@ -0,0 +1,228 @@ +/*  + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, August 24, 1994 11:58 am PDT */ +# include "cord.h" +# include <string.h> +# include <stdio.h> +/* This is a very incomplete test of the cord package.  It knows about	*/ +/* a few internals of the package (e.g. when C strings are returned)	*/ +/* that real clients shouldn't rely on.					*/ + +# define ABORT(string) \ +{ int x = 0; fprintf(stderr, "FAILED: %s\n", string); x = 1 / x; abort(); } + +int count; + +int test_fn(char c, void * client_data) +{ +    if (client_data != (void *)13) ABORT("bad client data"); +    if (count < 64*1024+1) { +        if ((count & 1) == 0) { +            if (c != 'b') ABORT("bad char"); +        } else { +            if (c != 'a') ABORT("bad char"); +        } +        count++; +        return(0); +    } else { +        if (c != 'c') ABORT("bad char"); +        count++; +        return(1); +    } +} + +char id_cord_fn(size_t i, void * client_data) +{ +    return((char)i); +} + +void test_basics() +{ +    CORD x = CORD_from_char_star("ab"); +    register int i; +    char c; +    CORD y; +    CORD_pos p; +     +    x = CORD_cat(x,x); +    if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); +    if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); +     +    for (i = 1; i < 16; i++) { +        x = CORD_cat(x,x); +    } +    x = CORD_cat(x,"c"); +    if (CORD_len(x) != 128*1024+1) ABORT("bad length"); +     +    count = 0; +    if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { +        ABORT("CORD_iter5 failed"); +    } +    if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); +     +    count = 0; +    CORD_set_pos(p, x, 64*1024-1); +    while(CORD_pos_valid(p)) { +       	(void) test_fn(CORD_pos_fetch(p), (void *)13); +	CORD_next(p); +    } +    if (count != 64*1024 + 2) ABORT("Position based iteration failed"); +     +    y = CORD_substr(x, 1023, 5); +    if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); +    if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); +     +    y = CORD_substr(x, 1024, 8); +    if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); +    if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result"); +     +    y = CORD_substr(x, 128*1024-1, 8); +    if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); +    if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result"); +     +    x = CORD_balance(x); +    if (CORD_len(x) != 128*1024+1) ABORT("bad length"); +     +    count = 0; +    if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { +        ABORT("CORD_iter5 failed"); +    } +    if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); +     +    y = CORD_substr(x, 1023, 5); +    if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); +    if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); +    y = CORD_from_fn(id_cord_fn, 0, 13); +    i = 0; +    CORD_set_pos(p, y, i); +    while(CORD_pos_valid(p)) { +        c = CORD_pos_fetch(p); +       	if(c != i) ABORT("Traversal of function node failed"); +	CORD_next(p); i++; +    } +    if (i != 13) ABORT("Bad apparent length for function node"); +} + +void test_extras() +{ +#   if defined(__OS2__) +#	define FNAME1 "tmp1" +#	define FNAME2 "tmp2" +#   elif defined(AMIGA) +#	define FNAME1 "T:tmp1" +#	define FNAME2 "T:tmp2" +#   else +#	define FNAME1 "/tmp/cord_test" +#	define FNAME2 "/tmp/cord_test2" +#   endif +    register int i; +    CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; +    CORD x = "{}"; +    CORD w, z; +    FILE *f; +    FILE *f1a, *f1b, *f2; +     +    w = CORD_cat(CORD_cat(y,y),y); +    z = CORD_catn(3,y,y,y); +    if (CORD_cmp(w,z) != 0) ABORT("CORD_catn comparison wrong"); +    for (i = 1; i < 100; i++) { +        x = CORD_cat(x, y); +    } +    z = CORD_balance(x); +    if (CORD_cmp(x,z) != 0) ABORT("balanced string comparison wrong"); +    if (CORD_cmp(x,CORD_cat(z, CORD_nul(13))) >= 0) ABORT("comparison 2"); +    if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0) ABORT("comparison 3"); +    if (CORD_cmp(x,CORD_cat(z, "13")) >= 0) ABORT("comparison 4"); +    if ((f = fopen(FNAME1, "w")) == 0) ABORT("open failed"); +    if (CORD_put(z,f) == EOF) ABORT("CORD_put failed"); +    if (fclose(f) == EOF) ABORT("fclose failed"); +    w = CORD_from_file(f1a = fopen(FNAME1, "rb")); +    if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); +    if (CORD_cmp(w,z) != 0) ABORT("file comparison wrong"); +    if (CORD_cmp(CORD_substr(w, 50*36+2, 36), y) != 0) +    	ABORT("file substr wrong"); +    z = CORD_from_file_lazy(f1b = fopen(FNAME1, "rb")); +    if (CORD_cmp(w,z) != 0) ABORT("File conversions differ"); +    if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); +    if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); +    if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1) ABORT("CORD_rchr failed"); +    x = y; +    for (i = 1; i < 14; i++) { +        x = CORD_cat(x,x); +    } +    if ((f = fopen(FNAME2, "w")) == 0) ABORT("2nd open failed"); +    if (CORD_put(x,f) == EOF) ABORT("CORD_put failed"); +    if (fclose(f) == EOF) ABORT("fclose failed"); +    w = CORD_from_file(f2 = fopen(FNAME2, "rb")); +    if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); +    if (CORD_cmp(w,x) != 0) ABORT("file comparison wrong"); +    if (CORD_cmp(CORD_substr(w, 1000*36, 36), y) != 0) +    	ABORT("file substr wrong"); +    if (strcmp(CORD_to_char_star(CORD_substr(w, 1000*36, 36)), y) != 0) +    	ABORT("char * file substr wrong"); +    if (strcmp(CORD_substr(w, 1000*36, 2), "ab") != 0) +    	ABORT("short file substr wrong"); +    if (CORD_str(x,1,"9a") != 35) ABORT("CORD_str failed 1"); +    if (CORD_str(x,0,"9abcdefghijk") != 35) ABORT("CORD_str failed 2"); +    if (CORD_str(x,0,"9abcdefghijx") != CORD_NOT_FOUND) +    	ABORT("CORD_str failed 3"); +    if (CORD_str(x,0,"9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4"); +    if (remove(FNAME1) != 0) { +    	/* On some systems, e.g. OS2, this may fail if f1 is still open. */ +    	if ((fclose(f1a) == EOF) & (fclose(f1b) == EOF)) +    		ABORT("fclose(f1) failed"); +    	if (remove(FNAME1) != 0) ABORT("remove 1 failed"); +    } +    if (remove(FNAME2) != 0) { +    	if (fclose(f2) == EOF) ABORT("fclose(f2) failed"); +    	if (remove(FNAME2) != 0) ABORT("remove 2 failed"); +    } +} + +void test_printf() +{ +    CORD result; +    char result2[200]; +    long l; +    short s; +    CORD x; +     +    if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) +    	ABORT("CORD_sprintf failed 1"); +    if (CORD_cmp(result, "   3.14") != 0)ABORT("CORD_sprintf goofed 1"); +    if (l != 7) ABORT("CORD_sprintf goofed 2"); +    if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) +    	ABORT("CORD_sprintf failed 2"); +    if (CORD_cmp(result, "ab     xyz") != 0)ABORT("CORD_sprintf goofed 3"); +    if (s != 7) ABORT("CORD_sprintf goofed 4"); +    x = "abcdefghij"; +    x = CORD_cat(x,x); +    x = CORD_cat(x,x); +    x = CORD_cat(x,x); +    if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) +    	ABORT("CORD_sprintf failed 3"); +    (void) sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); +    if (CORD_cmp(result, result2) != 0)ABORT("CORD_sprintf goofed 5"); +} + +main() +{ +#   ifdef THINK_C +        printf("cordtest:\n"); +#   endif +    test_basics(); +    test_extras(); +    test_printf(); +    CORD_fprintf(stderr, "SUCCEEDED\n"); +    return(0); +} diff --git a/gc/cord/cordxtra.c b/gc/cord/cordxtra.c new file mode 100644 index 0000000..a5be10d --- /dev/null +++ b/gc/cord/cordxtra.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* + * These are functions on cords that do not need to understand their + * implementation.  They serve also serve as example client code for + * cord_basics. + */ +/* Boehm, December 8, 1995 1:53 pm PST */ +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# include <stdarg.h> +# include "cord.h" +# include "ec.h" +# define I_HIDE_POINTERS	/* So we get access to allocation lock.	*/ +				/* We use this for lazy file reading, 	*/ +				/* so that we remain independent 	*/ +				/* of the threads primitives.		*/ +# include "gc.h" + +/* For now we assume that pointer reads and writes are atomic, 	*/ +/* i.e. another thread always sees the state before or after	*/ +/* a write.  This might be false on a Motorola M68K with	*/ +/* pointers that are not 32-bit aligned.  But there probably	*/ +/* aren't too many threads packages running on those.		*/ +# define ATOMIC_WRITE(x,y) (x) = (y) +# define ATOMIC_READ(x) (*(x)) + +/* The standard says these are in stdio.h, but they aren't always: */ +# ifndef SEEK_SET +#   define SEEK_SET 0 +# endif +# ifndef SEEK_END +#   define SEEK_END 2 +# endif + +# define BUFSZ 2048	/* Size of stack allocated buffers when		*/ +			/* we want large buffers.			*/ + +typedef void (* oom_fn)(void); + +# define OUT_OF_MEMORY {  if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ +			  ABORT("Out of memory\n"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +CORD CORD_cat_char(CORD x, char c) +{ +    register char * string; +     +    if (c == '\0') return(CORD_cat(x, CORD_nul(1))); +    string = GC_MALLOC_ATOMIC(2); +    if (string == 0) OUT_OF_MEMORY; +    string[0] = c; +    string[1] = '\0'; +    return(CORD_cat_char_star(x, string, 1)); +} + +CORD CORD_catn(int nargs, ...) +{ +    register CORD result = CORD_EMPTY; +    va_list args; +    register int i; + +    va_start(args, nargs); +    for (i = 0; i < nargs; i++) { +        register CORD next = va_arg(args, CORD); +        result = CORD_cat(result, next); +    } +    va_end(args); +    return(result); +} + +typedef struct { +	size_t len; +	size_t count; +	char * buf; +} CORD_fill_data; + +int CORD_fill_proc(char c, void * client_data) +{ +    register CORD_fill_data * d = (CORD_fill_data *)client_data; +    register size_t count = d -> count; +     +    (d -> buf)[count] = c; +    d -> count = ++count; +    if (count >= d -> len) { +    	return(1); +    } else { +    	return(0); +    } +} + +int CORD_batched_fill_proc(const char * s, void * client_data) +{ +    register CORD_fill_data * d = (CORD_fill_data *)client_data; +    register size_t count = d -> count; +    register size_t max = d -> len; +    register char * buf = d -> buf; +    register const char * t = s; +     +    while((buf[count] = *t++) != '\0') { +        count++; +        if (count >= max) { +            d -> count = count; +            return(1); +        } +    } +    d -> count = count; +    return(0); +} + +/* Fill buf with len characters starting at i.  			*/ +/* Assumes len characters are available.				*/  +void CORD_fill_buf(CORD x, size_t i, size_t len, char * buf) +{ +    CORD_fill_data fd; +     +    fd.len = len; +    fd.buf = buf; +    fd.count = 0; +    (void)CORD_iter5(x, i, CORD_fill_proc, CORD_batched_fill_proc, &fd); +} + +int CORD_cmp(CORD x, CORD y) +{ +    CORD_pos xpos; +    CORD_pos ypos; +    register size_t avail, yavail; +     +    if (y == CORD_EMPTY) return(x != CORD_EMPTY); +    if (x == CORD_EMPTY) return(-1); +    if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y)); +    CORD_set_pos(xpos, x, 0); +    CORD_set_pos(ypos, y, 0); +    for(;;) { +        if (!CORD_pos_valid(xpos)) { +            if (CORD_pos_valid(ypos)) { +            	return(-1); +            } else { +                return(0); +            } +        } +        if (!CORD_pos_valid(ypos)) { +            return(1); +        } +        if ((avail = CORD_pos_chars_left(xpos)) <= 0 +            || (yavail = CORD_pos_chars_left(ypos)) <= 0) { +            register char xcurrent = CORD_pos_fetch(xpos); +            register char ycurrent = CORD_pos_fetch(ypos); +            if (xcurrent != ycurrent) return(xcurrent - ycurrent); +            CORD_next(xpos); +            CORD_next(ypos); +        } else { +            /* process as many characters as we can	*/ +            register int result; +             +            if (avail > yavail) avail = yavail; +            result = strncmp(CORD_pos_cur_char_addr(xpos), +            		     CORD_pos_cur_char_addr(ypos), avail); +            if (result != 0) return(result); +            CORD_pos_advance(xpos, avail); +            CORD_pos_advance(ypos, avail); +        } +    } +} + +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) +{ +    CORD_pos xpos; +    CORD_pos ypos; +    register size_t count; +    register long avail, yavail; +     +    CORD_set_pos(xpos, x, x_start); +    CORD_set_pos(ypos, y, y_start); +    for(count = 0; count < len;) { +        if (!CORD_pos_valid(xpos)) { +            if (CORD_pos_valid(ypos)) { +            	return(-1); +            } else { +                return(0); +            } +        } +        if (!CORD_pos_valid(ypos)) { +            return(1); +        } +        if ((avail = CORD_pos_chars_left(xpos)) <= 0 +            || (yavail = CORD_pos_chars_left(ypos)) <= 0) { +            register char xcurrent = CORD_pos_fetch(xpos); +            register char ycurrent = CORD_pos_fetch(ypos); +            if (xcurrent != ycurrent) return(xcurrent - ycurrent); +            CORD_next(xpos); +            CORD_next(ypos); +            count++; +        } else { +            /* process as many characters as we can	*/ +            register int result; +             +            if (avail > yavail) avail = yavail; +            count += avail; +            if (count > len) avail -= (count - len); +            result = strncmp(CORD_pos_cur_char_addr(xpos), +            		     CORD_pos_cur_char_addr(ypos), (size_t)avail); +            if (result != 0) return(result); +            CORD_pos_advance(xpos, (size_t)avail); +            CORD_pos_advance(ypos, (size_t)avail); +        } +    } +    return(0); +} + +char * CORD_to_char_star(CORD x) +{ +    register size_t len = CORD_len(x); +    char * result = GC_MALLOC_ATOMIC(len + 1); +     +    if (result == 0) OUT_OF_MEMORY; +    CORD_fill_buf(x, 0, len, result); +    result[len] = '\0'; +    return(result); +} + +CORD CORD_from_char_star(const char *s) +{ +    char * result; +    size_t len = strlen(s); + +    if (0 == len) return(CORD_EMPTY); +    result = GC_MALLOC_ATOMIC(len + 1); +    if (result == 0) OUT_OF_MEMORY; +    memcpy(result, s, len+1); +    return(result); +} + +const char * CORD_to_const_char_star(CORD x) +{ +    if (x == 0) return(""); +    if (CORD_IS_STRING(x)) return((const char *)x); +    return(CORD_to_char_star(x)); +} + +char CORD_fetch(CORD x, size_t i) +{ +    CORD_pos xpos; +     +    CORD_set_pos(xpos, x, i); +    if (!CORD_pos_valid(xpos)) ABORT("bad index?"); +    return(CORD_pos_fetch(xpos)); +} + + +int CORD_put_proc(char c, void * client_data) +{ +    register FILE * f = (FILE *)client_data; +     +    return(putc(c, f) == EOF); +} + +int CORD_batched_put_proc(const char * s, void * client_data) +{ +    register FILE * f = (FILE *)client_data; +     +    return(fputs(s, f) == EOF); +} +     + +int CORD_put(CORD x, FILE * f) +{ +    if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) { +        return(EOF); +    } else { +    	return(1); +    } +} + +typedef struct { +    size_t pos;		/* Current position in the cord */ +    char target;	/* Character we're looking for	*/ +} chr_data; + +int CORD_chr_proc(char c, void * client_data) +{ +    register chr_data * d = (chr_data *)client_data; +     +    if (c == d -> target) return(1); +    (d -> pos) ++; +    return(0); +} + +int CORD_rchr_proc(char c, void * client_data) +{ +    register chr_data * d = (chr_data *)client_data; +     +    if (c == d -> target) return(1); +    (d -> pos) --; +    return(0); +} + +int CORD_batched_chr_proc(const char *s, void * client_data) +{ +    register chr_data * d = (chr_data *)client_data; +    register char * occ = strchr(s, d -> target); +     +    if (occ == 0) { +      	d -> pos += strlen(s); +      	return(0); +    } else { +    	d -> pos += occ - s; +    	return(1); +    } +} + +size_t CORD_chr(CORD x, size_t i, int c) +{ +    chr_data d; +     +    d.pos = i; +    d.target = c; +    if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) { +        return(d.pos); +    } else { +    	return(CORD_NOT_FOUND); +    } +} + +size_t CORD_rchr(CORD x, size_t i, int c) +{ +    chr_data d; +     +    d.pos = i; +    d.target = c; +    if (CORD_riter4(x, i, CORD_rchr_proc, &d)) { +        return(d.pos); +    } else { +    	return(CORD_NOT_FOUND); +    } +} + +/* Find the first occurrence of s in x at position start or later.	*/ +/* This uses an asymptotically poor algorithm, which should typically 	*/ +/* perform acceptably.  We compare the first few characters directly,	*/ +/* and call CORD_ncmp whenever there is a partial match.		*/ +/* This has the advantage that we allocate very little, or not at all.	*/ +/* It's very fast if there are few close misses.			*/ +size_t CORD_str(CORD x, size_t start, CORD s) +{ +    CORD_pos xpos; +    size_t xlen = CORD_len(x); +    size_t slen; +    register size_t start_len; +    const char * s_start; +    unsigned long s_buf = 0;	/* The first few characters of s	*/ +    unsigned long x_buf = 0;	/* Start of candidate substring.	*/ +    				/* Initialized only to make compilers	*/ +    				/* happy.				*/ +    unsigned long mask = 0; +    register size_t i; +    register size_t match_pos; +     +    if (s == CORD_EMPTY) return(start); +    if (CORD_IS_STRING(s)) { +        s_start = s; +        slen = strlen(s); +    } else { +        s_start = CORD_to_char_star(CORD_substr(s, 0, sizeof(unsigned long))); +        slen = CORD_len(s); +    } +    if (xlen < start || xlen - start < slen) return(CORD_NOT_FOUND); +    start_len = slen; +    if (start_len > sizeof(unsigned long)) start_len = sizeof(unsigned long); +    CORD_set_pos(xpos, x, start); +    for (i = 0; i < start_len; i++) { +        mask <<= 8; +        mask |= 0xff; +        s_buf <<= 8; +        s_buf |= s_start[i]; +        x_buf <<= 8; +        x_buf |= CORD_pos_fetch(xpos); +        CORD_next(xpos); +    } +    for (match_pos = start; ; match_pos++) { +    	if ((x_buf & mask) == s_buf) { +    	    if (slen == start_len || +    	     	CORD_ncmp(x, match_pos + start_len, +    	     	 	  s, start_len, slen - start_len) == 0) { +    	        return(match_pos); +    	    } +    	} +	if ( match_pos == xlen - slen ) { +	    return(CORD_NOT_FOUND); +	} +    	x_buf <<= 8; +        x_buf |= CORD_pos_fetch(xpos); +        CORD_next(xpos); +    } +} + +void CORD_ec_flush_buf(CORD_ec x) +{ +    register size_t len = x[0].ec_bufptr - x[0].ec_buf; +    char * s; + +    if (len == 0) return; +    s = GC_MALLOC_ATOMIC(len+1); +    memcpy(s, x[0].ec_buf, len); +    s[len] = '\0'; +    x[0].ec_cord = CORD_cat_char_star(x[0].ec_cord, s, len); +    x[0].ec_bufptr = x[0].ec_buf; +} + +void CORD_ec_append_cord(CORD_ec x, CORD s) +{ +    CORD_ec_flush_buf(x); +    x[0].ec_cord = CORD_cat(x[0].ec_cord, s); +} + +/*ARGSUSED*/ +char CORD_nul_func(size_t i, void * client_data) +{ +    return((char)(unsigned long)client_data); +} + + +CORD CORD_chars(char c, size_t i) +{ +    return(CORD_from_fn(CORD_nul_func, (void *)(unsigned long)c, i)); +} + +CORD CORD_from_file_eager(FILE * f) +{ +    register int c; +    CORD_ec ecord; +     +    CORD_ec_init(ecord); +    for(;;) { +        c = getc(f); +        if (c == 0) { +          /* Append the right number of NULs	*/ +          /* Note that any string of NULs is rpresented in 4 words,	*/ +          /* independent of its length.					*/ +            register size_t count = 1; +             +            CORD_ec_flush_buf(ecord); +            while ((c = getc(f)) == 0) count++; +            ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count)); +        } +        if (c == EOF) break; +        CORD_ec_append(ecord, c); +    } +    (void) fclose(f); +    return(CORD_balance(CORD_ec_to_cord(ecord))); +} + +/* The state maintained for a lazily read file consists primarily	*/ +/* of a large direct-mapped cache of previously read values.		*/ +/* We could rely more on stdio buffering.  That would have 2		*/ +/* disadvantages:							*/ +/*  	1) Empirically, not all fseek implementations preserve the	*/ +/*	   buffer whenever they could.					*/ +/*	2) It would fail if 2 different sections of a long cord		*/ +/*	   were being read alternately.					*/ +/* We do use the stdio buffer for read ahead.				*/ +/* To guarantee thread safety in the presence of atomic pointer		*/ +/* writes, cache lines are always replaced, and never modified in	*/ +/* place.								*/ + +# define LOG_CACHE_SZ 14 +# define CACHE_SZ (1 << LOG_CACHE_SZ) +# define LOG_LINE_SZ 9 +# define LINE_SZ (1 << LOG_LINE_SZ) + +typedef struct { +    size_t tag; +    char data[LINE_SZ]; +    	/* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ	*/ +} cache_line; + +typedef struct { +    FILE * lf_file; +    size_t lf_current;	/* Current file pointer value */ +    cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ]; +} lf_state; + +# define MOD_CACHE_SZ(n) ((n) & (CACHE_SZ - 1)) +# define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ) +# define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1)) +# define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ) +# define LINE_START(n) ((n) & ~(LINE_SZ - 1)) + +typedef struct { +    lf_state * state; +    size_t file_pos;	/* Position of needed character. */ +    cache_line * new_cache; +} refill_data; + +/* Executed with allocation lock. */ +static char refill_cache(client_data) +refill_data * client_data; +{ +    register lf_state * state = client_data -> state; +    register size_t file_pos = client_data -> file_pos; +    FILE *f = state -> lf_file; +    size_t line_start = LINE_START(file_pos); +    size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos)); +    cache_line * new_cache = client_data -> new_cache; +     +    if (line_start != state -> lf_current +        && fseek(f, line_start, SEEK_SET) != 0) { +    	    ABORT("fseek failed"); +    } +    if (fread(new_cache -> data, sizeof(char), LINE_SZ, f) +    	<= file_pos - line_start) { +    	ABORT("fread failed"); +    } +    new_cache -> tag = DIV_LINE_SZ(file_pos); +    /* Store barrier goes here. */ +    ATOMIC_WRITE(state -> lf_cache[line_no], new_cache); +    state -> lf_current = line_start + LINE_SZ; +    return(new_cache->data[MOD_LINE_SZ(file_pos)]); +} + +char CORD_lf_func(size_t i, void * client_data) +{ +    register lf_state * state = (lf_state *)client_data; +    register cache_line * volatile * cl_addr = +		&(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); +    register cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); +     +    if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { +    	/* Cache miss */ +    	refill_data rd; +    	 +        rd.state = state; +        rd.file_pos =  i; +        rd.new_cache = GC_NEW_ATOMIC(cache_line); +        if (rd.new_cache == 0) OUT_OF_MEMORY; +        return((char)(GC_word) +        	  GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd)); +    } +    return(cl -> data[MOD_LINE_SZ(i)]); +}     + +/*ARGSUSED*/ +void CORD_lf_close_proc(void * obj, void * client_data)   +{ +    if (fclose(((lf_state *)obj) -> lf_file) != 0) { +    	ABORT("CORD_lf_close_proc: fclose failed"); +    } +}			 + +CORD CORD_from_file_lazy_inner(FILE * f, size_t len) +{ +    register lf_state * state = GC_NEW(lf_state); +    register int i; +     +    if (state == 0) OUT_OF_MEMORY; +    if (len != 0) { +	/* Dummy read to force buffer allocation.  	*/ +	/* This greatly increases the probability	*/ +	/* of avoiding deadlock if buffer allocation	*/ +	/* is redirected to GC_malloc and the		*/ +	/* world is multithreaded.			*/ +	char buf[1]; + +	(void) fread(buf, 1, 1, f);  +	rewind(f); +    } +    state -> lf_file = f; +    for (i = 0; i < CACHE_SZ/LINE_SZ; i++) { +        state -> lf_cache[i] = 0; +    } +    state -> lf_current = 0; +    GC_REGISTER_FINALIZER(state, CORD_lf_close_proc, 0, 0, 0); +    return(CORD_from_fn(CORD_lf_func, state, len)); +} + +CORD CORD_from_file_lazy(FILE * f) +{ +    register long len; +     +    if (fseek(f, 0l, SEEK_END) != 0) { +        ABORT("Bad fd argument - fseek failed"); +    } +    if ((len = ftell(f)) < 0) { +        ABORT("Bad fd argument - ftell failed"); +    } +    rewind(f); +    return(CORD_from_file_lazy_inner(f, (size_t)len)); +} + +# define LAZY_THRESHOLD (128*1024 + 1) + +CORD CORD_from_file(FILE * f) +{ +    register long len; +     +    if (fseek(f, 0l, SEEK_END) != 0) { +        ABORT("Bad fd argument - fseek failed"); +    } +    if ((len = ftell(f)) < 0) { +        ABORT("Bad fd argument - ftell failed"); +    } +    rewind(f); +    if (len < LAZY_THRESHOLD) { +        return(CORD_from_file_eager(f)); +    } else { +        return(CORD_from_file_lazy_inner(f, (size_t)len)); +    } +} diff --git a/gc/cord/de.c b/gc/cord/de.c new file mode 100644 index 0000000..fda7142 --- /dev/null +++ b/gc/cord/de.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* + * A really simple-minded text editor based on cords. + * Things it does right: + * 	No size bounds. + *	Inbounded undo. + *	Shouldn't crash no matter what file you invoke it on (e.g. /vmunix) + *		(Make sure /vmunix is not writable before you try this.) + *	Scrolls horizontally. + * Things it does wrong: + *	It doesn't handle tabs reasonably (use "expand" first). + *	The command set is MUCH too small. + *	The redisplay algorithm doesn't let curses do the scrolling. + *	The rule for moving the window over the file is suboptimal. + */ +/* Boehm, February 6, 1995 12:27 pm PST */ + +/* Boehm, May 19, 1994 2:20 pm PDT */ +#include <stdio.h> +#include "gc.h" +#include "cord.h" + +#ifdef THINK_C +#define MACINTOSH +#include <ctype.h> +#endif + +#if defined(__BORLANDC__) && !defined(WIN32) +    /* If this is DOS or win16, we'll fail anyway.	*/ +    /* Might as well assume win32.			*/ +#   define WIN32 +#endif + +#if defined(WIN32) +#  include <windows.h> +#  include "de_win.h" +#elif defined(MACINTOSH) +#	include <console.h> +/* curses emulation. */ +#	define initscr() +#	define endwin() +#	define nonl() +#	define noecho() csetmode(C_NOECHO, stdout) +#	define cbreak() csetmode(C_CBREAK, stdout) +#	define refresh() +#	define addch(c) putchar(c) +#	define standout() cinverse(1, stdout) +#	define standend() cinverse(0, stdout) +#	define move(line,col) cgotoxy(col + 1, line + 1, stdout) +#	define clrtoeol() ccleol(stdout) +#	define de_error(s) { fprintf(stderr, s); getchar(); } +#	define LINES 25 +#	define COLS 80 +#else +#  include <curses.h> +#  define de_error(s) { fprintf(stderr, s); sleep(2); } +#endif +#include "de_cmds.h" + +/* List of line number to position mappings, in descending order. */ +/* There may be holes.						  */ +typedef struct LineMapRep { +    int line; +    size_t pos; +    struct LineMapRep * previous; +} * line_map; + +/* List of file versions, one per edit operation */ +typedef struct HistoryRep { +    CORD file_contents; +    struct HistoryRep * previous; +    line_map map;	/* Invalid for first record "now" */ +} * history; + +history now = 0; +CORD current;		/* == now -> file_contents.	*/ +size_t current_len;	/* Current file length.		*/ +line_map current_map = 0;	/* Current line no. to pos. map	 */ +size_t current_map_size = 0;	/* Number of current_map entries.	*/ +				/* Not always accurate, but reset	*/ +				/* by prune_map.			*/ +# define MAX_MAP_SIZE 3000 + +/* Current display position */ +int dis_line = 0; +int dis_col = 0; + +# define ALL -1 +# define NONE - 2 +int need_redisplay = 0;	/* Line that needs to be redisplayed.	*/ + + +/* Current cursor position. Always within file. */ +int line = 0;  +int col = 0; +size_t file_pos = 0;	/* Character position corresponding to cursor.	*/ + +/* Invalidate line map for lines > i */ +void invalidate_map(int i) +{ +    while(current_map -> line > i) { +        current_map = current_map -> previous; +        current_map_size--; +    } +} + +/* Reduce the number of map entries to save space for huge files. */ +/* This also affects maps in histories.				  */ +void prune_map() +{ +    line_map map = current_map; +    int start_line = map -> line; +     +    current_map_size = 0; +    for(; map != 0; map = map -> previous) { +    	current_map_size++; +    	if (map -> line < start_line - LINES && map -> previous != 0) { +    	    map -> previous = map -> previous -> previous; +    	} +    } +} +/* Add mapping entry */ +void add_map(int line, size_t pos) +{ +    line_map new_map = GC_NEW(struct LineMapRep); +     +    if (current_map_size >= MAX_MAP_SIZE) prune_map(); +    new_map -> line = line; +    new_map -> pos = pos; +    new_map -> previous = current_map; +    current_map = new_map; +    current_map_size++; +} + + + +/* Return position of column *c of ith line in   */ +/* current file. Adjust *c to be within the line.*/ +/* A 0 pointer is taken as 0 column.		 */ +/* Returns CORD_NOT_FOUND if i is too big.	 */ +/* Assumes i > dis_line.			 */ +size_t line_pos(int i, int *c) +{ +    int j; +    size_t cur; +    size_t next; +    line_map map = current_map; +     +    while (map -> line > i) map = map -> previous; +    if (map -> line < i - 2) /* rebuild */ invalidate_map(i); +    for (j = map -> line, cur = map -> pos; j < i;) { +	cur = CORD_chr(current, cur, '\n'); +        if (cur == current_len-1) return(CORD_NOT_FOUND); +        cur++; +        if (++j > current_map -> line) add_map(j, cur); +    } +    if (c != 0) { +        next = CORD_chr(current, cur, '\n'); +        if (next == CORD_NOT_FOUND) next = current_len - 1; +        if (next < cur + *c) { +            *c = next - cur; +        } +        cur += *c; +    } +    return(cur); +} + +void add_hist(CORD s) +{ +    history new_file = GC_NEW(struct HistoryRep); +     +    new_file -> file_contents = current = s; +    current_len = CORD_len(s); +    new_file -> previous = now; +    if (now != 0) now -> map = current_map; +    now = new_file; +} + +void del_hist(void) +{ +    now = now -> previous; +    current = now -> file_contents; +    current_map = now -> map; +    current_len = CORD_len(current); +} + +/* Current screen_contents; a dynamically allocated array of CORDs	*/ +CORD * screen = 0; +int screen_size = 0; + +# ifndef WIN32 +/* Replace a line in the curses stdscr.	All control characters are	*/ +/* displayed as upper case characters in standout mode.  This isn't	*/ +/* terribly appropriate for tabs.									*/ +void replace_line(int i, CORD s) +{ +    register int c; +    CORD_pos p; +    size_t len = CORD_len(s); +     +    if (screen == 0 || LINES > screen_size) { +        screen_size = LINES; +    	screen = (CORD *)GC_MALLOC(screen_size * sizeof(CORD)); +    } +#   if !defined(MACINTOSH) +        /* A gross workaround for an apparent curses bug: */ +        if (i == LINES-1 && len == COLS) { +            s = CORD_substr(s, 0, CORD_len(s) - 1); +        } +#   endif +    if (CORD_cmp(screen[i], s) != 0) { +        move(i, 0); clrtoeol(); move(i,0); + +        CORD_FOR (p, s) { +            c = CORD_pos_fetch(p) & 0x7f; +            if (iscntrl(c)) { +            	standout(); addch(c + 0x40); standend(); +            } else { +    	        addch(c); +    	    } +    	} +    	screen[i] = s; +    } +} +#else +# define replace_line(i,s) invalidate_line(i) +#endif + +/* Return up to COLS characters of the line of s starting at pos,	*/ +/* returning only characters after the given column.			*/ +CORD retrieve_line(CORD s, size_t pos, unsigned column) +{ +    CORD candidate = CORD_substr(s, pos, column + COLS); +    			/* avoids scanning very long lines	*/ +    int eol = CORD_chr(candidate, 0, '\n'); +    int len; +     +    if (eol == CORD_NOT_FOUND) eol = CORD_len(candidate); +    len = (int)eol - (int)column; +    if (len < 0) len = 0; +    return(CORD_substr(s, pos + column, len)); +} + +# ifdef WIN32 +#   define refresh(); + +    CORD retrieve_screen_line(int i) +    { +    	register size_t pos; +    	 +    	invalidate_map(dis_line + LINES);	/* Prune search */ +    	pos = line_pos(dis_line + i, 0); +    	if (pos == CORD_NOT_FOUND) return(CORD_EMPTY); +    	return(retrieve_line(current, pos, dis_col)); +    } +# endif + +/* Display the visible section of the current file	 */ +void redisplay(void) +{ +    register int i; +     +    invalidate_map(dis_line + LINES);	/* Prune search */ +    for (i = 0; i < LINES; i++) { +        if (need_redisplay == ALL || need_redisplay == i) { +            register size_t pos = line_pos(dis_line + i, 0); +             +            if (pos == CORD_NOT_FOUND) break; +            replace_line(i, retrieve_line(current, pos, dis_col)); +            if (need_redisplay == i) goto done; +        } +    } +    for (; i < LINES; i++) replace_line(i, CORD_EMPTY); +done: +    refresh(); +    need_redisplay = NONE; +} + +int dis_granularity; + +/* Update dis_line, dis_col, and dis_pos to make cursor visible.	*/ +/* Assumes line, col, dis_line, dis_pos are in bounds.			*/ +void normalize_display() +{ +    int old_line = dis_line; +    int old_col = dis_col; +     +    dis_granularity = 1; +    if (LINES > 15 && COLS > 15) dis_granularity = 2; +    while (dis_line > line) dis_line -= dis_granularity; +    while (dis_col > col) dis_col -= dis_granularity; +    while (line >= dis_line + LINES) dis_line += dis_granularity; +    while (col >= dis_col + COLS) dis_col += dis_granularity; +    if (old_line != dis_line || old_col != dis_col) { +        need_redisplay = ALL; +    } +} + +# if defined(WIN32) +# elif defined(MACINTOSH) +#		define move_cursor(x,y) cgotoxy(x + 1, y + 1, stdout) +# else +#		define move_cursor(x,y) move(y,x) +# endif + +/* Adjust display so that cursor is visible; move cursor into position	*/ +/* Update screen if necessary.						*/ +void fix_cursor(void) +{ +    normalize_display(); +    if (need_redisplay != NONE) redisplay(); +    move_cursor(col - dis_col, line - dis_line); +    refresh(); +#   ifndef WIN32 +      fflush(stdout); +#   endif +} + +/* Make sure line, col, and dis_pos are somewhere inside file.	*/ +/* Recompute file_pos.	Assumes dis_pos is accurate or past eof	*/ +void fix_pos() +{ +    int my_col = col; +     +    if ((size_t)line > current_len) line = current_len; +    file_pos = line_pos(line, &my_col); +    if (file_pos == CORD_NOT_FOUND) { +        for (line = current_map -> line, file_pos = current_map -> pos; +             file_pos < current_len; +             line++, file_pos = CORD_chr(current, file_pos, '\n') + 1); +    	line--; +        file_pos = line_pos(line, &col); +    } else { +    	col = my_col; +    } +} + +#if defined(WIN32) +#  define beep() Beep(1000 /* Hz */, 300 /* msecs */)  +#elif defined(MACINTOSH) +#	define beep() SysBeep(1) +#else +/* + * beep() is part of some curses packages and not others. + * We try to match the type of the builtin one, if any. + */ +#ifdef __STDC__ +    int beep(void) +#else +    int beep() +#endif +{ +    putc('\007', stderr); +    return(0); +} +#endif + +#   define NO_PREFIX -1 +#   define BARE_PREFIX -2 +int repeat_count = NO_PREFIX;	/* Current command prefix. */ + +int locate_mode = 0;			/* Currently between 2 ^Ls	*/ +CORD locate_string = CORD_EMPTY;	/* Current search string.	*/ + +char * arg_file_name; + +#ifdef WIN32 +/* Change the current position to whatever is currently displayed at	*/ +/* the given SCREEN coordinates.					*/ +void set_position(int c, int l) +{ +    line = l + dis_line; +    col = c + dis_col; +    fix_pos(); +    move_cursor(col - dis_col, line - dis_line); +} +#endif /* WIN32 */ + +/* Perform the command associated with character c.  C may be an	*/ +/* integer > 256 denoting a windows command, one of the above control	*/ +/* characters, or another ASCII character to be used as either a 	*/ +/* character to be inserted, a repeat count, or a search string, 	*/ +/* depending on the current state.					*/ +void do_command(int c) +{ +    int i; +    int need_fix_pos; +    FILE * out; +     +    if ( c == '\r') c = '\n'; +    if (locate_mode) { +        size_t new_pos; +           +        if (c == LOCATE) { +              locate_mode = 0; +              locate_string = CORD_EMPTY; +              return; +        } +        locate_string = CORD_cat_char(locate_string, (char)c); +        new_pos = CORD_str(current, file_pos - CORD_len(locate_string) + 1, +          		   locate_string); +        if (new_pos != CORD_NOT_FOUND) { +            need_redisplay = ALL; +            new_pos += CORD_len(locate_string); +            for (;;) { +              	file_pos = line_pos(line + 1, 0); +              	if (file_pos > new_pos) break; +              	line++; +            } +            col = new_pos - line_pos(line, 0); +            file_pos = new_pos; +            fix_cursor(); +        } else { +            locate_string = CORD_substr(locate_string, 0, +              				CORD_len(locate_string) - 1); +            beep(); +        } +        return; +    } +    if (c == REPEAT) { +      	repeat_count = BARE_PREFIX; return; +    } else if (c < 0x100 && isdigit(c)){ +        if (repeat_count == BARE_PREFIX) { +          repeat_count = c - '0'; return; +        } else if (repeat_count != NO_PREFIX) { +          repeat_count = 10 * repeat_count + c - '0'; return; +        } +    } +    if (repeat_count == NO_PREFIX) repeat_count = 1; +    if (repeat_count == BARE_PREFIX && (c == UP || c == DOWN)) { +      	repeat_count = LINES - dis_granularity; +    } +    if (repeat_count == BARE_PREFIX) repeat_count = 8; +    need_fix_pos = 0; +    for (i = 0; i < repeat_count; i++) { +        switch(c) { +          case LOCATE: +            locate_mode = 1; +            break; +          case TOP: +            line = col = file_pos = 0; +            break; +     	  case UP: +     	    if (line != 0) { +     	        line--; +     	        need_fix_pos = 1; +     	    } +     	    break; +     	  case DOWN: +     	    line++; +     	    need_fix_pos = 1; +     	    break; +     	  case LEFT: +     	    if (col != 0) { +     	        col--; file_pos--; +     	    } +     	    break; +     	  case RIGHT: +     	    if (CORD_fetch(current, file_pos) == '\n') break; +     	    col++; file_pos++; +     	    break; +     	  case UNDO: +     	    del_hist(); +     	    need_redisplay = ALL; need_fix_pos = 1; +     	    break; +     	  case BS: +     	    if (col == 0) { +     	        beep(); +     	        break; +     	    } +     	    col--; file_pos--; +     	    /* fall through: */ +     	  case DEL: +     	    if (file_pos == current_len-1) break; +     	    	/* Can't delete trailing newline */ +     	    if (CORD_fetch(current, file_pos) == '\n') { +     	        need_redisplay = ALL; need_fix_pos = 1; +     	    } else { +     	        need_redisplay = line - dis_line; +     	    } +     	    add_hist(CORD_cat( +     	    		CORD_substr(current, 0, file_pos), +     	    		CORD_substr(current, file_pos+1, current_len))); +     	    invalidate_map(line); +     	    break; +     	  case WRITE: +	    { +  		CORD name = CORD_cat(CORD_from_char_star(arg_file_name), +				     ".new"); + +    	        if ((out = fopen(CORD_to_const_char_star(name), "wb")) == NULL +  	            || CORD_put(current, out) == EOF) { +        	    de_error("Write failed\n"); +        	    need_redisplay = ALL; +                } else { +                    fclose(out); +                } +	    } +            break; +     	  default: +     	    { +     	        CORD left_part = CORD_substr(current, 0, file_pos); +     	        CORD right_part = CORD_substr(current, file_pos, current_len); +     	         +     	        add_hist(CORD_cat(CORD_cat_char(left_part, (char)c), +     	        		  right_part)); +     	        invalidate_map(line); +     	        if (c == '\n') { +     	            col = 0; line++; file_pos++; +     	            need_redisplay = ALL; +     	        } else { +     	            col++; file_pos++; +     	            need_redisplay = line - dis_line; +     	    	} +     	        break; +     	    } +        } +    } +    if (need_fix_pos) fix_pos(); +    fix_cursor(); +    repeat_count = NO_PREFIX; +} + +/* OS independent initialization */ + +void generic_init(void) +{ +    FILE * f; +    CORD initial; +     +    if ((f = fopen(arg_file_name, "rb")) == NULL) { +     	initial = "\n"; +    } else { +        initial = CORD_from_file(f); +        if (initial == CORD_EMPTY +            || CORD_fetch(initial, CORD_len(initial)-1) != '\n') { +            initial = CORD_cat(initial, "\n"); +        } +    } +    add_map(0,0); +    add_hist(initial); +    now -> map = current_map; +    now -> previous = now;  /* Can't back up further: beginning of the world */ +    need_redisplay = ALL; +    fix_cursor(); +} + +#ifndef WIN32 + +main(argc, argv) +int argc; +char ** argv; +{ +    int c; + +#if defined(MACINTOSH) +	console_options.title = "\pDumb Editor"; +	cshow(stdout); +	GC_init(); +	argc = ccommand(&argv); +#endif +     +    if (argc != 2) goto usage; +    arg_file_name = argv[1]; +    setvbuf(stdout, GC_MALLOC_ATOMIC(8192), _IOFBF, 8192); +    initscr(); +    noecho(); nonl(); cbreak(); +    generic_init(); +    while ((c = getchar()) != QUIT) { +		if (c == EOF) break; +	    do_command(c); +    } +done: +    move(LINES-1, 0); +    clrtoeol(); +    refresh(); +    nl(); +    echo(); +    endwin(); +    exit(0); +usage: +    fprintf(stderr, "Usage: %s file\n", argv[0]); +    fprintf(stderr, "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"); +    fprintf(stderr, "Undo: ^U    Write to <file>.new: ^W"); +    fprintf(stderr, "Quit:^D  Repeat count: ^R[n]\n"); +    fprintf(stderr, "Top: ^T   Locate (search, find): ^L text ^L\n"); +    exit(1); +} + +#endif  /* !WIN32 */ diff --git a/gc/cord/de_cmds.h b/gc/cord/de_cmds.h new file mode 100644 index 0000000..f42ddcf --- /dev/null +++ b/gc/cord/de_cmds.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:24 pm PDT */ + +#ifndef DE_CMDS_H + +# define DE_CMDS_H + +# define UP     16	/* ^P */ +# define DOWN   14	/* ^N */ +# define LEFT   2	/* ^B */ +# define RIGHT  6	/* ^F */ +# define DEL	127	/* ^? */ +# define BS     8	/* ^H */ +# define UNDO   21	/* ^U */ +# define WRITE  23	/* ^W */ +# define QUIT   4	/* ^D */ +# define REPEAT 18	/* ^R */ +# define LOCATE 12	/* ^L */ +# define TOP    20	/* ^T */ + +#endif + diff --git a/gc/cord/de_win.ICO b/gc/cord/de_win.ICOBinary files differ new file mode 100755 index 0000000..b20ac3e --- /dev/null +++ b/gc/cord/de_win.ICO diff --git a/gc/cord/de_win.RC b/gc/cord/de_win.RC new file mode 100644 index 0000000..554a300 --- /dev/null +++ b/gc/cord/de_win.RC @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to copy this garbage collector for any purpose, + * provided the above notices are retained on all copies. + */ +/* Boehm, May 13, 1994 9:50 am PDT */ + +#include "windows.h" +#include "de_cmds.h" +#include "de_win.h" + + + +ABOUTBOX DIALOG 19, 21, 163, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Demonstration Text Editor" +BEGIN +	ICON "DE", -1, 8, 8, 13, 13, WS_CHILD | WS_VISIBLE +	LTEXT "Demonstration Text Editor", -1, 44, 8, 118, 8, WS_CHILD | WS_VISIBLE | WS_GROUP +	LTEXT "Version 4.1", -1, 44, 16, 60, 8, WS_CHILD | WS_VISIBLE | WS_GROUP +	PUSHBUTTON "OK", IDOK, 118, 27, 24, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + + +DE MENU  +BEGIN +	POPUP "&File" +	BEGIN +		MENUITEM "&Save\t^W", IDM_FILESAVE +		MENUITEM "E&xit\t^D", IDM_FILEEXIT +	END + +	POPUP "&Edit" +	BEGIN +	    MENUITEM "Page &Down\t^R^N", IDM_EDITPDOWN +	    MENUITEM "Page &Up\t^R^P", IDM_EDITPUP +		MENUITEM "U&ndo\t^U", IDM_EDITUNDO +		MENUITEM "&Locate\t^L ... ^L", IDM_EDITLOCATE +		MENUITEM "D&own\t^N", IDM_EDITDOWN +	    MENUITEM "U&p\t^P", IDM_EDITUP +	    MENUITEM "Le&ft\t^B", IDM_EDITLEFT +	    MENUITEM "&Right\t^F", IDM_EDITRIGHT +	    MENUITEM "Delete &Backward\tBS", IDM_EDITBS +	    MENUITEM "Delete F&orward\tDEL", IDM_EDITDEL +	    MENUITEM "&Top\t^T", IDM_EDITTOP +	END +	 +	POPUP "&Help" +	BEGIN +		MENUITEM "&Contents", IDM_HELPCONTENTS +		MENUITEM "&About...", IDM_HELPABOUT +	END +	 +	MENUITEM "Page_&Down", IDM_EDITPDOWN +	MENUITEM "Page_&Up", IDM_EDITPUP +END + + +DE ACCELERATORS  +BEGIN +    "^R", IDM_EDITREPEAT +    "^N", IDM_EDITDOWN +    "^P", IDM_EDITUP +    "^L", IDM_EDITLOCATE +    "^B", IDM_EDITLEFT +    "^F", IDM_EDITRIGHT +    "^T", IDM_EDITTOP +	VK_DELETE, IDM_EDITDEL, VIRTKEY +	VK_BACK, IDM_EDITBS, VIRTKEY +END + + +DE ICON cord\de_win.ICO + diff --git a/gc/cord/de_win.c b/gc/cord/de_win.c new file mode 100644 index 0000000..fedbfbe --- /dev/null +++ b/gc/cord/de_win.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 6, 1995 12:29 pm PST */ + +/* + * The MS Windows specific part of de.   + * This started as the generic Windows application template + * made available by Rob Haack (rhaack@polaris.unm.edu), but + * significant parts didn't survive to the final version. + * + * This was written by a nonexpert windows programmer. + */ + + +#include "windows.h" +#include "gc.h" +#include "cord.h" +#include "de_cmds.h" +#include "de_win.h" + +int LINES = 0; +int COLS = 0; + +char       szAppName[]     = "DE"; +char       FullAppName[]   = "Demonstration Editor"; + +HWND        hwnd; + +void de_error(char *s) +{ +    MessageBox( hwnd, (LPSTR) s, +                (LPSTR) FullAppName, +                MB_ICONINFORMATION | MB_OK ); +    InvalidateRect(hwnd, NULL, TRUE); +} + +int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, +                      LPSTR command_line, int nCmdShow) +{ +   MSG         msg; +   WNDCLASS    wndclass; +   HANDLE      hAccel; + +   if (!hPrevInstance) +   { +      wndclass.style          = CS_HREDRAW | CS_VREDRAW; +      wndclass.lpfnWndProc    = WndProc; +      wndclass.cbClsExtra     = 0; +      wndclass.cbWndExtra     = DLGWINDOWEXTRA; +      wndclass.hInstance      = hInstance; +      wndclass.hIcon          = LoadIcon (hInstance, szAppName); +      wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW); +      wndclass.hbrBackground  = GetStockObject(WHITE_BRUSH); +      wndclass.lpszMenuName   = "DE"; +      wndclass.lpszClassName  = szAppName; + +      if (RegisterClass (&wndclass) == 0) { +          char buf[50]; +   	 +   	  sprintf(buf, "RegisterClass: error code: 0x%X", GetLastError()); +   	  de_error(buf); +   	  return(0); +      } +   } +    +   /* Empirically, the command line does not include the command name ... +   if (command_line != 0) { +       while (isspace(*command_line)) command_line++; +       while (*command_line != 0 && !isspace(*command_line)) command_line++; +       while (isspace(*command_line)) command_line++; +   } */ +    +   if (command_line == 0 || *command_line == 0) { +        de_error("File name argument required"); +        return( 0 ); +   } else { +        char *p = command_line; +         +        while (*p != 0 && !isspace(*p)) p++; +   	arg_file_name = CORD_to_char_star( +   			    CORD_substr(command_line, 0, p - command_line)); +   } + +   hwnd = CreateWindow (szAppName, +   			FullAppName, +   			WS_OVERLAPPEDWINDOW | WS_CAPTION, /* Window style */ +   			CW_USEDEFAULT, 0, /* default pos. */ +   			CW_USEDEFAULT, 0, /* default width, height */ +   			NULL,	/* No parent */ +   			NULL, 	/* Window class menu */ +   			hInstance, NULL); +   if (hwnd == NULL) { +   	char buf[50]; +   	 +   	sprintf(buf, "CreateWindow: error code: 0x%X", GetLastError()); +   	de_error(buf); +   	return(0); +   } + +   ShowWindow (hwnd, nCmdShow); + +   hAccel = LoadAccelerators( hInstance, szAppName ); +    +   while (GetMessage (&msg, NULL, 0, 0)) +   { +      if( !TranslateAccelerator( hwnd, hAccel, &msg ) ) +      { +         TranslateMessage (&msg); +         DispatchMessage (&msg); +      } +   } +   return msg.wParam; +} + +/* Return the argument with all control characters replaced by blanks.	*/ +char * plain_chars(char * text, size_t len) +{ +    char * result = GC_MALLOC_ATOMIC(len + 1); +    register size_t i; +     +    for (i = 0; i < len; i++) { +       if (iscntrl(text[i])) { +           result[i] = ' '; +       } else { +           result[i] = text[i]; +       } +    } +    result[len] = '\0'; +    return(result); +} + +/* Return the argument with all non-control-characters replaced by 	*/ +/* blank, and all control characters c replaced by c + 32.		*/ +char * control_chars(char * text, size_t len) +{ +    char * result = GC_MALLOC_ATOMIC(len + 1); +    register size_t i; +     +    for (i = 0; i < len; i++) { +       if (iscntrl(text[i])) { +           result[i] = text[i] + 0x40; +       } else { +           result[i] = ' '; +       } +    } +    result[len] = '\0'; +    return(result); +} + +int char_width; +int char_height; + +void get_line_rect(int line, int win_width, RECT * rectp) +{ +    rectp -> top = line * char_height; +    rectp -> bottom = rectp->top + char_height; +    rectp -> left = 0; +    rectp -> right = win_width; +} + +int caret_visible = 0;	/* Caret is currently visible.	*/ + +int screen_was_painted = 0;/* Screen has been painted at least once.	*/ + +void update_cursor(void); + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, +                          WPARAM wParam, LPARAM lParam) +{ +   static FARPROC lpfnAboutBox; +   static HANDLE  hInstance; +   HDC dc; +   PAINTSTRUCT ps; +   RECT client_area; +   RECT this_line; +   RECT dummy; +   TEXTMETRIC tm; +   register int i; +   int id; + +   switch (message) +   { +      case WM_CREATE: +           hInstance = ( (LPCREATESTRUCT) lParam)->hInstance; +           lpfnAboutBox = MakeProcInstance( (FARPROC) AboutBox, hInstance ); +           dc = GetDC(hwnd); +           SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); +           GetTextMetrics(dc, &tm); +           ReleaseDC(hwnd, dc); +           char_width = tm.tmAveCharWidth; +           char_height = tm.tmHeight + tm.tmExternalLeading; +           GetClientRect(hwnd, &client_area); +      	   COLS = (client_area.right - client_area.left)/char_width; +      	   LINES = (client_area.bottom - client_area.top)/char_height; +      	   generic_init(); +           return(0); + +      case WM_CHAR: +      	   if (wParam == QUIT) { +      	       SendMessage( hwnd, WM_CLOSE, 0, 0L ); +      	   } else { +      	       do_command(wParam); +      	   } +      	   return(0); +       +      case WM_SETFOCUS: +      	   CreateCaret(hwnd, NULL, char_width, char_height); +      	   ShowCaret(hwnd); +      	   caret_visible = 1; +      	   update_cursor(); +      	   return(0); +      	    +      case WM_KILLFOCUS: +      	   HideCaret(hwnd); +      	   DestroyCaret(); +      	   caret_visible = 0; +      	   return(0); +      	    +      case WM_LBUTTONUP: +      	   { +      	       unsigned xpos = LOWORD(lParam);	/* From left	*/ +      	       unsigned ypos = HIWORD(lParam);	/* from top */ +      	        +      	       set_position( xpos/char_width, ypos/char_height ); +      	       return(0); +      	   } +      	    +      case WM_COMMAND: +      	   id = LOWORD(wParam); +      	   if (id & EDIT_CMD_FLAG) { +               if (id & REPEAT_FLAG) do_command(REPEAT); +               do_command(CHAR_CMD(id)); +               return( 0 ); +           } else { +             switch(id) { +               case IDM_FILEEXIT: +                  SendMessage( hwnd, WM_CLOSE, 0, 0L ); +                  return( 0 ); + +               case IDM_HELPABOUT: +                  if( DialogBox( hInstance, "ABOUTBOX", +                                 hwnd, lpfnAboutBox ) ); +                     InvalidateRect( hwnd, NULL, TRUE ); +                  return( 0 ); +	       case IDM_HELPCONTENTS: +	     	  de_error( +	     	       "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n" +	     	       "Undo: ^U    Write: ^W   Quit:^D  Repeat count: ^R[n]\n" +	     	       "Top: ^T   Locate (search, find): ^L text ^L\n"); +	     	  return( 0 ); +	     } +	   } +           break; + +      case WM_CLOSE: +           DestroyWindow( hwnd ); +           return 0; + +      case WM_DESTROY: +           PostQuitMessage (0); +	   GC_win32_free_heap(); +           return 0; +       +      case WM_PAINT: +      	   dc = BeginPaint(hwnd, &ps); +      	   GetClientRect(hwnd, &client_area); +      	   COLS = (client_area.right - client_area.left)/char_width; +      	   LINES = (client_area.bottom - client_area.top)/char_height; +      	   SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); +      	   for (i = 0; i < LINES; i++) { +      	       get_line_rect(i, client_area.right, &this_line); +      	       if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) { +      	           CORD raw_line = retrieve_screen_line(i); +      	           size_t len = CORD_len(raw_line); +      	           char * text = CORD_to_char_star(raw_line); +      	           		/* May contain embedded NULLs	*/ +      	           char * plain = plain_chars(text, len); +      	           char * blanks = CORD_to_char_star(CORD_chars(' ', +      	           				                COLS - len)); +      	           char * control = control_chars(text, len); +#		   define RED RGB(255,0,0) +      	            +      	           SetBkMode(dc, OPAQUE); +      	           SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); +      	            +      	           TextOut(dc, this_line.left, this_line.top, +      	           	   plain, len); +      	           TextOut(dc, this_line.left + len * char_width, this_line.top, +      	           	   blanks, COLS - len); +      	           SetBkMode(dc, TRANSPARENT); +      	           SetTextColor(dc, RED); +      	           TextOut(dc, this_line.left, this_line.top, +      	           	   control, strlen(control)); +      	       } +      	   } +      	   EndPaint(hwnd, &ps); +      	   screen_was_painted = 1; +      	   return 0; +   } +   return DefWindowProc (hwnd, message, wParam, lParam); +} + +int last_col; +int last_line; + +void move_cursor(int c, int l) +{ +    last_col = c; +    last_line = l; +     +    if (caret_visible) update_cursor(); +} + +void update_cursor(void) +{ +    SetCaretPos(last_col * char_width, last_line * char_height); +    ShowCaret(hwnd); +} + +void invalidate_line(int i) +{ +    RECT line; +     +    if (!screen_was_painted) return; +    	/* Invalidating a rectangle before painting seems result in a	*/ +    	/* major performance problem.					*/ +    get_line_rect(i, COLS*char_width, &line); +    InvalidateRect(hwnd, &line, FALSE); +} + +LRESULT CALLBACK AboutBox( HWND hDlg, UINT message, +                           WPARAM wParam, LPARAM lParam ) +{ +   switch( message ) +   { +      case WM_INITDIALOG: +           SetFocus( GetDlgItem( hDlg, IDOK ) ); +           break; + +      case WM_COMMAND: +           switch( wParam ) +           { +              case IDOK: +                   EndDialog( hDlg, TRUE ); +                   break; +           } +           break; + +      case WM_CLOSE: +           EndDialog( hDlg, TRUE ); +           return TRUE; + +   } +   return FALSE; +} + diff --git a/gc/cord/de_win.h b/gc/cord/de_win.h new file mode 100644 index 0000000..57a47b4 --- /dev/null +++ b/gc/cord/de_win.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:25 pm PDT */ + +/* cord.h, de_cmds.h, and windows.h should be included before this. */ + + +# define OTHER_FLAG	0x100 +# define EDIT_CMD_FLAG	0x200 +# define REPEAT_FLAG	0x400 + +# define CHAR_CMD(i) ((i) & 0xff) + +/* MENU: DE */ +#define IDM_FILESAVE		(EDIT_CMD_FLAG + WRITE) +#define IDM_FILEEXIT		(OTHER_FLAG + 1) +#define IDM_HELPABOUT		(OTHER_FLAG + 2) +#define IDM_HELPCONTENTS	(OTHER_FLAG + 3) + +#define IDM_EDITPDOWN		(REPEAT_FLAG + EDIT_CMD_FLAG + DOWN) +#define IDM_EDITPUP		(REPEAT_FLAG + EDIT_CMD_FLAG + UP) +#define IDM_EDITUNDO		(EDIT_CMD_FLAG + UNDO) +#define IDM_EDITLOCATE		(EDIT_CMD_FLAG + LOCATE) +#define IDM_EDITDOWN		(EDIT_CMD_FLAG + DOWN) +#define IDM_EDITUP		(EDIT_CMD_FLAG + UP) +#define IDM_EDITLEFT		(EDIT_CMD_FLAG + LEFT) +#define IDM_EDITRIGHT		(EDIT_CMD_FLAG + RIGHT) +#define IDM_EDITBS		(EDIT_CMD_FLAG + BS) +#define IDM_EDITDEL		(EDIT_CMD_FLAG + DEL) +#define IDM_EDITREPEAT		(EDIT_CMD_FLAG + REPEAT) +#define IDM_EDITTOP		(EDIT_CMD_FLAG + TOP) + + + + +/* Windows UI stuff	*/ + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, +			  UINT wParam, LONG lParam); + +LRESULT CALLBACK AboutBox( HWND hDlg, UINT message, +			   UINT wParam, LONG lParam ); + + +/* Screen dimensions.  Maintained by de_win.c.	*/ +extern int LINES; +extern int COLS; + +/* File being edited.	*/ +extern char * arg_file_name; + +/* Current display position in file.  Maintained by de.c	*/ +extern int dis_line; +extern int dis_col; + +/* Current cursor position in file.				*/ +extern int line; +extern int col; + +/* + *  Calls from de_win.c to de.c + */ +   +CORD retrieve_screen_line(int i); +			/* Get the contents of i'th screen line.	*/ +			/* Relies on COLS.				*/ + +void set_position(int x, int y); +			/* Set column, row.  Upper left of window = (0,0). */ + +void do_command(int); +			/* Execute an editor command.			*/ +			/* Agument is a command character or one	*/ +			/* of the IDM_ commands.			*/ + +void generic_init(void); +			/* OS independent initialization */ + + +/* + * Calls from de.c to de_win.c + */ +  +void move_cursor(int column, int line); +			/* Physically move the cursor on the display,	*/ +			/* so that it appears at			*/ +			/* (column, line).				*/ + +void invalidate_line(int line); +			/* Invalidate line i on the screen.	*/ + +void de_error(char *s); +			/* Display error message.	*/
\ No newline at end of file diff --git a/gc/cord/ec.h b/gc/cord/ec.h new file mode 100644 index 0000000..c829b83 --- /dev/null +++ b/gc/cord/ec.h @@ -0,0 +1,70 @@ +# ifndef EC_H +# define EC_H + +# ifndef CORD_H +#  include "cord.h" +# endif + +/* Extensible cords are strings that may be destructively appended to.	*/ +/* They allow fast construction of cords from characters that are	*/ +/* being read from a stream.						*/ +/* + * A client might look like: + * + *	{ + *	    CORD_ec x; + *	    CORD result; + *	    char c; + *	    FILE *f; + * + *	    ... + *	    CORD_ec_init(x); + *	    while(...) { + *		c = getc(f); + *		... + *		CORD_ec_append(x, c); + *	    } + *	    result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +# ifndef CORD_BUFSZ +#   define CORD_BUFSZ 128 +# endif + +typedef struct CORD_ec_struct { +    CORD ec_cord; +    char * ec_bufptr; +    char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* This structure represents the concatenation of ec_cord with		*/ +/* ec_buf[0 ... (ec_bufptr-ec_buf-1)]					*/ + +/* Flush the buffer part of the extended chord into ec_cord.	*/ +/* Note that this is almost the only real function, and it is	*/ +/* implemented in 6 lines in cordxtra.c				*/ +void CORD_ec_flush_buf(CORD_ec x); +       +/* Convert an extensible cord to a cord. */ +# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord) + +/* Initialize an extensible cord. */ +# define CORD_ec_init(x) ((x)[0].ec_cord = 0, (x)[0].ec_bufptr = (x)[0].ec_buf) + +/* Append a character to an extensible cord.	*/ +# define CORD_ec_append(x, c) \ +    {  \ +	if ((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ) { \ +	  	CORD_ec_flush_buf(x); \ +	} \ +	*((x)[0].ec_bufptr)++ = (c); \ +    } + +/* Append a cord to an extensible cord.  Structure remains shared with 	*/ +/* original.								*/ +void CORD_ec_append_cord(CORD_ec x, CORD s); + +# endif /* EC_H */ diff --git a/gc/cord/gc.h b/gc/cord/gc.h new file mode 100644 index 0000000..3061409 --- /dev/null +++ b/gc/cord/gc.h @@ -0,0 +1,754 @@ +/*  + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved. + * Copyright 1996 by Silicon Graphics.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases.  For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental.  If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * If you are using Solaris threads, look at the end of this file. + * Everything else is best ignored unless you encounter performance + * problems. + */ +  +#ifndef _GC_H + +# define _GC_H +# define __GC +# include <stddef.h> + +#if defined(__CYGWIN32__) && defined(GC_USE_DLL) +#include "libgc_globals.h" +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# ifdef GC_BUILD +#   define GC_API __declspec(dllexport) +# else +#   define GC_API __declspec(dllimport) +# endif +#endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +#   define GC_API extern __declspec(dllexport) +# else +#   define GC_API extern __declspec(dllimport) +# endif +#endif + +#ifndef GC_API +#define GC_API extern +#endif + +# if defined(__STDC__) || defined(__cplusplus) +#   define GC_PROTO(args) args +    typedef void * GC_PTR; +# else +#   define GC_PROTO(args) () +    typedef char * GC_PTR; +#  endif + +# ifdef __cplusplus +    extern "C" { +# endif + + +/* Define word and signed_word to be unsigned and signed types of the 	*/ +/* size as char * or void *.  There seems to be no way to do this	*/ +/* even semi-portably.  The following is probably no better/worse 	*/ +/* than almost anything else.						*/ +/* The ANSI standard suggests that size_t and ptr_diff_t might be 	*/ +/* better choices.  But those appear to have incorrect definitions	*/ +/* on may systems.  Notably "typedef int size_t" seems to be both	*/ +/* frequent and WRONG.							*/ +typedef unsigned long GC_word; +typedef long GC_signed_word; + +/* Public read-only variables */ + +GC_API GC_word GC_gc_no;/* Counter incremented per collection.  	*/ +			/* Includes empty GCs at startup.		*/ +			 + +/* Public R/W variables */ + +GC_API GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)); +			/* When there is insufficient memory to satisfy */ +			/* an allocation request, we return		*/ +			/* (*GC_oom_fn)().  By default this just	*/ +			/* returns 0.					*/ +			/* If it returns, it must return 0 or a valid	*/ +			/* pointer to a previously allocated heap 	*/ +			/* object.					*/ + +GC_API int GC_find_leak; +			/* Do not actually garbage collect, but simply	*/ +			/* report inaccessible memory that was not	*/ +			/* deallocated with GC_free.  Initial value	*/ +			/* is determined by FIND_LEAK macro.		*/ + +GC_API int GC_quiet;	/* Disable statistics output.  Only matters if	*/ +			/* collector has been compiled with statistics	*/ +			/* enabled.  This involves a performance cost,	*/ +			/* and is thus not the default.			*/ + +GC_API int GC_finalize_on_demand; +			/* If nonzero, finalizers will only be run in 	*/ +			/* response to an eplit GC_invoke_finalizers	*/ +			/* call.  The default is determined by whether	*/ +			/* the FINALIZE_ON_DEMAND macro is defined	*/ +			/* when the collector is built.			*/ + +GC_API int GC_java_finalization; +			/* Mark objects reachable from finalizable 	*/ +			/* objects in a separate postpass.  This makes	*/ +			/* it a bit safer to use non-topologically-	*/ +			/* ordered finalization.  Default value is	*/ +			/* determined by JAVA_FINALIZATION macro.	*/ + +GC_API int GC_dont_gc;	/* Dont collect unless explicitly requested, e.g. */ +			/* because it's not safe.			  */ + +GC_API int GC_dont_expand; +			/* Dont expand heap unless explicitly requested */ +			/* or forced to.				*/ + +GC_API int GC_full_freq;    /* Number of partial collections between	*/ +			    /* full collections.  Matters only if	*/ +			    /* GC_incremental is set.			*/ +			 +GC_API GC_word GC_non_gc_bytes; +			/* Bytes not considered candidates for collection. */ +			/* Used only to control scheduling of collections. */ + +GC_API GC_word GC_free_space_divisor; +			/* We try to make sure that we allocate at 	*/ +			/* least N/GC_free_space_divisor bytes between	*/ +			/* collections, where N is the heap size plus	*/ +			/* a rough estimate of the root set size.	*/ +			/* Initially, GC_free_space_divisor = 4.	*/ +			/* Increasing its value will use less space	*/ +			/* but more collection time.  Decreasing it	*/ +			/* will appreciably decrease collection time	*/ +			/* at the expense of space.			*/ +			/* GC_free_space_divisor = 1 will effectively	*/ +			/* disable collections.				*/ + +GC_API GC_word GC_max_retries; +			/* The maximum number of GCs attempted before	*/ +			/* reporting out of memory after heap		*/ +			/* expansion fails.  Initially 0.		*/ +			 + +GC_API char *GC_stackbottom;	/* Cool end of user stack.		*/ +				/* May be set in the client prior to	*/ +				/* calling any GC_ routines.  This	*/ +				/* avoids some overhead, and 		*/ +				/* potentially some signals that can 	*/ +				/* confuse debuggers.  Otherwise the	*/ +				/* collector attempts to set it 	*/ +				/* automatically.			*/ +				/* For multithreaded code, this is the	*/ +				/* cold end of the stack for the	*/ +				/* primordial thread.			*/ +				 +/* Public procedures */ +/* + * general purpose allocation routines, with roughly malloc calling conv. + * The atomic versions promise that no relevant pointers are contained + * in the object.  The nonatomic versions guarantee that the new object + * is cleared.  GC_malloc_stubborn promises that no changes to the object + * will occur after GC_end_stubborn_change has been called on the + * result of GC_malloc_stubborn. GC_malloc_uncollectable allocates an object + * that is scanned for pointers to collectable objects, but is not itself + * collectable.  GC_malloc_uncollectable and GC_free called on the resulting + * object implicitly update GC_non_gc_bytes appropriately. + */ +GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes)); + +/* The following is only defined if the library has been suitably	*/ +/* compiled:								*/ +GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes)); + +/* Explicitly deallocate an object.  Dangerous if used incorrectly.     */ +/* Requires a pointer to the base of an object.				*/ +/* If the argument is stubborn, it should not be changeable when freed. */ +/* An object should not be enable for finalization when it is 		*/ +/* explicitly deallocated.						*/ +/* GC_free(0) is a no-op, as required by ANSI C for free.		*/ +GC_API void GC_free GC_PROTO((GC_PTR object_addr)); + +/* + * Stubborn objects may be changed only if the collector is explicitly informed. + * The collector is implicitly informed of coming change when such + * an object is first allocated.  The following routines inform the + * collector that an object will no longer be changed, or that it will + * once again be changed.  Only nonNIL pointer stores into the object + * are considered to be changes.  The argument to GC_end_stubborn_change + * must be exacly the value returned by GC_malloc_stubborn or passed to + * GC_change_stubborn.  (In the second case it may be an interior pointer + * within 512 bytes of the beginning of the objects.) + * There is a performance penalty for allowing more than + * one stubborn object to be changed at once, but it is acceptable to + * do so.  The same applies to dropping stubborn objects that are still + * changeable. + */ +GC_API void GC_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR)); + +/* Return a pointer to the base (lowest address) of an object given	*/ +/* a pointer to a location within the object.				*/ +/* Return 0 if displaced_pointer doesn't point to within a valid	*/ +/* object.								*/ +GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer)); + +/* Given a pointer to the base of an object, return its size in bytes.	*/ +/* The returned size may be slightly larger than what was originally	*/ +/* requested.								*/ +GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); + +/* For compatibility with C library.  This is occasionally faster than	*/ +/* a malloc followed by a bcopy.  But if you rely on that, either here	*/ +/* or with the standard C library, your code is broken.  In my		*/ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB	*/ +/* The resulting object has the same kind as the original.		*/ +/* If the argument is stubborn, the result will have changes enabled.	*/ +/* It is an error to have changes enabled for the original object.	*/ +/* Follows ANSI comventions for NULL old_object.			*/ +GC_API GC_PTR GC_realloc +	GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); +				    +/* Explicitly increase the heap size.	*/ +/* Returns 0 on failure, 1 on success.  */ +GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes)); + +/* Limit the heap size to n bytes.  Useful when you're debugging, 	*/ +/* especially on systems that don't handle running out of memory well.	*/ +/* n == 0 ==> unbounded.  This is the default.				*/ +GC_API void GC_set_max_heap_size GC_PROTO((GC_word n)); + +/* Inform the collector that a certain section of statically allocated	*/ +/* memory contains no pointers to garbage collected memory.  Thus it 	*/ +/* need not be scanned.  This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be	*/ +/* mistaken for dynamic library data segments on some systems.		*/ +GC_API void GC_exclude_static_roots GC_PROTO((GC_PTR start, GC_PTR finish)); + +/* Clear the set of root segments.  Wizards only. */ +GC_API void GC_clear_roots GC_PROTO((void)); + +/* Add a root segment.  Wizards only. */ +GC_API void GC_add_roots GC_PROTO((char * low_address, +				   char * high_address_plus_1)); + +/* Add a displacement to the set of those considered valid by the	*/ +/* collector.  GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid	*/ +/* pointer to n.  N must be small and less than the size of p.		*/ +/* (All pointers to the interior of objects from the stack are		*/ +/* considered valid in any case.  This applies to heap objects and	*/ +/* static data.)							*/ +/* Preferably, this should be called before any other GC procedures.	*/ +/* Calling it later adds to the probability of excess memory		*/ +/* retention.								*/ +/* This is a no-op if the collector was compiled with recognition of	*/ +/* arbitrary interior pointers enabled, which is now the default.	*/ +GC_API void GC_register_displacement GC_PROTO((GC_word n)); + +/* The following version should be used if any debugging allocation is	*/ +/* being done.								*/ +GC_API void GC_debug_register_displacement GC_PROTO((GC_word n)); + +/* Explicitly trigger a full, world-stop collection. 	*/ +GC_API void GC_gcollect GC_PROTO((void)); + +/* Trigger a full world-stopped collection.  Abort the collection if 	*/ +/* and when stop_func returns a nonzero value.  Stop_func will be 	*/ +/* called frequently, and should be reasonably fast.  This works even	*/ +/* if virtual dirty bits, and hence incremental collection is not 	*/ +/* available for this architecture.  Collections can be aborted faster	*/ +/* than normal pause times for incremental collection.  However,	*/ +/* aborted collections do no useful work; the next collection needs	*/ +/* to start from the beginning.						*/ +/* Return 0 if the collection was aborted, 1 if it succeeded.		*/ +typedef int (* GC_stop_func) GC_PROTO((void)); +GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func)); + +/* Return the number of bytes in the heap.  Excludes collector private	*/ +/* data structures.  Includes empty blocks and fragmentation loss.	*/ +/* Includes some pages that were allocated but never written.		*/ +GC_API size_t GC_get_heap_size GC_PROTO((void)); + +/* Return the number of bytes allocated since the last collection.	*/ +GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); + +/* Enable incremental/generational collection.	*/ +/* Not advisable unless dirty bits are 		*/ +/* available or most heap objects are		*/ +/* pointerfree(atomic) or immutable.		*/ +/* Don't use in leak finding mode.		*/ +/* Ignored if GC_dont_gc is true.		*/ +GC_API void GC_enable_incremental GC_PROTO((void)); + +/* Perform some garbage collection work, if appropriate.	*/ +/* Return 0 if there is no more work to be done.		*/ +/* Typically performs an amount of work corresponding roughly	*/ +/* to marking from one page.  May do more work if further	*/ +/* progress requires it, e.g. if incremental collection is	*/ +/* disabled.  It is reasonable to call this in a wait loop	*/ +/* until it returns 0.						*/ +GC_API int GC_collect_a_little GC_PROTO((void)); + +/* Allocate an object of size lb bytes.  The client guarantees that	*/ +/* as long as the object is live, it will be referenced by a pointer	*/ +/* that points to somewhere within the first 256 bytes of the object.	*/ +/* (This should normally be declared volatile to prevent the compiler	*/ +/* from invalidating this assertion.)  This routine is only useful	*/ +/* if a large array is being allocated.  It reduces the chance of 	*/ +/* accidentally retaining such an array as a result of scanning an	*/ +/* integer that happens to be an address inside the array.  (Actually,	*/ +/* it reduces the chance of the allocator not finding space for such	*/ +/* an array, since it will try hard to avoid introducing such a false	*/ +/* reference.)  On a SunOS 4.X or MS Windows system this is recommended */ +/* for arrays likely to be larger than 100K or so.  For other systems,	*/ +/* or if the collector is not configured to recognize all interior	*/ +/* pointers, the threshold is normally much higher.			*/ +GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); +GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +#   define GC_ADD_CALLER +#   define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#ifdef GC_ADD_CALLER +#  define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +#  define GC_EXTRA_PARAMS GC_word ra, char * descr_string, int descr_int +#else +#  define GC_EXTRAS __FILE__, __LINE__ +#  define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + +/* Debugging (annotated) allocation.  GC_gcollect will check 		*/ +/* objects allocated in this way for overwrites, etc.			*/ +GC_API GC_PTR GC_debug_malloc +	GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_atomic +	GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_uncollectable +	GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_stubborn +	GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); +GC_API GC_PTR GC_debug_realloc +	GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, +  		  GC_EXTRA_PARAMS)); +  			 	  +GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); +# ifdef GC_DEBUG +#   define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +#   define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +#   define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ +							GC_EXTRAS) +#   define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +#   define GC_FREE(p) GC_debug_free(p) +#   define GC_REGISTER_FINALIZER(p, f, d, of, od) \ +	GC_debug_register_finalizer(p, f, d, of, od) +#   define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ +	GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +#   define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS); +#   define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p) +#   define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +#   define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ +	GC_general_register_disappearing_link(link, GC_base(obj)) +#   define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +# else +#   define GC_MALLOC(sz) GC_malloc(sz) +#   define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +#   define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +#   define GC_REALLOC(old, sz) GC_realloc(old, sz) +#   define GC_FREE(p) GC_free(p) +#   define GC_REGISTER_FINALIZER(p, f, d, of, od) \ +	GC_register_finalizer(p, f, d, of, od) +#   define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ +	GC_register_finalizer_ignore_self(p, f, d, of, od) +#   define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz) +#   define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +#   define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +#   define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ +	GC_general_register_disappearing_link(link, obj) +#   define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +# endif +/* The following are included because they are often convenient, and	*/ +/* reduce the chance for a misspecifed size argument.  But calls may	*/ +/* expand to something syntactically incorrect if t is a complicated	*/ +/* type expression.  							*/ +# define GC_NEW(t) (t *)GC_MALLOC(sizeof (t)) +# define GC_NEW_ATOMIC(t) (t *)GC_MALLOC_ATOMIC(sizeof (t)) +# define GC_NEW_STUBBORN(t) (t *)GC_MALLOC_STUBBORN(sizeof (t)) +# define GC_NEW_UNCOLLECTABLE(t) (t *)GC_MALLOC_UNCOLLECTABLE(sizeof (t)) + +/* Finalization.  Some of these primitives are grossly unsafe.		*/ +/* The idea is to make them both cheap, and sufficient to build		*/ +/* a safer layer, closer to PCedar finalization.			*/ +/* The interface represents my conclusions from a long discussion	*/ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, 		*/ +/* Christian Jacobi, and Russ Atkinson.  It's not perfect, and		*/ +/* probably nobody else agrees with it.	    Hans-J. Boehm  3/13/92	*/ +typedef void (*GC_finalization_proc) +  	GC_PROTO((GC_PTR obj, GC_PTR client_data)); + +GC_API void GC_register_finalizer +    	GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, +		  GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer +    	GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, +		  GC_finalization_proc *ofn, GC_PTR *ocd)); +	/* When obj is no longer accessible, invoke		*/ +	/* (*fn)(obj, cd).  If a and b are inaccessible, and	*/ +	/* a points to b (after disappearing links have been	*/ +	/* made to disappear), then only a will be		*/ +	/* finalized.  (If this does not create any new		*/ +	/* pointers to b, then b will be finalized after the	*/ +	/* next collection.)  Any finalizable object that	*/ +	/* is reachable from itself by following one or more	*/ +	/* pointers will not be finalized (or collected).	*/ +	/* Thus cycles involving finalizable objects should	*/ +	/* be avoided, or broken by disappearing links.		*/ +	/* All but the last finalizer registered for an object  */ +	/* is ignored.						*/ +	/* Finalization may be removed by passing 0 as fn.	*/ +	/* Finalizers are implicitly unregistered just before   */ +	/* they are invoked.					*/ +	/* The old finalizer and client data are stored in	*/ +	/* *ofn and *ocd.					*/  +	/* Fn is never invoked on an accessible object,		*/ +	/* provided hidden pointers are converted to real 	*/ +	/* pointers only if the allocation lock is held, and	*/ +	/* such conversions are not performed by finalization	*/ +	/* routines.						*/ +	/* If GC_register_finalizer is aborted as a result of	*/ +	/* a signal, the object may be left with no		*/ +	/* finalization, even if neither the old nor new	*/ +	/* finalizer were NULL.					*/ +	/* Obj should be the nonNULL starting address of an 	*/ +	/* object allocated by GC_malloc or friends.		*/ +	/* Note that any garbage collectable object referenced	*/ +	/* by cd will be considered accessible until the	*/ +	/* finalizer is invoked.				*/ + +/* Another versions of the above follow.  It ignores		*/ +/* self-cycles, i.e. pointers from a finalizable object to	*/ +/* itself.  There is a stylistic argument that this is wrong,	*/ +/* but it's unavoidable for C++, since the compiler may		*/ +/* silently introduce these.  It's also benign in that specific	*/ +/* case.							*/ +GC_API void GC_register_finalizer_ignore_self +	GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, +		  GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer_ignore_self +	GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, +		  GC_finalization_proc *ofn, GC_PTR *ocd)); + +/* The following routine may be used to break cycles between	*/ +/* finalizable objects, thus causing cyclic finalizable		*/ +/* objects to be finalized in the correct order.  Standard	*/ +/* use involves calling GC_register_disappearing_link(&p),	*/ +/* where p is a pointer that is not followed by finalization	*/ +/* code, and should not be considered in determining 		*/ +/* finalization order.						*/ +GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */)); +	/* Link should point to a field of a heap allocated 	*/ +	/* object obj.  *link will be cleared when obj is	*/ +	/* found to be inaccessible.  This happens BEFORE any	*/ +	/* finalization code is invoked, and BEFORE any		*/ +	/* decisions about finalization order are made.		*/ +	/* This is useful in telling the finalizer that 	*/ +	/* some pointers are not essential for proper		*/ +	/* finalization.  This may avoid finalization cycles.	*/ +	/* Note that obj may be resurrected by another		*/ +	/* finalizer, and thus the clearing of *link may	*/ +	/* be visible to non-finalization code.  		*/ +	/* There's an argument that an arbitrary action should  */ +	/* be allowed here, instead of just clearing a pointer. */ +	/* But this causes problems if that action alters, or 	*/ +	/* examines connectivity.				*/ +	/* Returns 1 if link was already registered, 0		*/ +	/* otherwise.						*/ +	/* Only exists for backward compatibility.  See below:	*/ +	 +GC_API int GC_general_register_disappearing_link +	GC_PROTO((GC_PTR * /* link */, GC_PTR obj)); +	/* A slight generalization of the above. *link is	*/ +	/* cleared when obj first becomes inaccessible.  This	*/ +	/* can be used to implement weak pointers easily and	*/ +	/* safely. Typically link will point to a location	*/ +	/* holding a disguised pointer to obj.  (A pointer 	*/ +	/* inside an "atomic" object is effectively  		*/ +	/* disguised.)   In this way soft			*/ +	/* pointers are broken before any object		*/ +	/* reachable from them are finalized.  Each link	*/ +	/* May be registered only once, i.e. with one obj	*/ +	/* value.  This was added after a long email discussion */ +	/* with John Ellis.					*/ +	/* Obj must be a pointer to the first word of an object */ +	/* we allocated.  It is unsafe to explicitly deallocate */ +	/* the object containing link.  Explicitly deallocating */ +	/* obj may or may not cause link to eventually be	*/ +	/* cleared.						*/ +GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); +	/* Returns 0 if link was not actually registered.	*/ +	/* Undoes a registration by either of the above two	*/ +	/* routines.						*/ + +/* Auxiliary fns to make finalization work correctly with displaced	*/ +/* pointers introduced by the debugging allocators.			*/ +GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); +GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); + +GC_API int GC_invoke_finalizers GC_PROTO((void)); +	/* Run finalizers for all objects that are ready to	*/ +	/* be finalized.  Return the number of finalizers	*/ +	/* that were run.  Normally this is also called		*/ +	/* implicitly during some allocations.	If		*/ +	/* GC-finalize_on_demand is nonzero, it must be called	*/ +	/* explicitly.						*/ + +/* GC_set_warn_proc can be used to redirect or filter warning messages.	*/ +/* p may not be a NULL pointer.						*/ +typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); +GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); +    /* Returns old warning procedure.	*/ +	 +/* The following is intended to be used by a higher level	*/ +/* (e.g. cedar-like) finalization facility.  It is expected	*/ +/* that finalization code will arrange for hidden pointers to	*/ +/* disappear.  Otherwise objects can be accessed after they	*/ +/* have been collected.						*/ +/* Note that putting pointers in atomic objects or in 		*/ +/* nonpointer slots of "typed" objects is equivalent to 	*/ +/* disguising them in this way, and may have other advantages.	*/ +# if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) +    typedef GC_word GC_hidden_pointer; +#   define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +#   define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) +    /* Converting a hidden pointer to a real pointer requires verifying	*/ +    /* that the object still exists.  This involves acquiring the  	*/ +    /* allocator lock to avoid a race with the collector.		*/ +# endif /* I_HIDE_POINTERS */ + +typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); +GC_API GC_PTR GC_call_with_alloc_lock +        	GC_PROTO((GC_fn_type fn, GC_PTR client_data)); + +/* Check that p and q point to the same object.  		*/ +/* Fail conspicuously if they don't.				*/ +/* Returns the first argument.  				*/ +/* Succeeds if neither p nor q points to the heap.		*/ +/* May succeed if both p and q point to between heap objects.	*/ +GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q)); + +/* Checked pointer pre- and post- increment operations.  Note that	*/ +/* the second argument is in units of bytes, not multiples of the	*/ +/* object size.  This should either be invoked from a macro, or the	*/ +/* call should be automatically generated.				*/ +GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much)); +GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much)); + +/* Check that p is visible						*/ +/* to the collector as a possibly pointer containing location.		*/ +/* If it isn't fail conspicuously.					*/ +/* Returns the argument in all cases.  May erroneously succeed		*/ +/* in hard cases.  (This is intended for debugging use with		*/ +/* untyped allocations.  The idea is that it should be possible, though	*/ +/* slow, to add such a call to all indirect pointer stores.)		*/ +/* Currently useless for multithreaded worlds.				*/ +GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p)); + +/* Check that if p is a pointer to a heap page, then it points to	*/ +/* a valid displacement within a heap object.				*/ +/* Fail conspicuously if this property does not hold.			*/ +/* Uninteresting with ALL_INTERIOR_POINTERS.				*/ +/* Always returns its argument.						*/ +GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR	p)); + +/* Safer, but slow, pointer addition.  Probably useful mainly with 	*/ +/* a preprocessor.  Useful only for heap pointers.			*/ +#ifdef GC_DEBUG +#   define GC_PTR_ADD3(x, n, type_of_result) \ +	((type_of_result)GC_same_obj((x)+(n), (x))) +#   define GC_PRE_INCR3(x, n, type_of_result) \ +	((type_of_result)GC_pre_incr(&(x), (n)*sizeof(*x)) +#   define GC_POST_INCR2(x, type_of_result) \ +	((type_of_result)GC_post_incr(&(x), sizeof(*x)) +#   ifdef __GNUC__ +#       define GC_PTR_ADD(x, n) \ +	    GC_PTR_ADD3(x, n, typeof(x)) +#   define GC_PRE_INCR(x, n) \ +	    GC_PRE_INCR3(x, n, typeof(x)) +#   define GC_POST_INCR(x, n) \ +	    GC_POST_INCR3(x, typeof(x)) +#   else +	/* We can't do this right without typeof, which ANSI	*/ +	/* decided was not sufficiently useful.  Repeatedly	*/ +	/* mentioning the arguments seems too dangerous to be	*/ +	/* useful.  So does not casting the result.		*/ +#   	define GC_PTR_ADD(x, n) ((x)+(n)) +#   endif +#else	/* !GC_DEBUG */ +#   define GC_PTR_ADD3(x, n, type_of_result) ((x)+(n)) +#   define GC_PTR_ADD(x, n) ((x)+(n)) +#   define GC_PRE_INCR3(x, n, type_of_result) ((x) += (n)) +#   define GC_PRE_INCR(x, n) ((x) += (n)) +#   define GC_POST_INCR2(x, n, type_of_result) ((x)++) +#   define GC_POST_INCR(x, n) ((x)++) +#endif + +/* Safer assignment of a pointer to a nonstack location.	*/ +#ifdef GC_DEBUG +# ifdef __STDC__ +#   define GC_PTR_STORE(p, q) \ +	(*(void **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# else +#   define GC_PTR_STORE(p, q) \ +	(*(char **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# endif +#else /* !GC_DEBUG */ +#   define GC_PTR_STORE(p, q) *((p) = (q)) +#endif + +/* Fynctions called to report pointer checking errors */ +GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q)); + +GC_API void (*GC_is_valid_displacement_print_proc) +	GC_PROTO((GC_PTR p)); + +GC_API void (*GC_is_visible_print_proc) +	GC_PROTO((GC_PTR p)); + +#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +#   define SOLARIS_THREADS +#endif + +#ifdef SOLARIS_THREADS +/* We need to intercept calls to many of the threads primitives, so 	*/ +/* that we can locate thread stacks and stop the world.			*/ +/* Note also that the collector cannot see thread specific data.	*/ +/* Thread specific data should generally consist of pointers to		*/ +/* uncollectable objects, which are deallocated using the destructor	*/ +/* facility in thr_keycreate.						*/ +# include <thread.h> +# include <signal.h> +  int GC_thr_create(void *stack_base, size_t stack_size, +                    void *(*start_routine)(void *), void *arg, long flags, +                    thread_t *new_thread); +  int GC_thr_join(thread_t wait_for, thread_t *departed, void **status); +  int GC_thr_suspend(thread_t target_thread); +  int GC_thr_continue(thread_t target_thread); +  void * GC_dlopen(const char *path, int mode); + +# ifdef _SOLARIS_PTHREADS +#   include <pthread.h> +    extern int GC_pthread_create(pthread_t *new_thread, +    			         const pthread_attr_t *attr, +          			 void * (*thread_execp)(void *), void *arg); +    extern int GC_pthread_join(pthread_t wait_for, void **status); + +#   undef thread_t + +#   define pthread_join GC_pthread_join +#   define pthread_create GC_pthread_create +#endif + +# define thr_create GC_thr_create +# define thr_join GC_thr_join +# define thr_suspend GC_thr_suspend +# define thr_continue GC_thr_continue +# define dlopen GC_dlopen + +# endif /* SOLARIS_THREADS */ + + +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) +/* We treat these similarly. */ +# include <pthread.h> +# include <signal.h> + +  int GC_pthread_create(pthread_t *new_thread, +                        const pthread_attr_t *attr, +		        void *(*start_routine)(void *), void *arg); +  int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); +  int GC_pthread_join(pthread_t thread, void **retval); + +# define pthread_create GC_pthread_create +# define pthread_sigmask GC_pthread_sigmask +# define pthread_join GC_pthread_join + +#endif /* IRIX_THREADS || LINUX_THREADS */ + +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ +	defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ +	defined(IRIX_JDK_THREADS) +   	/* Any flavor of threads except SRC_M3.	*/ +/* This returns a list of objects, linked through their first		*/ +/* word.  Its use can greatly reduce lock contention problems, since	*/ +/* the allocation lock can be acquired and released many fewer times.	*/ +/* lb must be large enough to hold the pointer field.			*/ +GC_PTR GC_malloc_many(size_t lb); +#define GC_NEXT(p) (*(GC_PTR *)(p)) 	/* Retrieve the next element	*/ +					/* in returned list.		*/ +extern void GC_thr_init();	/* Needed for Solaris/X86	*/ + +#endif /* THREADS && !SRC_M3 */ + +/* + * If you are planning on putting + * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() + * from the statically loaded program section. + * This circumvents a Solaris 2.X (X<=4) linker bug. + */ +#if defined(sparc) || defined(__sparc) +#   define GC_INIT() { extern end, etext; \ +		       GC_noop(&end, &etext); } +#else +# if defined(__CYGWIN32__) && defined(GC_USE_DLL) +    /* +     * Similarly gnu-win32 DLLs need explicit initialization +     */ +#   define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } +# else +#   define GC_INIT() +# endif +#endif + +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ +     || defined(_WIN32) +  /* win32S may not free all resources on process exit.  */ +  /* This explicitly deallocates the heap.		 */ +    GC_API void GC_win32_free_heap (); +#endif + +#ifdef __cplusplus +    }  /* end of extern "C" */ +#endif + +#endif /* _GC_H */ diff --git a/gc/cord/private/cord_pos.h b/gc/cord/private/cord_pos.h new file mode 100644 index 0000000..d2b24bb --- /dev/null +++ b/gc/cord/private/cord_pos.h @@ -0,0 +1,118 @@ +/*  + * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:23 pm PDT */ +# ifndef CORD_POSITION_H + +/* The representation of CORD_position.  This is private to the	*/ +/* implementation, but the size is known to clients.  Also	*/ +/* the implementation of some exported macros relies on it.	*/ +/* Don't use anything defined here and not in cord.h.		*/ + +# define MAX_DEPTH 48 +	/* The maximum depth of a balanced cord + 1.		*/ +	/* We don't let cords get deeper than MAX_DEPTH.	*/ + +struct CORD_pe { +    CORD pe_cord; +    size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root 	*/ +/* to current position.						*/ +typedef struct CORD_Pos { +    size_t cur_pos; +    int path_len; +#	define CORD_POS_INVALID (0x55555555) +		/* path_len == INVALID <==> position invalid */ +    const char *cur_leaf;	/* Current leaf, if it is a string.	*/ +    				/* If the current leaf is a function,	*/ +    				/* then this may point to function_buf	*/ +    				/* containing the next few characters.	*/ +    				/* Always points to a valid string	*/ +    				/* containing the current character 	*/ +    				/* unless cur_end is 0.			*/ +    size_t cur_start;	/* Start position of cur_leaf	*/ +    size_t cur_end;	/* Ending position of cur_leaf	*/ +    			/* 0 if cur_leaf is invalid.	*/ +    struct CORD_pe path[MAX_DEPTH + 1]; +    	/* path[path_len] is the leaf corresponding to cur_pos	*/ +    	/* path[0].pe_cord is the cord we point to.		*/ +#   define FUNCTION_BUF_SZ 8 +    char function_buf[FUNCTION_BUF_SZ];	/* Space for next few chars	*/ +    					/* from function node.		*/ +} CORD_pos[1]; + +/* Extract the cord from a position:	*/ +CORD CORD_pos_to_cord(CORD_pos p); +	 +/* Extract the current index from a position:	*/ +size_t CORD_pos_to_index(CORD_pos p); +	 +/* Fetch the character located at the given position:	*/ +char CORD_pos_fetch(CORD_pos p); +	 +/* Initialize the position to refer to the give cord and index.	*/ +/* Note that this is the most expensive function on positions:	*/ +void CORD_set_pos(CORD_pos p, CORD x, size_t i); +	 +/* Advance the position to the next character.	*/ +/* P must be initialized and valid.		*/ +/* Invalidates p if past end:			*/ +void CORD_next(CORD_pos p); + +/* Move the position to the preceding character.	*/ +/* P must be initialized and valid.			*/ +/* Invalidates p if past beginning:			*/ +void CORD_prev(CORD_pos p); +	 +/* Is the position valid, i.e. inside the cord?		*/ +int CORD_pos_valid(CORD_pos p); + +char CORD__pos_fetch(CORD_pos); +void CORD__next(CORD_pos); +void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p)	\ +    (((p)[0].cur_end != 0)? \ +     	(p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ +     	: CORD__pos_fetch(p)) + +#define CORD_next(p)	\ +    (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ +    	(p)[0].cur_pos++ \ +    	: (CORD__next(p), 0)) + +#define CORD_prev(p)	\ +    (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ +    	(p)[0].cur_pos-- \ +    	: (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends:	*/ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos)) +	/* Number of characters in cache.  <= 0 ==> none	*/ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) +	/* Advance position by n characters	*/ +	/* 0 < n < CORD_pos_chars_left(p)	*/ + +#define CORD_pos_cur_char_addr(p) \ +	(p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) +	/* address of current character in cache.	*/ + +#endif | 
