diff options
author | Marc Espie <espie@cvs.openbsd.org> | 2000-02-09 01:27:25 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 2000-02-09 01:27:25 +0000 |
commit | 328f35a635ee7b14c4d6f2dbddacba138e62219b (patch) | |
tree | 5f0effaeb304116c7826d814dfa001d501a3b10a /gnu/usr.bin/texinfo/util | |
parent | c2daa957909e4243f84a2232ff4a481b6c44a393 (diff) |
TexInfo 4.0. New manpages, can create html.
A bit more grumpy about bad texinfo, though.
Diffstat (limited to 'gnu/usr.bin/texinfo/util')
-rw-r--r-- | gnu/usr.bin/texinfo/util/README | 6 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/deref.c | 317 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/fix-info-dir | 317 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/fixref.gawk | 145 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/outline.gawk | 145 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/prepinfo.awk | 356 | ||||
-rw-r--r-- | gnu/usr.bin/texinfo/util/texi-docstring-magic.el | 347 |
7 files changed, 1460 insertions, 173 deletions
diff --git a/gnu/usr.bin/texinfo/util/README b/gnu/usr.bin/texinfo/util/README index 5385522543b..20d7bdafd86 100644 --- a/gnu/usr.bin/texinfo/util/README +++ b/gnu/usr.bin/texinfo/util/README @@ -2,3 +2,9 @@ Assorted Texinfo-related programs and scripts. texindex, texi2dvi, and install-info get installed. The other items here are for your amusement and/or hacking pleasure. + +You may also be interested in a2ps, an ASCII->PostScript program which +has a Texinfo style option. Available from +http://www.inf.enst.fr/~demaille/a2ps/ and +ftp://ftp.enst.fr/pub/unix/a2ps/. + diff --git a/gnu/usr.bin/texinfo/util/deref.c b/gnu/usr.bin/texinfo/util/deref.c index c15bc1abcf1..e08be48105f 100644 --- a/gnu/usr.bin/texinfo/util/deref.c +++ b/gnu/usr.bin/texinfo/util/deref.c @@ -1,44 +1,28 @@ /* * deref.c - - * compile command: gcc -g -o deref deref.c - - * execute command: deref filename.texi > newfile.texi - - * To: bob@gnu.ai.mit.edu - * Subject: another tool - * Date: 18 Dec 91 16:03:13 EST (Wed) - * From: gatech!skeeve!arnold@eddie.mit.edu (Arnold D. Robbins) - * - * Here is deref.c. It turns texinfo cross references back into the - * one argument form. It has the same limitations as fixref; one xref per - * line and can't cross lines. You can use it to find references that do - * cross a line boundary this way: - * - * deref < manual > /dev/null 2>errs - * - * (This assumes bash or /bin/sh.) The file errs will have list of lines - * where deref could not find matching braces. - * - * A gawk manual processed by deref goes through makeinfo without complaint. - * Compile with gcc and you should be set. - * - * Enjoy, - * - * Arnold - * ----------- - */ - -/* - * deref.c * * Make all texinfo references into the one argument form. - * + * * Arnold Robbins - * arnold@skeeve.atl.ga.us - * December, 1991 + * arnold@gnu.org + * Written: December, 1991 + * Released: November, 1998 * - * Copyright, 1991, Arnold Robbins + * Copyright, 1991, 1998 Arnold David Robbins + * + * DEREF is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * DEREF 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* @@ -53,17 +37,19 @@ #include <errno.h> /* for gcc on the 3B1, delete if this gives you grief */ -extern int fclose (FILE * fp); -extern int fprintf (FILE * fp, const char *str,...); +extern int fclose(FILE *fp); +extern int fprintf(FILE *fp, const char *str, ...); +/* extern int sprintf(char *str, const char *fmt, ...); */ +extern int fputs(char *buf, FILE *fp); -extern char *strerror (int errno); -extern char *strchr (char *cp, int ch); -extern int strncmp (const char *s1, const char *s2, int count); +extern char *strerror(int errno); +extern char *strchr(char *cp, int ch); +extern int strncmp(const char *s1, const char *s2, int count); extern int errno; -void process (FILE * fp); -void repair (char *line, char *ref, int toffset); +void process(FILE *fp); +void repair(char *line, char *ref, int toffset); int Errs = 0; char *Name = "stdin"; @@ -73,166 +59,151 @@ char *Me; /* main --- handle arguments, global vars for errors */ int -main (int argc, char **argv) +main(int argc, char **argv) { - FILE *fp; - - Me = argv[0]; - - if (argc == 1) - process (stdin); - else - for (argc--, argv++; *argv != NULL; argc--, argv++) - { - if (argv[0][0] == '-' && argv[0][1] == '\0') - { - Name = "stdin"; - Line = 0; - process (stdin); - } - else if ((fp = fopen (*argv, "r")) != NULL) - { - Name = *argv; - Line = 0; - process (fp); - fclose (fp); - } + FILE *fp; + + Me = argv[0]; + + if (argc == 1) + process(stdin); else - { - fprintf (stderr, "%s: can not open: %s\n", - *argv, strerror (errno)); - Errs++; - } - } - return Errs != 0; + for (argc--, argv++; *argv != NULL; argc--, argv++) { + if (argv[0][0] == '-' && argv[0][1] == '\0') { + Name = "stdin"; + Line = 0; + process(stdin); + } else if ((fp = fopen(*argv, "r")) != NULL) { + Name = *argv; + Line = 0; + process(fp); + fclose(fp); + } else { + fprintf(stderr, "%s: can not open: %s\n", + *argv, strerror(errno)); + Errs++; + } + } + return Errs != 0; } /* isref --- decide if we've seen a texinfo cross reference */ int -isref (char *cp) +isref(char *cp) { - if (strncmp (cp, "@ref{", 5) == 0) - return 5; - if (strncmp (cp, "@xref{", 6) == 0) - return 6; - if (strncmp (cp, "@pxref{", 7) == 0) - return 7; - return 0; + if (strncmp(cp, "@ref{", 5) == 0) + return 5; + if (strncmp(cp, "@xref{", 6) == 0) + return 6; + if (strncmp(cp, "@pxref{", 7) == 0) + return 7; + return 0; } /* process --- read files, look for references, fix them up */ void -process (FILE * fp) +process(FILE *fp) { - char buf[BUFSIZ]; - char *cp; - int count; - - while (fgets (buf, sizeof buf, fp) != NULL) - { - Line++; - cp = strchr (buf, '@'); - if (cp == NULL) - { - fputs (buf, stdout); - continue; - } - do - { - count = isref (cp); - if (count == 0) - { - cp++; - cp = strchr (cp, '@'); - if (cp == NULL) - { - fputs (buf, stdout); - goto next; + char buf[BUFSIZ]; + char *cp; + int count; + + while (fgets(buf, sizeof buf, fp) != NULL) { + Line++; + cp = strchr(buf, '@'); + if (cp == NULL) { + fputs(buf, stdout); + continue; } - continue; - } - /* got one */ - repair (buf, cp, count); - break; + do { + count = isref(cp); + if (count == 0) { + cp++; + cp = strchr(cp, '@'); + if (cp == NULL) { + fputs(buf, stdout); + goto next; + } + continue; + } + /* got one */ + repair(buf, cp, count); + break; + } while (cp != NULL); + next: ; } - while (cp != NULL); - next:; - } } /* repair --- turn all texinfo cross references into the one argument form */ void -repair (char *line, char *ref, int toffset) +repair(char *line, char *ref, int toffset) { - int braces = 1; /* have seen first left brace */ - char *cp; - - ref += toffset; - - /* output line up to and including left brace in reference */ - for (cp = line; cp <= ref; cp++) - putchar (*cp); - - /* output node name */ - for (; *cp && *cp != '}' && *cp != ',' && *cp != '\n'; cp++) - putchar (*cp); - - if (*cp != '}') - { /* could have been one arg xref */ - /* skip to matching right brace */ - for (; braces > 0; cp++) - { - switch (*cp) - { - case '@': - cp++; /* blindly skip next character */ - break; - case '{': - braces++; - break; - case '}': - braces--; - break; - case '\n': - case '\0': - Errs++; - fprintf (stderr, - "%s: %s: %d: mismatched braces\n", - Me, Name, Line); - goto out; - default: - break; - } + int braces = 1; /* have seen first left brace */ + char *cp; + + ref += toffset; + + /* output line up to and including left brace in reference */ + for (cp = line; cp <= ref; cp++) + putchar(*cp); + + /* output node name */ + for (; *cp && *cp != '}' && *cp != ',' && *cp != '\n'; cp++) + putchar(*cp); + + if (*cp != '}') { /* could have been one arg xref */ + /* skip to matching right brace */ + for (; braces > 0; cp++) { + switch (*cp) { + case '@': + cp++; /* blindly skip next character */ + break; + case '{': + braces++; + break; + case '}': + braces--; + break; + case '\n': + case '\0': + Errs++; + fprintf(stderr, + "%s: %s: %d: mismatched braces\n", + Me, Name, Line); + goto out; + default: + break; + } + } + out: + ; } - out: - ; - } - - putchar ('}'); - if (*cp == '}') - cp++; - - /* now the rest of the line */ - for (; *cp; cp++) - putchar (*cp); - return; + + putchar('}'); + if (*cp == '}') + cp++; + + /* now the rest of the line */ + for (; *cp; cp++) + putchar(*cp); + return; } /* strerror --- return error string, delete if in your library */ char * -strerror (int errno) +strerror(int errno) { - static char buf[100]; - extern int sys_nerr; - extern char *sys_errlist[]; + static char buf[100]; + extern int sys_nerr; + extern char *sys_errlist[]; - if (errno < sys_nerr && errno >= 0) - return sys_errlist[errno]; + if (errno < sys_nerr && errno >= 0) + return sys_errlist[errno]; - sprintf (buf, "unknown error %d", errno); - return buf; + sprintf(buf, "unknown error %d", errno); + return buf; } diff --git a/gnu/usr.bin/texinfo/util/fix-info-dir b/gnu/usr.bin/texinfo/util/fix-info-dir new file mode 100644 index 00000000000..bf0901caa30 --- /dev/null +++ b/gnu/usr.bin/texinfo/util/fix-info-dir @@ -0,0 +1,317 @@ +#!/bin/sh +#fix-info-dir (GNU texinfo) +VERSION=1.1 +#Copyright (C) 1998 Free Software Foundation, Inc. +#fix-info-dir comes with NO WARRANTY, to the extent permitted by law. +#You may redistribute copies of fix-info-dir +#under the terms of the GNU General Public License. +#For more information about these matters, see the files named COPYING." +#fix-info-dir was derived from update-info and gen-dir-node +# The skeleton file contains info topic names in the +# order they should appear in the output. There are three special +# lines that alter the behavior: a line consisting of just "--" causes +# the next line to be echoed verbatim to the output. A line +# containing just "%%" causes all the remaining filenames (wildcards +# allowed) in the rest of the file to be ignored. A line containing +# just "!!" exits the script when reached (unless preceded by a line +# containing just "--"). +#Author: Richard L. Hawes, rhawes@dmapub.dma.org. + +# ###SECTION 1### Constants +set -h 2>/dev/null +# ENVIRONMENT +if test -z "$TMPDIR"; then + TMPDIR="/usr/tmp" +fi +if test -z "$LINENO"; then + LINENO="0" +fi + +MENU_BEGIN='^\*\([ ]\)\{1,\}Menu:' +MENU_ITEM='^\* ([^ ]).*:([ ])+\(' +MENU_FILTER1='s/^\*\([ ]\)\{1,\}/* /' +MENU_FILTER2='s/\([ ]\)\{1,\}$//g' + +TMP_FILE1="${TMPDIR}/fx${$}.info" +TMP_FILE2="${TMPDIR}/fy${$}.info" +TMP_FILE_LIST="$TMP_FILE1 $TMP_FILE2" + +TRY_HELP_MSG="Try --help for more information" + +# ###SECTION 100### main program +#variables set by options +CREATE_NODE="" +DEBUG=":" +MODE="" +# +Total="0" +Changed="" + +while test "$*"; do + case "$1" in + -c|--create) CREATE_NODE="y";; + --debug) set -eux; DEBUG="set>&2";; + -d|--delete) MODE="Detect_Invalid";; + +d);; + --version) +cat<<VersionEOF +fix-info-dir (GNU Texinfo) $VERSION +Copyright (C) 1998 Free Software Foundation, Inc. +fix-info-dir comes with NO WARRANTY, to the extent permitted by law. +You may redistribute copies of fix-info-dir +under the terms of the GNU General Public License. +For more information about these matters, see the files named COPYING. +Author: Richard L. Hawes +VersionEOF + exit;; + + --help) +cat<<HelpEndOfFile +Usage: fix-info-dir [OPTION]... [INFO_DIR/[DIR_FILE]] [SKELETON] + +It detects and inserts missing menu items into the info dir file. +The info dir must be the current directory. + +Options: +-c, --create create a new info node +-d, --delete delete invalid menu items (ignore missing menu items) + --debug print debug information to standard error path + --help print this help message and exit + --version print current version and exit +Backup of the info node has a '.old' suffix added. This is a shell script. +Environment Variables: TMPDIR +Email bug reports to bug-texinfo@gnu.org. +HelpEndOfFile + exit;; + + [-+]*) echo "$0:$LINENO: \"$1\" is not a valid option">&2 + echo "$TRY_HELP_MSG">&2 + exit 2;; + *) break;; + esac + shift +done + +ORIGINAL_DIR=`pwd` + +if test "$#" -gt "0"; then + INFO_DIR="$1" + shift +else + INFO_DIR=$DEFAULT_INFO_DIR +fi + +if test ! -d "${INFO_DIR}"; then + DIR_FILE=`basename ${INFO_DIR}`; + INFO_DIR=`dirname ${INFO_DIR}`; +else + DIR_FILE="dir" +fi + +cd "$INFO_DIR"||exit + + +if test "$CREATE_NODE"; then + if test "$#" -gt "0"; then + if test `expr $1 : /` = '1'; then + SKELETON="$1" + else + SKELETON="$ORIGINAL_DIR/$1" + fi + if test ! -r "$SKELETON" -a -f "$SKELETON"; then + echo "$0:$LINENO: $SKELETON is not readable">&2 + exit 2 + fi + shift + else + SKELETON=/dev/null + + fi +else + if test ! -f "$DIR_FILE"; then + echo "$0:$LINENO: $DIR_FILE is irregular or nonexistant">&2 + exit 2 + elif test ! -r "$DIR_FILE"; then + echo "$0:$LINENO: $DIR_FILE is not readable">&2 + exit 2 + elif test ! -w "$DIR_FILE"; then + echo "$0:$LINENO: $DIR_FILE is not writeable">&2 + exit 2 + fi +fi + +if test "$#" -gt "0"; then + echo "$0:$LINENO: Too many parameters">&2 + echo "$TRY_HELP_MSG">&2 + exit 2 +fi + +if test -f "$DIR_FILE"; then + cp "$DIR_FILE" "$DIR_FILE.old" + echo "Backed up $DIR_FILE to $DIR_FILE.old." +fi + +if test "$CREATE_NODE"; then + if test "$MODE"; then + echo "$0:$LINENO: ERROR: Illogical option combination: -d -c">&2 + echo "$TRY_HELP_MSG">&2 + exit 2 + fi + echo "Creating new Info Node: `pwd`/$DIR_FILE" + Changed="y" + +{ + + ### output the dir header + echo "-*- Text -*-" + echo "This file was generated automatically by $0." + echo "This version was generated on `date`" + echo "by `whoami`@`hostname` for `pwd`" + + cat<<DIR_FILE_END_OF_FILE +This is the file .../info/$DIR_FILE, which contains the topmost node of the +Info hierarchy. The first time you invoke Info you start off +looking at that node, which is ($DIR_FILE)Top. + + +File: $DIR_FILE Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "?" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs<Return>" visits the Emacs topic, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: The list of major topics begins on the next line. + +DIR_FILE_END_OF_FILE + +### go through the list of files in the skeleton. If an info file +### exists, grab the ENTRY information from it. If an entry exists +### use it, otherwise create a minimal $DIR_FILE entry. + + # Read one line from the file. This is so that we can echo lines with + # whitespace and quoted characters in them. + while read fileline; do + # flag fancy features + if test ! -z "$echoline"; then # echo line + echo "$fileline" + echoline="" + continue + elif test "${fileline}" = "--"; then + # echo the next line + echoline="1" + continue + elif test "${fileline}" = "%%"; then + # skip remaining files listed in skeleton file + skip="1" + continue + elif test "${fileline}" = "!!"; then + # quit now + break + fi + + # handle files if they exist + for file in $fileline""; do + fname= + if test -z "$file"; then + break + fi + # Find the file to operate upon. + if test -r "$file"; then + fname="$file" + elif test -r "${file}.info"; then + fname="${file}.info" + elif test -r "${file}.gz"; then + fname="${file}.gz" + elif test -r "${file}.info.gz"; then + fname="${file}.info.gz" + else + echo "$0:$LINENO: can't find info file for ${file}?">&2 + continue + fi + + # if we found something and aren't skipping, do the entry + if test "$skip"; then + continue + fi + + infoname=`echo $file|sed -e 's/.info$//'` + entry=`zcat -f $fname|\ + sed -e '1,/START-INFO-DIR-ENTRY/d'\ + -e '/END-INFO-DIR-ENTRY/,$d'` + if [ ! -z "${entry}" ]; then + echo "${entry}" + else + echo "* ${infoname}: (${infoname})." + fi + Total=`expr "$Total" + "1"` + done + done +}>$DIR_FILE<$SKELETON +fi + +trap ' eval "$DEBUG"; rm -f $TMP_FILE_LIST; exit ' 0 +trap ' rm -f $TMP_FILE_LIST + exit ' 1 +trap ' rm -f $TMP_FILE_LIST + echo "$0:$LINENO: received INT signal.">&2 + exit ' 2 +trap ' rm -f $TMP_FILE_LIST + echo "$0:$LINENO: received QUIT signal.">&2 + exit ' 3 + +sed -e "1,/$MENU_BEGIN/d" -e "$MENU_FILTER1" -e "$MENU_FILTER2"<$DIR_FILE\ +|sed -n -e '/\* /{ +s/).*$//g +s/\.gz$// +s/\.info$// +s/^.*(//p +}'|sort -u>$TMP_FILE1 +ls -F|sed -e '/\/$/d' -e '/[-.][0-9]/d'\ + -e "/^$DIR_FILE\$/d" -e "/^$DIR_FILE.old\$/d"\ + -e 's/[*@]$//' -e 's/\.gz$//' -e 's/\.info$//'|sort>$TMP_FILE2 + +if test -z "$MODE"; then + #Detect Missing + DONE_MSG="total menu item(s) were inserted into `pwd`/$DIR_FILE" + for Info_Name in `comm -13 $TMP_FILE1 $TMP_FILE2`; do + if test -r "$Info_Name"; then + Info_File="$Info_Name" + elif test -r "${Info_Name}.info"; then + Info_File="${Info_Name}.info" + elif test -r "${Info_Name}.gz"; then + Info_File="${Info_Name}.gz" + elif test -r "${Info_Name}.info.gz"; then + Info_File="${Info_Name}.info.gz" + else + echo "$0:$LINENO: can't find info file for ${Info_Name}?">&2 + continue + fi + Changed="y" + if install-info $Info_File $DIR_FILE; then + Total=`expr "$Total" + "1"` + fi + done +else + # Detect Invalid + DONE_MSG="total invalid menu item(s) were removed from `pwd`/$DIR_FILE" + for Info_Name in `comm -23 $TMP_FILE1 $TMP_FILE2`; do + Changed="y" + if install-info --remove $Info_Name $DIR_FILE; then + Total=`expr "$Total" + "1"` + fi + done +fi + +# print summary +if test "$Changed"; then + echo "$Total $DONE_MSG" +else + echo "Nothing to do" +fi +rm -f $TMP_FILE_LIST +eval "$DEBUG" +exit 0 diff --git a/gnu/usr.bin/texinfo/util/fixref.gawk b/gnu/usr.bin/texinfo/util/fixref.gawk new file mode 100644 index 00000000000..f0e3cf6d606 --- /dev/null +++ b/gnu/usr.bin/texinfo/util/fixref.gawk @@ -0,0 +1,145 @@ +#! /usr/local/bin/gawk -f + +# fixref.awk --- fix xrefs in texinfo documents +# Copyright, 1991, Arnold David Robbins, arnold@skeeve.atl.ga.us +# Copyright, 1998, Arnold David Robbins, arnold@gnu.org + +# FIXREF is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# FIXREF 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +# Updated: Jul 21 1992 --- change unknown +# Updated: Jul 18 1997 --- bug fix + +# usage: gawk -f fixref.awk input-file > output-file +# or if you have #!: fixref.awk input-file > output-file + +# Limitations: +# 1. no more than one cross reference on a line +# 2. cross references may not cross a newline + +BEGIN \ +{ + # we make two passes over the file. To do that we artificially + # tweak the argument vector to do a variable assignment + + if (ARGC != 2) { + printf("usage: %s texinfo-file\n", ARGV[0]) > "/dev/stderr" + exit 1 + } + ARGV[2] = "pass=2" + ARGV[3] = ARGV[1] + ARGC = 4 + + # examine paragraphs + RS = "" + + heading = "@(chapter|appendix|unnumbered|(appendix(sec|subsec|subsubsec))|section|subsection|subsubsection|unnumberedsec|heading|top)" + + pass = 1 + + # put space between paragraphs on output + ORS = "\n\n" +} + +pass == 1 && NF == 0 { next } + +# pass == 1 && /@node/ \ +# bug fix 7/18/96 +pass == 1 && /^@node/ \ +{ + lname = name = "" + n = split($0, lines, "\n") + for (i = 1; i <= n; i++) { + if (lines[i] ~ ("^" heading)) { + sub(heading, "", lines[i]) + sub(/^[ \t]*/, "", lines[i]) + lname = lines[i] +# printf "long name is '%s'\n", lines[i] + } else if (lines[i] ~ /@node/) { + sub(/@node[ \t]*/, "", lines[i]) + sub(/[ \t]*,.*$/, "", lines[i]) + name = lines[i] +# printf "node name is '%s'\n", lines[i] + } + } + if (name && lname) + names[name] = lname + else if (lname) + printf("node name for %s missing!\n", lname) > "/dev/stderr" + else + printf("long name for %s missing!\n", name) > "/dev/stderr" + + if (name ~ /:/) + printf("node `%s' contains a `:'\n", name) > "/dev/stderr" + + if (lname) { + if (lname ~ /:/) + printf("name `%s' contains a `:'\n", lname) > "/dev/stderr" + else if (lname ~ /,/) { + printf("name `%s' contains a `,'\n", lname) > "/dev/stderr" + gsub(/,/, " ", lname) + names[name] = lname # added 7/18/97 + } + } +} + +pass == 2 && /@(x|px)?ref{/ \ +{ + # split the paragraph into lines + # write them out one by one after fixing them + n = split($0, lines, "\n") + for (i = 1; i <= n; i++) + if (lines[i] ~ /@(x|px)?ref{/) { + res = updateref(lines[i]) + printf "%s\n", res + } else + printf "%s\n", lines[i] + + printf "\n" # avoid ORS + next +} + +function updateref(orig, refkind, line) +{ + line = orig # work on a copy + + # find the beginning of the reference + match(line, "@(x|px)?ref{") + refkind = substr(line, RSTART, RLENGTH) + + # pull out just the node name + sub(/.*ref{/, "", line) + sub(/}.*$/, "", line) + sub(/,.*/, "", line) + +# debugging +# printf("found ref to node '%s'\n", line) > "/dev/stderr" + + # If the node name and the section name are the same + # we don't want to bother doing this. + + if (! (line in names)) # sanity checking + printf("no long name for %s\n", line) > "/dev/stderr" + else if (names[line] != line && names[line] !~ /[:,]/) { + # build up new ref + newref = refkind line ", ," names[line] "}" + pat = refkind line "[^}]*}" + + sub(pat, newref, orig) + } + + return orig +} + +pass == 2 { print } diff --git a/gnu/usr.bin/texinfo/util/outline.gawk b/gnu/usr.bin/texinfo/util/outline.gawk new file mode 100644 index 00000000000..329bb17529a --- /dev/null +++ b/gnu/usr.bin/texinfo/util/outline.gawk @@ -0,0 +1,145 @@ +#! /usr/local/bin/gawk -f + +# texi.outline --- produce an outline from a texinfo source file +# +# Copyright (C) 1998 Arnold David Robbins (arnold@gnu.org) +# +# TEXI.OUTLINE is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# TEXI.OUTLINE 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +# NOTE: +# This program uses gensub(), which is specific to gawk. +# With some work (split, substr, etc), it could be made to work +# on other awks, but it's not worth the trouble for me. + +BEGIN \ +{ + # Levels at which different nodes can be + Level["@top"] = 0 + Level["@appendix"] = 1 + Level["@chapter"] = 1 + Level["@majorheading"] = 1 + Level["@unnumbered"] = 1 + Level["@appendixsec"] = 2 + Level["@heading"] = 2 + Level["@section"] = 2 + Level["@unnumberedsec"] = 2 + Level["@unnumberedsubsec"] = 3 + Level["@appendixsubsec"] = 3 + Level["@subheading"] = 3 + Level["@subsection"] = 3 + Level["@appendixsubsubsec"] = 4 + Level["@subsubheading"] = 4 + Level["@subsubsection"] = 4 + Level["@unnumberedsubsubsec"] = 4 + + # insure that we were called correctly + if (ARGC != 2) { + printf("usage: %s texinfo-file\n", ARGV[0]) > "/dev/stderr" + exit 1 + } + + # init header counters + app_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + app_h = 0 + l1_h = l2_h = l3_h = l4_h = 0 +} + +# skip lines we're not interested in +/^[^@]/ || ! ($1 in Level) { next } + +Level[$1] == 1 { + if ($1 !~ /^@unnumbered/ || $1 !~ /heading/) + l1_h++ + l2_h = l3_h = l4_h = 0 + Ntabs = 0 + Number = makenumber($1) + Title = maketitle($0) + print_title() +} + +Level[$1] == 2 { + l2_h++ + l3_h = l4_h = 0 + Ntabs = 1 + Number = makenumber($1) + Title = maketitle($0) + print_title() +} + +Level[$1] == 3 { + l3_h++ + l4_h = 0 + Ntabs = 2 + Number = makenumber($1) + Title = maketitle($0) + print_title() +} + +Level[$1] == 4 { + l4_h++ + Ntabs = 3 + Number = makenumber($1) + Title = maketitle($0) + print_title() +} + +# maketitle --- extract title + +function maketitle(str, text) +{ + $1 = "" # clobber section keyword + text = $0 + gsub(/^[ \t]*/, "", text) + text = gensub(/@[a-z]+{/, "", "g", text) + text = gensub(/([^@])}/, "\\1", "g", text) + return text +} + +# print_title --- print the title + +function print_title( i) +{ + for (i = 1; i <= Ntabs; i++) + printf "\t" + printf("%s %s\n", Number, Title) +} + +# makenumber --- construct a heading number from levels and section command + +function makenumber(command, result, lev1) +{ + result = "" + if (command ~ /^@appendix/) { + if (Level[command] == 1) + app_h++ + + lev1 = substr(app_letters, app_h, 1) + } else if (command ~ /^@unnumbered/ || command ~ /heading/) { + lev1 = "(unnumbered)" + } else + lev1 = l1_h "" + + result = lev1 "." + if (l2_h > 0) { + result = result l2_h "." + if (l3_h > 0) { + result = result l3_h "." + if (l4_h > 0) { + result = result l4_h "." + } + } + } + return result +} diff --git a/gnu/usr.bin/texinfo/util/prepinfo.awk b/gnu/usr.bin/texinfo/util/prepinfo.awk new file mode 100644 index 00000000000..273c97f8460 --- /dev/null +++ b/gnu/usr.bin/texinfo/util/prepinfo.awk @@ -0,0 +1,356 @@ +#! /usr/local/bin/gawk -f + +# prepinfo.awk --- fix node lines and menus +# +# Copyright, 1998, Arnold Robbins, arnold@gnu.org +# +# PREPINFO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PREPINFO 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +BEGIN \ +{ + # manifest constants + TRUE = 1 + FALSE = 0 + + # Levels at which different nodes can be + Level["@top"] = 0 + Level["@appendix"] = 1 + Level["@chapter"] = 1 + Level["@majorheading"] = 1 + Level["@unnumbered"] = 1 + Level["@appendixsec"] = 2 + Level["@heading"] = 2 + Level["@section"] = 2 + Level["@unnumberedsec"] = 2 + Level["@unnumberedsubsec"] = 3 + Level["@appendixsubsec"] = 3 + Level["@subheading"] = 3 + Level["@subsection"] = 3 + Level["@appendixsubsubsec"] = 4 + Level["@subsubheading"] = 4 + Level["@subsubsection"] = 4 + Level["@unnumberedsubsubsec"] = 4 + + # Length of menus + Menumargin = 78 + + # Length of menu item + Min_menitem_length = 29 + + # insure that we were called correctly + if (ARGC != 2) { + printf("usage: %s texinfo-file\n", ARGV[0]) > "/dev/stderr" + exit 1 + } + + # Arrange for two passes over input file + Pass = 1 + ARGV[2] = "Pass=2" + ARGV[3] = ARGV[1] + ARGC = 4 + Lastlevel = -1 + + # Initialize stacks + Up[-1] = "(dir)" + Prev[0] = "(dir)" + + if (Debug == "args") { + for (i = 0; i < ARGC; i++) + printf("ARGV[%d] = %s\n", i, ARGV[i]) > "/dev/stderr" + } +} + +$1 == "@node" \ +{ + Name = getnodename($0) + Nodeseen = TRUE + + if ((l = length(Name)) > Maxlen) + Maxlen = l + + if (Debug == "nodenames") + printf("Name = %s\n", Name) > "/dev/stderr" + + if (Pass == 1) + next +} + +Pass == 1 && /^@c(omment)?[ \t]+fakenode/ \ +{ + if (Debug == "fakenodes") + printf("fakenode at %d\n", FNR) > "/dev/stderr" + Fakenode = TRUE + next +} + +Pass == 1 && ($1 in Level) \ +{ + # skip fake nodes --- titles without associated @node lines + if (Fakenode) { + if (Debug == "fakenodes") + printf("%s at %d is a fakenode\n", $1, FNR) > "/dev/stderr" + Fakenode = FALSE + next + } + + if (Debug == "titles") + printf("Processing %s: Name = %s\n", $1, Name) > "/dev/stderr" + + # save type + type = $1 + + if (! Nodeseen) { + err_prefix() + printf("%s line with no @node or fakenode line\n", + type) > "/dev/stderr" + Badheading[FNR] = 1 + # ??? used ??? + next + } else + Nodeseen = FALSE # reset it + + # Squirrel away the info + levelnum = Level[type] + Node[Name ".level"] = levelnum + Node[Name ".name"] = Name + if (Debug == "titles") { + printf("Node[%s\".level\"] = %s\n", Name, Node[Name ".level"]) > "/dev/stderr" + printf("Node[%s\".name\"] = %s\n", Name, Node[Name ".name"]) > "/dev/stderr" + } + + if (levelnum == Lastlevel) { # e.g., two sections in a row + Node[Name ".up"] = Up[levelnum - 1] + if (levelnum in Prev) { + Node[Prev[levelnum] ".next"] = Name + Node[Name ".prev"] = Prev[levelnum] + } + Prev[levelnum] = Name + Up[levelnum] = Name # ??? + } else if (levelnum < Lastlevel) { # section, now chapter + Lastlevel = levelnum + Node[Name ".up"] = Up[levelnum - 1] + if (levelnum in Prev) { + Node[Name ".prev"] = Prev[levelnum] + Node[Prev[levelnum] ".next"] = Name + } + Prev[levelnum] = Name + Up[levelnum] = Name + } else { # chapter, now section, levelnum > Lastlevel + Node[Name ".up"] = Up[levelnum - 1] + Node[Up[Lastlevel] ".child"] = Name + Up[levelnum] = Name + Prev[levelnum] = Name + Lastlevel = levelnum + } + + # For master menu + if (Level[$1] >= 2) + List[++Sequence] = Name + + if (Debug == "titles") { + printf("Node[%s\".prev\"] = %s\n", Name, Node[Name ".prev"]) > "/dev/stderr" + printf("Node[%s\".up\"] = %s\n", Name, Node[Name ".up"]) > "/dev/stderr" + printf("Node[%s\".child\"] = %s\n", Name, Node[Name ".child"]) > "/dev/stderr" + } +} + +Pass == 2 && Debug == "dumptitles" && FNR <= 1 \ +{ + for (i in Node) + printf("Node[%s] = %s\n", i, Node[i]) | "sort 1>&2" + close("sort 1>&2") +} + +/^@menu/ && Pass == 1, /^@end[ \t]+menu/ && Pass == 1 \ +{ + if (/^@menu/ || /^@end[ \t]+menu/) + next + +# if (Debug == "menu") +# printf("processing: %s\n", $0) > "/dev/stderr" + + if (/^\*/) { + if (In_menitem) { # file away info from previousline + Node[node ".mendesc"] = desc + Node[node ".longdesc"] = longdesc + if (Debug == "mendesc") { + printf("Node[%s.mendesc] = %s\n", + node, Node[node ".mendesc"]) > "/dev/stderr" + printf("Node[%s.longdesc] = %s\n", + node, Node[node ".longdesc"]) > "/dev/stderr" + } + } + In_menitem = TRUE + + # pull apart menu item + $1 = "" # nuke ``*'' + $0 = $0 # reparse line + i1 = index($0, ":") + if (i1 <= 0) { + err_prefix() + printf("badly formed menu item") > "/dev/stderr" + next + } + if (substr($0, i1+1, 1) != ":") { # desc: node. long desc + i2 = index($0, ".") + if (i2 <= 0) { + err_prefix() + printf("badly formed menu item") > "/dev/stderr" + next + } + desc = substr($0, 1, i1 - 1) + sub(/^[ \t]+/, "", node) + sub(/[ \t]+$/, "", node) + longdesc = substr($0, i2 + 1) + } else { # nodname:: long desc + desc = "" + node = substr($0, 1, i1 - 1) + sub(/^[ \t]+/, "", node) + sub(/[ \t]+$/, "", node) + longdesc = substr($0, i1 + 2) + } + } else if (In_menitem) { # continuation line + longdesc = longdesc " " $0 + } else + In_menitem = FALSE + + Node[node ".mendesc"] = desc + Node[node ".longdesc"] = longdesc + if (Debug == "mendesc") { + printf("Node[%s.mendesc] = %s\n", + node, Node[node ".mendesc"]) > "/dev/stderr" + printf("Node[%s.longdesc] = %s\n", + node, Node[node ".longdesc"]) > "/dev/stderr" + } + + if (Debug == "menu") + printf("Menu:: Name %s: desc %s: longdesc %s\n", + node, desc, longdesc) > "/dev/stderr" +} + +function err_prefix() +{ + printf("%s: %s: %d: ", ARGV[0], FILENAME, FNR) > "/dev/stderr" +} + +function getnodename(str) +{ + sub(/@node[ \t]+/, "", str) + sub(/,.*/, "", str) + if (Debug == "nodenames") + printf("getnodename: return %s\n", str) > "/dev/stderr" + return str +} + +Pass == 2 && /^@node/ \ +{ + Name = getnodename($0) + + # Top node is special. It's next is the first child + n = Node[Name ".next"] + if (Node[Name ".level"] == 0 && n == "") + n = Node[Name ".child"] + + printf("@node %s, %s, %s, %s\n", Name, n, + Node[Name ".prev"] ? Node[Name ".prev"] : Node[Name ".up"], + Node[Name ".up"]) + next +} + +Pass == 2 && /^@menu/ \ +{ + # First, nuke current contents of menu + do { + if ((getline) <= 0) { + err_prefix() + printf("unexpected EOF inside menu\n") > "/dev/stderr" + exit 1 + } + } while (! /^@end[ \t]+menu/) + + # next, compute maximum length of a node name + max = 0 + for (n = Node[Name ".child"]; (n ".next") in Node; n = Node[n ".next"]) { + if ((n ".desc") in Node) + s = Node[n ".desc"] ": " n "." + else + s = n "::" + l = length(s) + if (l > max) + max = l + } + if (max < Min_menitem_length) + max = Min_menitem_length + + # now dump the menu + print "@menu" + + for (n = Node[Name ".child"]; (n ".next") in Node; n = Node[n ".next"]) { + print_menuitem(n, max) + } + print_menuitem(n, max) + + if (Name == "Top") { # Master Menu + if (Maxlen < Min_menitem_length) + Maxlen = Min_menitem_length + print "" + for (i = 1; i <= Sequence; i++) + print_menuitem(List[i], Maxlen) + print "" + } + print "@end menu" + next +} + +Pass == 2 # print + + +function print_menuitem(n, max, nodesc, i, dwords, count, p) +{ + nodesc = FALSE + if (! ((n ".longdesc") in Node)) { + err_prefix() + printf("warning: %s: no long description\n", n) > "/dev/stderr" + nodesc = TRUE + } else { + for (i in dwords) + delete dwords[i] + count = split(Node[n ".longdesc"], dwords, "[ \t\n]+") + } + if ((n ".desc") in Node) + s = Node[n ".desc"] ": " n "." + else + s = n "::" + printf("* %-*s", max, s) + + if (Debug == "mendescitem") + printf("<* %-*s>\n", max, s) > "/dev/stderr" + + p = max + 2 + if (! nodesc) { + for (i = 1; i <= count; i++) { + l = length(dwords[i]) + if (l == 0) + continue + if (p + l + 1 > Menumargin) { + printf("\n%*s", max + 2, " ") + p = max + 2 + } + printf(" %s", dwords[i]) + p += l + 1 + } + } + print "" +} diff --git a/gnu/usr.bin/texinfo/util/texi-docstring-magic.el b/gnu/usr.bin/texinfo/util/texi-docstring-magic.el new file mode 100644 index 00000000000..c645fbcf699 --- /dev/null +++ b/gnu/usr.bin/texinfo/util/texi-docstring-magic.el @@ -0,0 +1,347 @@ +;; texi-docstring-magic.el -- munge internal docstrings into texi +;; +;; Keywords: lisp, docs, tex +;; Author: David Aspinall <da@dcs.ed.ac.uk> +;; Copyright (C) 1998 David Aspinall +;; Maintainer: David Aspinall <da@dcs.ed.ac.uk> +;; +;; $Id: texi-docstring-magic.el,v 1.1.1.1 2000/02/09 01:26:24 espie Exp $ +;; +;; This package is distributed under the terms of the +;; GNU General Public License, Version 2. +;; You should have a copy of the GPL with your version of +;; GNU Emacs or the Texinfo distribution. +;; +;; +;; This package generates Texinfo source fragments from Emacs +;; docstrings. This avoids documenting functions and variables +;; in more than one place, and automatically adds Texinfo markup +;; to docstrings. +;; +;; It relies heavily on you following the Elisp documentation +;; conventions to produce sensible output, check the Elisp manual +;; for details. In brief: +;; +;; * The first line of a docstring should be a complete sentence. +;; * Arguments to functions should be written in upper case: ARG1..ARGN +;; * User options (variables users may want to set) should have docstrings +;; beginning with an asterisk. +;; +;; Usage: +;; +;; Write comments of the form: +;; +;; @c TEXI DOCSTRING MAGIC: my-package-function-or-variable-name +;; +;; In your texi source, mypackage.texi. From within an Emacs session +;; where my-package is loaded, visit mypackage.texi and run +;; M-x texi-docstring-magic to update all of the documentation strings. +;; +;; This will insert @defopt, @deffn and the like underneath the +;; magic comment strings. +;; +;; The default value for user options will be printed. +;; +;; Symbols are recognized if they are defined for faces, functions, +;; or variables (in that order). +;; +;; Automatic markup rules: +;; +;; 1. Indented lines are gathered into @lisp environment. +;; 2. Pieces of text `stuff' or surrounded in quotes marked up with @samp. +;; 3. Words *emphasized* are made @strong{emphasized} +;; 4. Words sym-bol which are symbols become @code{sym-bol}. +;; 5. Upper cased words ARG corresponding to arguments become @var{arg}. +;; In fact, you can any word longer than three letters, so that +;; metavariables can be used easily. +;; FIXME: to escape this, use `ARG' +;; 6. Words 'sym which are lisp-quoted are marked with @code{'sym}. +;; +;; ----- +;; +;; Useful key binding when writing Texinfo: +;; +;; (define-key TeXinfo-mode-map "C-cC-d" 'texi-docstring-magic-insert-magic) +;; +;; ----- +;; +;; Useful enhancements to do: +;; +;; * Use customize properties (e.g. group, simple types) +;; * Look for a "texi-docstring" property for symbols +;; so TeXInfo can be defined directly in case automatic markup +;; goes badly wrong. +;; * Add tags to special comments so that user can specify face, +;; function, or variable binding for a symbol in case more than +;; one binding exists. +;; +;; ------ + +(defun texi-docstring-magic-splice-sep (strings sep) + "Return concatenation of STRINGS spliced together with separator SEP." + (let (str) + (while strings + (setq str (concat str (car strings))) + (if (cdr strings) + (setq str (concat str sep))) + (setq strings (cdr strings))) + str)) + +(defconst texi-docstring-magic-munge-table + '(;; 1. Indented lines are gathered into @lisp environment. + ("\\(^.*\\S-.*$\\)" + t + (let + ((line (match-string 0 docstring))) + (if (eq (char-syntax (string-to-char line)) ?\ ) + ;; whitespace + (if in-quoted-region + line + (setq in-quoted-region t) + (concat "@lisp\n" line)) + ;; non-white space + (if in-quoted-region + (progn + (setq in-quoted-region nil) + (concat "@end lisp\n" line)) + line)))) + ;; 2. Pieces of text `stuff' or surrounded in quotes + ;; are marked up with @samp. NB: Must be backquote + ;; followed by forward quote for this to work. + ;; Can't use two forward quotes else problems with + ;; symbols. + ;; Odd hack: because ' is a word constituent in text/texinfo + ;; mode, putting this first enables the recognition of args + ;; and symbols put inside quotes. + ("\\(`\\([^']+\\)'\\)" + t + (concat "@samp{" (match-string 2 docstring) "}")) + ;; 3. Words *emphasized* are made @strong{emphasized} + ("\\(\\*\\(\\w+\\)\\*\\)" + t + (concat "@strong{" (match-string 2 docstring) "}")) + ;; 4. Words sym-bol which are symbols become @code{sym-bol}. + ;; Must have at least one hyphen to be recognized, + ;; terminated in whitespace, end of line, or punctuation. + ;; (Only consider symbols made from word constituents + ;; and hyphen. + ("\\(\\(\\w+\\-\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)" + (or (boundp (intern (match-string 2 docstring))) + (fboundp (intern (match-string 2 docstring)))) + (concat "@code{" (match-string 2 docstring) "}" + (match-string 4 docstring))) + ;; 5. Upper cased words ARG corresponding to arguments become + ;; @var{arg} + ;; In fact, include any word so long as it is more than 3 characters + ;; long. (Comes after symbols to avoid recognizing the + ;; lowercased form of an argument as a symbol) + ;; FIXME: maybe we don't want to downcase stuff already + ;; inside @samp + ;; FIXME: should - terminate? should _ be included? + ("\\([A-Z0-9\\-]+\\)\\(/\\|\)\\|}\\|\\s-\\|\\s.\\|$\\)" + (or (> (length (match-string 1 docstring)) 3) + (member (downcase (match-string 1 docstring)) args)) + (concat "@var{" (downcase (match-string 1 docstring)) "}" + (match-string 2 docstring))) + + ;; 6. Words 'sym which are lisp quoted are + ;; marked with @code. + ("\\(\\(\\s-\\|^\\)'\\(\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)" + t + (concat (match-string 2 docstring) + "@code{'" (match-string 3 docstring) "}" + (match-string 5 docstring))) + ;; 7,8. Clean up for @lisp environments left with spurious newlines + ;; after 1. + ("\\(\\(^\\s-*$\\)\n@lisp\\)" t "@lisp") + ("\\(\\(^\\s-*$\\)\n@end lisp\\)" t "@end lisp")) + "Table of regexp matches and replacements used to markup docstrings. +Format of table is a list of elements of the form + (regexp predicate replacement-form) +If regexp matches and predicate holds, then replacement-form is +evaluated to get the replacement for the match. +predicate and replacement-form can use variables arg, +and forms such as (match-string 1 docstring) +Match string 1 is assumed to determine the +length of the matched item, hence where parsing restarts from. +The replacement must cover the whole match (match string 0), +including any whitespace included to delimit matches.") + + +(defun texi-docstring-magic-munge-docstring (docstring args) + "Markup DOCSTRING for texi according to regexp matches." + (let ((case-fold-search nil)) + (dolist (test texi-docstring-magic-munge-table docstring) + (let ((regexp (nth 0 test)) + (predicate (nth 1 test)) + (replace (nth 2 test)) + (i 0) + in-quoted-region) + + (while (and + (< i (length docstring)) + (string-match regexp docstring i)) + (setq i (match-end 1)) + (if (eval predicate) + (let* ((origlength (- (match-end 0) (match-beginning 0))) + (replacement (eval replace)) + (newlength (length replacement))) + (setq docstring + (replace-match replacement t t docstring)) + (setq i (+ i (- newlength origlength)))))) + (if in-quoted-region + (setq docstring (concat docstring "\n@end lisp")))))) + ;; Force a new line after (what should be) the first sentence, + ;; if not already a new paragraph. + (let* + ((pos (string-match "\n" docstring)) + (needscr (and pos + (not (string= "\n" + (substring docstring + (1+ pos) + (+ pos 2))))))) + (if (and pos needscr) + (concat (substring docstring 0 pos) + "@*\n" + (substring docstring (1+ pos))) + docstring))) + +(defun texi-docstring-magic-texi (env grp name docstring args &optional endtext) + "Make a texi def environment ENV for entity NAME with DOCSTRING." + (concat "@def" env (if grp (concat " " grp) "") " " name + " " + (texi-docstring-magic-splice-sep args " ") + ;; " " + ;; (texi-docstring-magic-splice-sep extras " ") + "\n" + (texi-docstring-magic-munge-docstring docstring args) + "\n" + (or endtext "") + "@end def" env "\n")) + +(defun texi-docstring-magic-format-default (default) + "Make a default value string for the value DEFAULT. +Markup as @code{stuff} or @lisp stuff @end lisp." + (let ((text (format "%S" default))) + (concat + "\nThe default value is " + (if (string-match "\n" text) + ;; Carriage return will break @code, use @lisp + (if (stringp default) + (concat "the string: \n@lisp\n" default "\n@end lisp\n") + (concat "the value: \n@lisp\n" text "\n@end lisp\n")) + (concat "@code{" text "}.\n"))))) + + +(defun texi-docstring-magic-texi-for (symbol) + (cond + ;; Faces + ((find-face symbol) + (let* + ((face symbol) + (name (symbol-name face)) + (docstring (or (face-doc-string face) + "Not documented.")) + (useropt (eq ?* (string-to-char docstring)))) + ;; Chop off user option setting + (if useropt + (setq docstring (substring docstring 1))) + (texi-docstring-magic-texi "fn" "Face" name docstring nil))) + ((fboundp symbol) + ;; Functions. + ;; We don't handle macros, aliases, or compiled fns properly. + (let* + ((function symbol) + (name (symbol-name function)) + (docstring (or (documentation function) + "Not documented.")) + (def (symbol-function function)) + (argsyms (cond ((eq (car-safe def) 'lambda) + (nth 1 def)))) + (args (mapcar 'symbol-name argsyms))) + (if (commandp function) + (texi-docstring-magic-texi "fn" "Command" name docstring args) + (texi-docstring-magic-texi "un" nil name docstring args)))) + ((boundp symbol) + ;; Variables. + (let* + ((variable symbol) + (name (symbol-name variable)) + (docstring (or (documentation-property variable + 'variable-documentation) + "Not documented.")) + (useropt (eq ?* (string-to-char docstring))) + (default (if useropt + (texi-docstring-magic-format-default + (default-value symbol))))) + ;; Chop off user option setting + (if useropt + (setq docstring (substring docstring 1))) + (texi-docstring-magic-texi + (if useropt "opt" "var") nil name docstring nil default))) + (t + (error "Don't know anything about symbol %s" (symbol-name symbol))))) + +(defconst texi-docstring-magic-comment + "@c TEXI DOCSTRING MAGIC:" + "Magic string in a texi buffer expanded into @defopt, or @deffn.") + +(defun texi-docstring-magic () + "Update all texi docstring magic annotations in buffer." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((magic (concat "^" + (regexp-quote texi-docstring-magic-comment) + "\\s-*\\(\\(\\w\\|\\-\\)+\\)$")) + p + symbol) + (while (re-search-forward magic nil t) + (setq symbol (intern (match-string 1))) + (forward-line) + (setq p (point)) + ;; If comment already followed by an environment, delete it. + (if (and + (looking-at "@def\\(\\w+\\)\\s-") + (search-forward (concat "@end def" (match-string 1)) nil t)) + (progn + (forward-line) + (delete-region p (point)))) + (insert + (texi-docstring-magic-texi-for symbol)))))) + +(defun texi-docstring-magic-face-at-point () + (ignore-errors + (let ((stab (syntax-table))) + (unwind-protect + (save-excursion + (set-syntax-table emacs-lisp-mode-syntax-table) + (or (not (zerop (skip-syntax-backward "_w"))) + (eq (char-syntax (char-after (point))) ?w) + (eq (char-syntax (char-after (point))) ?_) + (forward-sexp -1)) + (skip-chars-forward "'") + (let ((obj (read (current-buffer)))) + (and (symbolp obj) (find-face obj) obj))) + (set-syntax-table stab))))) + +(defun texi-docstring-magic-insert-magic (symbol) + (interactive + (let* ((v (or (variable-at-point) + (function-at-point) + (texi-docstring-magic-face-at-point))) + (val (let ((enable-recursive-minibuffers t)) + (completing-read + (if v + (format "Magic docstring for symbol (default %s): " v) + "Magic docstring for symbol: ") + obarray '(lambda (sym) + (or (boundp sym) + (fboundp sym) + (find-face sym))) + t nil 'variable-history)))) + (list (if (equal val "") v (intern val))))) + (insert "\n" texi-docstring-magic-comment " " (symbol-name symbol))) + + +(provide 'texi-docstring-magic) |