Mercurial > emacs
changeset 118:49342840ba00
Initial revision
author | Jim Blandy <jimb@redhat.com> |
---|---|
date | Mon, 12 Nov 1990 20:20:45 +0000 |
parents | 08356dc1077c |
children | 7cfabf2a8964 |
files | src/casefiddle.c src/casetab.c src/marker.c src/ralloc.c src/unexhp9k800.c src/vms-pp.c src/vmsproc.c src/xmenu.c |
diffstat | 8 files changed, 2938 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/casefiddle.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,268 @@ +/* GNU Emacs case conversion functions. + Copyright (C) 1985 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "commands.h" +#include "syntax.h" + +enum case_action {CASE_UP, CASE_DOWN, CASE_CAPITALIZE, CASE_CAPITALIZE_UP}; + +Lisp_Object +casify_object (flag, obj) + enum case_action flag; + Lisp_Object obj; +{ + register int i, c, len; + register int inword = flag == CASE_DOWN; + + while (1) + { + if (XTYPE (obj) == Lisp_Int) + { + c = XINT (obj); + if (c >= 0 && c <= 0400) + { + if (inword) + XFASTINT (obj) = DOWNCASE (c); + else if (!UPPERCASEP (c)) + XFASTINT (obj) = UPCASE1 (c); + } + return obj; + } + if (XTYPE (obj) == Lisp_String) + { + obj = Fcopy_sequence (obj); + len = XSTRING (obj)->size; + for (i = 0; i < len; i++) + { + c = XSTRING (obj)->data[i]; + if (inword) + c = DOWNCASE (c); + else if (!UPPERCASEP (c)) + c = UPCASE1 (c); + XSTRING (obj)->data[i] = c; + if (flag == CASE_CAPITALIZE) + inword = SYNTAX (c) == Sword; + } + return obj; + } + obj = wrong_type_argument (Qchar_or_string_p, obj, 0); + } +} + +DEFUN ("upcase", Fupcase, Supcase, 1, 1, 0, + "Convert argument to upper case and return that.\n\ +The argument may be a character or string. The result has the same type.\n\ +The argument object is not altered. See also `capitalize'.") + (obj) + Lisp_Object obj; +{ + return casify_object (CASE_UP, obj); +} + +DEFUN ("downcase", Fdowncase, Sdowncase, 1, 1, 0, + "Convert argument to lower case and return that.\n\ +The argument may be a character or string. The result has the same type.\n\ +The argument object is not altered.") + (obj) + Lisp_Object obj; +{ + return casify_object (CASE_DOWN, obj); +} + +DEFUN ("capitalize", Fcapitalize, Scapitalize, 1, 1, 0, + "Convert argument to capitalized form and return that.\n\ +This means that each word's first character is upper case\n\ +and the rest is lower case.\n\ +The argument may be a character or string. The result has the same type.\n\ +The argument object is not altered.") + (obj) + Lisp_Object obj; +{ + return casify_object (CASE_CAPITALIZE, obj); +} + +/* flag is CASE_UP, CASE_DOWN or CASE_CAPITALIZE or CASE_CAPITALIZE_UP. + b and e specify range of buffer to operate on. */ + +casify_region (flag, b, e) + enum case_action flag; + Lisp_Object b, e; +{ + register int i; + register int c; + register int inword = flag == CASE_DOWN; + + if (EQ (b, e)) + /* Not modifying because nothing marked */ + return; + + validate_region (&b, &e); + modify_region (XFASTINT (b), XFASTINT (e)); + record_change (XFASTINT (b), XFASTINT (e) - XFASTINT (b)); + + for (i = XFASTINT (b); i < XFASTINT (e); i++) + { + c = FETCH_CHAR (i); + if (inword && flag != CASE_CAPITALIZE_UP) + c = DOWNCASE (c); + else if (!UPPERCASEP (c) + && (!inword || flag != CASE_CAPITALIZE_UP)) + c = UPCASE1 (c); + FETCH_CHAR (i) = c; + if ((int) flag >= (int) CASE_CAPITALIZE) + inword = SYNTAX (c) == Sword; + } + + signal_after_change (XFASTINT (b), + XFASTINT (e) - XFASTINT (b), + XFASTINT (e) - XFASTINT (b)); +} + +DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 2, "r", + "Convert the region to upper case. In programs, wants two arguments.\n\ +These arguments specify the starting and ending character numbers of\n\ +the region to operate on. When used as a command, the text between\n\ +point and the mark is operated on.\n\ +See also `capitalize-region'.") + (b, e) + Lisp_Object b, e; +{ + casify_region (CASE_UP, b, e); + return Qnil; +} + +DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 2, "r", + "Convert the region to lower case. In programs, wants two arguments.\n\ +These arguments specify the starting and ending character numbers of\n\ +the region to operate on. When used as a command, the text between\n\ +point and the mark is operated on.") + (b, e) + Lisp_Object b, e; +{ + casify_region (CASE_DOWN, b, e); + return Qnil; +} + +DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 2, "r", + "Convert the region to capitalized form.\n\ +Capitalized form means each word's first character is upper case\n\ +and the rest of it is lower case.\n\ +In programs, give two arguments, the starting and ending\n\ +character positions to operate on.") + (b, e) + Lisp_Object b, e; +{ + casify_region (CASE_CAPITALIZE, b, e); + return Qnil; +} + +/* Like Fcapitalize but change only the initials. */ + +Lisp_Object +upcase_initials_region (b, e) + Lisp_Object b, e; +{ + casify_region (CASE_CAPITALIZE_UP, b, e); + return Qnil; +} + +Lisp_Object +operate_on_word (arg) + Lisp_Object arg; +{ + Lisp_Object val, end; + int farend; + + CHECK_NUMBER (arg, 0); + farend = scan_words (point, XINT (arg)); + if (!farend) + farend = XINT (arg) > 0 ? ZV : BEGV; + + end = point > farend ? point : farend; + SET_PT (end); + XFASTINT (val) = farend; + + return val; +} + +DEFUN ("upcase-word", Fupcase_word, Supcase_word, 1, 1, "p", + "Convert following word (or ARG words) to upper case, moving over.\n\ +With negative argument, convert previous words but do not move.\n\ +See also `capitalize-word'.") + (arg) + Lisp_Object arg; +{ + Lisp_Object opoint; + + XFASTINT (opoint) = point; + casify_region (CASE_UP, opoint, operate_on_word (arg)); + return Qnil; +} + +DEFUN ("downcase-word", Fdowncase_word, Sdowncase_word, 1, 1, "p", + "Convert following word (or ARG words) to lower case, moving over.\n\ +With negative argument, convert previous words but do not move.") + (arg) + Lisp_Object arg; +{ + Lisp_Object opoint; + XFASTINT (opoint) = point; + casify_region (CASE_DOWN, opoint, operate_on_word (arg)); + return Qnil; +} + +DEFUN ("capitalize-word", Fcapitalize_word, Scapitalize_word, 1, 1, "p", + "Capitalize the following word (or ARG words), moving over.\n\ +This gives the word(s) a first character in upper case\n\ +and the rest lower case.\n\ +With negative argument, capitalize previous words but do not move.") + (arg) + Lisp_Object arg; +{ + Lisp_Object opoint; + XFASTINT (opoint) = point; + casify_region (CASE_CAPITALIZE, opoint, operate_on_word (arg)); + return Qnil; +} + +syms_of_casefiddle () +{ + defsubr (&Supcase); + defsubr (&Sdowncase); + defsubr (&Scapitalize); + defsubr (&Supcase_region); + defsubr (&Sdowncase_region); + defsubr (&Scapitalize_region); + defsubr (&Supcase_word); + defsubr (&Sdowncase_word); + defsubr (&Scapitalize_word); +} + +keys_of_casefiddle () +{ + initial_define_key (control_x_map, Ctl('U'), "upcase-region"); + initial_define_key (control_x_map, Ctl('L'), "downcase-region"); + initial_define_key (meta_map, 'u', "upcase-word"); + initial_define_key (meta_map, 'l', "downcase-word"); + initial_define_key (meta_map, 'c', "capitalize-word"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/casetab.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,250 @@ +/* GNU Emacs routines to deal with case tables. + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Howard Gayle. See chartab.c for details. */ + +#include "config.h" +#include "lisp.h" +#include "buffer.h" + +Lisp_Object Qcase_table_p; +Lisp_Object Vascii_downcase_table, Vascii_upcase_table; +Lisp_Object Vascii_canon_table, Vascii_eqv_table; + +void compute_trt_inverse (); + +DEFUN ("case-table-p", Fcase_table_p, Scase_table_p, 1, 1, 0, + "Return t iff ARG is a case table.\n\ +See `set-case-table' for more information on these data structures.") + (table) + Lisp_Object table; +{ + Lisp_Object down, up, canon, eqv; + down = Fcar_safe (table); + up = Fcar_safe (Fcdr_safe (table)); + canon = Fcar_safe (Fcdr_safe (Fcdr_safe (table))); + eqv = Fcar_safe (Fcdr_safe (Fcdr_safe (Fcdr_safe (table)))); + +#define STRING256_P(obj) \ + (XTYPE (obj) == Lisp_String && XSTRING (obj)->size == 256) + + return (STRING256_P (down) + && (NULL (up) || STRING256_P (up)) + && ((NULL (canon) && NULL (eqv)) + || (STRING256_P (canon) && STRING256_P (eqv))) + ? Qt : Qnil); +} + +static Lisp_Object +check_case_table (obj) + Lisp_Object obj; +{ + register Lisp_Object tem; + + while (tem = Fcase_table_p (obj), NULL (tem)) + obj = wrong_type_argument (Qcase_table_p, obj, 0); + return (obj); +} + +DEFUN ("current-case-table", Fcurrent_case_table, Scurrent_case_table, 0, 0, 0, + "Return the case table of the current buffer.") + () +{ + Lisp_Object down, up, canon, eqv; + + down = current_buffer->downcase_table; + up = current_buffer->upcase_table; + canon = current_buffer->case_canon_table; + eqv = current_buffer->case_eqv_table; + + return Fcons (down, Fcons (up, Fcons (canon, Fcons (eqv, Qnil)))); +} + +DEFUN ("standard-case-table", Fstandard_case_table, + Sstandard_case_table, 0, 0, 0, + "Return the standard case table.\n\ +This is the one used for new buffers.") + () +{ + return Fcons (Vascii_downcase_table, + Fcons (Vascii_upcase_table, + Fcons (Vascii_canon_table, + Fcons (Vascii_eqv_table, Qnil)))); +} + +DEFUN ("set-case-table", Fset_case_table, Sset_case_table, 1, 1, 0, + "Select a new case table for the current buffer.\n\ +A case table is a list (DOWNCASE UPCASE CANONICALIZE EQUIVALENCES)\n\ + where each element is either nil or a string of length 256.\n\ +DOWNCASE maps each character to its lower-case equivalent.\n\ +UPCASE maps each character to its upper-case equivalent;\n\ + if lower and upper case characters are in 1-1 correspondence,\n\ + you may use nil and the upcase table will be deduced from DOWNCASE.\n\ +CANONICALIZE maps each character to a canonical equivalent;\n\ + any two characters that are related by case-conversion have the same\n\ + canonical equivalent character.\n\ +EQUIVALENCES is a map that cyclicly permutes each equivalence class\n\ + (of characters with the same canonical equivalent).\n\ +Both CANONICALIZE and EQUIVALENCES may be nil, in which case\n\ + both are deduced from DOWNCASE and UPCASE.") + (table) + Lisp_Object table; +{ + set_case_table (table, 0); +} + +DEFUN ("set-standard-case-table", + Fset_standard_case_table, Sset_standard_case_table, 1, 1, 0, + "Select a new standard case table for new buffers.\n\ +See `set-case-table' for more info on case tables.") + (table) + Lisp_Object table; +{ + set_case_table (table, 1); +} + +set_case_table (table, standard) + Lisp_Object table; + int standard; +{ + Lisp_Object down, up, canon, eqv; + + check_case_table (table); + + down = Fcar_safe (table); + up = Fcar_safe (Fcdr_safe (table)); + canon = Fcar_safe (Fcdr_safe (Fcdr_safe (table))); + eqv = Fcar_safe (Fcdr_safe (Fcdr_safe (Fcdr_safe (table)))); + + if (NULL (up)) + { + up = Fmake_string (make_number (256), make_number (0)); + compute_trt_inverse (XSTRING (down)->data, XSTRING (up)->data); + } + + if (NULL (canon)) + { + register int i; + unsigned char *upvec = XSTRING (up)->data; + unsigned char *downvec = XSTRING (down)->data; + + canon = Fmake_string (make_number (256), make_number (0)); + eqv = Fmake_string (make_number (256), make_number (0)); + + /* Set up the CANON vector; for each character, + this sequence of upcasing and downcasing ought to + get the "preferred" lowercase equivalent. */ + for (i = 0; i < 256; i++) + XSTRING (canon)->data[i] = downvec[upvec[downvec[i]]]; + + compute_trt_inverse (XSTRING (canon)->data, XSTRING (eqv)->data); + } + + if (standard) + { + Vascii_downcase_table = down; + Vascii_upcase_table = up; + Vascii_canon_table = canon; + Vascii_eqv_table = eqv; + } + else + { + current_buffer->downcase_table = down; + current_buffer->upcase_table = up; + current_buffer->case_canon_table = canon; + current_buffer->case_eqv_table = eqv; + } + return table; +} + +/* Given a translate table TRT, store the inverse mapping into INVERSE. + Since TRT is not one-to-one, INVERSE is not a simple mapping. + Instead, it divides the space of characters into equivalence classes. + All characters in a given class form one circular list, chained through + the elements of INVERSE. */ + +void +compute_trt_inverse (trt, inverse) + register unsigned char *trt; + register unsigned char *inverse; +{ + register int i = 0400; + register unsigned char c, q; + + while (i--) + inverse[i] = i; + i = 0400; + while (i--) + { + if ((q = trt[i]) != (unsigned char) i) + { + c = inverse[q]; + inverse[q] = i; + inverse[i] = c; + } + } +} + +init_casetab_once () +{ + register int i; + Lisp_Object tem; + + tem = Fmake_string (make_number (256), make_number (0)); + Vascii_downcase_table = tem; + Vascii_canon_table = tem; + + for (i = 0; i < 256; i++) + XSTRING (tem)->data[i] = (i >= 'A' && i <= 'Z') ? i + 040 : i; + + tem = Fmake_string (make_number (256), make_number (0)); + Vascii_upcase_table = tem; + Vascii_eqv_table = tem; + + for (i = 0; i < 256; i++) + XSTRING (tem)->data[i] + = ((i >= 'A' && i <= 'Z') + ? i + ('a' - 'A') + : ((i >= 'a' && i <= 'z') + ? i + ('A' - 'a') + : i)); +} + +syms_of_casetab () +{ + Qcase_table_p = intern ("case-table-p"); + staticpro (&Qcase_table_p); + staticpro (&Vascii_downcase_table); + staticpro (&Vascii_upcase_table); + staticpro (&Vascii_canon_table); + staticpro (&Vascii_eqv_table); + + defsubr (&Scase_table_p); + defsubr (&Scurrent_case_table); + defsubr (&Sstandard_case_table); + defsubr (&Sset_case_table); + defsubr (&Sset_standard_case_table); + +#if 0 + DEFVAR_LISP ("ascii-downcase-table", &Vascii_downcase_table, + "String mapping ASCII characters to lowercase equivalents."); + DEFVAR_LISP ("ascii-upcase-table", &Vascii_upcase_table, + "String mapping ASCII characters to uppercase equivalents."); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/marker.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,295 @@ +/* Markers: examining, setting and killing. + Copyright (C) 1985 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "lisp.h" +#include "buffer.h" + +/* Operations on markers. */ + +DEFUN ("marker-buffer", Fmarker_buffer, Smarker_buffer, 1, 1, 0, + "Return the buffer that MARKER points into, or nil if none.\n\ +Returns nil if MARKER points into a dead buffer.") + (marker) + register Lisp_Object marker; +{ + register Lisp_Object buf; + CHECK_MARKER (marker, 0); + if (XMARKER (marker)->buffer) + { + XSET (buf, Lisp_Buffer, XMARKER (marker)->buffer); + /* Return marker's buffer only if it is not dead. */ + if (!NULL (XBUFFER (buf)->name)) + return buf; + } + return Qnil; +} + +DEFUN ("marker-position", Fmarker_position, Smarker_position, 1, 1, 0, + "Return the position MARKER points at, as a character number.") + (marker) + Lisp_Object marker; +{ + register Lisp_Object pos; + register int i; + register struct buffer *buf; + + CHECK_MARKER (marker, 0); + if (XMARKER (marker)->buffer) + { + buf = XMARKER (marker)->buffer; + i = XMARKER (marker)->bufpos; + + if (i > BUF_GPT (buf) + BUF_GAP_SIZE (buf)) + i -= BUF_GAP_SIZE (buf); + else if (i > BUF_GPT (buf)) + i = BUF_GPT (buf); + + if (i < BUF_BEG (buf) || i > BUF_Z (buf)) + abort (); + + XFASTINT (pos) = i; + return pos; + } + return Qnil; +} + +DEFUN ("set-marker", Fset_marker, Sset_marker, 2, 3, 0, + "Position MARKER before character number NUMBER in BUFFER.\n\ +BUFFER defaults to the current buffer.\n\ +If NUMBER is nil, makes marker point nowhere.\n\ +Then it no longer slows down editing in any buffer.\n\ +Returns MARKER.") + (marker, pos, buffer) + Lisp_Object marker, pos, buffer; +{ + register int charno; + register struct buffer *b; + register struct Lisp_Marker *m; + + CHECK_MARKER (marker, 0); + /* If position is nil or a marker that points nowhere, + make this marker point nowhere. */ + if (NULL (pos) + || (XTYPE (pos) == Lisp_Marker && !XMARKER (pos)->buffer)) + { + unchain_marker (marker); + return marker; + } + + CHECK_NUMBER_COERCE_MARKER (pos, 1); + if (NULL (buffer)) + b = current_buffer; + else + { + CHECK_BUFFER (buffer, 1); + b = XBUFFER (buffer); + /* If buffer is dead, set marker to point nowhere. */ + if (EQ (b->name, Qnil)) + { + unchain_marker (marker); + return marker; + } + } + + charno = XINT (pos); + m = XMARKER (marker); + + if (charno < BUF_BEG (b)) + charno = BUF_BEG (b); + if (charno > BUF_Z (b)) + charno = BUF_Z (b); + if (charno > BUF_GPT (b)) charno += BUF_GAP_SIZE (b); + m->bufpos = charno; + + if (m->buffer != b) + { + unchain_marker (marker); + m->chain = b->markers; + b->markers = marker; + m->buffer = b; + } + + return marker; +} + +/* This version of Fset_marker won't let the position + be outside the visible part. */ + +Lisp_Object +set_marker_restricted (marker, pos, buffer) + Lisp_Object marker, pos, buffer; +{ + register int charno; + register struct buffer *b; + register struct Lisp_Marker *m; + + CHECK_MARKER (marker, 0); + /* If position is nil or a marker that points nowhere, + make this marker point nowhere. */ + if (NULL (pos) || + (XTYPE (pos) == Lisp_Marker && !XMARKER (pos)->buffer)) + { + unchain_marker (marker); + return marker; + } + + CHECK_NUMBER_COERCE_MARKER (pos, 1); + if (NULL (buffer)) + b = current_buffer; + else + { + CHECK_BUFFER (buffer, 1); + b = XBUFFER (buffer); + /* If buffer is dead, set marker to point nowhere. */ + if (EQ (b->name, Qnil)) + { + unchain_marker (marker); + return marker; + } + } + + charno = XINT (pos); + m = XMARKER (marker); + + if (charno < BUF_BEGV (b)) + charno = BUF_BEGV (b); + if (charno > BUF_ZV (b)) + charno = BUF_ZV (b); + if (charno > BUF_GPT (b)) + charno += BUF_GAP_SIZE (b); + m->bufpos = charno; + + if (m->buffer != b) + { + unchain_marker (marker); + m->chain = b->markers; + b->markers = marker; + m->buffer = b; + } + + return marker; +} + +/* This is called during garbage collection, + so we must be careful to ignore and preserve mark bits, + including those in chain fields of markers. */ + +unchain_marker (marker) + register Lisp_Object marker; +{ + register Lisp_Object tail, prev, next; + register int omark; + register struct buffer *b; + + b = XMARKER (marker)->buffer; + if (b == 0) + return; + + if (EQ (b->name, Qnil)) + abort (); + + tail = b->markers; + prev = Qnil; + while (XSYMBOL (tail) != XSYMBOL (Qnil)) + { + next = XMARKER (tail)->chain; + XUNMARK (next); + + if (XMARKER (marker) == XMARKER (tail)) + { + if (NULL (prev)) + { + b->markers = next; + /* Deleting first marker from the buffer's chain. + Crash if new first marker in chain does not say + it belongs to this buffer. */ + if (!EQ (next, Qnil) && b != XMARKER (next)->buffer) + abort (); + } + else + { + omark = XMARKBIT (XMARKER (prev)->chain); + XMARKER (prev)->chain = next; + XSETMARKBIT (XMARKER (prev)->chain, omark); + } + break; + } + else + prev = tail; + tail = next; + } + XMARKER (marker)->buffer = 0; +} + +marker_position (marker) + Lisp_Object marker; +{ + register struct Lisp_Marker *m = XMARKER (marker); + register struct buffer *buf = m->buffer; + register int i = m->bufpos; + + if (!buf) + error ("Marker does not point anywhere"); + + if (i > BUF_GPT (buf) + BUF_GAP_SIZE (buf)) + i -= BUF_GAP_SIZE (buf); + else if (i > BUF_GPT (buf)) + i = BUF_GPT (buf); + + if (i < BUF_BEG (buf) || i > BUF_Z (buf)) + abort (); + + return i; +} + +DEFUN ("copy-marker", Fcopy_marker, Scopy_marker, 1, 1, 0, + "Return a new marker pointing at the same place as MARKER.\n\ +If argument is a number, makes a new marker pointing\n\ +at that position in the current buffer.") + (marker) + register Lisp_Object marker; +{ + register Lisp_Object new; + + while (1) + { + if (XTYPE (marker) == Lisp_Int + || XTYPE (marker) == Lisp_Marker) + { + new = Fmake_marker (); + Fset_marker (new, marker, + ((XTYPE (marker) == Lisp_Marker) + ? Fmarker_buffer (marker) + : Qnil)); + return new; + } + else + marker = wrong_type_argument (Qinteger_or_marker_p, marker); + } +} + +syms_of_marker () +{ + defsubr (&Smarker_position); + defsubr (&Smarker_buffer); + defsubr (&Sset_marker); + defsubr (&Scopy_marker); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ralloc.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,426 @@ +/* Block-relocating memory allocator. + Copyright (C) 1990 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTES: + + Only relocate the blocs neccessary for SIZE in r_alloc_sbrk, + rather than all of them. This means allowing for a possible + hole between the first bloc and the end of malloc storage. */ + +#include "config.h" +#include "lisp.h" /* Needed for xterm.h */ +#undef NULL +#include "mem_limits.h" +#include "xterm.h" /* Needed for BLOCK_INPUT */ + +#define NIL ((POINTER) 0) + + +/* System call to set the break value. */ +extern POINTER sbrk (); + +/* The break value, as seen by malloc (). */ +static POINTER virtual_break_value; + +/* The break value, viewed by the relocatable blocs. */ +static POINTER break_value; + +/* The REAL (i.e., page aligned) break value of the process. */ +static POINTER page_break_value; + +/* Macros for rounding. Note that rounding to any value is possible + by changing the definition of PAGE. */ +#define PAGE (getpagesize ()) +#define ALIGNED(addr) (((unsigned int) (addr) & (PAGE - 1)) == 0) +#define ROUNDUP(size) (((unsigned int) (size) + PAGE) & ~(PAGE - 1)) +#define ROUND_TO_PAGE(addr) (addr & (~(PAGE - 1))) +#define EXCEEDS_ELISP_PTR(ptr) ((unsigned int) (ptr) >> VALBITS) + +/* Level of warnings issued. */ +static int warnlevel; + +/* Function to call to issue a warning; + 0 means don't issue them. */ +static void (*warnfunction) (); + +static void +check_memory_limits (address) + POINTER address; +{ + SIZE data_size = address - data_space_start; + + switch (warnlevel) + { + case 0: + if (data_size > (lim_data / 4) * 3) + { + warnlevel++; + (*warnfunction) ("Warning: past 75% of memory limit"); + } + break; + + case 1: + if (data_size > (lim_data / 20) * 17) + { + warnlevel++; + (*warnfunction) ("Warning: past 85% of memory limit"); + } + break; + + case 2: + if (data_size > (lim_data / 20) * 19) + { + warnlevel++; + (*warnfunction) ("Warning: past 95% of memory limit"); + } + break; + + default: + (*warnfunction) ("Warning: past acceptable memory limits"); + break; + } + + if (EXCEEDS_ELISP_PTR (address)) + (*warnfunction) ("Warning: memory in use exceeds lisp pointer size"); +} + +/* Obtain SIZE bytes of space. If enough space is not presently available + in our process reserve, (i.e., (page_break_value - break_value)), + this means getting more page-aligned space from the system. */ + +static void +obtain (size) + SIZE size; +{ + SIZE already_available = page_break_value - break_value; + + if (already_available < size) + { + SIZE get = ROUNDUP (size); + + if (warnfunction) + check_memory_limits (page_break_value); + + if (((int) sbrk (get)) < 0) + abort (); + + page_break_value += get; + } + + break_value += size; +} + +/* Obtain SIZE bytes of space and return a pointer to the new area. */ + +static POINTER +get_more_space (size) + SIZE size; +{ + POINTER ptr = break_value; + obtain (size); + return ptr; +} + +/* Note that SIZE bytes of space have been relinquished by the process. + If SIZE is more than a page, return the space the system. */ + +static void +relinquish (size) + SIZE size; +{ + SIZE page_part = ROUND_TO_PAGE (size); + + if (page_part) + { + if (((int) (sbrk (- page_part))) < 0) + abort (); + + page_break_value -= page_part; + } + + break_value -= size; + bzero (break_value, (size - page_part)); +} + +typedef struct bp +{ + struct bp *next; + struct bp *prev; + POINTER *variable; + POINTER data; + SIZE size; +} *bloc_ptr; + +#define NIL_BLOC ((bloc_ptr) 0) +#define BLOC_PTR_SIZE (sizeof (struct bp)) + +/* Head and tail of the list of relocatable blocs. */ +static bloc_ptr first_bloc, last_bloc; + +/* Declared in dispnew.c, this version dosen't fuck up if regions overlap. */ +extern void safe_bcopy (); + +/* Find the bloc reference by the address in PTR. Returns a pointer + to that block. */ + +static bloc_ptr +find_bloc (ptr) + POINTER *ptr; +{ + register bloc_ptr p = first_bloc; + + while (p != NIL_BLOC) + { + if (p->variable == ptr && p->data == *ptr) + return p; + + p = p->next; + } + + return p; +} + +/* Allocate a bloc of SIZE bytes and append it to the chain of blocs. + Returns a pointer to the new bloc. */ + +static bloc_ptr +get_bloc (size) + SIZE size; +{ + register bloc_ptr new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE); + + new_bloc->data = get_more_space (size); + new_bloc->size = size; + new_bloc->next = NIL_BLOC; + new_bloc->variable = NIL; + + if (first_bloc) + { + new_bloc->prev = last_bloc; + last_bloc->next = new_bloc; + last_bloc = new_bloc; + } + else + { + first_bloc = last_bloc = new_bloc; + new_bloc->prev = NIL_BLOC; + } + + return new_bloc; +} + +/* Relocate all blocs from BLOC on upward in the list to the zone + indicated by ADDRESS. Direction of relocation is determined by + the position of ADDRESS relative to BLOC->data. + + Note that ordering of blocs is not affected by this function. */ + +static void +relocate_some_blocs (bloc, address) + bloc_ptr bloc; + POINTER address; +{ + register bloc_ptr b; + POINTER data_zone = bloc->data; + register SIZE data_zone_size = 0; + register SIZE offset = bloc->data - address; + POINTER new_data_zone = data_zone - offset; + + for (b = bloc; b != NIL_BLOC; b = b->next) + { + data_zone_size += b->size; + b->data -= offset; + *b->variable = b->data; + } + + safe_bcopy (data_zone, new_data_zone, data_zone_size); +} + +/* Free BLOC from the chain of blocs, relocating any blocs above it + and returning BLOC->size bytes to the free area. */ + +static void +free_bloc (bloc) + bloc_ptr bloc; +{ + if (bloc == first_bloc && bloc == last_bloc) + { + first_bloc = last_bloc = NIL_BLOC; + } + else if (bloc == last_bloc) + { + last_bloc = bloc->prev; + last_bloc->next = NIL_BLOC; + } + else if (bloc == first_bloc) + { + first_bloc = bloc->next; + first_bloc->prev = NIL_BLOC; + relocate_some_blocs (bloc->next, bloc->data); + } + else + { + bloc->next->prev = bloc->prev; + bloc->prev->next = bloc->next; + relocate_some_blocs (bloc->next, bloc->data); + } + + relinquish (bloc->size); + free (bloc); +} + +static int use_relocatable_buffers; + +/* Obtain SIZE bytes of storage from the free pool, or the system, + as neccessary. If relocatable blocs are in use, this means + relocating them. */ + +POINTER +r_alloc_sbrk (size) + long size; +{ + POINTER ptr; + + if (! use_relocatable_buffers) + return sbrk (size); + + if (size > 0) + { + obtain (size); + if (first_bloc) + { + relocate_some_blocs (first_bloc, first_bloc->data + size); + bzero (virtual_break_value, size); + } + } + else if (size < 0) + { + if (first_bloc) + relocate_some_blocs (first_bloc, first_bloc->data + size); + relinquish (- size); + } + + ptr = virtual_break_value; + virtual_break_value += size; + return ptr; +} + +/* Allocate a relocatable bloc of storage of size SIZE. A pointer to + the data is returned in *PTR. PTR is thus the address of some variable + which will use the data area. */ + +POINTER +r_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr new_bloc; + + BLOCK_INPUT; + new_bloc = get_bloc (size); + new_bloc->variable = ptr; + *ptr = new_bloc->data; + UNBLOCK_INPUT; + + return *ptr; +} + +/* Free a bloc of relocatable storage whose data is pointed to by PTR. */ + +void +r_alloc_free (ptr) + register POINTER *ptr; +{ + register bloc_ptr dead_bloc; + + BLOCK_INPUT; + dead_bloc = find_bloc (ptr); + if (dead_bloc == NIL_BLOC) + abort (); + + free_bloc (dead_bloc); + UNBLOCK_INPUT; +} + +/* Given a pointer at address PTR to relocatable data, resize it + to SIZE. This is done by obtaining a new block and freeing the + old, unless SIZE is less than or equal to the current bloc size, + in which case nothing happens and the current value is returned. + + The contents of PTR is changed to reflect the new bloc, and this + value is returned. */ + +POINTER +r_re_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr old_bloc, new_bloc; + + BLOCK_INPUT; + old_bloc = find_bloc (ptr); + if (old_bloc == NIL_BLOC) + abort (); + + if (size <= old_bloc->size) + return *ptr; + + new_bloc = get_bloc (size); + new_bloc->variable = ptr; + safe_bcopy (old_bloc->data, new_bloc->data, old_bloc->size); + *ptr = new_bloc->data; + + free_bloc (old_bloc); + UNBLOCK_INPUT; + + return *ptr; +} + +/* The hook `malloc' uses for the function which gets more space + from the system. */ +extern POINTER (*__morecore) (); + +/* Intialize various things for memory allocation. */ + +void +malloc_init (start, warn_func) + POINTER start; + void (*warn_func) (); +{ + static int malloc_initialized = 0; + + if (start) + data_space_start = start; + + if (malloc_initialized) + return; + + malloc_initialized = 1; + __morecore = r_alloc_sbrk; + virtual_break_value = break_value = sbrk (0); + page_break_value = (POINTER) ROUNDUP (break_value); + bzero (break_value, (page_break_value - break_value)); + use_relocatable_buffers = 1; + + lim_data = 0; + warnlevel = 0; + warnfunction = warn_func; + + get_lim_data (); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unexhp9k800.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,293 @@ +/* Unexec for HP 9000 Series 800 machines. + Bob Desinger <hpsemc!bd@hplabs.hp.com> + + Note that the GNU project considers support for HP operation a + peripheral activity which should not be allowed to divert effort + from development of the GNU system. Changes in this code will be + installed when users send them in, but aside from that we don't + plan to think about it, or about whether other Emacs maintenance + might break it. + + + Unexec creates a copy of the old a.out file, and replaces the old data + area with the current data area. When the new file is executed, the + process will see the same data structures and data values that the + original process had when unexec was called. + + Unlike other versions of unexec, this one copies symbol table and + debug information to the new a.out file. Thus, the new a.out file + may be debugged with symbolic debuggers. + + If you fix any bugs in this, I'd like to incorporate your fixes. + Send them to uunet!hpda!hpsemc!jmorris or jmorris%hpsemc@hplabs.HP.COM. + + CAVEATS: + This routine saves the current value of all static and external + variables. This means that any data structure that needs to be + initialized must be explicitly reset. Variables will not have their + expected default values. + + Unfortunately, the HP-UX signal handler has internal initialization + flags which are not explicitly reset. Thus, for signals to work in + conjunction with this routine, the following code must executed when + the new process starts up. + + void _sigreturn(); + ... + sigsetreturn(_sigreturn); +*/ + +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> + +#include <a.out.h> + +#define NBPG 2048 +#define roundup(x,n) ( ( (x)+(n-1) ) & ~(n-1) ) /* n is power of 2 */ +#define min(x,y) ( ((x)<(y))?(x):(y) ) + + +/* Create a new a.out file, same as old but with current data space */ + +unexec(new_name, old_name, new_end_of_text, dummy1, dummy2) + char new_name[]; /* name of the new a.out file to be created */ + char old_name[]; /* name of the old a.out file */ + char *new_end_of_text; /* ptr to new edata/etext; NOT USED YET */ + int dummy1, dummy2; /* not used by emacs */ +{ + int old, new; + int old_size, new_size; + struct header hdr; + struct som_exec_auxhdr auxhdr; + + /* For the greatest flexibility, should create a temporary file in + the same directory as the new file. When everything is complete, + rename the temp file to the new name. + This way, a program could update its own a.out file even while + it is still executing. If problems occur, everything is still + intact. NOT implemented. */ + + /* Open the input and output a.out files */ + old = open(old_name, O_RDONLY); + if (old < 0) + { perror(old_name); exit(1); } + new = open(new_name, O_CREAT|O_RDWR|O_TRUNC, 0777); + if (new < 0) + { perror(new_name); exit(1); } + + /* Read the old headers */ + read_header(old, &hdr, &auxhdr); + + /* Decide how large the new and old data areas are */ + old_size = auxhdr.exec_dsize; + new_size = sbrk(0) - auxhdr.exec_dmem; + + /* Copy the old file to the new, up to the data space */ + lseek(old, 0, 0); + copy_file(old, new, auxhdr.exec_dfile); + + /* Skip the old data segment and write a new one */ + lseek(old, old_size, 1); + save_data_space(new, &hdr, &auxhdr, new_size); + + /* Copy the rest of the file */ + copy_rest(old, new); + + /* Update file pointers since we probably changed size of data area */ + update_file_ptrs(new, &hdr, &auxhdr, auxhdr.exec_dfile, new_size-old_size); + + /* Save the modified header */ + write_header(new, &hdr, &auxhdr); + + /* Close the binary file */ + close(old); + close(new); + exit(0); +} + +/* Save current data space in the file, update header. */ + +save_data_space(file, hdr, auxhdr, size) + int file; + struct header *hdr; + struct som_exec_auxhdr *auxhdr; + int size; +{ + /* Write the entire data space out to the file */ + if (write(file, auxhdr->exec_dmem, size) != size) + { perror("Can't save new data space"); exit(1); } + + /* Update the header to reflect the new data size */ + auxhdr->exec_dsize = size; + auxhdr->exec_bsize = 0; +} + +/* Update the values of file pointers when something is inserted. */ + +update_file_ptrs(file, hdr, auxhdr, location, offset) + int file; + struct header *hdr; + struct som_exec_auxhdr *auxhdr; + unsigned int location; + int offset; +{ + struct subspace_dictionary_record subspace; + int i; + + /* Increase the overall size of the module */ + hdr->som_length += offset; + + /* Update the various file pointers in the header */ +#define update(ptr) if (ptr > location) ptr = ptr + offset + update(hdr->aux_header_location); + update(hdr->space_strings_location); + update(hdr->init_array_location); + update(hdr->compiler_location); + update(hdr->symbol_location); + update(hdr->fixup_request_location); + update(hdr->symbol_strings_location); + update(hdr->unloadable_sp_location); + update(auxhdr->exec_tfile); + update(auxhdr->exec_dfile); + + /* Do for each subspace dictionary entry */ + lseek(file, hdr->subspace_location, 0); + for (i = 0; i < hdr->subspace_total; i++) + { + if (read(file, &subspace, sizeof(subspace)) != sizeof(subspace)) + { perror("Can't read subspace record"); exit(1); } + + /* If subspace has a file location, update it */ + if (subspace.initialization_length > 0 + && subspace.file_loc_init_value > location) + { + subspace.file_loc_init_value += offset; + lseek(file, -sizeof(subspace), 1); + if (write(file, &subspace, sizeof(subspace)) != sizeof(subspace)) + { perror("Can't update subspace record"); exit(1); } + } + } + + /* Do for each initialization pointer record */ + /* (I don't think it applies to executable files, only relocatables) */ +#undef update +} + +/* Read in the header records from an a.out file. */ + +read_header(file, hdr, auxhdr) + int file; + struct header *hdr; + struct som_exec_auxhdr *auxhdr; +{ + + /* Read the header in */ + lseek(file, 0, 0); + if (read(file, hdr, sizeof(*hdr)) != sizeof(*hdr)) + { perror("Couldn't read header from a.out file"); exit(1); } + + if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC + && hdr->a_magic != DEMAND_MAGIC) + { + fprintf(stderr, "a.out file doesn't have legal magic number\n"); + exit(1); + } + + lseek(file, hdr->aux_header_location, 0); + if (read(file, auxhdr, sizeof(*auxhdr)) != sizeof(*auxhdr)) + { + perror("Couldn't read auxiliary header from a.out file"); + exit(1); + } +} + +/* Write out the header records into an a.out file. */ + +write_header(file, hdr, auxhdr) + int file; + struct header *hdr; + struct som_exec_auxhdr *auxhdr; +{ + /* Update the checksum */ + hdr->checksum = calculate_checksum(hdr); + + /* Write the header back into the a.out file */ + lseek(file, 0, 0); + if (write(file, hdr, sizeof(*hdr)) != sizeof(*hdr)) + { perror("Couldn't write header to a.out file"); exit(1); } + lseek(file, hdr->aux_header_location, 0); + if (write(file, auxhdr, sizeof(*auxhdr)) != sizeof(*auxhdr)) + { perror("Couldn't write auxiliary header to a.out file"); exit(1); } +} + +/* Calculate the checksum of a SOM header record. */ + +calculate_checksum(hdr) + struct header *hdr; +{ + int checksum, i, *ptr; + + checksum = 0; ptr = (int *) hdr; + + for (i=0; i<sizeof(*hdr)/sizeof(int)-1; i++) + checksum ^= ptr[i]; + + return(checksum); +} + +/* Copy size bytes from the old file to the new one. */ + +copy_file(old, new, size) + int new, old; + int size; +{ + int len; + int buffer[8196]; /* word aligned will be faster */ + + for (; size > 0; size -= len) + { + len = min(size, sizeof(buffer)); + if (read(old, buffer, len) != len) + { perror("Read failure on a.out file"); exit(1); } + if (write(new, buffer, len) != len) + { perror("Write failure in a.out file"); exit(1); } + } +} + +/* Copy the rest of the file, up to EOF. */ + +copy_rest(old, new) + int new, old; +{ + int buffer[4096]; + int len; + + /* Copy bytes until end of file or error */ + while ( (len = read(old, buffer, sizeof(buffer))) > 0) + if (write(new, buffer, len) != len) break; + + if (len != 0) + { perror("Unable to copy the rest of the file"); exit(1); } +} + +#ifdef DEBUG +display_header(hdr, auxhdr) + struct header *hdr; + struct som_exec_auxhdr *auxhdr; +{ + /* Display the header information (debug) */ + printf("\n\nFILE HEADER\n"); + printf("magic number %d \n", hdr->a_magic); + printf("text loc %.8x size %d \n", auxhdr->exec_tmem, auxhdr->exec_tsize); + printf("data loc %.8x size %d \n", auxhdr->exec_dmem, auxhdr->exec_dsize); + printf("entry %x \n", auxhdr->exec_entry); + printf("Bss segment size %u\n", auxhdr->exec_bsize); + printf("\n"); + printf("data file loc %d size %d\n", + auxhdr->exec_dfile, auxhdr->exec_dsize); + printf("som_length %d\n", hdr->som_length); + printf("unloadable sploc %d size %d\n", + hdr->unloadable_sp_location, hdr->unloadable_sp_size); +} +#endif /* DEBUG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vms-pp.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,242 @@ +/* vms_pp - preprocess emacs files in such a way that they can be + * compiled on VMS without warnings. + * Copyright (C) 1986 Free Software Foundation, Inc. + + This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + * + * Usage: + * vms_pp infile outfile + * implicit inputs: + * The file "vms_pp.trans" has the names and their translations. + * description: + * Vms_pp takes the input file and scans it, replacing the long + * names with shorter names according to the table read in from + * vms_pp.trans. The line is then written to the output file. + * + * Additionally, the "#undef foo" construct is replaced with: + * #ifdef foo + * #undef foo + * #endif + * + * The construct #if defined(foo) is replaced with + * #ifdef foo + * #define foo_VAL 1 + * #else + * #define foo_VAL 0 + * #endif + * #define defined(XX) XX_val + * #if defined(foo) + * + * This last contruction only works on single line #if's and takes + * advantage of a questionable C pre-processor trick. If there are + * comments within the #if, that contain "defined", then this will + * bomb. + */ +#include <stdio.h> + +#define Max_table 100 +#define Table_name "vms_pp.trans" +#define Word_member \ +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$" + +static FILE *in,*out; /* read from, write to */ +struct item { /* symbol table entries */ + char *name; + char *value; +}; +static struct item name_table[Max_table]; /* symbol table */ +static int defined_defined = 0; /* small optimization */ + +main(argc,argv) int argc; char **argv; { + char buffer[1024]; + + if(argc != 3) { /* check argument count */ + fprintf(stderr,"usage: vms_pp infile outfile"); + exit(); + } + init_table(); /* read in translation table */ + +/* open input and output files + */ + if((in = fopen(argv[1],"r")) == NULL) { + fprintf(stderr,"vms_pp: unable to open file '%s'",argv[1]); + exit(); + } + if((out = fopen(argv[2],"w")) == NULL) { + fprintf(stderr,"vms_pp: unable to create file '%s'",argv[2]); + exit(); + } + + while(fgets(buffer,1023,in) != NULL) { /* loop through buffer until end */ + process_line(buffer); /* process the line */ + fputs(buffer,out); /* write out the line */ + } +} + +/* buy - allocate and copy a string + */ +static char *buy(str) char *str; { + char *temp; + + if(!(temp = malloc(strlen(str)+1))) { + fprintf(stderr,"vms_pp: can't allocate memory"); + exit(); + } + strcpy(temp,str); + return temp; +} + +/* gather_word - return a buffer full of the next word + */ +static char *gather_word(ptr,word) char *ptr, *word;{ + for(; strchr(Word_member,*ptr); ptr++,word++) + *word = *ptr; + *word = 0; + return ptr; +} + +/* skip_white - skip white space + */ +static char *skip_white(ptr) char *ptr; { + while(*ptr == ' ' || *ptr == '\t') + ptr++; + return ptr; +} + +/* init_table - initialize translation table. + */ +init_table() { + char buf[256],*ptr,word[128]; + FILE *in; + int i; + + if((in = fopen(Table_name,"r")) == NULL) { /* open file */ + fprintf(stderr,"vms_pp: can't open '%s'",Table_name); + exit(); + } + for(i = 0; fgets(buf,255,in) != NULL;) { /* loop through lines */ + ptr = skip_white(buf); + if(*ptr == '!') /* skip comments */ + continue; + ptr = gather_word(ptr,word); /* get long word */ + if(*word == 0) { /* bad entry */ + fprintf(stderr,"vms_pp: bad input line '%s'\n",buf); + continue; + } + name_table[i].name = buy(word); /* set up the name */ + ptr = skip_white(ptr); /* skip white space */ + ptr = gather_word(ptr,word); /* get equivalent name */ + if(*word == 0) { /* bad entry */ + fprintf(stderr,"vms_pp: bad input line '%s'\n",buf); + continue; + } + name_table[i].value = buy(word); /* and the equivalent name */ + i++; /* increment to next position */ + } + for(; i < Max_table; i++) /* mark rest as unused */ + name_table[i].name = 0; +} + +/* process_line - do actual line processing + */ +process_line(buf) char *buf; { + char *in_ptr,*out_ptr; + char word[128],*ptr; + int len; + + check_pp(buf); /* check for preprocessor lines */ + + for(in_ptr = out_ptr = buf; *in_ptr;) { + if(!strchr(Word_member,*in_ptr)) /* non alpha-numeric? just copy */ + *out_ptr++ = *in_ptr++; + else { + in_ptr = gather_word(in_ptr,word); /* get the 'word' */ + if(strlen(word) > 31) /* length is too long */ + replace_word(word); /* replace the word */ + for(ptr = word; *ptr; ptr++,out_ptr++) /* copy out the word */ + *out_ptr = *ptr; + } + } + *out_ptr = 0; +} + +/* check_pp - check for preprocessor lines + */ +check_pp(buf) char *buf; { + char *ptr,*p; + char word[128]; + + ptr = skip_white(buf); /* skip white space */ + if(*ptr != '#') /* is this a preprocessor line? */ + return; /* no, just return */ + + ptr = skip_white(++ptr); /* skip white */ + ptr = gather_word(ptr,word); /* get command word */ + if(!strcmp("undef",word)) { /* undef? */ + ptr = skip_white(ptr); + ptr = gather_word(ptr,word); /* get the symbol to undef */ + fprintf(out,"#ifdef %s\n",word); + fputs(buf,out); + strcpy(buf,"#endif"); + return; + } + if(!strcmp("if",word)) { /* check for if */ + for(;;) { + ptr = strchr(ptr,'d'); /* look for d in defined */ + if(!ptr) /* are we done? */ + return; + if(strchr(Word_member,*(ptr-1))){ /* at beginning of word? */ + ptr++; continue; /* no, continue looking */ + } + ptr = gather_word(ptr,word); /* get the word */ + if(strcmp(word,"defined")) /* skip if not defined */ + continue; + ptr = skip_white(ptr); /* skip white */ + if(*ptr != '(') /* look for open paren */ + continue; /* error, continue */ + ptr++; /* skip paren */ + ptr = skip_white(ptr); /* more white skipping */ + ptr = gather_word(ptr,word); /* get the thing to test */ + if(!*word) /* null word is bad */ + continue; + fprintf(out,"#ifdef %s\n",word); /* generate the code */ + fprintf(out,"#define %s_VAL 1\n",word); + fprintf(out,"#else\n"); + fprintf(out,"#define %s_VAL 0\n",word); + fprintf(out,"#endif\n"); + if(!defined_defined) { + fprintf(out,"#define defined(XXX) XXX/**/_VAL\n"); + defined_defined = 1; + } + } + } +} + +/* replace_word - look the word up in the table, and replace it + * if a match is found. + */ +replace_word(word) char *word; { + int i; + + for(i = 0; i < Max_table && name_table[i].name; i++) + if(!strcmp(word,name_table[i].name)) { + strcpy(word,name_table[i].value); + return; + } + fprintf(stderr,"couldn't find '%s'\n",word); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vmsproc.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,786 @@ +/* Interfaces to subprocesses on VMS. + Copyright (C) 1988 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* + Event flag and `select' emulation + + 0 is never used + 1 is the terminal + 23 is the timer event flag + 24-31 are reserved by VMS +*/ +#include <ssdef.h> +#include <iodef.h> +#include <dvidef.h> +#include <clidef.h> +#include "vmsproc.h" + +#define KEYBOARD_EVENT_FLAG 1 +#define TIMER_EVENT_FLAG 23 + +static VMS_PROC_STUFF procList[MAX_EVENT_FLAGS+1]; + +get_kbd_event_flag () +{ + /* + Return the first event flag for keyboard input. + */ + VMS_PROC_STUFF *vs = &procList[KEYBOARD_EVENT_FLAG]; + + vs->busy = 1; + vs->pid = 0; + return (vs->eventFlag); +} + +get_timer_event_flag () +{ + /* + Return the last event flag for use by timeouts + */ + VMS_PROC_STUFF *vs = &procList[TIMER_EVENT_FLAG]; + + vs->busy = 1; + vs->pid = 0; + return (vs->eventFlag); +} + +VMS_PROC_STUFF * +get_vms_process_stuff () +{ + /* + Return a process_stuff structure + + We use 1-23 as our event flags to simplify implementing + a VMS `select' call. + */ + int i; + VMS_PROC_STUFF *vs; + + for (i=1, vs = procList; i<MAX_EVENT_FLAGS; i++, vs++) + { + if (!vs->busy) + { + vs->busy = 1; + vs->inputChan = 0; + vs->pid = 0; + sys$clref (vs->eventFlag); + return (vs); + } + } + return ((VMS_PROC_STUFF *)0); +} + +give_back_vms_process_stuff (vs) + VMS_PROC_STUFF *vs; +{ + /* + Return an event flag to our pool + */ + vs->busy = 0; + vs->inputChan = 0; + vs->pid = 0; +} + +VMS_PROC_STUFF * +get_vms_process_pointer (pid) + int pid; +{ + /* + Given a pid, return the VMS_STUFF pointer + */ + int i; + VMS_PROC_STUFF *vs; + + /* Don't search the last one */ + for (i=0, vs=procList; i<MAX_EVENT_FLAGS; i++, vs++) + { + if (vs->busy && vs->pid == pid) + return (vs); + } + return ((VMS_PROC_STUFF *)0); +} + +start_vms_process_read (vs) + VMS_PROC_STUFF *vs; +{ + /* + Start an asynchronous read on a VMS process + We will catch up with the output sooner or later + */ + int status; + int ProcAst (); + + status = sys$qio (vs->eventFlag, vs->outputChan, IO$_READVBLK, + vs->iosb, 0, vs, + vs->inputBuffer, sizeof (vs->inputBuffer), 0, 0, 0, 0); + if (status != SS$_NORMAL) + return (0); + else + return (1); +} + +extern int waiting_for_ast; /* in sysdep.c */ +extern int timer_ef; +extern int input_ef; + +select (nDesc, rdsc, wdsc, edsc, timeOut) + int nDesc; + int *rdsc; + int *wdsc; + int *edsc; + int *timeOut; +{ + /* Emulate a select call + + We know that we only use event flags 1-23 + + timeout == 100000 & bit 0 set means wait on keyboard input until + something shows up. If timeout == 0, we just read the event + flags and return what we find. */ + + int nfds = 0; + int status; + int time[2]; + int delta = -10000000; + int zero = 0; + int timeout = *timeOut; + unsigned long mask, readMask, waitMask; + + if (rdsc) + readMask = *rdsc << 1; /* Unix mask is shifted over 1 */ + else + readMask = 0; /* Must be a wait call */ + + sys$clref (KEYBOARD_EVENT_FLAG); + sys$setast (0); /* Block interrupts */ + sys$readef (KEYBOARD_EVENT_FLAG, &mask); /* See what is set */ + mask &= readMask; /* Just examine what we need */ + if (mask == 0) + { /* Nothing set, we must wait */ + if (timeout != 0) + { /* Not just inspecting... */ + if (!(timeout == 100000 && + readMask == (1 << KEYBOARD_EVENT_FLAG))) + { + lib$emul (&timeout, &delta, &zero, time); + sys$setimr (TIMER_EVENT_FLAG, time, 0, 1); + waitMask = readMask | (1 << TIMER_EVENT_FLAG); + } + else + waitMask = readMask; + if (waitMask & (1 << KEYBOARD_EVENT_FLAG)) + { + sys$clref (KEYBOARD_EVENT_FLAG); + waiting_for_ast = 1; /* Only if reading from 0 */ + } + sys$setast (1); + sys$wflor (KEYBOARD_EVENT_FLAG, waitMask); + sys$cantim (1, 0); + sys$readef (KEYBOARD_EVENT_FLAG, &mask); + if (readMask & (1 << KEYBOARD_EVENT_FLAG)) + waiting_for_ast = 0; + } + } + sys$setast (1); + + /* + Count number of descriptors that are ready + */ + mask &= readMask; + if (rdsc) + *rdsc = (mask >> 1); /* Back to Unix format */ + for (nfds = 0; mask; mask >>= 1) + { + if (mask & 1) + nfds++; + } + return (nfds); +} + +#define MAX_BUFF 1024 + +write_to_vms_process (vs, buf, len) + VMS_PROC_STUFF *vs; + char *buf; + int len; +{ + /* + Write something to a VMS process. + + We have to map newlines to carriage returns for VMS. + */ + char ourBuff[MAX_BUFF]; + short iosb[4]; + int status; + int in, out; + + while (len > 0) + { + out = map_nl_to_cr (buf, ourBuff, len, MAX_BUFF); + status = sys$qiow (0, vs->inputChan, IO$_WRITEVBLK|IO$M_NOFORMAT, + iosb, 0, 0, ourBuff, out, 0, 0, 0, 0); + if (status != SS$_NORMAL || (status = iosb[0]) != SS$_NORMAL) + { + error ("Could not write to subprocess: %x", status); + return (0); + } + len =- out; + } + return (1); +} + +static +map_nl_to_cr (in, out, maxIn, maxOut) + char *in; + char *out; + int maxIn; + int maxOut; +{ + /* + Copy `in' to `out' remapping `\n' to `\r' + */ + int c; + int o; + + for (o=0; maxIn-- > 0 && o < maxOut; o++) + { + c = *in++; + *out++ = (c == '\n') ? '\r' : c; + } + return (o); +} + +clean_vms_buffer (buf, len) + char *buf; + int len; +{ + /* + Sanitize output from a VMS subprocess + Strip CR's and NULLs + */ + char *oBuf = buf; + char c; + int l = 0; + + while (len-- > 0) + { + c = *buf++; + if (c == '\r' || c == '\0') + ; + else + { + *oBuf++ = c; + l++; + } + } + return (l); +} + +/* + For the CMU PTY driver +*/ +#define PTYNAME "PYA0:" + +get_pty_channel (inDevName, outDevName, inChannel, outChannel) + char *inDevName; + char *outDevName; + int *inChannel; + int *outChannel; +{ + int PartnerUnitNumber; + int status; + struct { + int l; + char *a; + } d; + struct { + short BufLen; + short ItemCode; + int *BufAddress; + int *ItemLength; + } g[2]; + + d.l = strlen (PTYNAME); + d.a = PTYNAME; + *inChannel = 0; /* Should be `short' on VMS */ + *outChannel = 0; + *inDevName = *outDevName = '\0'; + status = sys$assign (&d, inChannel, 0, 0); + if (status == SS$_NORMAL) + { + *outChannel = *inChannel; + g[0].BufLen = sizeof (PartnerUnitNumber); + g[0].ItemCode = DVI$_UNIT; + g[0].BufAddress = &PartnerUnitNumber; + g[0].ItemLength = (int *)0; + g[1].BufLen = g[1].ItemCode = 0; + status = sys$getdviw (0, *inChannel, 0, &g, 0, 0, 0, 0); + if (status == SS$_NORMAL) + { + sprintf (inDevName, "_TPA%d:", PartnerUnitNumber); + strcpy (outDevName, inDevName); + } + } + return (status); +} + +VMSgetwd (buf) + char *buf; +{ + /* + Return the current directory + */ + char curdir[256]; + char *getenv (); + char *s; + short len; + int status; + struct + { + int l; + char *a; + } d; + + s = getenv ("SYS$DISK"); + if (s) + strcpy (buf, s); + else + *buf = '\0'; + + d.l = 255; + d.a = curdir; + status = sys$setddir (0, &len, &d); + if (status & 1) + { + curdir[len] = '\0'; + strcat (buf, curdir); + } +} + +static +call_process_ast (vs) + VMS_PROC_STUFF *vs; +{ + sys$setef (vs->eventFlag); +} + +void +child_setup (in, out, err, new_argv, env) + int in, out, err; + register char **new_argv; + char **env; +{ + /* ??? I suspect that maybe this shouldn't be done on VMS. */ +#ifdef subprocesses + /* Close Emacs's descriptors that this process should not have. */ + close_process_descs (); +#endif + + if (XTYPE (current_buffer->directory) == Lisp_String) + chdir (XSTRING (current_buffer->directory)->data); +} + +DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0, + "Call PROGRAM synchronously in a separate process.\n\ +Program's input comes from file INFILE (nil means null device, `NLA0:').\n\ +Insert output in BUFFER before point; t means current buffer;\n\ + nil for BUFFER means discard it; 0 means discard and don't wait.\n\ +Fourth arg DISPLAY non-nil means redisplay buffer as output is inserted.\n\ +Remaining arguments are strings passed as command arguments to PROGRAM.\n\ +This function waits for PROGRAM to terminate, unless BUFFER is 0;\n\ +if you quit, the process is killed.") + (nargs, args) + int nargs; + register Lisp_Object *args; +{ + Lisp_Object display, buffer, path; + char oldDir[512]; + int inchannel, outchannel; + int len; + int call_process_ast (); + struct + { + int l; + char *a; + } dcmd, din, dout; + char inDevName[65]; + char outDevName[65]; + short iosb[4]; + int status; + int SpawnFlags = CLI$M_NOWAIT; + VMS_PROC_STUFF *vs; + VMS_PROC_STUFF *get_vms_process_stuff (); + int fd[2]; + int filefd; + register int pid; + char buf[1024]; + int count = specpdl_ptr - specpdl; + register unsigned char **new_argv; + struct buffer *old = current_buffer; + + CHECK_STRING (args[0], 0); + + if (nargs <= 1 || NULL (args[1])) + args[1] = build_string ("NLA0:"); + else + args[1] = Fexpand_file_name (args[1], current_buffer->directory); + + CHECK_STRING (args[1], 1); + + { + register Lisp_Object tem; + buffer = tem = args[2]; + if (nargs <= 2) + buffer = Qnil; + else if (!(EQ (tem, Qnil) || EQ (tem, Qt) + || XFASTINT (tem) == 0)) + { + buffer = Fget_buffer (tem); + CHECK_BUFFER (buffer, 2); + } + } + + display = nargs >= 3 ? args[3] : Qnil; + + { + /* + if (args[0] == "*dcl*" then we need to skip pas the "-c", + else args[0] is the program to run. + */ + register int i; + int arg0; + int firstArg; + + if (strcmp (XSTRING (args[0])->data, "*dcl*") == 0) + { + arg0 = 5; + firstArg = 6; + } + else + { + arg0 = 0; + firstArg = 4; + } + len = XSTRING (args[arg0])->size + 1; + for (i = firstArg; i < nargs; i++) + { + CHECK_STRING (args[i], i); + len += XSTRING (args[i])->size + 1; + } + new_argv = alloca (len); + strcpy (new_argv, XSTRING (args[arg0])->data); + for (i = firstArg; i < nargs; i++) + { + strcat (new_argv, " "); + strcat (new_argv, XSTRING (args[i])->data); + } + dcmd.l = len-1; + dcmd.a = new_argv; + + status = get_pty_channel (inDevName, outDevName, &inchannel, &outchannel); + if (!(status & 1)) + error ("Error getting PTY channel: %x", status); + if (XTYPE (buffer) == Lisp_Int) + { + dout.l = strlen ("NLA0:"); + dout.a = "NLA0:"; + } + else + { + dout.l = strlen (outDevName); + dout.a = outDevName; + } + + vs = get_vms_process_stuff (); + if (!vs) + { + sys$dassgn (inchannel); + sys$dassgn (outchannel); + error ("Too many VMS processes"); + } + vs->inputChan = inchannel; + vs->outputChan = outchannel; + } + + filefd = open (XSTRING (args[1])->data, O_RDONLY, 0); + if (filefd < 0) + { + sys$dassgn (inchannel); + sys$dassgn (outchannel); + give_back_vms_process_stuff (vs); + report_file_error ("Opening process input file", Fcons (args[1], Qnil)); + } + else + close (filefd); + + din.l = XSTRING (args[1])->size; + din.a = XSTRING (args[1])->data; + + /* + Start a read on the process channel + */ + if (XTYPE (buffer) != Lisp_Int) + { + start_vms_process_read (vs); + SpawnFlags = CLI$M_NOWAIT; + } + else + SpawnFlags = 0; + + /* + On VMS we need to change the current directory + of the parent process before forking so that + the child inherit that directory. We remember + where we were before changing. + */ + VMSgetwd (oldDir); + child_setup (0, 0, 0, 0, 0); + status = lib$spawn (&dcmd, &din, &dout, &SpawnFlags, 0, &vs->pid, + &vs->exitStatus, 0, call_process_ast, vs); + chdir (oldDir); + + if (status != SS$_NORMAL) + { + sys$dassgn (inchannel); + sys$dassgn (outchannel); + give_back_vms_process_stuff (vs); + error ("Error calling LIB$SPAWN: %x", status); + } + pid = vs->pid; + + if (XTYPE (buffer) == Lisp_Int) + { +#ifndef subprocesses + wait_without_blocking (); +#endif subprocesses + return Qnil; + } + + record_unwind_protect (call_process_cleanup, + Fcons (make_number (fd[0]), make_number (pid))); + + + if (XTYPE (buffer) == Lisp_Buffer) + Fset_buffer (buffer); + + immediate_quit = 1; + QUIT; + + while (1) + { + sys$waitfr (vs->eventFlag); + if (vs->iosb[0] & 1) + { + immediate_quit = 0; + if (!NULL (buffer)) + { + vs->iosb[1] = clean_vms_buffer (vs->inputBuffer, vs->iosb[1]); + InsCStr (vs->inputBuffer, vs->iosb[1]); + } + if (!NULL (display) && INTERACTIVE) + redisplay_preserve_echo_area (); + immediate_quit = 1; + QUIT; + if (!start_vms_process_read (vs)) + break; /* The other side went away */ + } + else + break; + } + sys$dassgn (inchannel); + sys$dassgn (outchannel); + give_back_vms_process_stuff (vs); + + /* Wait for it to terminate, unless it already has. */ + wait_for_termination (pid); + + immediate_quit = 0; + + set_current_buffer (old); + + unbind_to (count); + + return Qnil; +} + +create_process (process, new_argv) + Lisp_Object process; + char *new_argv; +{ + int pid, inchannel, outchannel, forkin, forkout; + char old_dir[512]; + char in_dev_name[65]; + char out_dev_name[65]; + short iosb[4]; + int status; + int spawn_flags = CLI$M_NOWAIT; + int child_sig (); + struct { + int l; + char *a; + } din, dout, dprompt, dcmd; + VMS_PROC_STUFF *vs; + VMS_PROC_STUFF *get_vms_process_stuff (); + + status = get_pty_channel (in_dev_name, out_dev_name, &inchannel, &outchannel); + if (!(status & 1)) + { + remove_process (process); + error ("Error getting PTY channel: %x", status); + } + dout.l = strlen (out_dev_name); + dout.a = out_dev_name; + dprompt.l = strlen (DCL_PROMPT); + dprompt.a = DCL_PROMPT; + + if (strcmp (new_argv, "*dcl*") == 0) + { + din.l = strlen (in_dev_name); + din.a = in_dev_name; + dcmd.l = 0; + dcmd.a = (char *)0; + } + else + { + din.l = strlen ("NLA0:"); + din.a = "NLA0:"; + dcmd.l = strlen (new_argv); + dcmd.a = new_argv; + } + + /* Delay interrupts until we have a chance to store + the new fork's pid in its process structure */ + sys$setast (0); + + vs = get_vms_process_stuff (); + if (vs == 0) + { + sys$setast (1); + remove_process (process); + error ("Too many VMS processes"); + } + vs->inputChan = inchannel; + vs->outputChan = outchannel; + + /* Start a read on the process channel */ + start_vms_process_read (vs); + + /* Switch current directory so that the child inherits it. */ + VMSgetwd (old_dir); + child_setup (0, 0, 0, 0, 0); + + status = lib$spawn (&dcmd, &din, &dout, &spawn_flags, 0, &vs->pid, + &vs->exitStatus, 0, child_sig, vs, &dprompt); + chdir (old_dir); + + if (status != SS$_NORMAL) + { + sys$setast (1); + remove_process (process); + error ("Error calling LIB$SPAWN: %x", status); + } + vs->pid &= 0xffff; /* It needs to fit in a FASTINT, + we don't need the rest of the bits */ + pid = vs->pid; + + /* + ON VMS process->infd holds the (event flag-1) + that we use for doing I/O on that process. + `input_wait_mask' is the cluster of event flags + we can wait on. + + Event flags returned start at 1 for the keyboard. + Since Unix expects descriptor 0 for the keyboard, + we substract one from the event flag. + */ + inchannel = vs->eventFlag-1; + + /* Record this as an active process, with its channels. + As a result, child_setup will close Emacs's side of the pipes. */ + chan_process[inchannel] = process; + XFASTINT (XPROCESS (process)->infd) = inchannel; + XFASTINT (XPROCESS (process)->outfd) = outchannel; + XFASTINT (XPROCESS (process)->flags) = RUNNING; + + /* Delay interrupts until we have a chance to store + the new fork's pid in its process structure */ + +#define NO_ECHO "set term/noecho\r" + sys$setast (0); + /* + Send a command to the process to not echo input + + The CMU PTY driver does not support SETMODEs. + */ + write_to_vms_process (vs, NO_ECHO, strlen (NO_ECHO)); + + XFASTINT (XPROCESS (process)->pid) = pid; + sys$setast (1); +} + +child_sig (vs) + VMS_PROC_STUFF *vs; +{ + register int pid; + Lisp_Object tail, proc; + register struct Lisp_Process *p; + int old_errno = errno; + + pid = vs->pid; + sys$setef (vs->eventFlag); + + for (tail = Vprocess_alist; XSYMBOL (tail) != XSYMBOL (Qnil); tail = XCONS (tail)->cdr) + { + proc = XCONS (XCONS (tail)->car)->cdr; + p = XPROCESS (proc); + if (EQ (p->childp, Qt) && XFASTINT (p->pid) == pid) + break; + } + + if (XSYMBOL (tail) == XSYMBOL (Qnil)) + return; + + child_changed++; + XFASTINT (p->flags) = EXITED | CHANGED; + /* Truncate the exit status to 24 bits so that it fits in a FASTINT */ + XFASTINT (p->reason) = (vs->exitStatus) & 0xffffff; +} + +syms_of_vmsproc () +{ + defsubr (&Scall_process); +} + +init_vmsproc () +{ + char *malloc (); + int i; + VMS_PROC_STUFF *vs; + + for (vs=procList, i=0; i<MAX_EVENT_FLAGS+1; i++, vs++) + { + vs->busy = 0; + vs->eventFlag = i; + sys$clref (i); + vs->inputChan = 0; + vs->pid = 0; + } + procList[0].busy = 1; /* Zero is reserved */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xmenu.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,378 @@ +/* X Communication module for terminals which understand the X protocol. + Copyright (C) 1986, 1988 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* X pop-up deck-of-cards menu facility for gnuemacs. + * + * Written by Jon Arnold and Roman Budzianowski + * Mods and rewrite by Robert Krawitz + * + */ + +/* $Source: /u2/third_party/gnuemacs.chow/src/RCS/xmenu.c,v $ + * $Author: rlk $ + * $Locker: $ + * $Header: xmenu.c,v 1.6 86/08/26 17:23:26 rlk Exp $ + * + */ + +#ifndef lint +static char *rcsid_GXMenu_c = "$Header: xmenu.c,v 1.6 86/08/26 17:23:26 rlk Exp $"; +#endif lint +#ifdef XDEBUG +#include <stdio.h> +#endif + +/* On 4.3 this loses if it comes after xterm.h. */ +#include <signal.h> +#include "config.h" +#include "lisp.h" +#include "screen.h" +#include "window.h" + +/* This may include sys/types.h, and that somehow loses + if this is not done before the other system files. */ +#include "xterm.h" + +/* Load sys/types.h if not already loaded. + In some systems loading it twice is suicidal. */ +#ifndef makedev +#include <sys/types.h> +#endif + +#include "dispextern.h" + +#ifdef HAVE_X11 +#include "../oldXMenu/XMenu.h" +#else +#include <X/XMenu.h> +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) +#define max(x,y) (((x) > (y)) ? (x) : (y)) + +#define NUL 0 + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif TRUE + +#ifdef HAVE_X11 +extern Display *x_current_display; +#else +#define ButtonReleaseMask ButtonReleased +#endif /* not HAVE_X11 */ + +Lisp_Object xmenu_show (); +extern int x_error_handler (); + +/*************************************************************/ + +#if 0 +/* Ignoring the args is easiest. */ +xmenu_quit () +{ + error ("Unknown XMenu error"); +} +#endif + +DEFUN ("x-popup-menu",Fx_popup_menu, Sx_popup_menu, 1, 2, 0, + "Pop up a deck-of-cards menu and return user's selection.\n\ +ARG is a position specification: a list ((XOFFSET YOFFSET) WINDOW)\n\ +where XOFFSET and YOFFSET are positions in characters from the top left\n\ +corner of WINDOW's screen. A mouse-event list will serve for this.\n\ +This controls the position of the center of the first line\n\ +in the first pane of the menu, not the top left of the menu as a whole.\n\ +\n\ +MENU is a specifier for a menu. It is a list of the form\n\ +\(TITLE PANE1 PANE2...), and each pane is a list of form\n\ +\(TITLE (LINE ITEM)...). Each line should be a string, and item should\n\ +be the return value for that line (i.e. if it is selected.") + (arg, menu) + Lisp_Object arg, menu; +{ + int number_of_panes; + Lisp_Object XMenu_return; + int XMenu_xpos, XMenu_ypos; + char **menus; + char ***names; + Lisp_Object **obj_list; + int *items; + char *title; + char *error_name; + Lisp_Object ltitle, selection; + int i, j; + SCREEN_PTR s; + Lisp_Object x, y, window; + + window = Fcar (Fcdr (arg)); + x = Fcar (Fcar (arg)); + y = Fcar (Fcdr (Fcar (arg))); + CHECK_WINDOW (window, 0); + CHECK_NUMBER (x, 0); + CHECK_NUMBER (y, 0); + s = XSCREEN (WINDOW_SCREEN (XWINDOW (window))); + + XMenu_xpos = FONT_WIDTH (s->display.x->font) * XINT (x); + XMenu_ypos = FONT_HEIGHT (s->display.x->font) * XINT (y); + XMenu_xpos += s->display.x->left_pos; + XMenu_ypos += s->display.x->top_pos; + + ltitle = Fcar (menu); + CHECK_STRING (ltitle, 1); + title = (char *) XSTRING (ltitle)->data; + number_of_panes=list_of_panes (&obj_list, &menus, &names, &items, Fcdr (menu)); +#ifdef XDEBUG + fprintf (stderr, "Panes= %d\n", number_of_panes); + for (i=0; i < number_of_panes; i++) + { + fprintf (stderr, "Pane %d lines %d title %s\n", i, items[i], menus[i]); + for (j=0; j < items[i]; j++) + { + fprintf (stderr, " Item %d %s\n", j, names[i][j]); + } + } +#endif + BLOCK_INPUT; + selection = xmenu_show (ROOT_WINDOW, XMenu_xpos, XMenu_ypos, names, menus, + items, number_of_panes, obj_list, title, &error_name); + UNBLOCK_INPUT; + /** fprintf (stderr, "selection = %x\n", selection); **/ + if (selection != NUL) + { /* selected something */ + XMenu_return = selection; + } + else + { /* nothing selected */ + XMenu_return = Qnil; + } + /* now free up the strings */ + for (i=0; i < number_of_panes; i++) + { + free (names[i]); + free (obj_list[i]); + } + free (menus); + free (obj_list); + free (names); + free (items); + /* free (title); */ + if (error_name) error (error_name); + return XMenu_return; +} + +struct indices { + int pane; + int line; +}; + +Lisp_Object +xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt, + pane_cnt, item_list, title, error) + Window parent; + int startx, starty; /* upper left corner position BROKEN */ + char **line_list[]; /* list of strings for items */ + char *pane_list[]; /* list of pane titles */ + char *title; + int pane_cnt; /* total number of panes */ + Lisp_Object *item_list[]; /* All items */ + int line_cnt[]; /* Lines in each pane */ + char **error; /* Error returned */ +{ + XMenu *GXMenu; + int last, panes, selidx, lpane, status; + int lines, sofar; + Lisp_Object entry; + /* struct indices *datap, *datap_save; */ + char *datap; + int ulx, uly, width, height; + int dispwidth, dispheight; + + *error = (char *) 0; /* Initialize error pointer to null */ + GXMenu = XMenuCreate (XDISPLAY parent, "emacs"); + if (GXMenu == NUL) + { + *error = "Can't create menu"; + return (0); + } + + for (panes=0, lines=0; panes < pane_cnt; lines += line_cnt[panes], panes++) + ; + /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */ + /*datap = (char *) xmalloc (lines * sizeof (char)); + datap_save = datap;*/ + + for (panes = 0, sofar=0;panes < pane_cnt;sofar +=line_cnt[panes], panes++) + { + /* create all the necessary panes */ + lpane = XMenuAddPane (XDISPLAY GXMenu, pane_list[panes], TRUE); + if (lpane == XM_FAILURE) + { + XMenuDestroy (XDISPLAY GXMenu); + *error = "Can't create pane"; + return (0); + } + for (selidx = 0; selidx < line_cnt[panes] ; selidx++) + { + /* add the selection stuff to the menus */ + /* datap[selidx+sofar].pane = panes; + datap[selidx+sofar].line = selidx; */ + if (XMenuAddSelection (XDISPLAY GXMenu, lpane, 0, + line_list[panes][selidx], TRUE) + == XM_FAILURE) + { + XMenuDestroy (XDISPLAY GXMenu); + /* free (datap); */ + *error = "Can't add selection to menu"; + /* error ("Can't add selection to menu"); */ + return (0); + } + } + } + /* all set and ready to fly */ + XMenuRecompute (XDISPLAY GXMenu); + dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display)); + dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display)); + startx = min (startx, dispwidth); + starty = min (starty, dispheight); + startx = max (startx, 1); + starty = max (starty, 1); + XMenuLocate (XDISPLAY GXMenu, 0, 0, startx, starty, + &ulx, &uly, &width, &height); + if (ulx+width > dispwidth) + { + startx -= (ulx + width) - dispwidth; + ulx = dispwidth - width; + } + if (uly+height > dispheight) + { + starty -= (uly + height) - dispheight; + uly = dispheight - height; + } + if (ulx < 0) startx -= ulx; + if (uly < 0) starty -= uly; + + XMenuSetFreeze (GXMenu, TRUE); + panes = selidx = 0; + + status = XMenuActivate (XDISPLAY GXMenu, &panes, &selidx, + startx, starty, ButtonReleaseMask, &datap); + switch (status) + { + case XM_SUCCESS: +#ifdef XDEBUG + fprintf (stderr, "pane= %d line = %d\n", panes, selidx); +#endif + entry = item_list[panes][selidx]; + break; + case XM_FAILURE: + /*free (datap_save); */ + XMenuDestroy (XDISPLAY GXMenu); + *error = "Can't activate menu"; + /* error ("Can't activate menu"); */ + case XM_IA_SELECT: + case XM_NO_SELECT: + entry = Qnil; + break; + } + XMenuDestroy (XDISPLAY GXMenu); + /*free (datap_save);*/ + return (entry); +} + +syms_of_xmenu () +{ + defsubr (&Sx_popup_menu); +} + +list_of_panes (vector, panes, names, items, menu) + Lisp_Object ***vector; /* RETURN all menu objects */ + char ***panes; /* RETURN pane names */ + char ****names; /* RETURN all line names */ + int **items; /* RETURN number of items per pane */ + Lisp_Object menu; +{ + Lisp_Object tail, item, item1; + int i; + + if (XTYPE (menu) != Lisp_Cons) menu = wrong_type_argument (Qlistp, menu); + + i= XFASTINT (Flength (menu, 1)); + + *vector = (Lisp_Object **) xmalloc (i * sizeof (Lisp_Object *)); + *panes = (char **) xmalloc (i * sizeof (char *)); + *items = (int *) xmalloc (i * sizeof (int)); + *names = (char ***) xmalloc (i * sizeof (char **)); + + for (i=0, tail = menu; !NULL (tail); tail = Fcdr (tail), i++) + { + item = Fcdr (Fcar (tail)); + if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item); +#ifdef XDEBUG + fprintf (stderr, "list_of_panes check tail, i=%d\n", i); +#endif + item1 = Fcar (Fcar (tail)); + CHECK_STRING (item1, 1); +#ifdef XDEBUG + fprintf (stderr, "list_of_panes check pane, i=%d%s\n", i, + XSTRING (item1)->data); +#endif + (*panes)[i] = (char *) XSTRING (item1)->data; + (*items)[i] = list_of_items ((*vector)+i, (*names)+i, item); + /* (*panes)[i] = (char *) xmalloc ((XSTRING (item1)->size)+1); + bcopy (XSTRING (item1)->data, (*panes)[i], XSTRING (item1)->size + 1) + ; */ + } + return i; +} + + +list_of_items (vector, names, pane) /* get list from emacs and put to vector */ + Lisp_Object **vector; /* RETURN menu "objects" */ + char ***names; /* RETURN line names */ + Lisp_Object pane; +{ + Lisp_Object tail, item, item1; + int i; + + if (XTYPE (pane) != Lisp_Cons) pane = wrong_type_argument (Qlistp, pane); + + i= XFASTINT (Flength (pane, 1)); + + *vector = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object)); + *names = (char **) xmalloc (i * sizeof (char *)); + + for (i=0, tail = pane; !NULL (tail); tail = Fcdr (tail), i++) + { + item = Fcar (tail); + if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item); +#ifdef XDEBUG + fprintf (stderr, "list_of_items check tail, i=%d\n", i); +#endif + (*vector)[i] = Fcdr (item); + item1 = Fcar (item); + CHECK_STRING (item1, 1); +#ifdef XDEBUG + fprintf (stderr, "list_of_items check item, i=%d%s\n", i, + XSTRING (item1)->data); +#endif + (*names)[i] = (char *) XSTRING (item1)->data; + } + return i; +}