diff options
Diffstat (limited to 'usr.bin')
409 files changed, 72843 insertions, 1274 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile index f879f00eedf0..ba0d7cb35eca 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,19 +1,19 @@ # @(#)Makefile 5.8.1.1 (Berkeley) 5/8/91 # -SUBDIR= ar basename bdes biff cal calendar checknr chat chpass cksum cmp col \ - colcrt colrm column comm compress crontab ctags cut dirname \ - du elvis elvisrecover env error expand false file find finger fmt \ - fold fpr from fsplit fstat ftp getopt gprof groups head hexdump id \ - indent join ktrace last lastcomm leave lex locate lock logger \ - login logname look lorder m4 machine mail make mesg mkdep mkfifo \ - mkstr more msgs mt netstat nfsstat nice nm nohup pagesize \ +SUBDIR= ar at basename bdes biff cal calendar checknr chat chpass cksum cmp \ + col colcrt colrm column comm compress crontab ctags cut dirname \ + du elvis elvisrecover env error expand f2c false file find finger fmt \ + fold fpr from fsplit fstat ftp getopt gprof groups head hexdump \ + id indent ipcrm ipcs join ktrace last lastcomm leave lex locate lock \ + logger login logname look lorder m4 machine mail make mesg mkdep \ + mkfifo mkstr more msgs mt netstat nfsstat nice nm nohup pagesize \ passwd paste printenv printf quota ranlib rdist ref renice rev rlogin \ rpcgen rpcinfo rsh rup ruptime rusers rwall rwho script sed shar \ showmount size soelim split strings strip su symorder syscons tail \ talk tcopy tee telnet tftp time tip tn3270 touch tput tr true tset \ tsort tty ul uname unexpand unifdef uniq unvis users uudecode uuencode \ - vacation vgrind vis vmstat w wall wc what whereis which who whoami \ + vacation vgrind vi vis vmstat w wall wc what whereis which who whoami \ whois window write xargs xinstall xstr yacc yes .include <bsd.subdir.mk> diff --git a/usr.bin/ar/archive.c b/usr.bin/ar/archive.c index d21c30a26f5d..d477c20ed1c4 100644 --- a/usr.bin/ar/archive.c +++ b/usr.bin/ar/archive.c @@ -137,7 +137,8 @@ get_arobj(fd) { struct ar_hdr *hdr; register int len, nr; - register char *p, buf[20]; + register char *p; + char buf[20]; nr = read(fd, hb, sizeof(HDR)); if (nr != sizeof(HDR)) { diff --git a/usr.bin/at/Makefile b/usr.bin/at/Makefile new file mode 100644 index 000000000000..d93e932640ad --- /dev/null +++ b/usr.bin/at/Makefile @@ -0,0 +1,15 @@ +# $Id: Makefile,v 1.1 1994/01/05 01:08:51 nate Exp $ + +PROG= at +SRCS= at.c panic.c parsetime.c +LINKS= ${BINDIR}/at ${BINDIR}/atq \ + ${BINDIR}/at ${BINDIR}/atrm \ + ${BINDIR}/at ${BINDIR}/batch +MLINKS= at.1 batch.1 \ + at.1 atq.1 \ + at.1 atrm.1 + +BINOWN= root +BINMODE= 4555 + +.include <bsd.prog.mk> diff --git a/usr.bin/at/at.1 b/usr.bin/at/at.1 new file mode 100644 index 000000000000..f69e51e71008 --- /dev/null +++ b/usr.bin/at/at.1 @@ -0,0 +1,216 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: at.1,v 1.2 1994/01/05 01:10:24 nate Exp $ +.\" +.Dd December 5, 1993 +.Dt "AT" 1 +.Os FreeBSD 1.1 +.Sh NAME +.Nm at, batch, atq, atrm +.Nd queue, examine, or delete jobs for later execution +.\" +.Sh SYOPSIS +.Nm at +.Op Fl q Ar queue +.Op Fl f Ar file +.Op Fl m +.Ar time +.Pp +.Nm atq +.Op Fl q Ar queue +.Op Fl v +.Pp +.Nm atrm +.Ar job +.Op Ar job ... +.Pp +.Nm batch +.Op Fl f Ar file +.Op Fl m +.Ar time +.Sh DESCRIPTION +The +.Nm at +and +.Nm batch +utilities read commands from the standard input or a specified file +which are to be executed at a later time, using +.Xr sh 1 . +.Pp +The functions of the commands are as follows: +.Bl -tag -width indent +.It Nm at +Executes commands at a specified time. +.It Nm atq +Lists the user's pending jobs, unless the user is +the superuser. In that case, everybody's jobs are +listed. +.It Nm atrm +Deletes jobs. +.It Nm batch +executes commands when system load levels permit. +In other words, it executes the commands when the load +average drops below a specified level. +.El +.Pp +For both +.Nm at +and +.Nm batch , +the working directory, environment (except for the variables +.Nm TERM , +.Nm TERMCAP , +.Nm DISPLAY , +and +.Nm _ ) +and the umask are retained from the time of invocation. The user +will be mailed the standard output and standard error from +his commands if any output is generated. If +.Nm at +is executed from a +.Xr su 1 +shell, the owner of the login whell will receive the mail. +.Sh OPTIONS +.Bl -tag -width indent +The available options are as follows: +.It Fl q Ar queue +Use the specified queue. A queue designation consists +of a single letter; valid queue designation range from +.Ar a +to +.Ar l . +The +.Ar a +queue is the default, and +.Ar b +is the batch queue. Queues with higher letters run with +increased niceness. If +.Nm atq +is given a specific queue, it will only show jobs pending +in that queue. +.It Fl m +Send mail to the user when the job has completed, even if +there was no output. +.It Fl f Ar file +Reads the job from +.Ar file +rather than the standard input. +.It Fl v +Shows completed but not yet deleted jobs in the queue. +.Sh TIME SPECIFICATION +.Nm At +allows some moderately complex time specifications. +It accepts times of the form +.Ar HHMM +or +.Ar HH:MM +to run a job at a specific time of day. If +that time is already passed, the next day is assumed. +You may also specify +.Nm midnight , +.Nm noon , +or +.Nm teatime +(4PM) and you can give a time of day suffixed with +.Nm AM +or +.Nm PM +for running in the morning or the evening. You can +also specify the date on which the job will be run +by giving a date in the form +.Ar month-name day +with an optional +.Ar year , +or giving a date of the form +.Ar MMDDYY , +.Ar MM/DD/YY +or +.Ar DD.MM.YY . +You can also give times like +.Nm now + +.Ar count time-units , +where the time units can be +.Nm minutes, hours, days, +or +.Nm weeks +You can suffix the time with +.Nm today +to run the job today, or +.Nm tomorrow +to run the job tomorrow. +.Pp +For example, to run a job at 4PM three days from now, you +would specify a time of +.Nm 4PM + 3 days . +To run a job at 10:00AM on on July 31, you would specify +a time of +.Nm 10AM Jul 31 . +Finally, to run a job at 1AM tomorrow, you would specify +a time of +.Nm 1AM tomorrow . +.Sh FILES +.Bl -tag -width /var/at/lockfile -compact +.It Pa /var/at/jobs +Directory containing job files +.It Pa /var/at/spool +Directory containing output spool files +.It Pa /var/at/lockfile +Job-creation lock file. +.It Pa /var/run/utmp +.El +.Sh SEE ALSO +.Xr crond 8 , +.Xr nice 1 , +.Xr sh 1 , +.Xr atrun 8 +.Sh AUTHOR +.Bl -tag +Thomas Koenig, ig25@rz.uni-karlsruhe.de +.El +.Sh BUGS +Traditional access control to +.Nm at +and +.Nm batch +via the files +.Pa /var/at/at.allow +and +.Pa /var/at/at.deny +is not implemented. +.Pp +If the file +.Pa /var/run/utmp +is not available or corrupted, or if the user is not +logged in at the time +.Nm at +is invoked, the mail is sent to the userid found in the +environment variable +.Nm LOGNAME . +If that is undefined or empty, the current userid is assumed. diff --git a/usr.bin/at/at.c b/usr.bin/at/at.c new file mode 100644 index 000000000000..5a24fbd57670 --- /dev/null +++ b/usr.bin/at/at.c @@ -0,0 +1,562 @@ +/* + * at.c : Put file into atrun queue + * Copyright (C) 1993 Thomas Koenig + * + * Atrun & Atq modifications + * Copyright (C) 1993 David Parsons + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _USE_BSD 1 + +/* System Headers */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +/* Local headers */ +#include "at.h" +#include "panic.h" +#include "parsetime.h" +#include "pathnames.h" +#define MAIN +#include "privs.h" + +/* Macros */ +#define ALARMC 10 /* Number of seconds to wait for timeout */ + +#define SIZE 255 +#define TIMESIZE 50 + +/* File scope variables */ +static char rcsid[] = "$Id: at.c,v 1.1 1994/01/05 01:08:59 nate Exp $"; +char *no_export[] = +{ + "TERM", "TERMCAP", "DISPLAY", "_" +}; +static send_mail = 0; + +/* External variables */ +extern char **environ; +int fcreated; +char *namep; +char atfile[FILENAME_MAX]; + +char *atinput = (char *) 0; /* where to get input from */ +char atqueue = 0; /* which queue to examine for jobs (atq) */ +char atverify = 0; /* verify time instead of queuing job */ + +/* Function declarations */ +static void sigc __P((int signo)); +static void alarmc __P((int signo)); +static char *cwdname __P((void)); +static void writefile __P((time_t runtimer, char queue)); +static void list_jobs __P((void)); + +/* Signal catching functions */ + +static void +sigc(signo) + int signo; +{ +/* If the user presses ^C, remove the spool file and exit + */ + if (fcreated) { + PRIV_START + unlink(atfile); + PRIV_END + } + + exit(EXIT_FAILURE); +} + +static void +alarmc(signo) + int signo; +{ +/* Time out after some seconds + */ + panic("File locking timed out"); +} + +/* Local functions */ + +static char * +cwdname() +{ +/* Read in the current directory; the name will be overwritten on + * subsequent calls. + */ + static char *ptr = NULL; + static size_t size = SIZE; + + if (ptr == NULL) + ptr = (char *) malloc(size); + + while (1) { + if (ptr == NULL) + panic("Out of memory"); + + if (getcwd(ptr, size - 1) != NULL) + return ptr; + + if (errno != ERANGE) + perr("Cannot get directory"); + + free(ptr); + size += SIZE; + ptr = (char *) malloc(size); + } +} + +static void +writefile(runtimer, queue) + time_t runtimer; + char queue; +{ + /* + * This does most of the work if at or batch are invoked for + * writing a job. + */ + int i; + char *ap, *ppos, *mailname; + struct passwd *pass_entry; + struct stat statbuf; + int fdes, lockdes, fd2; + FILE *fp, *fpin; + struct sigaction act; + char **atenv; + int ch; + mode_t cmask; + struct flock lock; + + /* + * Install the signal handler for SIGINT; terminate after removing the + * spool file if necessary + */ + act.sa_handler = sigc; + sigemptyset(&(act.sa_mask)); + act.sa_flags = 0; + + sigaction(SIGINT, &act, NULL); + + strcpy(atfile, _PATH_ATJOBS); + ppos = atfile + strlen(_PATH_ATJOBS); + + /* + * Loop over all possible file names for running something at this + * particular time, see if a file is there; the first empty slot at + * any particular time is used. Lock the file _PATH_LOCKFILE first + * to make sure we're alone when doing this. + */ + + PRIV_START + + if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT, 0600)) < 0) + perr2("Cannot open lockfile ", _PATH_LOCKFILE); + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + act.sa_handler = alarmc; + sigemptyset(&(act.sa_mask)); + act.sa_flags = 0; + + /* + * Set an alarm so a timeout occurs after ALARMC seconds, in case + * something is seriously broken. + */ + sigaction(SIGALRM, &act, NULL); + alarm(ALARMC); + fcntl(lockdes, F_SETLKW, &lock); + alarm(0); + + for (i = 0; i < AT_MAXJOBS; i++) { + sprintf(ppos, "%c%8lx.%3x", queue, + (unsigned long) (runtimer / 60), i); + for (ap = ppos; *ap != '\0'; ap++) + if (*ap == ' ') + *ap = '0'; + + if (stat(atfile, &statbuf) != 0) { + if (errno == ENOENT) + break; + else + perr2("Cannot access ", _PATH_ATJOBS); + } + } /* for */ + + if (i >= AT_MAXJOBS) + panic("Too many jobs already"); + + /* + * Create the file. The x bit is only going to be set after it has + * been completely written out, to make sure it is not executed in + * the meantime. To make sure they do not get deleted, turn off + * their r bit. Yes, this is a kluge. + */ + cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); + if ((fdes = creat(atfile, O_WRONLY)) == -1) + perr("Cannot create atjob file"); + + if ((fd2 = dup(fdes)) < 0) + perr("Error in dup() of job file"); + + if (fchown(fd2, real_uid, -1) != 0) + perr("Cannot give away file"); + + PRIV_END + + /* + * We no longer need suid root; now we just need to be able to + * write to the directory, if necessary. + */ + + REDUCE_PRIV(0); + + /* + * We've successfully created the file; let's set the flag so it + * gets removed in case of an interrupt or error. + */ + fcreated = 1; + + /* Now we can release the lock, so other people can access it */ + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + fcntl(lockdes, F_SETLKW, &lock); + close(lockdes); + + if ((fp = fdopen(fdes, "w")) == NULL) + panic("Cannot reopen atjob file"); + + /* + * Get the userid to mail to, first by trying getlogin(), which + * reads /etc/utmp, then from LOGNAME, finally from getpwuid(). + */ + mailname = getlogin(); + if (mailname == NULL) + mailname = getenv("LOGNAME"); + + if ((mailname == NULL) || (mailname[0] == '\0') + || (strlen(mailname) > 8)) { + pass_entry = getpwuid(getuid()); + if (pass_entry != NULL) + mailname = pass_entry->pw_name; + } + + if (atinput != (char *) NULL) { + fpin = freopen(atinput, "r", stdin); + if (fpin == NULL) + perr("Cannot open input file"); + } + fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail); + + /* Write out the umask at the time of invocation */ + fprintf(fp, "umask %lo\n", (unsigned long) cmask); + + /* + * Write out the environment. Anything that may look like a special + * character to the shell is quoted, except for \n, which is done + * with a pair of "'s. Dont't export the no_export list (such as + * TERM or DISPLAY) because we don't want these. + */ + for (atenv = environ; *atenv != NULL; atenv++) { + int export = 1; + char *eqp; + + eqp = strchr(*atenv, '='); + if (ap == NULL) + eqp = *atenv; + else { + int i; + + for (i = 0;i < sizeof(no_export) / + sizeof(no_export[0]); i++) { + export = export + && (strncmp(*atenv, no_export[i], + (size_t) (eqp - *atenv)) != 0); + } + eqp++; + } + + if (export) { + fwrite(*atenv, sizeof(char), eqp - *atenv, fp); + for (ap = eqp; *ap != '\0'; ap++) { + if (*ap == '\n') + fprintf(fp, "\"\n\""); + else { + if (!isalnum(*ap)) + fputc('\\', fp); + + fputc(*ap, fp); + } + } + fputs("; export ", fp); + fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp); + fputc('\n', fp); + + } + } + /* + * Cd to the directory at the time and write out all the commands + * the user supplies from stdin. + */ + fprintf(fp, "cd %s\n", cwdname()); + + while ((ch = getchar()) != EOF) + fputc(ch, fp); + + fprintf(fp, "\n"); + if (ferror(fp)) + panic("Output error"); + + if (ferror(stdin)) + panic("Input error"); + + fclose(fp); + + /* + * Set the x bit so that we're ready to start executing + */ + if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + perr("Cannot give away file"); + + close(fd2); + fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos); +} + +static void +list_jobs() +{ + /* + * List all a user's jobs in the queue, by looping through + * _PATH_ATJOBS, or everybody's if we are root + */ + struct passwd *pw; + DIR *spool; + struct dirent *dirent; + struct stat buf; + struct tm runtime; + unsigned long ctm; + char queue; + time_t runtimer; + char timestr[TIMESIZE]; + int first = 1; + + PRIV_START + + if (chdir(_PATH_ATJOBS) != 0) + perr2("Cannot change to ", _PATH_ATJOBS); + + if ((spool = opendir(".")) == NULL) + perr2("Cannot open ", _PATH_ATJOBS); + + /* Loop over every file in the directory */ + while ((dirent = readdir(spool)) != NULL) { + if (stat(dirent->d_name, &buf) != 0) + perr2("Cannot stat in ", _PATH_ATJOBS); + + /* + * See it's a regular file and has its x bit turned on and + * is the user's + */ + if (!S_ISREG(buf.st_mode) + || ((buf.st_uid != real_uid) && !(real_uid == 0)) + || !(S_IXUSR & buf.st_mode || atverify)) + continue; + + if (sscanf(dirent->d_name, "%c%8lx", &queue, &ctm) != 2) + continue; + + if (atqueue && (queue != atqueue)) + continue; + + runtimer = 60 * (time_t) ctm; + runtime = *localtime(&runtimer); + strftime(timestr, TIMESIZE, "%X %x", &runtime); + if (first) { + printf("Date\t\t\tOwner\tQueue\tJob#\n"); + first = 0; + } + pw = getpwuid(buf.st_uid); + + printf("%s\t%s\t%c%s\t%s\n", + timestr, + pw ? pw->pw_name : "???", + queue, + (S_IXUSR & buf.st_mode) ? "" : "(done)", + dirent->d_name); + } + PRIV_END +} + +static void +delete_jobs(argc, argv) + int argc; + char **argv; +{ + /* Delete every argument (job - ID) given */ + int i; + struct stat buf; + + PRIV_START + + if (chdir(_PATH_ATJOBS) != 0) + perr2("Cannot change to ", _PATH_ATJOBS); + + for (i = optind; i < argc; i++) { + if (stat(argv[i], &buf) != 0) + perr(argv[i]); + if ((buf.st_uid != real_uid) && !(real_uid == 0)) { + fprintf(stderr, "%s: Not owner\n", argv[i]); + exit(EXIT_FAILURE); + } + if (unlink(argv[i]) != 0) + perr(argv[i]); + } + PRIV_END +} /* delete_jobs */ + +/* Global functions */ + +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + char queue = 'a'; + char *pgm; + + enum { + ATQ, ATRM, AT, BATCH + }; /* what program we want to run */ + int program = AT; /* our default program */ + char *options = "q:f:mv"; /* default options for at */ + time_t timer; + + RELINQUISH_PRIVS + + /* Eat any leading paths */ + if ((pgm = strrchr(argv[0], '/')) == NULL) + pgm = argv[0]; + else + pgm++; + + namep = pgm; + + /* find out what this program is supposed to do */ + if (strcmp(pgm, "atq") == 0) { + program = ATQ; + options = "q:v"; + } else if (strcmp(pgm, "atrm") == 0) { + program = ATRM; + options = ""; + } else if (strcmp(pgm, "batch") == 0) { + program = BATCH; + options = "f:mv"; + } + + /* process whatever options we can process */ + opterr = 1; + while ((c = getopt(argc, argv, options)) != EOF) + switch (c) { + case 'v': /* verify time settings */ + atverify = 1; + break; + + case 'm': /* send mail when job is complete */ + send_mail = 1; + break; + + case 'f': + atinput = optarg; + break; + + case 'q': /* specify queue */ + if (strlen(optarg) > 1) + usage(); + + atqueue = queue = *optarg; + if ((!islower(queue)) || (queue > 'l')) + usage(); + break; + + default: + usage(); + break; + } + /* end of options eating */ + + /* select our program */ + switch (program) { + case ATQ: + + REDUCE_PRIV(0); + + list_jobs(); + break; + + case ATRM: + + REDUCE_PRIV(0); + + delete_jobs(argc, argv); + break; + + case AT: + timer = parsetime(argc, argv); + if (atverify) { + struct tm *tm = localtime(&timer); + + fprintf(stderr, "%s\n", asctime(tm)); + } + writefile(timer, queue); + break; + + case BATCH: + writefile(time(NULL), 'b'); + break; + + default: + panic("Internal error"); + break; + } + exit(EXIT_SUCCESS); +} diff --git a/usr.bin/at/at.h b/usr.bin/at/at.h new file mode 100644 index 000000000000..eb157bf89cb4 --- /dev/null +++ b/usr.bin/at/at.h @@ -0,0 +1,34 @@ +/* + * at.h - header for at(1) + * Copyright (c) 1993 by Thomas Koenig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: at.h,v 1.1 1994/01/05 01:09:01 nate Exp $ + */ + +extern int fcreated; +extern char *namep; +extern char atfile[]; +extern char atverify; + +#define AT_MAXJOBS 255 /* max jobs outstanding per user */ diff --git a/usr.bin/at/panic.c b/usr.bin/at/panic.c new file mode 100644 index 000000000000..ae55082309b1 --- /dev/null +++ b/usr.bin/at/panic.c @@ -0,0 +1,91 @@ +/* + * panic.c - terminate fast in case of error + * Copyright (c) 1993 by Thomas Koenig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* System Headers */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* Local headers */ + +#include "panic.h" +#include "at.h" + +/* File scope variables */ + +static char rcsid[] = "$Id: panic.c,v 1.1 1994/01/05 01:09:04 nate Exp $"; + +/* External variables */ + +/* Global functions */ + +void +panic(a) + char *a; +{ +/* Something fatal has happened, print error message and exit. + */ + fprintf(stderr, "%s: %s\n", namep, a); + if (fcreated) + unlink(atfile); + + exit(EXIT_FAILURE); +} + +void +perr(a) + char *a; +{ +/* Some operating system error; print error message and exit. + */ + perror(a); + if (fcreated) + unlink(atfile); + + exit(EXIT_FAILURE); +} + +void +perr2(a, b) + char *a, *b; +{ + fprintf(stderr, "%s", a); + perr(b); +} + +void +usage(void) +{ +/* Print usage and exit. +*/ + fprintf(stderr, "Usage: at [-q x] [-f file] [-m] time\n" + " atq [-q x] [-v]\n" + " atrm [-q x] job ...\n" + " batch [-f file] [-m]\n"); + exit(EXIT_FAILURE); +} diff --git a/usr.bin/at/panic.h b/usr.bin/at/panic.h new file mode 100644 index 000000000000..35db3b5115b5 --- /dev/null +++ b/usr.bin/at/panic.h @@ -0,0 +1,32 @@ +/* + * panic.h - header for at(1) + * Copyright (c) 1993 Thomas Koenig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: panic.h,v 1.1 1994/01/05 01:09:06 nate Exp $ + */ + +void panic __P((char *a)); +void perr __P((char *a)); +void perr2 __P((char *a, char *b)); +void usage __P((void)); diff --git a/usr.bin/at/parsetime.c b/usr.bin/at/parsetime.c new file mode 100644 index 000000000000..bd24660cd062 --- /dev/null +++ b/usr.bin/at/parsetime.c @@ -0,0 +1,591 @@ +/* + * parsetime.c - parse time for at(1) + * Copyright (C) 1993 Thomas Koenig + * + * modifications for english-language times + * Copyright (C) 1993 David Parsons + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS + * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ + * |NOON | |[TOMORROW] | + * |MIDNIGHT | |NUMBER [SLASH NUMBER [SLASH NUMBER]]| + * \TEATIME / \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ + */ + +/* System Headers */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <ctype.h> + +/* Local headers */ + +#include "at.h" +#include "panic.h" + + +/* Structures and unions */ + +enum { /* symbols */ + MIDNIGHT, NOON, TEATIME, + PM, AM, TOMORROW, TODAY, NOW, + MINUTES, HOURS, DAYS, WEEKS, + NUMBER, PLUS, DOT, SLASH, ID, JUNK, + JAN, FEB, MAR, APR, MAY, JUN, + JUL, AUG, SEP, OCT, NOV, DEC +}; + +/* + * parse translation table - table driven parsers can be your FRIEND! + */ +struct { + char *name; /* token name */ + int value; /* token id */ +} Specials[] = { + { "midnight", MIDNIGHT }, /* 00:00:00 of today or tomorrow */ + { "noon", NOON }, /* 12:00:00 of today or tomorrow */ + { "teatime", TEATIME }, /* 16:00:00 of today or tomorrow */ + { "am", AM }, /* morning times for 0-12 clock */ + { "pm", PM }, /* evening times for 0-12 clock */ + { "tomorrow", TOMORROW }, /* execute 24 hours from time */ + { "today", TODAY }, /* execute today - don't advance time */ + { "now", NOW }, /* opt prefix for PLUS */ + + { "minute", MINUTES }, /* minutes multiplier */ + { "min", MINUTES }, + { "m", MINUTES }, + { "minutes", MINUTES }, /* (pluralized) */ + { "hour", HOURS }, /* hours ... */ + { "hr", HOURS }, /* abbreviated */ + { "h", HOURS }, + { "hours", HOURS }, /* (pluralized) */ + { "day", DAYS }, /* days ... */ + { "d", DAYS }, + { "days", DAYS }, /* (pluralized) */ + { "week", WEEKS }, /* week ... */ + { "w", WEEKS }, + { "weeks", WEEKS }, /* (pluralized) */ + { "jan", JAN }, + { "feb", FEB }, + { "mar", MAR }, + { "apr", APR }, + { "may", MAY }, + { "jun", JUN }, + { "jul", JUL }, + { "aug", AUG }, + { "sep", SEP }, + { "oct", OCT }, + { "nov", NOV }, + { "dec", DEC } +} ; + +/* File scope variables */ + +static char **scp; /* scanner - pointer at arglist */ +static char scc; /* scanner - count of remaining arguments */ +static char *sct; /* scanner - next char pointer in current argument */ +static int need; /* scanner - need to advance to next argument */ + +static char *sc_token; /* scanner - token buffer */ +static size_t sc_len; /* scanner - lenght of token buffer */ +static int sc_tokid; /* scanner - token id */ + +static char rcsid[] = "$Id: parsetime.c,v 1.1 1994/01/05 01:09:08 nate Exp $"; + +/* Local functions */ + +/* + * parse a token, checking if it's something special to us + */ +static int +parse_token(arg) + char *arg; +{ + int i; + + for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) + if (strcasecmp(Specials[i].name, arg) == 0) { + return sc_tokid = Specials[i].value; + } + + /* not special - must be some random id */ + return ID; +} /* parse_token */ + + +/* + * init_scanner() sets up the scanner to eat arguments + */ +static void +init_scanner(argc, argv) + int argc; + char **argv; +{ + scp = argv; + scc = argc; + need = 1; + sc_len = 1; + while (--argc > 0) + sc_len += strlen(*++argv); + + sc_token = (char *) malloc(sc_len); + if (sc_token == NULL) + panic("Insufficient virtual memory"); +} /* init_scanner */ + +/* + * token() fetches a token from the input stream + */ +static int +token() +{ + int idx; + + while (1) { + memset(sc_token, 0, sc_len); + sc_tokid = EOF; + idx = 0; + + /* + * if we need to read another argument, walk along the argument list; + * when we fall off the arglist, we'll just return EOF forever + */ + if (need) { + if (scc < 1) + return sc_tokid; + sct = *scp; + scp++; + scc--; + need = 0; + } + /* + * eat whitespace now - if we walk off the end of the argument, + * we'll continue, which puts us up at the top of the while loop + * to fetch the next argument in + */ + while (isspace(*sct)) + ++sct; + if (!*sct) { + need = 1; + continue; + } + + /* + * preserve the first character of the new token + */ + sc_token[0] = *sct++; + + /* + * then see what it is + */ + if (isdigit(sc_token[0])) { + while (isdigit(*sct)) + sc_token[++idx] = *sct++; + sc_token[++idx] = 0; + return sc_tokid = NUMBER; + } else if (isalpha(sc_token[0])) { + while (isalpha(*sct)) + sc_token[++idx] = *sct++; + sc_token[++idx] = 0; + return parse_token(sc_token); + } + else if (sc_token[0] == ':' || sc_token[0] == '.') + return sc_tokid = DOT; + else if (sc_token[0] == '+') + return sc_tokid = PLUS; + else if (*sct == '/') + return sc_tokid = SLASH; + else + return sc_tokid = JUNK; + } /* while (1) */ +} /* token */ + + +/* + * plonk() gives an appropriate error message if a token is incorrect + */ +static void +plonk(tok) + int tok; +{ + panic((tok == EOF) ? "incomplete time" + : "garbled time"); +} /* plonk */ + + +/* + * expect() gets a token and dies most horribly if it's not the token we want + */ +static void +expect(desired) + int desired; +{ + if (token() != desired) + plonk(sc_tokid); /* and we die here... */ +} /* expect */ + + +/* + * dateadd() adds a number of minutes to a date. It is extraordinarily + * stupid regarding day-of-month overflow, and will most likely not + * work properly + */ +static void +dateadd(minutes, tm) + int minutes; + struct tm *tm; +{ + /* increment days */ + + while (minutes > 24*60) { + minutes -= 24*60; + tm->tm_mday++; + } + + /* increment hours */ + while (minutes > 60) { + minutes -= 60; + tm->tm_hour++; + if (tm->tm_hour > 23) { + tm->tm_mday++; + tm->tm_hour = 0; + } + } + + /* increment minutes */ + tm->tm_min += minutes; + + if (tm->tm_min > 59) { + tm->tm_hour++; + tm->tm_min -= 60; + + if (tm->tm_hour > 23) { + tm->tm_mday++; + tm->tm_hour = 0; + } + } +} /* dateadd */ + + +/* + * plus() parses a now + time + * + * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS] + * + */ +static void +plus(tm) + struct tm *tm; +{ + int delay; + + expect(NUMBER); + + delay = atoi(sc_token); + + switch (token()) { + case WEEKS: + delay *= 7; + case DAYS: + delay *= 24; + case HOURS: + delay *= 60; + case MINUTES: + dateadd(delay, tm); + return; + } + plonk(sc_tokid); +} /* plus */ + + +/* + * tod() computes the time of day + * [NUMBER [DOT NUMBER] [AM|PM]] + */ +static void +tod(tm) + struct tm *tm; +{ + int hour, minute = 0; + int tlen; + + hour = atoi(sc_token); + tlen = strlen(sc_token); + + /* + * first pick out the time of day - if it's 4 digits, we assume + * a HHMM time, otherwise it's HH DOT MM time + */ + if (token() == DOT) { + expect(NUMBER); + minute = atoi(sc_token); + if (minute > 59) + panic("garbled time"); + token(); + } else if (tlen == 4) { + minute = hour%100; + if (minute > 59) + panic("garbeld time"); + hour = hour/100; + } + + /* + * check if an AM or PM specifier was given + */ + if (sc_tokid == AM || sc_tokid == PM) { + if (hour > 12) + panic("garbled time"); + + if (sc_tokid == PM) + hour += 12; + token(); + } else if (hour > 23) + panic("garbled time"); + + /* + * if we specify an absolute time, we don't want to bump the day even + * if we've gone past that time - but if we're specifying a time plus + * a relative offset, it's okay to bump things + */ + if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) + tm->tm_mday++; + + tm->tm_hour = hour; + tm->tm_min = minute; + if (tm->tm_hour == 24) { + tm->tm_hour = 0; + tm->tm_mday++; + } +} /* tod */ + + +/* + * assign_date() assigns a date, wrapping to next year if needed + */ +static void +assign_date(tm, mday, mon, year) + struct tm *tm; + long mday, mon, year; +{ + if (year > 99) { + if (year > 1899) + year -= 1900; + else + panic("garbled time"); + } + + if (year < 0 && + (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) + year = tm->tm_year + 1; + + tm->tm_mday = mday; + tm->tm_mon = mon; + + if (year >= 0) + tm->tm_year = year; +} /* assign_date */ + + +/* + * month() picks apart a month specification + * + * /[<month> NUMBER [NUMBER]] \ + * |[TOMORROW] | + * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| + * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ + */ +static void +month(tm) + struct tm *tm; +{ + long year= (-1); + long mday, mon; + int tlen; + + switch (sc_tokid) { + case PLUS: + plus(tm); + break; + + case TOMORROW: + /* do something tomorrow */ + tm->tm_mday ++; + case TODAY: /* force ourselves to stay in today - no further processing */ + token(); + break; + + case JAN: case FEB: case MAR: case APR: case MAY: case JUN: + case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: + /* + * do month mday [year] + */ + mon = (sc_tokid-JAN); + expect(NUMBER); + mday = atol(sc_token)-1; + if (token() == NUMBER) { + year = atol(sc_token); + token(); + } + assign_date(tm, mday, mon, year); + break; + + case NUMBER: + /* + * get numeric MMDDYY, mm/dd/yy, or dd.mm.yy + */ + tlen = strlen(sc_token); + mon = atol(sc_token); + token(); + + if (sc_tokid == SLASH || sc_tokid == DOT) { + int sep; + + sep = sc_tokid; + expect(NUMBER); + mday = atol(sc_token); + if (token() == sep) { + expect(NUMBER); + year = atol(sc_token); + token(); + } + + /* + * flip months and days for european timing + */ + if (sep == DOT) { + int x = mday; + mday = mon; + mon = x; + } + } else if (tlen == 6 || tlen == 8) { + if (tlen == 8) { + year = (mon % 10000) - 1900; + mon /= 10000; + } else { + year = mon % 100; + mon /= 100; + } + mday = mon % 100; + mon /= 100; + } else + panic("garbled time"); + + mon--; + if (mon < 0 || mon > 11 || mday < 1 || mday > 31) + panic("garbled time"); + + assign_date(tm, mday, mon, year); + break; + } /* case */ +} /* month */ + + +/* Global functions */ + +time_t +parsetime(argc, argv) + int argc; + char **argv; +{ +/* + * Do the argument parsing, die if necessary, and return the time the job + * should be run. + */ + time_t nowtimer, runtimer; + struct tm nowtime, runtime; + int hr = 0; + /* this MUST be initialized to zero for midnight/noon/teatime */ + + nowtimer = time(NULL); + nowtime = *localtime(&nowtimer); + + runtime = nowtime; + runtime.tm_sec = 0; + runtime.tm_isdst = 0; + + if (argc <= optind) + usage(); + + init_scanner(argc-optind, argv+optind); + + switch (token()) { + case NOW: /* now is optional prefix for PLUS tree */ + expect(PLUS); + case PLUS: + plus(&runtime); + break; + + case NUMBER: + tod(&runtime); + month(&runtime); + break; + + /* + * evil coding for TEATIME|NOON|MIDNIGHT - we've initialised + * hr to zero up above, then fall into this case in such a + * way so we add +12 +4 hours to it for teatime, +12 hours + * to it for noon, and nothing at all for midnight, then + * set our runtime to that hour before leaping into the + * month scanner + */ + case TEATIME: + hr += 4; + case NOON: + hr += 12; + case MIDNIGHT: + if (runtime.tm_hour >= hr) + runtime.tm_mday++; + runtime.tm_hour = hr; + runtime.tm_min = 0; + token(); + /* fall through to month setting */ + default: + month(&runtime); + break; + } /* ugly case statement */ + expect(EOF); + + /* + * adjust for daylight savings time + */ + runtime.tm_isdst = -1; + runtimer = mktime(&runtime); + if (runtime.tm_isdst > 0) { + runtimer -= 3600; + runtimer = mktime(&runtime); + } + + if (runtimer < 0) + panic("garbled time"); + + if (nowtimer > runtimer) + panic("Trying to travel back in time"); + + return runtimer; +} /* parsetime */ diff --git a/usr.bin/at/parsetime.h b/usr.bin/at/parsetime.h new file mode 100644 index 000000000000..dd03868b1b04 --- /dev/null +++ b/usr.bin/at/parsetime.h @@ -0,0 +1,29 @@ +/* + * at.h - header for at(1) + * Copyright (c) 1993 by Thomas Koenig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: parsetime.h,v 1.1 1994/01/05 01:09:10 nate Exp $ + */ + +time_t parsetime __P((int argc, char **argv)); diff --git a/usr.bin/at/pathnames.h b/usr.bin/at/pathnames.h new file mode 100644 index 000000000000..5a544fd9dbd3 --- /dev/null +++ b/usr.bin/at/pathnames.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: pathnames.h,v 1.1 1994/01/05 01:09:12 nate Exp $ + */ + +#ifndef _PATHNAMES_H_ +#define _PATHNAMES_H_ + +#include <paths.h> + +#define _PATH_ATJOBS "/var/at/jobs/" +#define _PATH_ATSPOOL "/var/at/spool/" +#define _PATH_LOCKFILE "/var/at/lockfile" + +#endif /* !_PATHNAMES_H_ */ diff --git a/usr.bin/at/privs.h b/usr.bin/at/privs.h new file mode 100644 index 000000000000..67bc444e710b --- /dev/null +++ b/usr.bin/at/privs.h @@ -0,0 +1,92 @@ +/* + * privs.h - header for privileged operations + * Copyright (c) 1993 by Thomas Koenig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: privs.h,v 1.1 1994/01/05 01:09:14 nate Exp $ + */ + +#ifndef _PRIVS_H +#define _PRIVS_H + +#include <unistd.h> + +/* Relinquish privileges temporarily for a setuid program + * with the option of getting them back later. This is done by swapping + * the real and effective userid BSD style. Call RELINQUISH_PRIVS once + * at the beginning of the main program. This will cause all operatons + * to be executed with the real userid. When you need the privileges + * of the setuid invocation, call PRIV_START; when you no longer + * need it, call PRIV_END. Note that it is an error to call PRIV_START + * and not PRIV_END within the same function. + * + * Use RELINQUISH_PRIVS_ROOT(a) if your program started out running + * as root, and you want to drop back the effective userid to a + * and the effective group id to b, with the option to get them back + * later. + * + * If you no longer need root privileges, but those of some other + * userid/groupid, you can call REDUCE_PRIV(a) when your effective + * is the user's. + * + * Problems: Do not use return between PRIV_START and PRIV_END; this + * will cause the program to continue running in an unprivileged + * state. + * + * It is NOT safe to call exec(), system() or popen() with a user- + * supplied program (i.e. without carefully checking PATH and any + * library load paths) with relinquished privileges; the called program + * can aquire them just as easily. Set both effective and real userid + * to the real userid before calling any of them. + */ + +#ifndef MAIN +extern +#endif +uid_t real_uid, effective_uid; + +#define RELINQUISH_PRIVS { \ + real_uid = getuid(); \ + effective_uid = geteuid(); \ + setreuid(effective_uid,real_uid); \ +} + +#define RELINQUISH_PRIVS_ROOT(a) { \ + real_uid = (a); \ + effective_uid = geteuid(); \ + setreuid(effective_uid,real_uid); \ +} + +#define PRIV_START { \ + setreuid(real_uid,effective_uid); + +#define PRIV_END \ + setreuid(effective_uid,real_uid); \ +} + +#define REDUCE_PRIV(a) { \ + setreuid(real_uid,effective_uid); \ + effective_uid = (a); \ + setreuid(effective_uid,real_uid); \ +} +#endif diff --git a/usr.bin/basename/Makefile b/usr.bin/basename/Makefile index f31a89a699d5..d29e796b269b 100644 --- a/usr.bin/basename/Makefile +++ b/usr.bin/basename/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.2 (Berkeley) 5/11/90 +# from: @(#)Makefile 5.2 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/11/23 00:01:04 jtc Exp $ PROG= basename MLINKS= basename.1 dirname.1 diff --git a/usr.bin/basename/basename.1 b/usr.bin/basename/basename.1 index 2568d4485ef8..8f332628817f 100644 --- a/usr.bin/basename/basename.1 +++ b/usr.bin/basename/basename.1 @@ -32,7 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)basename.1 6.7 (Berkeley) 6/27/91 +.\" from: @(#)basename.1 6.7 (Berkeley) 6/27/91 +.\" $Id: basename.1,v 1.2.2.1 1994/05/01 16:08:06 jkh Exp $ .\" .Dd June 27, 1991 .Dt BASENAME 1 @@ -56,15 +57,7 @@ and a .Ar suffix , if given. The resulting filename is written to the standard output. -If -.Ar string -ends in the slash character, -.Ql / , -or is the same as the -.Ar suffix -argument, -a newline is output. -A non-existant suffix is ignored. +A non-existent suffix is ignored. .Pp .Nm Dirname deletes the filename portion, beginning @@ -85,13 +78,15 @@ Both the .Nm basename and .Nm dirname +utilities exit 0 on success, and >0 if an error occurs. .Sh SEE ALSO -.Xr csh 1 +.Xr csh 1 , .Xr sh 1 .Sh STANDARDS The .Nm basename and .Nm dirname -functions are expected to be POSIX 1003.2 compatible. +utilities conform to +.St -p1003.2-92 . diff --git a/usr.bin/basename/basename.c b/usr.bin/basename/basename.c index 18ff63686de0..29c43bf41660 100644 --- a/usr.bin/basename/basename.c +++ b/usr.bin/basename/basename.c @@ -38,22 +38,28 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)basename.c 5.1 (Berkeley) 3/9/91"; +/*static char sccsid[] = "from: @(#)basename.c 5.1 (Berkeley) 3/9/91";*/ +static char rcsid[] = "$Id: basename.c,v 1.2 1993/11/23 00:01:06 jtc Exp $"; #endif /* not lint */ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <locale.h> +static void usage __P((void)); + +int main(argc, argv) int argc; char **argv; { - extern int optind; register char *p; int ch; - while ((ch = getopt(argc, argv, "")) != EOF) + setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "")) != -1) switch(ch) { case '?': default: @@ -89,8 +95,10 @@ main(argc, argv) * (3) If there are any trailing slash characters in string, they * shall be removed. */ - for (; *p; ++p); - while (*--p == '/'); + for (; *p; ++p) + ; + while (*--p == '/') + ; *++p = '\0'; /* @@ -125,6 +133,7 @@ main(argc, argv) exit(0); } +static void usage() { (void)fprintf(stderr, "usage: basename string [suffix]\n"); diff --git a/usr.bin/cal/cal.c b/usr.bin/cal/cal.c index 6bc6a7b80336..f308ab82cf72 100644 --- a/usr.bin/cal/cal.c +++ b/usr.bin/cal/cal.c @@ -199,6 +199,7 @@ monthly(month, year) for (col = 0, p = lineout; col < 7; col++, p += julian ? J_DAY_LEN : DAY_LEN) ascii_day(p, days[row * 7 + col]); + *p = 0; trim_trailing_spaces(lineout); (void)printf("%s\n", lineout); } diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1 index 901bcc7102ff..79c72146cabc 100644 --- a/usr.bin/chpass/chpass.1 +++ b/usr.bin/chpass/chpass.1 @@ -165,7 +165,7 @@ style database of user attributes. .Pp The user's .Ar home directory -is the full UNIX path name where the user +is the full path name where the user will be placed at login. .Pp The diff --git a/usr.bin/chpass/util.c b/usr.bin/chpass/util.c index b3db7c9ede5f..dce81ca60d13 100644 --- a/usr.bin/chpass/util.c +++ b/usr.bin/chpass/util.c @@ -60,8 +60,8 @@ ttoa(tval) if (tval) { tp = localtime(&tval); - (void)sprintf(tbuf, "%s %d, 19%d", months[tp->tm_mon], - tp->tm_mday, tp->tm_year); + (void)sprintf(tbuf, "%s %d, %d", months[tp->tm_mon], + tp->tm_mday, tp->tm_year + TM_YEAR_BASE); } else *tbuf = '\0'; diff --git a/usr.bin/cmp/cmp.1 b/usr.bin/cmp/cmp.1 index ab003e1c37b6..cf64dfd48920 100644 --- a/usr.bin/cmp/cmp.1 +++ b/usr.bin/cmp/cmp.1 @@ -32,7 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)cmp.1 6.7 (Berkeley) 6/27/91 +.\" from: @(#)cmp.1 6.7 (Berkeley) 6/27/91 +.\" $Id: cmp.1,v 1.2 1993/11/23 00:17:16 jtc Exp $ .\" .Dd June 27, 1991 .Dt CMP 1 @@ -95,4 +96,5 @@ An error occurred. .Sh STANDARDS The .Nm cmp -function is expected to be POSIX 1003.2 compatible. +utility conforms to +.St -p1003.2-92 . diff --git a/usr.bin/cmp/cmp.c b/usr.bin/cmp/cmp.c index c512819bef49..6ef2d70a8173 100644 --- a/usr.bin/cmp/cmp.c +++ b/usr.bin/cmp/cmp.c @@ -38,7 +38,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)cmp.c 5.3 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)cmp.c 5.3 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: cmp.c,v 1.2 1993/11/23 00:17:17 jtc Exp $"; #endif /* not lint */ #include <sys/param.h> @@ -58,14 +59,14 @@ char *file1, *file2; main(argc, argv) int argc; - char *argv[]; + char **argv; { extern char *optarg; extern int optind; int ch; u_long otoi(); - while ((ch = getopt(argc, argv, "-ls")) != EOF) + while ((ch = getopt(argc, argv, "ls")) != -1) switch (ch) { case 'l': /* print all differences */ all = 1; @@ -73,25 +74,19 @@ main(argc, argv) case 's': /* silent run */ silent = 1; break; - case '-': /* must be after any flags */ - --optind; - goto endargs; case '?': default: usage(); } -endargs: argv += optind; argc -= optind; if (argc < 2 || argc > 4) usage(); - if (all && silent) { - fprintf(stderr, - "cmp: only one of -l and -s may be specified.\n"); - exit(EXITERR); - } + if (all && silent) + usage (); + if (strcmp(file1 = argv[0], "-") == 0) fd1 = 0; else if ((fd1 = open(file1, O_RDONLY, 0)) < 0) @@ -257,10 +252,8 @@ error(filename) endoffile(filename) char *filename; { - /* 32V put this message on stdout, S5 does it on stderr. */ - /* POSIX.2 currently does it on stdout-- Hooray! */ if (!silent) - (void) printf("cmp: EOF on %s\n", filename); + (void) fprintf(stderr, "cmp: EOF on %s\n", filename); exit(EXITDIFF); } @@ -270,6 +263,6 @@ endoffile(filename) */ usage() { - fputs("usage: cmp [-ls] file1 file2 [skip1] [skip2]\n", stderr); + fputs("usage: cmp [-l | -s] file1 file2 [skip1] [skip2]\n", stderr); exit(EXITERR); } diff --git a/usr.bin/comm/comm.1 b/usr.bin/comm/comm.1 index 3f743469eaf2..b177a6da842b 100644 --- a/usr.bin/comm/comm.1 +++ b/usr.bin/comm/comm.1 @@ -32,7 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)comm.1 6.7 (Berkeley) 6/27/91 +.\" from: @(#)comm.1 6.7 (Berkeley) 6/27/91 +.\" $Id: comm.1,v 1.2 1993/11/23 00:20:03 jtc Exp $ .\" .Dd June 27, 1991 .Os @@ -80,7 +81,15 @@ printed in column number three will have one. .Nm Comm assumes that the files are lexically sorted; all characters participate in line comparisons. -.Pp +.\" .Sh ENVIRONMENT +.\" .Bl -tag -width indent +.\" .It Ev LANG +.\" .It Ev LC_ALL +.\" .It Ev LC_CTYPE +.\" .It Ev LC_COLLATE +.\" .It Ev LC_MESSAGES +.\" .El +.Sh DIAGNOSTICS .Nm Comm exits 0 on success, >0 if an error occurred. .Sh SEE ALSO @@ -91,4 +100,5 @@ exits 0 on success, >0 if an error occurred. .Sh STANDARDS The .Nm comm -command is expected to be POSIX 1003.2 compatible. +utility conforms to +.St -p1003.2-92 . diff --git a/usr.bin/comm/comm.c b/usr.bin/comm/comm.c index e09e1a0f6018..3c90f6ed3930 100644 --- a/usr.bin/comm/comm.c +++ b/usr.bin/comm/comm.c @@ -41,34 +41,41 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)comm.c 5.7 (Berkeley) 11/1/90"; +/*static char sccsid[] = "from: @(#)comm.c 5.7 (Berkeley) 11/1/90";*/ +static char rcsid[] = "$Id: comm.c,v 1.2 1993/11/23 00:20:04 jtc Exp $"; #endif /* not lint */ -#include <sys/file.h> -#include <limits.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <locale.h> +#include <unistd.h> #define MAXLINELEN (_POSIX2_LINE_MAX + 1) char *tabs[] = { "", "\t", "\t\t" }; +FILE *file __P((const char *)); +void show __P((FILE *, char *, char *)); +void usage __P((void)); + +int main(argc,argv) int argc; - char *argv[]; + char **argv; { register int comp, file1done, file2done, read1, read2; register char *col1, *col2, *col3; int ch, flag1, flag2, flag3; - FILE *fp1, *fp2, *file(); + FILE *fp1, *fp2; char **p, line1[MAXLINELEN], line2[MAXLINELEN]; - extern int optind; + + setlocale(LC_ALL, ""); flag1 = flag2 = flag3 = 1; - while ((ch = getopt(argc, argv, "-123")) != EOF) + while ((ch = getopt(argc, argv, "123")) != -1) switch(ch) { - case '-': - --optind; - goto done; case '1': flag1 = 0; break; @@ -82,7 +89,7 @@ main(argc,argv) default: usage(); } -done: argc -= optind; + argc -= optind; argv += optind; if (argc != 2) @@ -121,7 +128,7 @@ done: argc -= optind; } /* lines are the same */ - if (!(comp = strcmp(line1, line2))) { + if (!(comp = strcoll(line1, line2))) { read1 = read2 = 1; if (col3) (void)printf("%s%s", col3, line1); @@ -144,6 +151,7 @@ done: argc -= optind; exit(0); } +void show(fp, offset, buf) FILE *fp; char *offset, *buf; @@ -155,7 +163,7 @@ show(fp, offset, buf) FILE * file(name) - char *name; + const char *name; { FILE *fp; @@ -168,8 +176,9 @@ file(name) return(fp); } +void usage() { - (void)fprintf(stderr, "usage: comm [-123] [ - ] file1 file2\n"); + (void)fprintf(stderr, "usage: comm [-123] file1 file2\n"); exit(1); } diff --git a/usr.bin/compress/doc/revision.log b/usr.bin/compress/doc/revision.log index 1aefc0f0dd9e..bea6ae770bf9 100644 --- a/usr.bin/compress/doc/revision.log +++ b/usr.bin/compress/doc/revision.log @@ -1,5 +1,5 @@ /* - * $Header: /a/cvs/386BSD/src/usr.bin/compress/doc/revision.log,v 1.1.1.1 1993/06/12 14:53:55 rgrimes Exp $ + * $Header: /home/cvs/386BSD/src/usr.bin/compress/doc/revision.log,v 1.1.1.1 1993/06/12 14:53:55 rgrimes Exp $ * $Log: revision.log,v $ # Revision 1.1.1.1 1993/06/12 14:53:55 rgrimes # Initial import, 0.1 + pk 0.2.4-B1 @@ -116,4 +116,4 @@ */ static char rcs_ident[] = - "$Header: /a/cvs/386BSD/src/usr.bin/compress/doc/revision.log,v 1.1.1.1 1993/06/12 14:53:55 rgrimes Exp $"; + "$Header: /home/cvs/386BSD/src/usr.bin/compress/doc/revision.log,v 1.1.1.1 1993/06/12 14:53:55 rgrimes Exp $"; diff --git a/usr.bin/crontab/Makefile b/usr.bin/crontab/Makefile index 64577960205a..2a251b747aa3 100644 --- a/usr.bin/crontab/Makefile +++ b/usr.bin/crontab/Makefile @@ -1,19 +1,10 @@ -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00131 -# -------------------- ----- ---------------------- -# -# 06 Apr 93 Adam Glass Fixed so manual pages install, install crontab -# with proper permissions and owners -# PROG= crontab SRCS= crontab.c misc.c entry.c env.c -CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../libexec/crond -DDEBUGGING=1 -DBSD -DCRONDIR='"/var/cron"' -fstrength-reduce +CFLAGS+= -I${.CURDIR}/../../usr.sbin/cron BINOWN =root BINMODE=4111 MAN1= crontab.1 MAN5= crontab.5 .include <bsd.prog.mk> -.PATH: ${.CURDIR}/../../libexec/crond +.PATH: ${.CURDIR}/../../usr.sbin/cron diff --git a/usr.bin/crontab/crontab.1 b/usr.bin/crontab/crontab.1 index ab1acbb1abe9..b4601e58df5b 100644 --- a/usr.bin/crontab/crontab.1 +++ b/usr.bin/crontab/crontab.1 @@ -1,6 +1,4 @@ -.\" $Header: /a/cvs/386BSD/src/usr.bin/crontab/crontab.1,v 1.1.1.1 1993/06/12 14:53:53 rgrimes Exp $ -.\" -.\"/* Copyright 1988,1990 by Paul Vixie +.\"/* Copyright 1988,1990,1993 by Paul Vixie .\" * All rights reserved .\" * .\" * Distribute freely, except: don't remove my name from the source or @@ -14,27 +12,28 @@ .\" * .\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and .\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013, -.\" * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul +.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul .\" */ -.TH CRONTAB 1 "9 December 1988" +.\" +.\" $Header: /home/cvs/386BSD/src/usr.bin/crontab/crontab.1,v 1.2.2.1 1994/05/01 16:08:10 jkh Exp $ +.\" +.\" From Id: crontab.1,v 2.4 1993/12/31 10:47:33 vixie Exp +.\" +.TH CRONTAB 1 "29 December 1993" .UC 4 .SH NAME -crontab \- maintain crontab files for individual users +crontab \- maintain crontab files for individual users (V3) .SH SYNOPSIS -crontab [ -u user ] { -l | -d | -r file } +crontab [ -u user ] file +.br +crontab [ -u user ] { -l | -r | -e } .SH DESCRIPTION .I Crontab is the program used to install, deinstall or list the tables used to drive the -.IR crond (8) -daemon in Vixie Cron. Each user has their own crontab, and though these are -files in /var, they are not readable or writable by anyone or anything -except the super-user or the -.IR crond (8) -or -.I crontab -programs. +.IR cron (8) +daemon in Vixie Cron. Each user can have their own crontab, and though +these are files in /var, they are not intended to be edited directly. .PP If the .I allow @@ -66,28 +65,38 @@ you should always use the .I -u option for safety's sake. .PP +The first form of this command is used to install a new crontab from some +named file or standard input if the pseudo-filename ``-'' is given. +.PP The .I -l option causes the current crontab to be displayed on standard output. .PP The -.I -d -option causes the current crontab to be deleted. +.I -r +option causes the current crontab to be removed. .PP The -.I -r -option is used to replace the current -crontab (if any) with the contents of the named file. +.I -e +option is used to edit the current crontab using the editor specified by +the \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit +from the editor, the modified crontab will be installed automatically. .SH "SEE ALSO" -crontab(5), crond(8) +crontab(5), cron(8) .SH FILES .nf /var/cron/allow /var/cron/deny .fi +.SH STANDARDS +The +.I crontab +command conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax +differs from previous versions of Vixie Cron, as well as from the classic +SVR3 syntax. .SH DIAGNOSTICS A fairly informative usage message appears if you run it with a bad command line. .SH AUTHOR .nf -Paul Vixie, paul@vixie.sf.ca.us +Paul Vixie <paul@vix.com> diff --git a/usr.bin/crontab/crontab.386bsd b/usr.bin/crontab/crontab.386bsd deleted file mode 100644 index 9070efa38c02..000000000000 --- a/usr.bin/crontab/crontab.386bsd +++ /dev/null @@ -1,27 +0,0 @@ -# /etc/crontab.src - root's crontab for Vixie's cron -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00131 -# -------------------- ----- ---------------------- -# -# 06 Apr 93 Adam Glass Fixed to be a better template for 386bsd -# -SHELL=/bin/sh -PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin -HOME=/var/log -# -#minute hour mday month wday command -# -#0/10 * * * * /etc/dmesg - >> messages -#0/15 * * * * /usr/lib/atrun -#37 4 * * * ./cronrun-day > cronout-day 2>&1 -#43 4 * * Sun ./cronrun-wk > cronout-wk 2>&1 -#51 4 1 * * ./cronrun-mon > cronout-mon 2>&1 -0/15 * * * * /usr/libexec/atrun -0 2 * * * /etc/daily > /var/log/daily.out 2>&1 -30 3 * * 6 /etc/weekly > /var/log/weekly.out 2>&1 -30 5 1 * * /etc/monthly > /var/log/monthly.out 2>&1 -#45 * * * * /bin/echo /usr/local/bin/uucico -r1 | /bin/su uucpa -#0-59/5 * * * * /usr/local/bin/uucico -r1 -#37 * * * * touch /var/spool/uucp/jpunix/C./C.A0000 diff --git a/usr.bin/crontab/crontab.5 b/usr.bin/crontab/crontab.5 index 3ea488704842..e8e683428aee 100644 --- a/usr.bin/crontab/crontab.5 +++ b/usr.bin/crontab/crontab.5 @@ -1,6 +1,4 @@ -.\" $Header: /a/cvs/386BSD/src/usr.bin/crontab/crontab.5,v 1.1 1993/07/02 07:25:42 root Exp $ -.\" -.\"/* Copyright 1988,1990 by Paul Vixie +.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie .\" * All rights reserved .\" * .\" * Distribute freely, except: don't remove my name from the source or @@ -14,10 +12,14 @@ .\" * .\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and .\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013, -.\" * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul +.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul .\" */ -.TH CRONTAB 5 "15 January 1990" +.\" +.\" $Header: /home/cvs/386BSD/src/usr.bin/crontab/crontab.5,v 1.3.2.1 1994/05/01 16:08:13 jkh Exp $ +.\" +.\" From Id: crontab.5,v 2.4 1994/01/15 20:43:43 vixie Exp +.\" +.TH CRONTAB 5 "24 January 1994" .UC 4 .SH NAME crontab \- tables for driving cron @@ -25,7 +27,7 @@ crontab \- tables for driving cron A .I crontab file contains instructions to the -.IR crond (8) +.IR cron (8) daemon of the general form: ``run this command at this time on this date''. Each user has their own crontab, and commands in any given crontab will be executed as the user who owns the crontab. Uucp and News will usually have @@ -56,19 +58,17 @@ leading or trailing blanks. .PP Several environment variables are set up automatically by the -.IR crond (8) -daemon from the /etc/passwd line of the crontab's owner: USER, HOME, and SHELL. -HOME and SHELL may be overridden by settings in the crontab; USER may not. -.PP -(Note: for UUCP, always set SHELL=/bin/sh, or -.IR crond (8) -will cheerfully try to execute your commands using /usr/lib/uucp/uucico.) +.IR cron (8) +daemon. +SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd +line of the crontab's owner. +HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not. .PP -(Another note: the USER variable is sometimes called LOGNAME or worse on -System V... on these systems, LOGNAME will be set rather than USER.) +(Another note: the LOGNAME variable is sometimes called USER on BSD systems... +on these systems, USER will be set also.) .PP -In addition to USER, HOME, and SHELL, -.IR crond (8) +In addition to LOGNAME, HOME, and SHELL, +.IR cron (8) will look at MAILTO if it has any reason to send mail as a result of running commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no @@ -79,13 +79,14 @@ usually doesn't read its mail. .PP The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has five time and date fields, +followed by a user name if this is the system crontab file, followed by a command. Commands are executed by -.IR crond (8) +.IR cron (8) when the minute, hour, and month of year fields match the current time, .I and when at least one of the two day fields (day of month, or day of week) match the current time (see ``Note'' below). -.IR crond (8) +.IR cron (8) examines cron entries once every minute. The time and date fields are: .IP @@ -105,8 +106,7 @@ month 0-12 (or names, see below) day of week 0-7 (0 or 7 is Sun, or use names) .br .PP -A field may be an asterisk (*), which always matches the -current time. +A field may be an asterisk (*), which always stands for ``first\-last''. .PP Ranges of numbers are allowed. Ranges are two numbers separated with a hyphen. The specified range is inclusive. For example, @@ -120,7 +120,9 @@ Step values can be used in conjunction with ranges. Following a range with ``/<number>'' specifies skips of the number's value through the range. For example, ``0-23/2'' can be used in the hours field to specify command execution every other hour (the alternative -in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). +in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are +also permitted after an asterisk, so if you want to say ``every two +hours'', just use ``*/2''. .PP Names can also be used for the ``month'' and ``day of week'' fields. Use the first three letters of the particular @@ -130,7 +132,7 @@ lists of names are not allowed. The ``sixth'' field (the rest of the line) specifies the command to be run. The entire command portion of the line, up to a newline or % -character, will be executed by the user's login shell or by the shell +character, will be executed by /bin/sh or by the shell specified in the SHELL variable of the cronfile. Percent-signs (%) in the command, unless escaped with backslash (\\), will be changed into newline characters, and all data @@ -164,7 +166,7 @@ MAILTO=paul 5 4 * * sun echo "run at 5 after 4 every sunday" .fi .SH SEE ALSO -crond(8), crontab(1) +cron(8), crontab(1) .SH EXTENSIONS When specifying day of week, both day 0 and day 7 will be considered Sunday. BSD and ATT seem to disagree about this. @@ -185,4 +187,4 @@ feature can be turned off and no mail will be sent at all (SysV can't do this either). .SH AUTHOR .nf -Paul Vixie, paul@vixie.sf.ca.us +Paul Vixie <paul@vix.com> diff --git a/usr.bin/crontab/crontab.c b/usr.bin/crontab/crontab.c index e65dbbdb3d32..a325aa0d02f0 100644 --- a/usr.bin/crontab/crontab.c +++ b/usr.bin/crontab/crontab.c @@ -1,38 +1,4 @@ -#if !defined(lint) && !defined(LINT) -static char rcsid[] = "$Header: /a/cvs/386BSD/src/usr.bin/crontab/crontab.c,v 1.1.1.1 1993/06/12 14:53:53 rgrimes Exp $"; -#endif - -/* Revision 1.5 87/05/02 17:33:22 paul - * pokecron? (RCS file has the rest of the log) - * - * Revision 1.5 87/05/02 17:33:22 paul - * baseline for mod.sources release - * - * Revision 1.4 87/03/31 13:11:48 paul - * I won't say that rs@mirror gave me this idea but crontab uses getopt() now - * - * Revision 1.3 87/03/30 23:43:48 paul - * another suggestion from rs@mirror: - * use getpwuid(getuid)->pw_name instead of getenv("USER") - * this is a boost to security... - * - * Revision 1.2 87/02/11 17:40:12 paul - * changed command syntax to allow append and replace instead of append as - * default and no replace at all. - * - * Revision 1.1 87/01/26 23:49:06 paul - * Initial revision - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00131 - * -------------------- ----- ---------------------- - * - * 06 Apr 93 Adam Glass Fixes so it compiles quitely - * - */ - -/* Copyright 1988,1990 by Paul Vixie +/* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved * * Distribute freely, except: don't remove my name from the source or @@ -46,8 +12,17 @@ static char rcsid[] = "$Header: /a/cvs/386BSD/src/usr.bin/crontab/crontab.c,v 1. * * Send bug reports, bug fixes, enhancements, requests, flames, etc., and * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013, - * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul + * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul + * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp + */ + +#if !defined(lint) && !defined(LINT) +static char rcsid[] = "$Header: /home/cvs/386BSD/src/usr.bin/crontab/crontab.c,v 1.3 1994/01/27 19:06:16 nate Exp $"; +#endif + +/* crontab - install and manage per-user crontab files + * vix 02may87 [RCS has the rest of the log] + * vix 26jan87 [original] */ @@ -55,50 +30,76 @@ static char rcsid[] = "$Header: /a/cvs/386BSD/src/usr.bin/crontab/crontab.c,v 1. #include "cron.h" -#include <pwd.h> #include <errno.h> +#include <fcntl.h> #include <sys/file.h> -#if defined(BSD) +#include <sys/stat.h> +#ifdef USE_UTIMES # include <sys/time.h> -#endif /*BSD*/ +#else +# include <time.h> +# include <utime.h> +#endif +#if defined(POSIX) +# include <locale.h> +#endif -/* extern char *sprintf(); */ +#define NHEADER_LINES 3 -static int Pid; -static char User[MAX_UNAME], RealUser[MAX_UNAME]; -static char Filename[MAX_FNAME]; -static FILE *NewCrontab; -static int CheckErrorCount; -static enum {opt_unknown, opt_list, opt_delete, opt_replace} - Option; -extern void log_it(); +enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; #if DEBUGGING -static char *Options[] = {"???", "list", "delete", "replace"}; +static char *Options[] = { "???", "list", "delete", "edit", "replace" }; #endif -void -usage() + +static PID_T Pid; +static char User[MAX_UNAME], RealUser[MAX_UNAME]; +static char Filename[MAX_FNAME]; +static FILE *NewCrontab; +static int CheckErrorCount; +static enum opt_t Option; +static struct passwd *pw; +static void list_cmd __P((void)), + delete_cmd __P((void)), + edit_cmd __P((void)), + poke_daemon __P((void)), + check_error __P((char *)), + parse_args __P((int c, char *v[])); +static int replace_cmd __P((void)); + + +static void +usage(msg) + char *msg; { - fprintf(stderr, "usage: %s [-u user] ...\n", ProgramName); - fprintf(stderr, " ... -l (list user's crontab)\n"); - fprintf(stderr, " ... -d (delete user's crontab)\n"); - fprintf(stderr, " ... -r file (replace user's crontab)\n"); + fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg); + fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName); + fprintf(stderr, "\t%s [-u user] { -e | -l | -r }\n", ProgramName); + fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n"); + fprintf(stderr, "\t-e\t(edit user's crontab)\n"); + fprintf(stderr, "\t-l\t(list user's crontab)\n"); + fprintf(stderr, "\t-r\t(delete user's crontab)\n"); exit(ERROR_EXIT); } +int main(argc, argv) int argc; char *argv[]; { - void parse_args(), set_cron_uid(), set_cron_cwd(), - list_cmd(), delete_cmd(), replace_cmd(); + int exitstatus; Pid = getpid(); ProgramName = argv[0]; + +#if defined(POSIX) + setlocale(LC_ALL, ""); +#endif + #if defined(BSD) setlinebuf(stderr); #endif @@ -113,35 +114,31 @@ main(argc, argv) log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); exit(ERROR_EXIT); } - switch (Option) - { + exitstatus = OK_EXIT; + switch (Option) { case opt_list: list_cmd(); break; case opt_delete: delete_cmd(); break; - case opt_replace: replace_cmd(); + case opt_edit: edit_cmd(); + break; + case opt_replace: if (replace_cmd() < 0) + exitstatus = ERROR_EXIT; break; } + exit(0); + /*NOTREACHED*/ } - void +static void parse_args(argc, argv) int argc; char *argv[]; { - void usage(); - char *getenv(), *strcpy(); - int getuid(); - struct passwd *getpwnam(); - extern int getopt(), optind; - extern char *optarg; - - struct passwd *pw; int argch; - if (!(pw = getpwuid(getuid()))) - { + if (!(pw = getpwuid(getuid()))) { fprintf(stderr, "%s: your UID isn't in the passwd file.\n", ProgramName); fprintf(stderr, "bailing out.\n"); @@ -151,13 +148,11 @@ parse_args(argc, argv) strcpy(RealUser, User); Filename[0] = '\0'; Option = opt_unknown; - while (EOF != (argch = getopt(argc, argv, "u:ldr:x:"))) - { - switch (argch) - { + while (EOF != (argch = getopt(argc, argv, "u:lerx:"))) { + switch (argch) { case 'x': if (!set_debug_flags(optarg)) - usage(); + usage("bad debug option"); break; case 'u': if (getuid() != ROOT_UID) @@ -166,7 +161,7 @@ parse_args(argc, argv) "must be privileged to use -u\n"); exit(ERROR_EXIT); } - if ((struct passwd *)NULL == getpwnam(optarg)) + if (!(pw = getpwnam(optarg))) { fprintf(stderr, "%s: user `%s' unknown\n", ProgramName, optarg); @@ -176,38 +171,40 @@ parse_args(argc, argv) break; case 'l': if (Option != opt_unknown) - usage(); + usage("only one operation permitted"); Option = opt_list; break; - case 'd': + case 'r': if (Option != opt_unknown) - usage(); + usage("only one operation permitted"); Option = opt_delete; break; - case 'r': + case 'e': if (Option != opt_unknown) - usage(); - Option = opt_replace; - (void) strcpy(Filename, optarg); + usage("only one operation permitted"); + Option = opt_edit; break; default: - usage(); + usage("unrecognized option"); } } endpwent(); - if (Option == opt_unknown || argv[optind] != NULL) - usage(); + if (Option != opt_unknown) { + if (argv[optind] != NULL) { + usage("no arguments permitted after this option"); + } + } else { + if (argv[optind] != NULL) { + Option = opt_replace; + (void) strcpy (Filename, argv[optind]); + } else { + usage("file name must be specified for replace"); + } + } if (Option == opt_replace) { - if (!Filename[0]) { - /* getopt(3) says this can't be true - * but I'm paranoid today. - */ - fprintf(stderr, "filename must be given for -a or -r\n"); - usage(); - } /* we have to open the file here because we're going to * chdir(2) into /var/cron before we get around to * reading the file. @@ -215,30 +212,43 @@ parse_args(argc, argv) if (!strcmp(Filename, "-")) { NewCrontab = stdin; } else { + /* relinquish the setuid status of the binary during + * the open, lest nonroot users read files they should + * not be able to read. we can't use access() here + * since there's a race condition. thanks go out to + * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting + * the race. + */ + + if (swap_uids() < OK) { + perror("swapping uids"); + exit(ERROR_EXIT); + } if (!(NewCrontab = fopen(Filename, "r"))) { perror(Filename); exit(ERROR_EXIT); } + if (swap_uids() < OK) { + perror("swapping uids back"); + exit(ERROR_EXIT); + } } } Debug(DMISC, ("user=%s, file=%s, option=%s\n", - User, Filename, Options[(int)Option])) + User, Filename, Options[(int)Option])) } - void -list_cmd() -{ - extern errno; +static void +list_cmd() { char n[MAX_FNAME]; FILE *f; int ch; log_it(RealUser, Pid, "LIST", User); (void) sprintf(n, CRON_TAB(User)); - if (!(f = fopen(n, "r"))) - { + if (!(f = fopen(n, "r"))) { if (errno == ENOENT) fprintf(stderr, "no crontab for %s\n", User); else @@ -255,18 +265,13 @@ list_cmd() } - void -delete_cmd() -{ - extern errno; - int unlink(); - void poke_daemon(); +static void +delete_cmd() { char n[MAX_FNAME]; log_it(RealUser, Pid, "DELETE", User); (void) sprintf(n, CRON_TAB(User)); - if (unlink(n)) - { + if (unlink(n)) { if (errno == ENOENT) fprintf(stderr, "no crontab for %s\n", User); else @@ -277,58 +282,250 @@ delete_cmd() } - void +static void check_error(msg) char *msg; { - CheckErrorCount += 1; - fprintf(stderr, "\"%s\", line %d: %s\n", Filename, LineNumber, msg); + CheckErrorCount++; + fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); } - void -replace_cmd() -{ - entry *load_entry(); - int load_env(); - int unlink(); - void free_entry(); - void check_error(); - void poke_daemon(); - extern errno; +static void +edit_cmd() { + char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; + FILE *f; + int ch, t, x; + struct stat statbuf; + time_t mtime; + WAIT_T waiter; + PID_T pid, xpid; + + log_it(RealUser, Pid, "BEGIN EDIT", User); + (void) sprintf(n, CRON_TAB(User)); + if (!(f = fopen(n, "r"))) { + if (errno != ENOENT) { + perror(n); + exit(ERROR_EXIT); + } + fprintf(stderr, "no crontab for %s - using an empty one\n", + User); + if (!(f = fopen("/dev/null", "r"))) { + perror("/dev/null"); + exit(ERROR_EXIT); + } + } + (void) sprintf(Filename, "/tmp/crontab.%d", Pid); + if (-1 == (t = open(Filename, O_CREAT|O_EXCL|O_RDWR, 0600))) { + perror(Filename); + goto fatal; + } +#ifdef HAS_FCHOWN + if (fchown(t, getuid(), getgid()) < 0) { +#else + if (chown(Filename, getuid(), getgid()) < 0) { +#endif + perror("fchown"); + goto fatal; + } + if (!(NewCrontab = fdopen(t, "r+"))) { + perror("fdopen"); + goto fatal; + } + + Set_LineNum(1) + + /* ignore the top few comments since we probably put them there. + */ + for (x = 0; x < NHEADER_LINES; x++) { + ch = get_char(f); + if (EOF == ch) + break; + if ('#' != ch) { + putc(ch, NewCrontab); + break; + } + while (EOF != (ch = get_char(f))) + if (ch == '\n') + break; + if (EOF == ch) + break; + } + + /* copy the rest of the crontab (if any) to the temp file. + */ + if (EOF != ch) + while (EOF != (ch = get_char(f))) + putc(ch, NewCrontab); + fclose(f); + if (fflush(NewCrontab) < OK) { + perror(Filename); + exit(ERROR_EXIT); + } + again: + rewind(NewCrontab); + if (ferror(NewCrontab)) { + fprintf(stderr, "%s: error while writing new crontab to %s\n", + ProgramName, Filename); + fatal: unlink(Filename); + exit(ERROR_EXIT); + } + if (fstat(t, &statbuf) < 0) { + perror("fstat"); + goto fatal; + } + mtime = statbuf.st_mtime; + + if ((!(editor = getenv("VISUAL"))) + && (!(editor = getenv("EDITOR"))) + ) { + editor = EDITOR; + } + + /* we still have the file open. editors will generally rewrite the + * original file rather than renaming/unlinking it and starting a + * new one; even backup files are supposed to be made by copying + * rather than by renaming. if some editor does not support this, + * then don't use it. the security problems are more severe if we + * close and reopen the file around the edit. + */ + + switch (pid = fork()) { + case -1: + perror("fork"); + goto fatal; + case 0: + /* child */ + if (setuid(getuid()) < 0) { + perror("setuid(getuid())"); + exit(ERROR_EXIT); + } + if (chdir("/tmp") < 0) { + perror("chdir(/tmp)"); + exit(ERROR_EXIT); + } + if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) { + fprintf(stderr, "%s: editor or filename too long\n", + ProgramName); + exit(ERROR_EXIT); + } + sprintf(q, "%s %s", editor, Filename); + execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL); + perror(editor); + exit(ERROR_EXIT); + /*NOTREACHED*/ + default: + /* parent */ + break; + } + + /* parent */ + xpid = wait(&waiter); + if (xpid != pid) { + fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n", + ProgramName, xpid, pid, editor); + goto fatal; + } + if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + fprintf(stderr, "%s: \"%s\" exited with status %d\n", + ProgramName, editor, WEXITSTATUS(waiter)); + goto fatal; + } + if (WIFSIGNALED(waiter)) { + fprintf(stderr, + "%s: \"%s\" killed; signal %d (%score dumped)\n", + ProgramName, editor, WTERMSIG(waiter), + WCOREDUMP(waiter) ?"" :"no "); + goto fatal; + } + if (fstat(t, &statbuf) < 0) { + perror("fstat"); + goto fatal; + } + if (mtime == statbuf.st_mtime) { + fprintf(stderr, "%s: no changes made to crontab\n", + ProgramName); + goto remove; + } + fprintf(stderr, "%s: installing new crontab\n", ProgramName); + switch (replace_cmd()) { + case 0: + break; + case -1: + for (;;) { + printf("Do you want to retry the same edit? "); + fflush(stdout); + q[0] = '\0'; + (void) fgets(q, sizeof q, stdin); + switch (islower(q[0]) ? q[0] : tolower(q[0])) { + case 'y': + goto again; + case 'n': + goto abandon; + default: + fprintf(stderr, "Enter Y or N\n"); + } + } + /*NOTREACHED*/ + case -2: + abandon: + fprintf(stderr, "%s: edits left in %s\n", + ProgramName, Filename); + goto done; + default: + fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n"); + goto fatal; + } + remove: + unlink(Filename); + done: + log_it(RealUser, Pid, "END EDIT", User); +} + + +/* returns 0 on success + * -1 on syntax error + * -2 on install error + */ +static int +replace_cmd() { char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; FILE *tmp; - int ch; + int ch, eof; entry *e; - int status; time_t now = time(NULL); + char **envp = env_init(); (void) sprintf(n, "tmp.%d", Pid); (void) sprintf(tn, CRON_TAB(n)); - if (!(tmp = fopen(tn, "w"))) { + if (!(tmp = fopen(tn, "w+"))) { perror(tn); - exit(ERROR_EXIT); + return (-2); } - /* write a signature at the top of the file. for brian. + /* write a signature at the top of the file. + * + * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. */ + fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n"); fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now)); fprintf(tmp, "# (Cron version -- %s)\n", rcsid); /* copy the crontab to the tmp */ + rewind(NewCrontab); Set_LineNum(1) while (EOF != (ch = get_char(NewCrontab))) putc(ch, tmp); - fclose(NewCrontab); + ftruncate(fileno(tmp), ftell(tmp)); fflush(tmp); rewind(tmp); if (ferror(tmp)) { fprintf(stderr, "%s: error while writing new crontab to %s\n", ProgramName, tn); fclose(tmp); unlink(tn); - exit(ERROR_EXIT); + return (-2); } /* check the syntax of the file being installed. @@ -338,80 +535,91 @@ replace_cmd() * in the file proper -- kludged it by stopping after first error. * vix 31mar87 */ - CheckErrorCount = 0; - while (!CheckErrorCount && (status = load_env(envstr, tmp)) >= OK) - { - if (status == FALSE) - { - if (NULL != (e = load_entry(NewCrontab, check_error))) - free((char *) e); + Set_LineNum(1 - NHEADER_LINES) + CheckErrorCount = 0; eof = FALSE; + while (!CheckErrorCount && !eof) { + switch (load_env(envstr, tmp)) { + case ERR: + eof = TRUE; + break; + case FALSE: + e = load_entry(tmp, check_error, pw, envp); + if (e) + free(e); + break; + case TRUE: + break; } } - if (CheckErrorCount != 0) - { + if (CheckErrorCount != 0) { fprintf(stderr, "errors in crontab file, can't install.\n"); fclose(tmp); unlink(tn); - exit(ERROR_EXIT); + return (-1); } +#ifdef HAS_FCHOWN if (fchown(fileno(tmp), ROOT_UID, -1) < OK) +#else + if (chown(tn, ROOT_UID, -1) < OK) +#endif { perror("chown"); fclose(tmp); unlink(tn); - exit(ERROR_EXIT); + return (-2); } +#ifdef HAS_FCHMOD if (fchmod(fileno(tmp), 0600) < OK) +#else + if (chmod(tn, 0600) < OK) +#endif { perror("chown"); fclose(tmp); unlink(tn); - exit(ERROR_EXIT); + return (-2); } if (fclose(tmp) == EOF) { perror("fclose"); unlink(tn); - exit(ERROR_EXIT); + return (-2); } (void) sprintf(n, CRON_TAB(User)); - if (rename(tn, n)) - { + if (rename(tn, n)) { fprintf(stderr, "%s: error renaming %s to %s\n", ProgramName, tn, n); perror("rename"); unlink(tn); - exit(ERROR_EXIT); + return (-2); } log_it(RealUser, Pid, "REPLACE", User); poke_daemon(); + + return (0); } - void -poke_daemon() -{ -#if defined(BSD) +static void +poke_daemon() { +#ifdef USE_UTIMES struct timeval tvs[2]; struct timezone tz; (void) gettimeofday(&tvs[0], &tz); tvs[1] = tvs[0]; - if (utimes(SPOOL_DIR, tvs) < OK) - { + if (utimes(SPOOL_DIR, tvs) < OK) { fprintf(stderr, "crontab: can't update mtime on spooldir\n"); perror(SPOOL_DIR); return; } -#endif /*BSD*/ -#if defined(ATT) - if (utime(SPOOL_DIR, NULL) < OK) - { +#else + if (utime(SPOOL_DIR, NULL) < OK) { fprintf(stderr, "crontab: can't update mtime on spooldir\n"); perror(SPOOL_DIR); return; } -#endif /*ATT*/ +#endif /*USE_UTIMES*/ } diff --git a/usr.bin/ctags/ctags.1 b/usr.bin/ctags/ctags.1 index 363204a3edc4..d94912f4392c 100644 --- a/usr.bin/ctags/ctags.1 +++ b/usr.bin/ctags/ctags.1 @@ -83,7 +83,7 @@ that take arguments are tagged automatically. .It Fl f Places the tag descriptions in a file called .Ar tagsfile . -The default behaviour is to place them in a file +The default behavior is to place them in a file .Ar tags . .It Fl t create tags for typedefs, structs, unions, and enums. diff --git a/usr.bin/dirname/Makefile b/usr.bin/dirname/Makefile index 446ed1a4344a..402481741c51 100644 --- a/usr.bin/dirname/Makefile +++ b/usr.bin/dirname/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.2 (Berkeley) 5/11/90 +# from: @(#)Makefile 5.2 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/11/23 00:01:34 jtc Exp $ PROG= dirname NOMAN= noman diff --git a/usr.bin/dirname/dirname.c b/usr.bin/dirname/dirname.c index 662cd5bfb42c..8a37986cd18a 100644 --- a/usr.bin/dirname/dirname.c +++ b/usr.bin/dirname/dirname.c @@ -38,21 +38,27 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)dirname.c 5.6 (Berkeley) 3/9/91"; +/*static char sccsid[] = "from: @(#)dirname.c 5.6 (Berkeley) 3/9/91";*/ +static char rcsid[] = "$Id: dirname.c,v 1.2 1993/11/23 00:01:35 jtc Exp $"; #endif /* not lint */ #include <stdio.h> #include <stdlib.h> +#include <locale.h> +static void usage __P((void)); + +int main(argc, argv) int argc; char **argv; { - extern int optind; register char *p; int ch; - while ((ch = getopt(argc, argv, "")) != EOF) + setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "")) != -1) switch(ch) { case '?': default: @@ -86,8 +92,10 @@ main(argc, argv) * (3) If there are any trailing slash characters in string, they * shall be removed. */ - for (; *p; ++p); - while (*--p == '/'); + for (; *p; ++p) + ; + while (*--p == '/') + ; *++p = '\0'; /* @@ -132,6 +140,7 @@ main(argc, argv) exit(0); } +static void usage() { (void)fprintf(stderr, "usage: dirname path\n"); diff --git a/usr.bin/elvis/Makefile b/usr.bin/elvis/Makefile index eb65165b4821..d13400abdb19 100644 --- a/usr.bin/elvis/Makefile +++ b/usr.bin/elvis/Makefile @@ -1,13 +1,13 @@ CFLAGS+=-DBSD -DREGEX DPADD= ${LIBTERM} -LDADD= -ltermcap -lgnuregex +LDADD= -ltermcap PROG= elvis SRCS= blk.c cmd1.c cmd2.c ctype.c curses.c cut.c ex.c input.c main.c misc.c \ modify.c move1.c move2.c move3.c move4.c move5.c opts.c recycle.c \ redraw.c regexp.c regsub.c system.c tio.c tmp.c unix.c vars.c vcmd.c vi.c -LINKS= ${BINDIR}/elvis ${BINDIR}/vi ${BINDIR}/elvis ${BINDIR}/ex \ - ${BINDIR}/elvis ${BINDIR}/view +#LINKS= ${BINDIR}/elvis ${BINDIR}/vi ${BINDIR}/elvis ${BINDIR}/ex \ +# ${BINDIR}/elvis ${BINDIR}/view .include <bsd.prog.mk> diff --git a/usr.bin/elvis/cmd1.c b/usr.bin/elvis/cmd1.c index c3d3f1b7f298..2c9d58a871ec 100644 --- a/usr.bin/elvis/cmd1.c +++ b/usr.bin/elvis/cmd1.c @@ -388,14 +388,14 @@ void cmd_global(frommark, tomark, cmd, bang, extra) /* parse & compile the search pattern */ cmdptr = parseptrn(extra); +#ifdef REGEX + re = optpat(extra + 1); +#else if (!extra[1]) { msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); return; } -#ifdef REGEX - re = optpat(extra + 1); -#else re = regcomp(extra + 1); #endif if (!re) diff --git a/usr.bin/elvis/cmd2.c b/usr.bin/elvis/cmd2.c index 361799f85e9f..25192c0b52da 100644 --- a/usr.bin/elvis/cmd2.c +++ b/usr.bin/elvis/cmd2.c @@ -260,9 +260,11 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) /* substitute for the matched part */ #ifdef REGEX - regsub(rm, startp, endp, subst, d); + if (regsub(rm, startp, endp, subst, d) < 0) + return; #else - regsub(re, subst, d); + if (regsub(re, subst, d) < 0) + return; #endif #ifdef REGEX s = endp; diff --git a/usr.bin/elvis/elvis.1 b/usr.bin/elvis/elvis.1 index 8d10b78a44db..69d590b242ff 100644 --- a/usr.bin/elvis/elvis.1 +++ b/usr.bin/elvis/elvis.1 @@ -1,3 +1,5 @@ +.\" $Id: elvis.1,v 1.3 1994/02/23 12:06:09 rgrimes Exp $ +.\" .TH ELVIS 1 .SH NAME elvis, ex, vi, view, input - The editor @@ -25,9 +27,10 @@ as though the "-i" flag was given. .SH OPTIONS .IP \fB-r\fP To the real vi, this flag means that a previous edit should be recovered. -\fIElvis\fP, though, has a separate program, called \fIelvrec(1)\fP, for recovering -files. -When you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run \fIelvrec\fP. +\fIElvis\fP, though, has a separate program, called \fIelvisrecover(1)\fP, +for recovering files. +When you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run +\fIelvisrecover\fP. .IP \fB-R\fP This sets the "readonly" option, so you won't accidentally overwrite a file. diff --git a/usr.bin/elvis/regexp.c b/usr.bin/elvis/regexp.c index 43a8672a1354..2c5383e716f1 100644 --- a/usr.bin/elvis/regexp.c +++ b/usr.bin/elvis/regexp.c @@ -49,45 +49,120 @@ regex_t * optpat(s) char *s; { - static char *neuter(); + char *neuter(); + char *expand_tilde(); int n; if (*s == '\0') { - if (!previous) regerr("RE error: no previous pattern"); + if (!previous) regerr("no previous pattern"); return previous; } else if (previous && !patlock) regfree(previous); else if ((previous = (regex_t *) malloc(sizeof(regex_t))) == NULL) { - regerr("RE error: out of memory"); + regerr("out of memory"); return previous; } patlock = 0; - if (n = regcomp(previous, *o_magic ? s : neuter(s), - *o_ignorecase ? REG_ICASE : 0)) { - regerr("RE error: %d", n); + if ((s = *o_magic ? expand_tilde(s) : neuter(s)) == NULL) { + free(previous); + return previous = NULL; + } else if (n = regcomp(previous, s, *o_ignorecase ? REG_ICASE : 0)) { + regerr("%d", n); free(previous); return previous = NULL; } return previous; } -/* escape BRE meta-characters in a string */ -static char * +extern char *last_repl; /* replacement text from previous substitute */ + +/* expand_tilde: expand ~'s in a BRE */ +char * +expand_tilde(s) + char *s; +{ + char *literalize(); + static char *hd = NULL; + + char *t, *repl; + int size; + int offset; + int m; + + free(hd); + hd = t = malloc(size = strlen(s) + 1); + while (*s) + if (*s == '\\' && *(s + 1) == '~') { + *t++ = *s++; + *t++ = *s++; + } else if (*s != '~') + *t++ = *s++; + else { + if (!last_repl) { + regerr("no previous replacement"); + return NULL; + } else if ((repl = literalize(last_repl)) == NULL) + return NULL; + m = strlen(repl); + offset = t - hd; + if ((hd = realloc(hd, size += m)) == NULL) { + regerr("out of memory"); + return NULL; + } + t = hd + offset; + strcpy(t, repl); + t += m; + s++; + } + *t = '\0'; + return hd; +} + + +/* escape BRE meta-characters and expand ~'s in a string */ +char * neuter(s) char *s; { + char *literalize(); static char *hd = NULL; - char *t; - int n = strlen(s); + char *t, *repl; + int size; + int offset; + int m; free(hd); - if ((hd = t = (char *) malloc(n + n + 1)) == NULL) + if ((hd = t = (char *) malloc(size = 2 * strlen(s) + 1)) == NULL) { + regerr("out of memory"); return NULL; + } if (*s == '^') *t++ = *s++; while (*s) { - if (*s == '.' || *s == '\\' || *s == '[' || *s == '*') + if (*s == '\\' && (*(s + 1) == '.' || *(s + 1) == '*' || + *(s + 1) == '[')) { + s++; + *t++ = *s++; + continue; + } else if (*s == '\\' && *(s + 1) == '~') { + if (!last_repl) { + regerr("no previous replacement"); + return NULL; + } else if ((repl = literalize(last_repl)) == NULL) + return NULL; + m = strlen(repl); + offset = t - hd; + if ((hd = realloc(hd, size += m)) == NULL) { + regerr("out of memory"); + return NULL; + } + t = hd + (offset); + strcpy(t, repl); + t += m; + s += 2; + continue; + } else if (*s == '.' || *s == '\\' || *s == '[' || *s == '*') *t++ = '\\'; *t++ = *s++; } @@ -95,6 +170,63 @@ neuter(s) return hd; } + +/* escape BRE meta-characters in a string */ +char * +literalize(s) + char *s; +{ + static char *hd = NULL; + + char *t; + int size; + int offset; + int m; + + free(hd); + if ((hd = t = (char *) malloc(size = 2 * strlen(s) + 1)) == NULL) { + regerr("out of memory"); + return NULL; + } + if (*o_magic) + while (*s) { + if (*s == '~' || *s == '&') { + regerr("can't use ~ or & in pattern"); + return NULL; + } else if (*s == '\\') { + s++; + if (isdigit(*s)) { + regerr("can't use \\d in pattern"); + return NULL; + } else if (*s == '&' || *s == '~') { + *t++ = '\\'; + *t++ = *s++; + } + } else if (*s == '^' || *s == '$' || *s == '.' || + *s == '[' || *s == '*') { + *t++ = '\\'; + *t++ = *s++; + } else + *t++ = *s++; + } + else + while (*s) { + if (*s == '\\') { + s++; + if (*s == '&' || *s == '~') { + regerr("can't use \\~ or \\& in pattern"); + return NULL; + } else if (isdigit(*s)) { + regerr("can't use \\d in pattern"); + return NULL; + } + } else + *t++ = *s++; + } + *t = '\0'; + return hd; +} + #else static char *previous; /* the previous regexp, used when null regexp is given */ diff --git a/usr.bin/elvis/regexp.h b/usr.bin/elvis/regexp.h index eb8726a67fdd..9dd79795d7d8 100644 --- a/usr.bin/elvis/regexp.h +++ b/usr.bin/elvis/regexp.h @@ -17,5 +17,5 @@ typedef struct regexp { extern regexp *regcomp(); extern int regexec(); -extern void regsub(); +extern int regsub(); extern void regerr(); diff --git a/usr.bin/elvis/regsub.c b/usr.bin/elvis/regsub.c index fe74e89f4f3f..12bd4fd52334 100644 --- a/usr.bin/elvis/regsub.c +++ b/usr.bin/elvis/regsub.c @@ -13,16 +13,17 @@ # include "regexp.h" #endif +char *last_repl; /* a copy of the text from the previous subst */ /* perform substitutions after a regexp match */ #ifdef REGEX -void regsub(rm, startp, endp, src, dst) +int regsub(rm, startp, endp, src, dst) regmatch_t *rm; /* the regexp with pointers into matched text */ char *startp, *endp; REG char *src; /* the replacement string */ REG char *dst; /* where to put the result of the subst */ #else -void regsub(re, src, dst) +int regsub(re, src, dst) regexp *re; /* the regexp with pointers into matched text */ REG char *src; /* the replacement string */ REG char *dst; /* where to put the result of the subst */ @@ -35,7 +36,6 @@ void regsub(re, src, dst) #ifndef CRUNCH int mod = 0;/* used to track \U, \L, \u, \l, and \E */ int len; /* used to calculate length of subst string */ - static char *prev; /* a copy of the text from the previous subst */ /* replace \~ (or maybe ~) by previous substitution text */ @@ -48,13 +48,13 @@ void regsub(re, src, dst) if (c == (*o_magic ? '\0' : '\\') && *cpy == '~') # endif { - if (!prev) + if (!last_repl) { regerr("No prev text to substitute for ~"); - return; + return -1; } - len += strlen(prev) - 1; + len += strlen(last_repl) - 1; # ifndef NO_MAGIC if (!*o_magic) # endif @@ -74,7 +74,7 @@ void regsub(re, src, dst) if (!cpy) { regerr("Not enough memory for ~ expansion"); - return; + return -1; } /* copy src into start, replacing the ~s by the previous text */ @@ -83,8 +83,8 @@ void regsub(re, src, dst) # ifndef NO_MAGIC if (*o_magic && *src == '~') { - strcpy(cpy, prev); - cpy += strlen(prev); + strcpy(cpy, last_repl); + cpy += strlen(last_repl); src++; } else if (!*o_magic && *src == '\\' && *(src + 1) == '~') @@ -92,10 +92,14 @@ void regsub(re, src, dst) if (*src == '\\' && *(src + 1) == '~') # endif /* NO_MAGIC */ { - strcpy(cpy, prev); - cpy += strlen(prev); + strcpy(cpy, last_repl); + cpy += strlen(last_repl); src += 2; } + else if (*o_magic && *src == '\\' && *(src + 1) == '~') { + *cpy++ = *src++; + *cpy++ = *src++; + } else { *cpy++ = *src++; @@ -111,9 +115,9 @@ void regsub(re, src, dst) checkmem(); /* remember this as the "previous" for next time */ - if (prev) - _free_(prev); - prev = src = start; + if (last_repl) + _free_(last_repl); + last_repl = src = start; #endif /* undef CRUNCH */ @@ -275,4 +279,5 @@ void regsub(re, src, dst) } } *dst = '\0'; + return 0; } diff --git a/usr.bin/elvis/vi.c b/usr.bin/elvis/vi.c index 2c4648f46193..7250e6efe5a4 100644 --- a/usr.bin/elvis/vi.c +++ b/usr.bin/elvis/vi.c @@ -510,7 +510,8 @@ void vi() /* do it */ tcurs = (*keyptr->func)(cursor, count, i); } - count = 0L; + if (keyptr->args != C_C_K_CUT) + count = 0L; break; case CURSOR_MOVED: diff --git a/usr.bin/env/env.1 b/usr.bin/env/env.1 index fcefb261e5d2..8631f3fce2e1 100644 --- a/usr.bin/env/env.1 +++ b/usr.bin/env/env.1 @@ -31,9 +31,10 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)printenv.1 6.7 (Berkeley) 7/28/91 +.\" from: @(#)printenv.1 6.7 (Berkeley) 7/28/91 +.\" $Id: env.1,v 1.2.2.1 1994/05/01 16:08:19 jkh Exp $ .\" -.Dd July 14, 1993 +.Dd August 27, 1993 .Dt ENV 1 .Os .Sh NAME @@ -44,13 +45,13 @@ .Op Fl i .Op Ar name=value ... .Oo -.Ar command +.Ar utility .Op argument ... .Oc .Sh DESCRIPTION .Nm env executes -.Ar command +.Ar utility after modifying the environment as specified on the command line. The option .Ar name=value @@ -67,47 +68,56 @@ to completely ignore the environment it inherits. .Pp If no -.Ar command +.Ar utility is specified, .Nm env prints out the names and values -of the variables in the environment, with one name/value pair per line. +of the variables in the environment, with one +.Ar name=value +pair per line. .Sh DIAGNOSTICS If the -.Ar command +.Ar utility is invoked, the exit status of .Nm env shall be the exit status of -.Ar command ; +.Ar utility; otherwise, the .Nm env -command exits with one of the following values: +utility exits with one of the following values: .Bl -tag -width Ds .It 0 The .Nm env -command completed successfully +utility completed successfully .It 1-125 -An error occured. +An error occurred in the +.Nm env +utility. .It 126 -The -.Ar command +The utility specified by +.Ar utility was found, but could not be invoked. .It 127 -The -.Ar command +The utility specified by +.Ar utility could not be found. .El +.Sh COMPATIBILITY +The historic +.Fl +option has been deprecated but is still supported in this implementation. .Sh SEE ALSO .Xr execvp 3 , .Xr environ 7 .Sh STANDARDS The .Nm env -command is expected to be -.St -p1003.2 -compliant. +utility conforms to +.St -p1003.2-92 . .Sh BUGS -.Nm env -doesn't handle commands with equal (``='') signs in their +.Nm Env +doesn't handle commands with equal +.Pq Dq = +signs in their names, for obvious reasons. diff --git a/usr.bin/env/env.c b/usr.bin/env/env.c index 7583a696bc55..6db59920db4d 100644 --- a/usr.bin/env/env.c +++ b/usr.bin/env/env.c @@ -38,15 +38,21 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)env.c 5.3 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)env.c 5.3 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: env.c,v 1.3 1993/11/23 00:32:59 jtc Exp $"; #endif /* not lint */ #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <locale.h> #include <errno.h> +#include <unistd.h> +#include <err.h> static void usage(); +int main(argc, argv) int argc; char **argv; @@ -57,7 +63,9 @@ main(argc, argv) char *cleanenv[1]; int ch; - while ((ch = getopt(argc, argv, "-i")) != EOF) + setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "-i")) != -1) switch((char)ch) { case '-': /* obsolete */ case 'i': @@ -75,12 +83,10 @@ main(argc, argv) if (*argv) { /* return 127 if the command to be run could not be found; 126 if the command was was found but could not be invoked */ - int status; execvp(*argv, argv); - status = (errno == ENOENT) ? 127 : 126; - (void)fprintf(stderr, "env: %s: %s\n", *argv, strerror(errno)); - exit(status); + err((errno == ENOENT) ? 127 : 126, "%s", *argv); + /* NOTREACHED */ } for (ep = environ; *ep; ep++) diff --git a/usr.bin/f2c/Makefile b/usr.bin/f2c/Makefile new file mode 100644 index 000000000000..eea5cf4ce5bb --- /dev/null +++ b/usr.bin/f2c/Makefile @@ -0,0 +1,32 @@ +# Makefile for f2c, a Fortran 77 to C converter + +PROG= f2c + +g = -O -g +CFLAGS = $g -DANSI_Libraries -I${.CURDIR} -I. +SHELL = /bin/sh + +SRCSd = main.c init.c gram.c lex.c proc.c equiv.c data.c format.c \ + expr.c exec.c intr.c io.c misc.c error.c mem.c names.c \ + output.c p1output.c pread.c put.c putpcc.c vax.c formatdata.c \ + parse_args.c niceprintf.c cds.c sysdep.c version.c +SRCS = $(SRCSd) malloc.c + +GRAMFILES = ${.CURDIR}/gram.head ${.CURDIR}/gram.dcl ${.CURDIR}/gram.expr\ + ${.CURDIR}/gram.exec ${.CURDIR}/gram.io + +gram.c: ${GRAMFILES} ${.CURDIR}/defs.h tokdefs.h + (sed < tokdefs.h "s/#define/%token/" ; \ + cat ${GRAMFILES}) > gram.in + $(YACC) $(YFLAGS) gram.in + echo "(expect 4 shift/reduce)" + sed 's/^# line.*/\/* & *\//' y.tab.c >gram.c + rm -f gram.in y.tab.c + +tokdefs.h: ${.CURDIR}/tokens + grep -n . <${.CURDIR}/tokens | sed "s/\([^:]*\):\(.*\)/#define \2 \1/" >tokdefs.h + +CLEANFILES+=\ + gram.c tokdefs.h y.tab.h + +.include <bsd.prog.mk> diff --git a/usr.bin/f2c/Notice b/usr.bin/f2c/Notice new file mode 100644 index 000000000000..64af9f12dc4e --- /dev/null +++ b/usr.bin/f2c/Notice @@ -0,0 +1,23 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + diff --git a/usr.bin/f2c/README b/usr.bin/f2c/README new file mode 100644 index 000000000000..ed88aaa81c75 --- /dev/null +++ b/usr.bin/f2c/README @@ -0,0 +1,94 @@ +Type "make" to check the validity of the f2c source and compile f2c. + +On a PC, you may need to compile xsum.c with -DMSDOS (i.e., with +MSDOS #defined). If your system does not understand ANSI/ISO C +syntax (i.e., if you have a K&R C compiler), compile xsum.c with +-DKR_headers. (Eventually this will also be required of the f2c +source proper.) + +On non-Unix systems where files have separate binary and text modes, +you may need to "make xsumr.out" rather than "make xsum.out". + +If (in accordance with what follows) you need to modify the makefile +or any of the source files, first issue a "make xsum.out" (or, if +appropriate, "make xsumr.out") to check the validity of the f2c source, +then make your changes, then type "make f2c". + +The file usignal.h is for the benefit of strictly ANSI include files +on a UNIX system -- the ANSI signal.h does not define SIGHUP or SIGQUIT. +You may need to modify usignal.h if you are not running f2c on a UNIX +system. + +Should you get the message "xsum0.out xsum1.out differ", see what lines +are different (`diff xsum0.out xsum1.out`) and ask netlib to send you +the files in question "from f2c/src". For example, if exec.c and +expr.c have incorrect check sums, you would send netlib the message + send exec.c expr.c from f2c/src + +On some systems, the malloc and free in malloc.c let f2c run faster +than do the standard malloc and free. Other systems cannot tolerate +redefinition of malloc and free. If yours is such a system, you may +either modify the makefile appropriately, or simply execute + cc -c -DCRAY malloc.c +before typing "make". Still other systems have a -lmalloc that +provides performance competitive with that from malloc.c; you may +wish to compare the two on your system. + +On some BSD systems, you may need to create a file named "string.h" +whose single line is +#include <strings.h> +you may need to add " -Dstrchr=index" to the "CFLAGS =" assignment +in the makefile, and you may need to add " memset.o" to the "OBJECTS =" +assignment in the makefile -- see the comments in memset.c . + +For non-UNIX systems, you may need to change some things in sysdep.c, +such as the choice of intermediate file names. + +On some systems, you may need to modify parts of sysdep.h (which is +included by defs.h). In particular, for Sun 4.1 systems and perhaps +some others, you need to comment out the typedef of size_t. For some +systems (e.g., IRIX 4.0.1 and AIX) it is better to add +#define ANSI_Libraries +to the beginning of sysdep.h (or to supply -DANSI_Libraries in the +makefile). + +Alas, some systems #define __STDC__ but do not provide a true standard +(ANSI or ISO) C environment, e.g. do not provide stdlib.h . If yours +is such a system, then (a) you should complain loudly to your vendor +about __STDC__ being erroneously defined, and (b) you should insert +#undef __STDC__ +at the beginning of sysdep.h . You may need to make other adjustments. + +For some non-ANSI versions of stdio, you must change the values given +to binread and binwrite in sysdep.c from "rb" and "wb" to "r" and "w". +You may need to make this change if you run f2c and get an error +message of the form + Compiler error ... cannot open intermediate file ... + +On many systems, it is best to combine libF77 and libI77 into a single +library, say libf2c, as suggested in "readme from f2c". If you do this, +then you should adjust the definition of link_msg in sysdep.c +appropriately (e.g., replacing "-lF77 -lI77" by "-lf2c"). + +Some older C compilers object to + typedef void (*foo)(); +or to + typedef void zap; + zap (*foo)(); +If yours is such a compiler, change the definition of VOID in +f2c.h from void to int. + +For convenience with systems that use control-Z to denote end-of-file, +f2c treats control-Z characters (ASCII 26, '\x1a') that appear at the +beginning of a line as an end-of-file indicator. You can disable this +test by compiling lex.c with NO_EOF_CHAR_CHECK #defined, or can +change control-Z to some other character by #defining EOF_CHAR to +be the desired value. + +Please send bug reports to dmg@research.att.com . The old index file +(now called "readme" due to unfortunate changes in netlib conventions: +"send readme from f2c") will report recent changes in the recent-change +log at its end; all changes will be shown in the "changes" file +("send changes from f2c"). To keep current source, you will need to +request xsum0.out and version.c, in addition to the changed source +files. diff --git a/usr.bin/f2c/cds.c b/usr.bin/f2c/cds.c new file mode 100644 index 000000000000..3a9a9dc0288d --- /dev/null +++ b/usr.bin/f2c/cds.c @@ -0,0 +1,190 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* Put strings representing decimal floating-point numbers + * into canonical form: always have a decimal point or + * exponent field; if using an exponent field, have the + * number before it start with a digit and decimal point + * (if the number has more than one digit); only have an + * exponent field if it saves space. + * + * Arrange that the return value, rv, satisfies rv[0] == '-' || rv[-1] == '-' . + */ + +#include "sysdep.h" + + char * +cds(s, z0) + char *s, *z0; +{ + int ea, esign, et, i, k, nd = 0, sign = 0, tz; + char c, *z; + char ebuf[24]; + long ex = 0; + static char etype[Table_size], *db; + static int dblen = 64; + + if (!db) { + etype['E'] = 1; + etype['e'] = 1; + etype['D'] = 1; + etype['d'] = 1; + etype['+'] = 2; + etype['-'] = 3; + db = Alloc(dblen); + } + + while((c = *s++) == '0'); + if (c == '-') + { sign = 1; c = *s++; } + else if (c == '+') + c = *s++; + k = strlen(s) + 2; + if (k >= dblen) { + do dblen <<= 1; + while(k >= dblen); + free(db); + db = Alloc(dblen); + } + if (etype[(unsigned char)c] >= 2) + while(c == '0') c = *s++; + tz = 0; + while(c >= '0' && c <= '9') { + if (c == '0') + tz++; + else { + if (nd) + for(; tz; --tz) + db[nd++] = '0'; + else + tz = 0; + db[nd++] = c; + } + c = *s++; + } + ea = -tz; + if (c == '.') { + while((c = *s++) >= '0' && c <= '9') { + if (c == '0') + tz++; + else { + if (tz) { + ea += tz; + if (nd) + for(; tz; --tz) + db[nd++] = '0'; + else + tz = 0; + } + db[nd++] = c; + ea++; + } + } + } + if (et = etype[(unsigned char)c]) { + esign = et == 3; + c = *s++; + if (et == 1) { + if(etype[(unsigned char)c] > 1) { + if (c == '-') + esign = 1; + c = *s++; + } + } + while(c >= '0' && c <= '9') { + ex = 10*ex + (c - '0'); + c = *s++; + } + if (esign) + ex = -ex; + } + switch(c) { + case 0: + break; +#ifndef VAX + case 'i': + case 'I': + Fatal("Overflow evaluating constant expression."); + case 'n': + case 'N': + Fatal("Constant expression yields NaN."); +#endif + default: + Fatal("unexpected character in cds."); + } + ex -= ea; + if (!nd) { + if (!z0) + z0 = mem(4,0); + strcpy(z0, "-0."); + sign = 0; + } + else if (ex > 2 || ex + nd < -2) { + sprintf(ebuf, "%ld", ex + nd - 1); + k = strlen(ebuf) + nd + 3; + if (nd > 1) + k++; + if (!z0) + z0 = mem(k,0); + z = z0; + *z++ = '-'; + *z++ = *db; + if (nd > 1) { + *z++ = '.'; + for(k = 1; k < nd; k++) + *z++ = db[k]; + } + *z++ = 'e'; + strcpy(z, ebuf); + } + else { + k = (int)(ex + nd); + i = nd + 3; + if (k < 0) + i -= k; + else if (ex > 0) + i += ex; + if (!z0) + z0 = mem(i,0); + z = z0; + *z++ = '-'; + if (ex >= 0) { + for(k = 0; k < nd; k++) + *z++ = db[k]; + while(--ex >= 0) + *z++ = '0'; + *z++ = '.'; + } + else { + for(i = 0; i < k;) + *z++ = db[i++]; + *z++ = '.'; + while(++k <= 0) + *z++ = '0'; + while(i < nd) + *z++ = db[i++]; + } + *z = 0; + } + return sign ? z0 : z0+1; + } diff --git a/usr.bin/f2c/data.c b/usr.bin/f2c/data.c new file mode 100644 index 000000000000..5d112163086f --- /dev/null +++ b/usr.bin/f2c/data.c @@ -0,0 +1,442 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" + +/* ROUTINES CALLED DURING DATA AND PARAMETER STATEMENT PROCESSING */ + +static char datafmt[] = "%s\t%09ld\t%d"; +static char *cur_varname; + +/* another initializer, called from parser */ +dataval(repp, valp) +register expptr repp, valp; +{ + int i, nrep; + ftnint elen; + register Addrp p; + Addrp nextdata(); + + if (parstate < INDATA) { + frexpr(repp); + goto ret; + } + if(repp == NULL) + nrep = 1; + else if (ISICON(repp) && repp->constblock.Const.ci >= 0) + nrep = repp->constblock.Const.ci; + else + { + err("invalid repetition count in DATA statement"); + frexpr(repp); + goto ret; + } + frexpr(repp); + + if( ! ISCONST(valp) ) + { + err("non-constant initializer"); + goto ret; + } + + if(toomanyinit) goto ret; + for(i = 0 ; i < nrep ; ++i) + { + p = nextdata(&elen); + if(p == NULL) + { + err("too many initializers"); + toomanyinit = YES; + goto ret; + } + setdata((Addrp)p, (Constp)valp, elen); + frexpr((expptr)p); + } + +ret: + frexpr(valp); +} + + +Addrp nextdata(elenp) +ftnint *elenp; +{ + register struct Impldoblock *ip; + struct Primblock *pp; + register Namep np; + register struct Rplblock *rp; + tagptr p; + expptr neltp; + register expptr q; + int skip; + ftnint off, vlen; + + while(curdtp) + { + p = (tagptr)curdtp->datap; + if(p->tag == TIMPLDO) + { + ip = &(p->impldoblock); + if(ip->implb==NULL || ip->impub==NULL || ip->varnp==NULL) + fatali("bad impldoblock 0%o", (int) ip); + if(ip->isactive) + ip->varvp->Const.ci += ip->impdiff; + else + { + q = fixtype(cpexpr(ip->implb)); + if( ! ISICON(q) ) + goto doerr; + ip->varvp = (Constp) q; + + if(ip->impstep) + { + q = fixtype(cpexpr(ip->impstep)); + if( ! ISICON(q) ) + goto doerr; + ip->impdiff = q->constblock.Const.ci; + frexpr(q); + } + else + ip->impdiff = 1; + + q = fixtype(cpexpr(ip->impub)); + if(! ISICON(q)) + goto doerr; + ip->implim = q->constblock.Const.ci; + frexpr(q); + + ip->isactive = YES; + rp = ALLOC(Rplblock); + rp->rplnextp = rpllist; + rpllist = rp; + rp->rplnp = ip->varnp; + rp->rplvp = (expptr) (ip->varvp); + rp->rpltag = TCONST; + } + + if( (ip->impdiff>0 && (ip->varvp->Const.ci <= ip->implim)) + || (ip->impdiff<0 && (ip->varvp->Const.ci >= ip->implim)) ) + { /* start new loop */ + curdtp = ip->datalist; + goto next; + } + + /* clean up loop */ + + if(rpllist) + { + rp = rpllist; + rpllist = rpllist->rplnextp; + free( (charptr) rp); + } + else + Fatal("rpllist empty"); + + frexpr((expptr)ip->varvp); + ip->isactive = NO; + curdtp = curdtp->nextp; + goto next; + } + + pp = (struct Primblock *) p; + np = pp->namep; + cur_varname = np->fvarname; + skip = YES; + + if(p->primblock.argsp==NULL && np->vdim!=NULL) + { /* array initialization */ + q = (expptr) mkaddr(np); + off = typesize[np->vtype] * curdtelt; + if(np->vtype == TYCHAR) + off *= np->vleng->constblock.Const.ci; + q->addrblock.memoffset = + mkexpr(OPPLUS, q->addrblock.memoffset, mkintcon(off) ); + if( (neltp = np->vdim->nelt) && ISCONST(neltp)) + { + if(++curdtelt < neltp->constblock.Const.ci) + skip = NO; + } + else + err("attempt to initialize adjustable array"); + } + else + q = mklhs((struct Primblock *)cpexpr((expptr)pp), 0); + if(skip) + { + curdtp = curdtp->nextp; + curdtelt = 0; + } + if(q->headblock.vtype == TYCHAR) + if(ISICON(q->headblock.vleng)) + *elenp = q->headblock.vleng->constblock.Const.ci; + else { + err("initialization of string of nonconstant length"); + continue; + } + else *elenp = typesize[q->headblock.vtype]; + + if (np->vstg == STGBSS) { + vlen = np->vtype==TYCHAR + ? np->vleng->constblock.Const.ci + : typesize[np->vtype]; + if(vlen > 0) + np->vstg = STGINIT; + } + return( (Addrp) q ); + +doerr: + err("nonconstant implied DO parameter"); + frexpr(q); + curdtp = curdtp->nextp; + +next: + curdtelt = 0; + } + + return(NULL); +} + + + +LOCAL FILEP dfile; + + +setdata(varp, valp, elen) +register Addrp varp; +ftnint elen; +register Constp valp; +{ + struct Constblock con; + register int type; + int i, k, valtype; + ftnint offset; + char *dataname(), *varname; + static Addrp badvar; + register unsigned char *s; + static int last_lineno; + static char *last_varname; + + if (varp->vstg == STGCOMMON) { + if (!(dfile = blkdfile)) + dfile = blkdfile = opf(blkdfname, textwrite); + } + else { + if (procclass == CLBLOCK) { + if (varp != badvar) { + badvar = varp; + warn1("%s is not in a COMMON block", + varp->uname_tag == UNAM_NAME + ? varp->user.name->fvarname + : "???"); + } + return; + } + if (!(dfile = initfile)) + dfile = initfile = opf(initfname, textwrite); + } + varname = dataname(varp->vstg, varp->memno); + offset = varp->memoffset->constblock.Const.ci; + type = varp->vtype; + valtype = valp->vtype; + if(type!=TYCHAR && valtype==TYCHAR) + { + if(! ftn66flag + && (last_varname != cur_varname || last_lineno != lineno)) { + /* prevent multiple warnings */ + last_lineno = lineno; + warn1( + "non-character datum %.42s initialized with character string", + last_varname = cur_varname); + } + varp->vleng = ICON(typesize[type]); + varp->vtype = type = TYCHAR; + } + else if( (type==TYCHAR && valtype!=TYCHAR) || + (cktype(OPASSIGN,type,valtype) == TYERROR) ) + { + err("incompatible types in initialization"); + return; + } + if(type == TYADDR) + con.Const.ci = valp->Const.ci; + else if(type != TYCHAR) + { + if(valtype == TYUNKNOWN) + con.Const.ci = valp->Const.ci; + else consconv(type, &con, valp); + } + + k = 1; + + switch(type) + { + case TYLOGICAL: + if (tylogical != TYLONG) + type = tylogical; + case TYINT1: + case TYLOGICAL1: + case TYLOGICAL2: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + dataline(varname, offset, type); + prconi(dfile, con.Const.ci); + break; + + case TYADDR: + dataline(varname, offset, type); + prcona(dfile, con.Const.ci); + break; + + case TYCOMPLEX: + case TYDCOMPLEX: + k = 2; + case TYREAL: + case TYDREAL: + dataline(varname, offset, type); + prconr(dfile, &con, k); + break; + + case TYCHAR: + k = valp -> vleng -> constblock.Const.ci; + if (elen < k) + k = elen; + s = (unsigned char *)valp->Const.ccp; + for(i = 0 ; i < k ; ++i) { + dataline(varname, offset++, TYCHAR); + fprintf(dfile, "\t%d\n", *s++); + } + k = elen - valp->vleng->constblock.Const.ci; + if(k > 0) { + dataline(varname, offset, TYBLANK); + fprintf(dfile, "\t%d\n", k); + } + break; + + default: + badtype("setdata", type); + } + +} + + + +/* + output form of name is padded with blanks and preceded + with a storage class digit +*/ +char *dataname(stg,memno) + int stg; + long memno; +{ + static char varname[64]; + register char *s, *t; + char buf[16], *memname(); + + if (stg == STGCOMMON) { + varname[0] = '2'; + sprintf(s = buf, "Q.%ld", memno); + } + else { + varname[0] = stg==STGEQUIV ? '1' : '0'; + s = memname(stg, memno); + } + t = varname + 1; + while(*t++ = *s++); + *t = 0; + return(varname); +} + + + + + +frdata(p0) +chainp p0; +{ + register struct Chain *p; + register tagptr q; + + for(p = p0 ; p ; p = p->nextp) + { + q = (tagptr)p->datap; + if(q->tag == TIMPLDO) + { + if(q->impldoblock.isbusy) + return; /* circular chain completed */ + q->impldoblock.isbusy = YES; + frdata(q->impldoblock.datalist); + free( (charptr) q); + } + else + frexpr(q); + } + + frchain( &p0); +} + + + +dataline(varname, offset, type) +char *varname; +ftnint offset; +int type; +{ + fprintf(dfile, datafmt, varname, offset, type); +} + + void +make_param(p, e) + register struct Paramblock *p; + expptr e; +{ + register expptr q; + + p->vclass = CLPARAM; + impldcl((Namep)p); + p->paramval = q = mkconv(p->vtype, e); + if (p->vtype == TYCHAR) { + if (q->tag == TEXPR) + p->paramval = q = fixexpr(q); + if (!ISCONST(q) || q->constblock.vtype != TYCHAR) { + errstr("invalid value for character parameter %s", + p->fvarname); + return; + } + if (!(e = p->vleng)) + p->vleng = ICON(q->constblock.vleng->constblock.Const.ci + + q->constblock.Const.ccp1.blanks); + else if (q->constblock.vleng->constblock.Const.ci + > e->constblock.Const.ci) { + q->constblock.vleng->constblock.Const.ci + = e->constblock.Const.ci; + q->constblock.Const.ccp1.blanks = 0; + } + else + q->constblock.Const.ccp1.blanks + = e->constblock.Const.ci + - q->constblock.vleng->constblock.Const.ci; + } + } diff --git a/usr.bin/f2c/defines.h b/usr.bin/f2c/defines.h new file mode 100644 index 000000000000..fc7eb1834fe5 --- /dev/null +++ b/usr.bin/f2c/defines.h @@ -0,0 +1,296 @@ +#define PDP11 4 + +#define BIGGEST_CHAR 0x7f /* Assumes 32-bit arithmetic */ +#define BIGGEST_SHORT 0x7fff /* Assumes 32-bit arithmetic */ +#define BIGGEST_LONG 0x7fffffff /* Assumes 32-bit arithmetic */ + +#define M(x) (1<<x) /* Mask (x) returns 2^x */ + +#define ALLOC(x) (struct x *) ckalloc((int)sizeof(struct x)) +#define ALLEXPR (expptr) ckalloc((int)sizeof(union Expression) ) +typedef int *ptr; +typedef char *charptr; +typedef FILE *FILEP; +typedef int flag; +typedef char field; /* actually need only 4 bits */ +typedef long int ftnint; +#define LOCAL static + +#define NO 0 +#define YES 1 + +#define CNULL (char *) 0 /* Character string null */ +#define PNULL (ptr) 0 +#define CHNULL (chainp) 0 /* Chain null */ +#define ENULL (expptr) 0 + + +/* BAD_MEMNO - used to distinguish between long string constants and other + constants in the table */ + +#define BAD_MEMNO -32768 + + +/* block tag values -- syntactic stuff */ + +#define TNAME 1 +#define TCONST 2 +#define TEXPR 3 +#define TADDR 4 +#define TPRIM 5 /* Primitive datum - should not appear in an + expptr variable, it should have already been + identified */ +#define TLIST 6 +#define TIMPLDO 7 +#define TERROR 8 + + +/* parser states - order is important, since there are several tests for + state < INDATA */ + +#define OUTSIDE 0 +#define INSIDE 1 +#define INDCL 2 +#define INDATA 3 +#define INEXEC 4 + +/* procedure classes */ + +#define PROCMAIN 1 +#define PROCBLOCK 2 +#define PROCSUBR 3 +#define PROCFUNCT 4 + + +/* storage classes -- vstg values. BSS and INIT are used in the later + merge pass over identifiers; and they are entered differently into the + symbol table */ + +#define STGUNKNOWN 0 +#define STGARG 1 /* adjustable dimensions */ +#define STGAUTO 2 /* for stack references */ +#define STGBSS 3 /* uninitialized storage (normal variables) */ +#define STGINIT 4 /* initialized storage */ +#define STGCONST 5 +#define STGEXT 6 /* external storage */ +#define STGINTR 7 /* intrinsic (late decision) reference. See + chapter 5 of the Fortran 77 standard */ +#define STGSTFUNCT 8 +#define STGCOMMON 9 +#define STGEQUIV 10 +#define STGREG 11 /* register - the outermost DO loop index will be + in a register (because the compiler is one + pass, it can't know where the innermost loop is + */ +#define STGLENG 12 +#define STGNULL 13 +#define STGMEMNO 14 /* interemediate-file pointer to constant table */ + +/* name classes -- vclass values, also procclass values */ + +#define CLUNKNOWN 0 +#define CLPARAM 1 /* Parameter - macro definition */ +#define CLVAR 2 /* variable */ +#define CLENTRY 3 +#define CLMAIN 4 +#define CLBLOCK 5 +#define CLPROC 6 +#define CLNAMELIST 7 /* in data with this tag, the vdcldone flag should + be ignored (according to vardcl()) */ + + +/* vprocclass values -- there is some overlap with the vclass values given + above */ + +#define PUNKNOWN 0 +#define PEXTERNAL 1 +#define PINTRINSIC 2 +#define PSTFUNCT 3 +#define PTHISPROC 4 /* here to allow recursion - further distinction + is given in the CL tag (those just above). + This applies to the presence of the name of a + function used within itself. The function name + means either call the function again, or assign + some value to the storage allocated to the + function's return value. */ + +/* control stack codes - these are part of a state machine which handles + the nesting of blocks (i.e. what to do about the ELSE statement) */ + +#define CTLDO 1 +#define CTLIF 2 +#define CTLELSE 3 +#define CTLIFX 4 + + +/* operators for both Fortran input and C output. They are common because + so many are shared between the trees */ + +#define OPPLUS 1 +#define OPMINUS 2 +#define OPSTAR 3 +#define OPSLASH 4 +#define OPPOWER 5 +#define OPNEG 6 +#define OPOR 7 +#define OPAND 8 +#define OPEQV 9 +#define OPNEQV 10 +#define OPNOT 11 +#define OPCONCAT 12 +#define OPLT 13 +#define OPEQ 14 +#define OPGT 15 +#define OPLE 16 +#define OPNE 17 +#define OPGE 18 +#define OPCALL 19 +#define OPCCALL 20 +#define OPASSIGN 21 +#define OPPLUSEQ 22 +#define OPSTAREQ 23 +#define OPCONV 24 +#define OPLSHIFT 25 +#define OPMOD 26 +#define OPCOMMA 27 +#define OPQUEST 28 +#define OPCOLON 29 +#define OPABS 30 +#define OPMIN 31 +#define OPMAX 32 +#define OPADDR 33 +#define OPCOMMA_ARG 34 +#define OPBITOR 35 +#define OPBITAND 36 +#define OPBITXOR 37 +#define OPBITNOT 38 +#define OPRSHIFT 39 +#define OPWHATSIN 40 /* dereferencing operator */ +#define OPMINUSEQ 41 /* assignment operators */ +#define OPSLASHEQ 42 +#define OPMODEQ 43 +#define OPLSHIFTEQ 44 +#define OPRSHIFTEQ 45 +#define OPBITANDEQ 46 +#define OPBITXOREQ 47 +#define OPBITOREQ 48 +#define OPPREINC 49 /* Preincrement (++x) operator */ +#define OPPREDEC 50 /* Predecrement (--x) operator */ +#define OPDOT 51 /* structure field reference */ +#define OPARROW 52 /* structure pointer field reference */ +#define OPNEG1 53 /* simple negation under forcedouble */ +#define OPDMIN 54 /* min(a,b) macro under forcedouble */ +#define OPDMAX 55 /* max(a,b) macro under forcedouble */ +#define OPASSIGNI 56 /* assignment for inquire stmt */ +#define OPIDENTITY 57 /* for turning TADDR into TEXPR */ +#define OPCHARCAST 58 /* for casting to char * (in I/O stmts) */ +#define OPDABS 59 /* abs macro under forcedouble */ +#define OPMIN2 60 /* min(a,b) macro */ +#define OPMAX2 61 /* max(a,b) macro */ + +/* label type codes -- used with the ASSIGN statement */ + +#define LABUNKNOWN 0 +#define LABEXEC 1 +#define LABFORMAT 2 +#define LABOTHER 3 + + +/* INTRINSIC function codes*/ + +#define INTREND 0 +#define INTRCONV 1 +#define INTRMIN 2 +#define INTRMAX 3 +#define INTRGEN 4 /* General intrinsic, e.g. cos v. dcos, zcos, ccos */ +#define INTRSPEC 5 +#define INTRBOOL 6 +#define INTRCNST 7 /* constants, e.g. bigint(1.0) v. bigint (1d0) */ + + +/* I/O statement codes - these all form Integer Constants, and are always + reevaluated */ + +#define IOSTDIN ICON(5) +#define IOSTDOUT ICON(6) +#define IOSTDERR ICON(0) + +#define IOSBAD (-1) +#define IOSPOSITIONAL 0 +#define IOSUNIT 1 +#define IOSFMT 2 + +#define IOINQUIRE 1 +#define IOOPEN 2 +#define IOCLOSE 3 +#define IOREWIND 4 +#define IOBACKSPACE 5 +#define IOENDFILE 6 +#define IOREAD 7 +#define IOWRITE 8 + + +/* User name tags -- these identify the form of the original identifier + stored in a struct Addrblock structure (in the user field). */ + +#define UNAM_UNKNOWN 0 /* Not specified */ +#define UNAM_NAME 1 /* Local symbol, store in the hash table */ +#define UNAM_IDENT 2 /* Character string not stored elsewhere */ +#define UNAM_EXTERN 3 /* External reference; check symbol table + using memno as index */ +#define UNAM_CONST 4 /* Constant value */ +#define UNAM_CHARP 5 /* pointer to string */ +#define UNAM_REF 6 /* subscript reference with -s */ + + +#define IDENT_LEN 31 /* Maximum length user.ident */ + +/* type masks - TYLOGICAL defined in ftypes */ + +#define MSKLOGICAL M(TYLOGICAL)|M(TYLOGICAL1)|M(TYLOGICAL2) +#define MSKADDR M(TYADDR) +#define MSKCHAR M(TYCHAR) +#ifdef TYQUAD +#define MSKINT M(TYINT1)|M(TYSHORT)|M(TYLONG)|M(TYQUAD) +#else +#define MSKINT M(TYINT1)|M(TYSHORT)|M(TYLONG) +#endif +#define MSKREAL M(TYREAL)|M(TYDREAL) /* DREAL means Double Real */ +#define MSKCOMPLEX M(TYCOMPLEX)|M(TYDCOMPLEX) +#define MSKSTATIC (M(STGINIT)|M(STGBSS)|M(STGCOMMON)|M(STGEQUIV)|M(STGCONST)) + +/* miscellaneous macros */ + +/* ONEOF (x, y) -- x is the number of one of the OR'ed masks in y (i.e., x is + the log of one of the OR'ed masks in y) */ + +#define ONEOF(x,y) (M(x) & (y)) +#define ISCOMPLEX(z) ONEOF(z, MSKCOMPLEX) +#define ISREAL(z) ONEOF(z, MSKREAL) +#define ISNUMERIC(z) ONEOF(z, MSKINT|MSKREAL|MSKCOMPLEX) +#define ISICON(z) (z->tag==TCONST && ISINT(z->constblock.vtype)) +#define ISLOGICAL(z) ONEOF(z, MSKLOGICAL) + +/* ISCHAR assumes that z has some kind of structure, i.e. is not null */ + +#define ISCHAR(z) (z->headblock.vtype==TYCHAR) +#define ISINT(z) ONEOF(z, MSKINT) /* z is a tag, i.e. a mask number */ +#define ISCONST(z) (z->tag==TCONST) +#define ISERROR(z) (z->tag==TERROR) +#define ISPLUSOP(z) (z->tag==TEXPR && z->exprblock.opcode==OPPLUS) +#define ISSTAROP(z) (z->tag==TEXPR && z->exprblock.opcode==OPSTAR) +#define ISONE(z) (ISICON(z) && z->constblock.Const.ci==1) +#define INT(z) ONEOF(z, MSKINT|MSKCHAR) /* has INT storage in real life */ +#define ICON(z) mkintcon( (ftnint)(z) ) + +/* NO66 -- F77 feature is being used + NOEXT -- F77 extension is being used */ + +#define NO66(s) if(no66flag) err66(s) +#define NOEXT(s) if(noextflag) errext(s) + +/* round a up to the nearest multiple of b: + + a = b * floor ( (a + (b - 1)) / b )*/ + +#define roundup(a,b) ( b * ( (a+b-1)/b) ) diff --git a/usr.bin/f2c/defs.h b/usr.bin/f2c/defs.h new file mode 100644 index 000000000000..6bb2ca27bc2d --- /dev/null +++ b/usr.bin/f2c/defs.h @@ -0,0 +1,784 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories, Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "sysdep.h" + +#include "ftypes.h" +#include "defines.h" +#include "machdefs.h" + +#define MAXDIM 20 +#define MAXINCLUDES 10 +#define MAXLITERALS 200 /* Max number of constants in the literal + pool */ +#define MAXTOKENLEN 502 /* length of longest token */ +#define MAXCTL 20 +#define MAXHASH 401 +#define MAXSTNO 801 +#define MAXEXT 200 +#define MAXEQUIV 150 +#define MAXLABLIST 258 /* Max number of labels in an alternate + return CALL or computed GOTO */ +#define MAXCONTIN 99 /* Max continuation lines */ + +/* These are the primary pointer types used in the compiler */ + +typedef union Expression *expptr, *tagptr; +typedef struct Chain *chainp; +typedef struct Addrblock *Addrp; +typedef struct Constblock *Constp; +typedef struct Exprblock *Exprp; +typedef struct Nameblock *Namep; + +extern FILEP opf(); +extern FILEP infile; +extern FILEP diagfile; +extern FILEP textfile; +extern FILEP asmfile; +extern FILEP c_file; /* output file for all functions; extern + declarations will have to be prepended */ +extern FILEP pass1_file; /* Temp file to hold the function bodies + read on pass 1 */ +extern FILEP expr_file; /* Debugging file */ +extern FILEP initfile; /* Intermediate data file pointer */ +extern FILEP blkdfile; /* BLOCK DATA file */ + +extern int current_ftn_file; +extern int maxcontin; + +extern char *blkdfname, *initfname, *sortfname; +extern long int headoffset; /* Since the header block requires data we + don't know about until AFTER each + function has been processed, we keep a + pointer to the current (dummy) header + block (at the top of the assembly file) + here */ + +extern char main_alias[]; /* name given to PROGRAM psuedo-op */ +extern char token [ ]; +extern int toklen; +extern long lineno; +extern char *infname; +extern int needkwd; +extern struct Labelblock *thislabel; + +/* Used to allow runtime expansion of internal tables. In particular, + these values can exceed their associated constants */ + +extern int maxctl; +extern int maxequiv; +extern int maxstno; +extern int maxhash; +extern int maxext; + +extern flag nowarnflag; +extern flag ftn66flag; /* Generate warnings when weird f77 + features are used (undeclared dummy + procedure, non-char initialized with + string, 1-dim subscript in EQUIV) */ +extern flag no66flag; /* Generate an error when a generic + function (f77 feature) is used */ +extern flag noextflag; /* Generate an error when an extension to + Fortran 77 is used (hex/oct/bin + constants, automatic, static, double + complex types) */ +extern flag zflag; /* enable double complex intrinsics */ +extern flag shiftcase; +extern flag undeftype; +extern flag shortsubs; /* Use short subscripts on arrays? */ +extern flag onetripflag; /* if true, always execute DO loop body */ +extern flag checksubs; +extern flag debugflag; +extern int nerr; +extern int nwarn; + +extern int parstate; +extern flag headerdone; /* True iff the current procedure's header + data has been written */ +extern int blklevel; +extern flag saveall; +extern flag substars; /* True iff some formal parameter is an + asterisk */ +extern int impltype[ ]; +extern ftnint implleng[ ]; +extern int implstg[ ]; + +extern int tycomplex, tyint, tyioint, tyreal; +extern int tylog, tylogical; /* TY____ of the implementation of logical. + This will be LONG unless '-2' is given + on the command line */ +extern int type_choice[]; +extern char *typename[]; + +extern int typesize[]; /* size (in bytes) of an object of each + type. Indexed by TY___ macros */ +extern int typealign[]; +extern int proctype; /* Type of return value in this procedure */ +extern char * procname; /* External name of the procedure, or last ENTRY name */ +extern int rtvlabel[ ]; /* Return value labels, indexed by TY___ macros */ +extern Addrp retslot; +extern Addrp xretslot[]; +extern int cxslot; /* Complex return argument slot (frame pointer offset)*/ +extern int chslot; /* Character return argument slot (fp offset) */ +extern int chlgslot; /* Argument slot for length of character buffer */ +extern int procclass; /* Class of the current procedure: either CLPROC, + CLMAIN, CLBLOCK or CLUNKNOWN */ +extern ftnint procleng; /* Length of function return value (e.g. char + string length). If this is -1, then the length is + not known at compile time */ +extern int nentry; /* Number of entry points (other than the original + function call) into this procedure */ +extern flag multitype; /* YES iff there is more than one return value + possible */ +extern int blklevel; +extern long lastiolabno; +extern int lastlabno; +extern int lastvarno; +extern int lastargslot; /* integer offset pointing to the next free + location for an argument to the current routine */ +extern int argloc; +extern int autonum[]; /* for numbering + automatic variables, e.g. temporaries */ +extern int retlabel; +extern int ret0label; +extern int dorange; /* Number of the label which terminates + the innermost DO loop */ +extern int regnum[ ]; /* Numbers of DO indicies named in + regnamep (below) */ +extern Namep regnamep[ ]; /* List of DO indicies in registers */ +extern int maxregvar; /* number of elts in regnamep */ +extern int highregvar; /* keeps track of the highest register + number used by DO index allocator */ +extern int nregvar; /* count of DO indicies in registers */ + +extern chainp templist[]; +extern int maxdim; +extern chainp earlylabs; +extern chainp holdtemps; +extern struct Entrypoint *entries; +extern struct Rplblock *rpllist; +extern struct Chain *curdtp; +extern ftnint curdtelt; +extern chainp allargs; /* union of args in entries */ +extern int nallargs; /* total number of args */ +extern int nallchargs; /* total number of character args */ +extern flag toomanyinit; /* True iff too many initializers in a + DATA statement */ + +extern flag inioctl; +extern int iostmt; +extern Addrp ioblkp; +extern int nioctl; +extern int nequiv; +extern int eqvstart; /* offset to eqv number to guarantee uniqueness + and prevent <something> from going negative */ +extern int nintnames; + +/* Chain of tagged blocks */ + +struct Chain + { + chainp nextp; + char * datap; /* Tagged block */ + }; + +extern chainp chains; + +/* Recall that field is intended to hold four-bit characters */ + +/* This structure exists only to defeat the type checking */ + +struct Headblock + { + field tag; + field vtype; + field vclass; + field vstg; + expptr vleng; /* Expression for length of char string - + this may be a constant, or an argument + generated by mkarg() */ + } ; + +/* Control construct info (for do loops, else, etc) */ + +struct Ctlframe + { + unsigned ctltype:8; + unsigned dostepsign:8; /* 0 - variable, 1 - pos, 2 - neg */ + unsigned dowhile:1; + int ctlabels[4]; /* Control labels, defined below */ + int dolabel; /* label marking end of this DO loop */ + Namep donamep; /* DO index variable */ + expptr domax; /* constant or temp variable holding MAX + loop value; or expr of while(expr) */ + expptr dostep; /* expression */ + Namep loopname; + }; +#define endlabel ctlabels[0] +#define elselabel ctlabels[1] +#define dobodylabel ctlabels[1] +#define doposlabel ctlabels[2] +#define doneglabel ctlabels[3] +extern struct Ctlframe *ctls; /* Keeps info on DO and BLOCK IF + structures - this is the stack + bottom */ +extern struct Ctlframe *ctlstack; /* Pointer to current nesting + level */ +extern struct Ctlframe *lastctl; /* Point to end of + dynamically-allocated array */ + +typedef struct { + int type; + chainp cp; + } Atype; + +typedef struct { + int defined, dnargs, nargs, changes; + Atype atypes[1]; + } Argtypes; + +/* External Symbols */ + +struct Extsym + { + char *fextname; /* Fortran version of external name */ + char *cextname; /* C version of external name */ + field extstg; /* STG -- should be COMMON, UNKNOWN or EXT + */ + unsigned extype:4; /* for transmitting type to output routines */ + unsigned used_here:1; /* Boolean - true on the second pass + through a function if the block has + been referenced */ + unsigned exused:1; /* Has been used (for help with error msgs + about externals typed differently in + different modules) */ + unsigned exproto:1; /* type specified in a .P file */ + unsigned extinit:1; /* Procedure has been defined, + or COMMON has DATA */ + unsigned extseen:1; /* True if previously referenced */ + chainp extp; /* List of identifiers in the common + block for this function, stored as + Namep (hash table pointers) */ + chainp allextp; /* List of lists of identifiers; we keep one + list for each layout of this common block */ + int curno; /* current number for this common block, + used for constructing appending _nnn + to the common block name */ + int maxno; /* highest curno value for this common block */ + ftnint extleng; + ftnint maxleng; + Argtypes *arginfo; + }; +typedef struct Extsym Extsym; + +extern Extsym *extsymtab; /* External symbol table */ +extern Extsym *nextext; +extern Extsym *lastext; +extern int complex_seen, dcomplex_seen; + +/* Statement labels */ + +struct Labelblock + { + int labelno; /* Internal label */ + unsigned blklevel:8; /* level of nesting , for branch-in-loop + checking */ + unsigned labused:1; + unsigned fmtlabused:1; + unsigned labinacc:1; /* inaccessible? (i.e. has its scope + vanished) */ + unsigned labdefined:1; /* YES or NO */ + unsigned labtype:2; /* LAB{FORMAT,EXEC,etc} */ + ftnint stateno; /* Original label */ + char *fmtstring; /* format string */ + }; + +extern struct Labelblock *labeltab; /* Label table - keeps track of + all labels, including undefined */ +extern struct Labelblock *labtabend; +extern struct Labelblock *highlabtab; + +/* Entry point list */ + +struct Entrypoint + { + struct Entrypoint *entnextp; + Extsym *entryname; /* Name of this ENTRY */ + chainp arglist; + int typelabel; /* Label for function exit; this + will return the proper type of + object */ + Namep enamep; /* External name */ + }; + +/* Primitive block, or Primary block. This is a general template returned + by the parser, which will be interpreted in context. It is a template + for an identifier (variable name, function name), parenthesized + arguments (array subscripts, function parameters) and substring + specifications. */ + +struct Primblock + { + field tag; + field vtype; + unsigned parenused:1; /* distinguish (a) from a */ + Namep namep; /* Pointer to structure Nameblock */ + struct Listblock *argsp; + expptr fcharp; /* first-char-index-pointer (in + substring) */ + expptr lcharp; /* last-char-index-pointer (in + substring) */ + }; + + +struct Hashentry + { + int hashval; + Namep varp; + }; +extern struct Hashentry *hashtab; /* Hash table */ +extern struct Hashentry *lasthash; + +struct Intrpacked /* bits for intrinsic function description */ + { + unsigned f1:3; + unsigned f2:4; + unsigned f3:7; + unsigned f4:1; + }; + +struct Nameblock + { + field tag; + field vtype; + field vclass; + field vstg; + expptr vleng; /* length of character string, if applicable */ + char *fvarname; /* name in the Fortran source */ + char *cvarname; /* name in the resulting C */ + chainp vlastdim; /* datap points to new_vars entry for the */ + /* system variable, if any, storing the final */ + /* dimension; we zero the datap if this */ + /* variable is needed */ + unsigned vprocclass:3; /* P____ macros - selects the varxptr + field below */ + unsigned vdovar:1; /* "is it a DO variable?" for register + and multi-level loop checking */ + unsigned vdcldone:1; /* "do I think I'm done?" - set when the + context is sufficient to determine its + status */ + unsigned vadjdim:1; /* "adjustable dimension?" - needed for + information about copies */ + unsigned vsave:1; + unsigned vimpldovar:1; /* used to prevent erroneous error messages + for variables used only in DATA stmt + implicit DOs */ + unsigned vis_assigned:1;/* True if this variable has had some + label ASSIGNED to it; hence + varxptr.assigned_values is valid */ + unsigned vimplstg:1; /* True if storage type is assigned implicitly; + this allows a COMMON variable to participate + in a DIMENSION before the COMMON declaration. + */ + unsigned vcommequiv:1; /* True if EQUIVALENCEd onto STGCOMMON */ + unsigned vfmt_asg:1; /* True if char *var_fmt needed */ + unsigned vpassed:1; /* True if passed as a character-variable arg */ + unsigned vknownarg:1; /* True if seen in a previous entry point */ + unsigned visused:1; /* True if variable is referenced -- so we */ + /* can omit variables that only appear in DATA */ + unsigned vnamelist:1; /* Appears in a NAMELIST */ + unsigned vimpltype:1; /* True if implicitly typed and not + invoked as a function or subroutine + (so we can consistently type procedures + declared external and passed as args + but never invoked). + */ + unsigned vtypewarned:1; /* so we complain just once about + changed types of external procedures */ + unsigned vinftype:1; /* so we can restore implicit type to a + procedure if it is invoked as a function + after being given a different type by -it */ + unsigned vinfproc:1; /* True if -it infers this to be a procedure */ + unsigned vcalled:1; /* has been invoked */ + unsigned vdimfinish:1; /* need to invoke dim_finish() */ + unsigned vrefused:1; /* Need to #define name_ref (for -s) */ + unsigned vsubscrused:1; /* Need to #define name_subscr (for -2) */ + unsigned veqvadjust:1; /* voffset has been adjusted for equivalence */ + +/* The vardesc union below is used to store the number of an intrinsic + function (when vstg == STGINTR and vprocclass == PINTRINSIC), or to + store the index of this external symbol in extsymtab (when vstg == + STGEXT and vprocclass == PEXTERNAL) */ + + union { + int varno; /* Return variable for a function. + This is used when a function is + assigned a return value. Also + used to point to the COMMON + block, when this is a field of + that block. Also points to + EQUIV block when STGEQUIV */ + struct Intrpacked intrdesc; /* bits for intrinsic function*/ + } vardesc; + struct Dimblock *vdim; /* points to the dimensions if they exist */ + ftnint voffset; /* offset in a storage block (the variable + name will be "v.%d", voffset in a + common blck on the vax). Also holds + pointers for automatic variables. When + STGEQUIV, this is -(offset from array + base) */ + union { + chainp namelist; /* points to names in the NAMELIST, + if this is a NAMELIST name */ + chainp vstfdesc; /* points to (formals, expr) pair */ + chainp assigned_values; /* list of integers, each being a + statement label assigned to + this variable in the current function */ + } varxptr; + int argno; /* for multiple entries */ + Argtypes *arginfo; + }; + + +/* PARAMETER statements */ + +struct Paramblock + { + field tag; + field vtype; + field vclass; + field vstg; + expptr vleng; + char *fvarname; + char *cvarname; + expptr paramval; + } ; + + +/* Expression block */ + +struct Exprblock + { + field tag; + field vtype; + field vclass; + field vstg; + expptr vleng; /* in the case of a character expression, this + value is inherited from the children */ + unsigned opcode; + expptr leftp; + expptr rightp; + }; + + +union Constant + { + struct { + char *ccp0; + ftnint blanks; + } ccp1; + ftnint ci; /* Constant long integer */ + double cd[2]; + char *cds[2]; + }; +#define ccp ccp1.ccp0 + +struct Constblock + { + field tag; + field vtype; + field vclass; + field vstg; /* vstg = 1 when using Const.cds */ + expptr vleng; + union Constant Const; + }; + + +struct Listblock + { + field tag; + field vtype; + chainp listp; + }; + + + +/* Address block - this is the FINAL form of identifiers before being + sent to pass 2. We'll want to add the original identifier here so that it can + be preserved in the translation. + + An example identifier is q.7. The "q" refers to the storage class + (field vstg), the 7 to the variable number (int memno). */ + +struct Addrblock + { + field tag; + field vtype; + field vclass; + field vstg; + expptr vleng; + /* put union...user here so the beginning of an Addrblock + * is the same as a Constblock. + */ + union { + Namep name; /* contains a pointer into the hash table */ + char ident[IDENT_LEN + 1]; /* C string form of identifier */ + char *Charp; + union Constant Const; /* Constant value */ + struct { + double dfill[2]; + field vstg1; + } kludge; /* so we can distinguish string vs binary + * floating-point constants */ + } user; + long memno; /* when vstg == STGCONST, this is the + numeric part of the assembler label + where the constant value is stored */ + expptr memoffset; /* used in subscript computations, usually */ + unsigned istemp:1; /* used in stack management of temporary + variables */ + unsigned isarray:1; /* used to show that memoffset is + meaningful, even if zero */ + unsigned ntempelt:10; /* for representing temporary arrays, as + in concatenation */ + unsigned dbl_builtin:1; /* builtin to be declared double */ + unsigned charleng:1; /* so saveargtypes can get i/o calls right */ + unsigned cmplx_sub:1; /* used in complex arithmetic under -s */ + unsigned skip_offset:1; /* used in complex arithmetic under -s */ + unsigned parenused:1; /* distinguish (a) from a */ + ftnint varleng; /* holds a copy of a constant length which + is stored in the vleng field (e.g. + a double is 8 bytes) */ + int uname_tag; /* Tag describing which of the unions() + below to use */ + char *Field; /* field name when dereferencing a struct */ +}; /* struct Addrblock */ + + +/* Errorbock - placeholder for errors, to allow the compilation to + continue */ + +struct Errorblock + { + field tag; + field vtype; + }; + + +/* Implicit DO block, especially related to DATA statements. This block + keeps track of the compiler's location in the implicit DO while it's + running. In particular, the isactive and isbusy flags tell where + it is */ + +struct Impldoblock + { + field tag; + unsigned isactive:1; + unsigned isbusy:1; + Namep varnp; + Constp varvp; + chainp impdospec; + expptr implb; + expptr impub; + expptr impstep; + ftnint impdiff; + ftnint implim; + struct Chain *datalist; + }; + + +/* Each of these components has a first field called tag. This union + exists just for allocation simplicity */ + +union Expression + { + field tag; + struct Addrblock addrblock; + struct Constblock constblock; + struct Errorblock errorblock; + struct Exprblock exprblock; + struct Headblock headblock; + struct Impldoblock impldoblock; + struct Listblock listblock; + struct Nameblock nameblock; + struct Paramblock paramblock; + struct Primblock primblock; + } ; + + + +struct Dimblock + { + int ndim; + expptr nelt; /* This is NULL if the array is unbounded */ + expptr baseoffset; /* a constant or local variable holding + the offset in this procedure */ + expptr basexpr; /* expression for comuting the offset, if + it's not constant. If this is + non-null, the register named in + baseoffset will get initialized to this + value in the procedure's prolog */ + struct + { + expptr dimsize; /* constant or register holding the size + of this dimension */ + expptr dimexpr; /* as above in basexpr, this is an + expression for computing a variable + dimension */ + } dims[1]; /* Dimblocks are allocated with enough + space for this to become dims[ndim] */ + }; + + +/* Statement function identifier stack - this holds the name and value of + the parameters in a statement function invocation. For example, + + f(x,y,z)=x+y+z + . + . + y = f(1,2,3) + + generates a stack of depth 3, with <x 1>, <y 2>, <z 3> AT THE INVOCATION, NOT + at the definition */ + +struct Rplblock /* name replacement block */ + { + struct Rplblock *rplnextp; + Namep rplnp; /* Name of the formal parameter */ + expptr rplvp; /* Value of the actual parameter */ + expptr rplxp; /* Initialization of temporary variable, + if required; else null */ + int rpltag; /* Tag on the value of the actual param */ + }; + + + +/* Equivalence block */ + +struct Equivblock + { + struct Eqvchain *equivs; /* List (Eqvchain) of primblocks + holding variable identifiers */ + flag eqvinit; + long int eqvtop; + long int eqvbottom; + int eqvtype; + } ; +#define eqvleng eqvtop + +extern struct Equivblock *eqvclass; + + +struct Eqvchain + { + struct Eqvchain *eqvnextp; + union + { + struct Primblock *eqvlhs; + Namep eqvname; + } eqvitem; + long int eqvoffset; + } ; + + + +/* For allocation purposes only, and to keep lint quiet. In particular, + don't count on the tag being able to tell you which structure is used */ + + +/* There is a tradition in Fortran that the compiler not generate the same + bit pattern more than is necessary. This structure is used to do just + that; if two integer constants have the same bit pattern, just generate + it once. This could be expanded to optimize without regard to type, by + removing the type check in putconst() */ + +struct Literal + { + short littype; + short litnum; /* numeric part of the assembler + label for this constant value */ + int lituse; /* usage count */ + union { + ftnint litival; + double litdval[2]; + ftnint litival2[2]; /* length, nblanks for strings */ + } litval; + char *cds[2]; + }; + +extern struct Literal *litpool; +extern int maxliterals, nliterals; +extern char Letters[]; +#define letter(x) Letters[x] + +struct Dims { expptr lb, ub; }; + + +/* popular functions with non integer return values */ + + +int *ckalloc(); +char *varstr(), *nounder(), *addunder(); +char *copyn(), *copys(); +chainp hookup(), mkchain(), revchain(); +ftnint convci(); +char *convic(); +char *setdoto(); +double convcd(); +Namep mkname(); +struct Labelblock *mklabel(), *execlab(); +Extsym *mkext(), *newentry(); +expptr addrof(), call1(), call2(), call3(), call4(); +Addrp builtin(), mktmp(), mktmp0(), mktmpn(), autovar(); +Addrp mkplace(), mkaddr(), putconst(), memversion(); +expptr mkprim(), mklhs(), mkexpr(), mkconv(), mkfunct(), fixexpr(), fixtype(); +expptr errnode(), mkaddcon(), mkintcon(), putcxop(); +tagptr cpexpr(); +ftnint lmin(), lmax(), iarrlen(); +char *dbconst(), *flconst(); + +void puteq (), putex1 (); +expptr putx (), putsteq (), putassign (); + +extern int forcedouble; /* force real functions to double */ +extern int doin_setbound; /* special handling for array bounds */ +extern int Ansi; +extern char *cds(), *cpstring(), *dtos(), *string_num(); +extern char *c_type_decl(); +extern char hextoi_tab[]; +#define hextoi(x) hextoi_tab[(x) & 0xff] +extern char *casttypes[], *ftn_types[], *protorettypes[], *usedcasts[]; +extern int Castargs, infertypes; +extern FILE *protofile; +extern void exit(), inferdcl(), protowrite(), save_argtypes(); +extern char binread[], binwrite[], textread[], textwrite[]; +extern char *ei_first, *ei_last, *ei_next; +extern char *wh_first, *wh_last, *wh_next; +extern void putwhile(); +extern char *halign; +extern flag keepsubs; +#ifdef TYQUAD +extern flag use_tyquad; +#endif +extern int n_keywords, n_st_fields; +extern char *c_keywords[], *st_fields[]; diff --git a/usr.bin/f2c/dependencies b/usr.bin/f2c/dependencies new file mode 100644 index 000000000000..9937e0bbb5ce --- /dev/null +++ b/usr.bin/f2c/dependencies @@ -0,0 +1,60 @@ +f2c/src* +Notice= +notice +README= +readme +cds.c= +data.c= +defines.h= +defs.h= +equiv.c= +error.c= +exec.c= +expr.c= +f2c.1= +f2c.1t= +f2c.h= +format.c= +format.h= +formatdata.c= +ftypes.h= +gram.dcl= +gram.exec= +gram.expr= +gram.head= +gram.io= +init.c= +intr.c= +io.c= +iob.h= +lex.c= +machdefs.h= +main.c= +makefile= +malloc.c= +mem.c= +memset.c= +misc.c= +names.c= +names.h= +niceprintf.c= +niceprintf.h= +output.c= +output.h= +p1defs.h= +p1output.c= +parse.h= +parse_args.c= +pccdefs.h= +pread.c= +proc.c= +put.c= +putpcc.c= +sysdep.c= +sysdep.h= +tokens= +usignal.h= +vax.c= +version.c= +xsum.c= +xsum0.out= diff --git a/usr.bin/f2c/disclaimer b/usr.bin/f2c/disclaimer new file mode 100644 index 000000000000..59db1ecf42fd --- /dev/null +++ b/usr.bin/f2c/disclaimer @@ -0,0 +1,15 @@ +f2c is a Fortran to C converter under development by + David Gay (AT&T Bell Labs) + Stu Feldman (Bellcore) + Mark Maimone (Carnegie-Mellon University) + Norm Schryer (AT&T Bell Labs) +Please send bug reports to dmg@research.att.com or uunet!research!dmg. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. diff --git a/usr.bin/f2c/equiv.c b/usr.bin/f2c/equiv.c new file mode 100644 index 000000000000..019e206d26e5 --- /dev/null +++ b/usr.bin/f2c/equiv.c @@ -0,0 +1,383 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" + +LOCAL eqvcommon(), eqveqv(), nsubs(); + +/* ROUTINES RELATED TO EQUIVALENCE CLASS PROCESSING */ + +/* called at end of declarations section to process chains + created by EQUIVALENCE statements + */ +doequiv() +{ + register int i; + int inequiv; /* True if one namep occurs in + several EQUIV declarations */ + int comno; /* Index into Extsym table of the last + COMMON block seen (implicitly assuming + that only one will be given) */ + int ovarno; + ftnint comoffset; /* Index into the COMMON block */ + ftnint offset; /* Offset from array base */ + ftnint leng; + register struct Equivblock *equivdecl; + register struct Eqvchain *q; + struct Primblock *primp; + register Namep np; + int k, k1, ns, pref, t; + chainp cp; + extern int type_pref[]; + char *s; + + for(i = 0 ; i < nequiv ; ++i) + { + +/* Handle each equivalence declaration */ + + equivdecl = &eqvclass[i]; + equivdecl->eqvbottom = equivdecl->eqvtop = 0; + comno = -1; + + + + for(q = equivdecl->equivs ; q ; q = q->eqvnextp) + { + offset = 0; + primp = q->eqvitem.eqvlhs; + vardcl(np = primp->namep); + if(primp->argsp || primp->fcharp) + { + expptr offp, suboffset(); + +/* Pad ones onto the end of an array declaration when needed */ + + if(np->vdim!=NULL && np->vdim->ndim>1 && + nsubs(primp->argsp)==1 ) + { + if(! ftn66flag) + warni + ("1-dim subscript in EQUIVALENCE, %d-dim declared", + np -> vdim -> ndim); + cp = NULL; + ns = np->vdim->ndim; + while(--ns > 0) + cp = mkchain((char *)ICON(1), cp); + primp->argsp->listp->nextp = cp; + } + + offp = suboffset(primp); + if(ISICON(offp)) + offset = offp->constblock.Const.ci; + else { + dclerr + ("nonconstant subscript in equivalence ", + np); + np = NULL; + } + frexpr(offp); + } + +/* Free up the primblock, since we now have a hash table (Namep) entry */ + + frexpr((expptr)primp); + + if(np && (leng = iarrlen(np))<0) + { + dclerr("adjustable in equivalence", np); + np = NULL; + } + + if(np) switch(np->vstg) + { + case STGUNKNOWN: + case STGBSS: + case STGEQUIV: + if (in_vector(np->cvarname, st_fields, + n_st_fields) >= 0) { + k = strlen(np->cvarname); + strcpy(s = mem(k+2,0), np->cvarname); + s[k] = '_'; + s[k+1] = 0; + np->cvarname = s; + } + break; + + case STGCOMMON: + +/* The code assumes that all COMMON references in a given EQUIVALENCE will + be to the same COMMON block, and will all be consistent */ + + comno = np->vardesc.varno; + comoffset = np->voffset + offset; + break; + + default: + dclerr("bad storage class in equivalence", np); + np = NULL; + break; + } + + if(np) + { + q->eqvoffset = offset; + +/* eqvbottom gets the largest difference between the array base address + and the address specified in the EQUIV declaration */ + + equivdecl->eqvbottom = + lmin(equivdecl->eqvbottom, -offset); + +/* eqvtop gets the largest difference between the end of the array and + the address given in the EQUIVALENCE */ + + equivdecl->eqvtop = + lmax(equivdecl->eqvtop, leng-offset); + } + q->eqvitem.eqvname = np; + } + +/* Now all equivalenced variables are in the hash table with the proper + offset, and eqvtop and eqvbottom are set. */ + + if(comno >= 0) + +/* Get rid of all STGEQUIVS, they will be mapped onto STGCOMMON variables + */ + + eqvcommon(equivdecl, comno, comoffset); + else for(q = equivdecl->equivs ; q ; q = q->eqvnextp) + { + if(np = q->eqvitem.eqvname) + { + inequiv = NO; + if(np->vstg==STGEQUIV) + if( (ovarno = np->vardesc.varno) == i) + { + +/* Can't EQUIV different elements of the same array */ + + if(np->voffset + q->eqvoffset != 0) + dclerr + ("inconsistent equivalence", np); + } + else { + offset = np->voffset; + inequiv = YES; + } + + np->vstg = STGEQUIV; + np->vardesc.varno = i; + np->voffset = - q->eqvoffset; + + if(inequiv) + +/* Combine 2 equivalence declarations */ + + eqveqv(i, ovarno, q->eqvoffset + offset); + } + } + } + +/* Now each equivalence declaration is distinct (all connections have been + merged in eqveqv()), and some may be empty. */ + + for(i = 0 ; i < nequiv ; ++i) + { + equivdecl = & eqvclass[i]; + if(equivdecl->eqvbottom!=0 || equivdecl->eqvtop!=0) { + +/* a live chain */ + + k = TYCHAR; + pref = 1; + for(q = equivdecl->equivs ; q; q = q->eqvnextp) + if ((np = q->eqvitem.eqvname) + && !np->veqvadjust) { + np->veqvadjust = 1; + np->voffset -= equivdecl->eqvbottom; + t = typealign[k1 = np->vtype]; + if (pref < type_pref[k1]) { + k = k1; + pref = type_pref[k1]; + } + if(np->voffset % t != 0) { + dclerr("bad alignment forced by equivalence", np); + --nerr; /* don't give bad return code for this */ + } + } + equivdecl->eqvtype = k; + } + freqchain(equivdecl); + } +} + + + + + +/* put equivalence chain p at common block comno + comoffset */ + +LOCAL eqvcommon(p, comno, comoffset) +struct Equivblock *p; +int comno; +ftnint comoffset; +{ + int ovarno; + ftnint k, offq; + register Namep np; + register struct Eqvchain *q; + + if(comoffset + p->eqvbottom < 0) + { + errstr("attempt to extend common %s backward", + extsymtab[comno].fextname); + freqchain(p); + return; + } + + if( (k = comoffset + p->eqvtop) > extsymtab[comno].extleng) + extsymtab[comno].extleng = k; + + + for(q = p->equivs ; q ; q = q->eqvnextp) + if(np = q->eqvitem.eqvname) + { + switch(np->vstg) + { + case STGUNKNOWN: + case STGBSS: + np->vstg = STGCOMMON; + np->vcommequiv = 1; + np->vardesc.varno = comno; + +/* np -> voffset will point to the base of the array */ + + np->voffset = comoffset - q->eqvoffset; + break; + + case STGEQUIV: + ovarno = np->vardesc.varno; + +/* offq will point to the current element, even if it's in an array */ + + offq = comoffset - q->eqvoffset - np->voffset; + np->vstg = STGCOMMON; + np->vcommequiv = 1; + np->vardesc.varno = comno; + +/* np -> voffset will point to the base of the array */ + + np->voffset += offq; + if(ovarno != (p - eqvclass)) + eqvcommon(&eqvclass[ovarno], comno, offq); + break; + + case STGCOMMON: + if(comno != np->vardesc.varno || + comoffset != np->voffset+q->eqvoffset) + dclerr("inconsistent common usage", np); + break; + + + default: + badstg("eqvcommon", np->vstg); + } + } + + freqchain(p); + p->eqvbottom = p->eqvtop = 0; +} + + +/* Move all items on ovarno chain to the front of nvarno chain. + * adjust offsets of ovarno elements and top and bottom of nvarno chain + */ + +LOCAL eqveqv(nvarno, ovarno, delta) +int ovarno, nvarno; +ftnint delta; +{ + register struct Equivblock *neweqv, *oldeqv; + register Namep np; + struct Eqvchain *q, *q1; + + neweqv = eqvclass + nvarno; + oldeqv = eqvclass + ovarno; + neweqv->eqvbottom = lmin(neweqv->eqvbottom, oldeqv->eqvbottom - delta); + neweqv->eqvtop = lmax(neweqv->eqvtop, oldeqv->eqvtop - delta); + oldeqv->eqvbottom = oldeqv->eqvtop = 0; + + for(q = oldeqv->equivs ; q ; q = q1) + { + q1 = q->eqvnextp; + if( (np = q->eqvitem.eqvname) && np->vardesc.varno==ovarno) + { + q->eqvnextp = neweqv->equivs; + neweqv->equivs = q; + q->eqvoffset += delta; + np->vardesc.varno = nvarno; + np->voffset -= delta; + } + else free( (charptr) q); + } + oldeqv->equivs = NULL; +} + + + + +freqchain(p) +register struct Equivblock *p; +{ + register struct Eqvchain *q, *oq; + + for(q = p->equivs ; q ; q = oq) + { + oq = q->eqvnextp; + free( (charptr) q); + } + p->equivs = NULL; +} + + + + + +/* nsubs -- number of subscripts in this arglist (just the length of the + list) */ + +LOCAL nsubs(p) +register struct Listblock *p; +{ + register int n; + register chainp q; + + n = 0; + if(p) + for(q = p->listp ; q ; q = q->nextp) + ++n; + + return(n); +} diff --git a/usr.bin/f2c/error.c b/usr.bin/f2c/error.c new file mode 100644 index 000000000000..fd68d144d49a --- /dev/null +++ b/usr.bin/f2c/error.c @@ -0,0 +1,252 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" + +warni(s,t) + char *s; + int t; +{ + char buf[100]; + sprintf(buf,s,t); + warn(buf); + } + +warn1(s,t) +char *s, *t; +{ + char buff[100]; + sprintf(buff, s, t); + warn(buff); +} + + +warn(s) +char *s; +{ + if(nowarnflag) + return; + if (infname && *infname) + fprintf(diagfile, "Warning on line %ld of %s: %s\n", + lineno, infname, s); + else + fprintf(diagfile, "Warning on line %ld: %s\n", lineno, s); + fflush(diagfile); + ++nwarn; +} + + +errstr(s, t) +char *s, *t; +{ + char buff[100]; + sprintf(buff, s, t); + err(buff); +} + + + +erri(s,t) +char *s; +int t; +{ + char buff[100]; + sprintf(buff, s, t); + err(buff); +} + +errl(s,t) +char *s; +long t; +{ + char buff[100]; + sprintf(buff, s, t); + err(buff); +} + + char *err_proc = 0; + +err(s) +char *s; +{ + if (err_proc) + fprintf(diagfile, + "Error processing %s before line %ld", + err_proc, lineno); + else + fprintf(diagfile, "Error on line %ld", lineno); + if (infname && *infname) + fprintf(diagfile, " of %s", infname); + fprintf(diagfile, ": %s\n", s); + fflush(diagfile); + ++nerr; +} + + +yyerror(s) +char *s; +{ + err(s); +} + + + +dclerr(s, v) +char *s; +Namep v; +{ + char buff[100]; + + if(v) + { + sprintf(buff, "Declaration error for %s: %s", v->fvarname, s); + err(buff); + } + else + errstr("Declaration error %s", s); +} + + + +execerr(s, n) +char *s, *n; +{ + char buf1[100], buf2[100]; + + sprintf(buf1, "Execution error %s", s); + sprintf(buf2, buf1, n); + err(buf2); +} + + +Fatal(t) +char *t; +{ + fprintf(diagfile, "Compiler error line %ld", lineno); + if (infname) + fprintf(diagfile, " of %s", infname); + fprintf(diagfile, ": %s\n", t); + done(3); +} + + + + +fatalstr(t,s) +char *t, *s; +{ + char buff[100]; + sprintf(buff, t, s); + Fatal(buff); +} + + + +fatali(t,d) +char *t; +int d; +{ + char buff[100]; + sprintf(buff, t, d); + Fatal(buff); +} + + + +badthing(thing, r, t) +char *thing, *r; +int t; +{ + char buff[50]; + sprintf(buff, "Impossible %s %d in routine %s", thing, t, r); + Fatal(buff); +} + + + +badop(r, t) +char *r; +int t; +{ + badthing("opcode", r, t); +} + + + +badtag(r, t) +char *r; +int t; +{ + badthing("tag", r, t); +} + + + + + +badstg(r, t) +char *r; +int t; +{ + badthing("storage class", r, t); +} + + + + +badtype(r, t) +char *r; +int t; +{ + badthing("type", r, t); +} + + +many(s, c, n) +char *s, c; +int n; +{ + char buff[250]; + + sprintf(buff, + "Too many %s.\nTable limit now %d.\nTry rerunning with the -N%c%d option.\n", + s, n, c, 2*n); + Fatal(buff); +} + + +err66(s) +char *s; +{ + errstr("Fortran 77 feature used: %s", s); + --nerr; +} + + + +errext(s) +char *s; +{ + errstr("f2c extension used: %s", s); + --nerr; +} diff --git a/usr.bin/f2c/exec.c b/usr.bin/f2c/exec.c new file mode 100644 index 000000000000..b986492ebc10 --- /dev/null +++ b/usr.bin/f2c/exec.c @@ -0,0 +1,830 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "p1defs.h" +#include "names.h" + +LOCAL void exar2(), popctl(), pushctl(); + +/* Logical IF codes +*/ + + +exif(p) +expptr p; +{ + pushctl(CTLIF); + putif(p, 0); /* 0 => if, not elseif */ +} + + + +exelif(p) +expptr p; +{ + if (ctlstack->ctltype == CTLIF || ctlstack->ctltype == CTLIFX) + putif(p, 1); /* 1 ==> elseif */ + else + execerr("elseif out of place", CNULL); +} + + + + + +exelse() +{ + register struct Ctlframe *c; + + for(c = ctlstack; c->ctltype == CTLIFX; --c); + if(c->ctltype == CTLIF) { + p1_else (); + c->ctltype = CTLELSE; + } + else + execerr("else out of place", CNULL); + } + + +exendif() +{ + while(ctlstack->ctltype == CTLIFX) { + popctl(); + p1else_end(); + } + if(ctlstack->ctltype == CTLIF) { + popctl(); + p1_endif (); + } + else if(ctlstack->ctltype == CTLELSE) { + popctl(); + p1else_end (); + } + else + execerr("endif out of place", CNULL); + } + + +new_endif() +{ + if (ctlstack->ctltype == CTLIF || ctlstack->ctltype == CTLIFX) + pushctl(CTLIFX); + else + err("new_endif bug"); + } + +/* pushctl -- Start a new control construct, initialize the labels (to + zero) */ + + LOCAL void +pushctl(code) + int code; +{ + register int i; + + if(++ctlstack >= lastctl) + many("loops or if-then-elses", 'c', maxctl); + ctlstack->ctltype = code; + for(i = 0 ; i < 4 ; ++i) + ctlstack->ctlabels[i] = 0; + ctlstack->dowhile = 0; + ++blklevel; +} + + + LOCAL void +popctl() +{ + if( ctlstack-- < ctls ) + Fatal("control stack empty"); + --blklevel; +} + + + +/* poplab -- update the flags in labeltab */ + +LOCAL poplab() +{ + register struct Labelblock *lp; + + for(lp = labeltab ; lp < highlabtab ; ++lp) + if(lp->labdefined) + { + /* mark all labels in inner blocks unreachable */ + if(lp->blklevel > blklevel) + lp->labinacc = YES; + } + else if(lp->blklevel > blklevel) + { + /* move all labels referred to in inner blocks out a level */ + lp->blklevel = blklevel; + } +} + + +/* BRANCHING CODE +*/ + +exgoto(lab) +struct Labelblock *lab; +{ + lab->labused = 1; + p1_goto (lab -> stateno); +} + + + + + + + +exequals(lp, rp) +register struct Primblock *lp; +register expptr rp; +{ + if(lp->tag != TPRIM) + { + err("assignment to a non-variable"); + frexpr((expptr)lp); + frexpr(rp); + } + else if(lp->namep->vclass!=CLVAR && lp->argsp) + { + if(parstate >= INEXEC) + err("statement function amid executables"); + mkstfunct(lp, rp); + } + else + { + expptr new_lp, new_rp; + + if(parstate < INDATA) + enddcl(); + new_lp = mklhs (lp, keepsubs); + new_rp = fixtype (rp); + puteq(new_lp, new_rp); + } +} + + + +/* Make Statement Function */ + +long laststfcn = -1, thisstno; +int doing_stmtfcn; + +mkstfunct(lp, rp) +struct Primblock *lp; +expptr rp; +{ + register struct Primblock *p; + register Namep np; + chainp args; + + laststfcn = thisstno; + np = lp->namep; + if(np->vclass == CLUNKNOWN) + np->vclass = CLPROC; + else + { + dclerr("redeclaration of statement function", np); + return; + } + np->vprocclass = PSTFUNCT; + np->vstg = STGSTFUNCT; + +/* Set the type of the function */ + + impldcl(np); + if (np->vtype == TYCHAR && !np->vleng) + err("character statement function with length (*)"); + args = (lp->argsp ? lp->argsp->listp : CHNULL); + np->varxptr.vstfdesc = mkchain((char *)args, (chainp)rp); + + for(doing_stmtfcn = 1 ; args ; args = args->nextp) + +/* It is an error for the formal parameters to have arguments or + subscripts */ + + if( ((tagptr)(args->datap))->tag!=TPRIM || + (p = (struct Primblock *)(args->datap) )->argsp || + p->fcharp || p->lcharp ) + err("non-variable argument in statement function definition"); + else + { + +/* Replace the name on the left-hand side */ + + args->datap = (char *)p->namep; + vardcl(p -> namep); + free((char *)p); + } + doing_stmtfcn = 0; +} + + static void +mixed_type(np) + Namep np; +{ + char buf[128]; + sprintf(buf, "%s function %.90s invoked as subroutine", + ftn_types[np->vtype], np->fvarname); + warn(buf); + } + + +excall(name, args, nstars, labels) +Namep name; +struct Listblock *args; +int nstars; +struct Labelblock *labels[ ]; +{ + register expptr p; + + if (name->vtype != TYSUBR) { + if (name->vinfproc && !name->vcalled) { + name->vtype = TYSUBR; + frexpr(name->vleng); + name->vleng = 0; + } + else if (!name->vimpltype && name->vtype != TYUNKNOWN) + mixed_type(name); + else + settype(name, TYSUBR, (ftnint)0); + } + p = mkfunct( mkprim(name, args, CHNULL) ); + +/* Subroutines and their identifiers acquire the type INT */ + + p->exprblock.vtype = p->exprblock.leftp->headblock.vtype = TYINT; + +/* Handle the alternate return mechanism */ + + if(nstars > 0) + putcmgo(putx(fixtype(p)), nstars, labels); + else + putexpr(p); +} + + + +exstop(stop, p) +int stop; +register expptr p; +{ + char *str; + int n; + expptr mkstrcon(); + + if(p) + { + if( ! ISCONST(p) ) + { + execerr("pause/stop argument must be constant", CNULL); + frexpr(p); + p = mkstrcon(0, CNULL); + } + else if( ISINT(p->constblock.vtype) ) + { + str = convic(p->constblock.Const.ci); + n = strlen(str); + if(n > 0) + { + p->constblock.Const.ccp = copyn(n, str); + p->constblock.Const.ccp1.blanks = 0; + p->constblock.vtype = TYCHAR; + p->constblock.vleng = (expptr) ICON(n); + } + else + p = (expptr) mkstrcon(0, CNULL); + } + else if(p->constblock.vtype != TYCHAR) + { + execerr("pause/stop argument must be integer or string", CNULL); + p = (expptr) mkstrcon(0, CNULL); + } + } + else p = (expptr) mkstrcon(0, CNULL); + + { + expptr subr_call; + + subr_call = call1(TYSUBR, (stop ? "s_stop" : "s_paus"), p); + putexpr( subr_call ); + } +} + +/* DO LOOP CODE */ + +#define DOINIT par[0] +#define DOLIMIT par[1] +#define DOINCR par[2] + + +/* Macros for ctlstack -> dostepsign */ + +#define VARSTEP 0 +#define POSSTEP 1 +#define NEGSTEP 2 + + +/* exdo -- generate DO loop code. In the case of a variable increment, + positive increment tests are placed above the body, negative increment + tests are placed below (see enddo() ) */ + +exdo(range, loopname, spec) +int range; /* end label */ +Namep loopname; +chainp spec; /* input spec must have at least 2 exprs */ +{ + register expptr p; + register Namep np; + chainp cp; /* loops over the fields in spec */ + register int i; + int dotype; /* type of the index variable */ + int incsign; /* sign of the increment, if it's constant + */ + Addrp dovarp; /* loop index variable */ + expptr doinit; /* constant or register for init param */ + expptr par[3]; /* local specification parameters */ + + expptr init, test, inc; /* Expressions in the resulting FOR loop */ + + + test = ENULL; + + pushctl(CTLDO); + dorange = ctlstack->dolabel = range; + ctlstack->loopname = loopname; + +/* Declare the loop index */ + + np = (Namep)spec->datap; + ctlstack->donamep = NULL; + if (!np) { /* do while */ + ctlstack->dowhile = 1; +#if 0 + if (loopname) { + if (loopname->vtype == TYUNKNOWN) { + loopname->vdcldone = 1; + loopname->vclass = CLLABEL; + loopname->vprocclass = PLABEL; + loopname->vtype = TYLABEL; + } + if (loopname->vtype == TYLABEL) + if (loopname->vdovar) + dclerr("already in use as a loop name", + loopname); + else + loopname->vdovar = 1; + else + dclerr("already declared; cannot be a loop name", + loopname); + } +#endif + putwhile((expptr)spec->nextp); + NOEXT("do while"); + spec->nextp = 0; + frchain(&spec); + return; + } + if(np->vdovar) + { + errstr("nested loops with variable %s", np->fvarname); + ctlstack->donamep = NULL; + return; + } + +/* Create a memory-resident version of the index variable */ + + dovarp = mkplace(np); + if( ! ONEOF(dovarp->vtype, MSKINT|MSKREAL) ) + { + err("bad type on do variable"); + return; + } + ctlstack->donamep = np; + + np->vdovar = YES; + +/* Now dovarp points to the index to be used within the loop, dostgp + points to the one which may need to be stored */ + + dotype = dovarp->vtype; + +/* Count the input specifications and type-check each one independently; + this just eliminates non-numeric values from the specification */ + + for(i=0 , cp = spec->nextp ; cp!=NULL && i<3 ; cp = cp->nextp) + { + p = par[i++] = fixtype((tagptr)cp->datap); + if( ! ONEOF(p->headblock.vtype, MSKINT|MSKREAL) ) + { + err("bad type on DO parameter"); + return; + } + } + + frchain(&spec); + switch(i) + { + case 0: + case 1: + err("too few DO parameters"); + return; + + default: + err("too many DO parameters"); + return; + + case 2: + DOINCR = (expptr) ICON(1); + + case 3: + break; + } + + +/* Now all of the local specification fields are set, but their types are + not yet consistent */ + +/* Declare the loop initialization value, casting it properly and declaring a + register if need be */ + + if (ISCONST (DOINIT) || !onetripflag) +/* putx added 6-29-89 (mwm), not sure if fixtype is required, but I doubt it + since mkconv is called just before */ + doinit = putx (mkconv (dotype, DOINIT)); + else { + doinit = (expptr) mktmp(dotype, ENULL); + puteq (cpexpr (doinit), DOINIT); + } /* else */ + +/* Declare the loop ending value, casting it to the type of the index + variable */ + + if( ISCONST(DOLIMIT) ) + ctlstack->domax = mkconv(dotype, DOLIMIT); + else { + ctlstack->domax = (expptr) mktmp0(dotype, ENULL); + puteq (cpexpr (ctlstack -> domax), DOLIMIT); + } /* else */ + +/* Declare the loop increment value, casting it to the type of the index + variable */ + + if( ISCONST(DOINCR) ) + { + ctlstack->dostep = mkconv(dotype, DOINCR); + if( (incsign = conssgn(ctlstack->dostep)) == 0) + err("zero DO increment"); + ctlstack->dostepsign = (incsign > 0 ? POSSTEP : NEGSTEP); + } + else + { + ctlstack->dostep = (expptr) mktmp0(dotype, ENULL); + ctlstack->dostepsign = VARSTEP; + puteq (cpexpr (ctlstack -> dostep), DOINCR); + } + +/* All data is now properly typed and in the ctlstack, except for the + initial value. Assignments of temps have been generated already */ + + switch (ctlstack -> dostepsign) { + case VARSTEP: + test = mkexpr (OPQUEST, mkexpr (OPLT, + cpexpr (ctlstack -> dostep), ICON(0)), + mkexpr (OPCOLON, + mkexpr (OPGE, cpexpr((expptr)dovarp), + cpexpr (ctlstack -> domax)), + mkexpr (OPLE, cpexpr((expptr)dovarp), + cpexpr (ctlstack -> domax)))); + break; + case POSSTEP: + test = mkexpr (OPLE, cpexpr((expptr)dovarp), + cpexpr (ctlstack -> domax)); + break; + case NEGSTEP: + test = mkexpr (OPGE, cpexpr((expptr)dovarp), + cpexpr (ctlstack -> domax)); + break; + default: + erri ("exdo: bad dostepsign '%d'", ctlstack -> dostepsign); + break; + } /* switch (ctlstack -> dostepsign) */ + + if (onetripflag) + test = mkexpr (OPOR, test, + mkexpr (OPEQ, cpexpr((expptr)dovarp), cpexpr (doinit))); + init = mkexpr (OPASSIGN, cpexpr((expptr)dovarp), doinit); + inc = mkexpr (OPPLUSEQ, (expptr)dovarp, cpexpr (ctlstack -> dostep)); + + if (!onetripflag && ISCONST (ctlstack -> domax) && ISCONST (doinit) + && ctlstack -> dostepsign != VARSTEP) { + expptr tester; + + tester = mkexpr (OPMINUS, cpexpr (doinit), + cpexpr (ctlstack -> domax)); + if (incsign == conssgn (tester)) + warn ("DO range never executed"); + frexpr (tester); + } /* if !onetripflag && */ + + p1_for (init, test, inc); +} + +exenddo(np) + Namep np; +{ + Namep np1; + int here; + struct Ctlframe *cf; + + if( ctlstack < ctls ) + goto misplaced; + here = ctlstack->dolabel; + if (ctlstack->ctltype != CTLDO + || here >= 0 && (!thislabel || thislabel->labelno != here)) { + misplaced: + err("misplaced ENDDO"); + return; + } + if (np != ctlstack->loopname) { + if (np1 = ctlstack->loopname) + errstr("expected \"enddo %s\"", np1->fvarname); + else + err("expected unnamed ENDDO"); + for(cf = ctls; cf < ctlstack; cf++) + if (cf->ctltype == CTLDO && cf->loopname == np) { + here = cf->dolabel; + break; + } + } + enddo(here); + } + + +enddo(here) +int here; +{ + register struct Ctlframe *q; + Namep np; /* name of the current DO index */ + Addrp ap; + register int i; + register expptr e; + +/* Many DO's can end at the same statement, so keep looping over all + nested indicies */ + + while(here == dorange) + { + if(np = ctlstack->donamep) + { + p1for_end (); + +/* Now we're done with all of the tests, and the loop has terminated. + Store the index value back in long-term memory */ + + if(ap = memversion(np)) + puteq((expptr)ap, (expptr)mkplace(np)); + for(i = 0 ; i < 4 ; ++i) + ctlstack->ctlabels[i] = 0; + deregister(ctlstack->donamep); + ctlstack->donamep->vdovar = NO; + e = ctlstack->dostep; + if (e->tag == TADDR && e->addrblock.istemp) + frtemp((Addrp)e); + else + frexpr(e); + e = ctlstack->domax; + if (e->tag == TADDR && e->addrblock.istemp) + frtemp((Addrp)e); + else + frexpr(e); + } + else if (ctlstack->dowhile) + p1for_end (); + +/* Set dorange to the closing label of the next most enclosing DO loop + */ + + popctl(); + poplab(); + dorange = 0; + for(q = ctlstack ; q>=ctls ; --q) + if(q->ctltype == CTLDO) + { + dorange = q->dolabel; + break; + } + } +} + +exassign(vname, labelval) + register Namep vname; +struct Labelblock *labelval; +{ + Addrp p; + expptr mkaddcon(); + register Addrp q; + char *fs; + register chainp cp, cpprev; + register ftnint k, stno; + + p = mkplace(vname); + if( ! ONEOF(p->vtype, MSKINT|MSKADDR) ) { + err("noninteger assign variable"); + return; + } + + /* If the label hasn't been defined, then we do things twice: + * once for an executable stmt label, once for a format + */ + + /* code for executable label... */ + +/* Now store the assigned value in a list associated with this variable. + This will be used later to generate a switch() statement in the C output */ + + fs = labelval->fmtstring; + if (!labelval->labdefined || !fs) { + + if (vname -> vis_assigned == 0) { + vname -> varxptr.assigned_values = CHNULL; + vname -> vis_assigned = 1; + } + + /* don't duplicate labels... */ + + stno = labelval->stateno; + cpprev = 0; + for(k = 0, cp = vname->varxptr.assigned_values; + cp; cpprev = cp, cp = cp->nextp, k++) + if ((ftnint)cp->datap == stno) + break; + if (!cp) { + cp = mkchain((char *)stno, CHNULL); + if (cpprev) + cpprev->nextp = cp; + else + vname->varxptr.assigned_values = cp; + labelval->labused = 1; + } + putout(mkexpr(OPASSIGN, (expptr)p, mkintcon(k))); + } + + /* Code for FORMAT label... */ + + if (!labelval->labdefined || fs) { + extern void fmtname(); + + labelval->fmtlabused = 1; + p = ALLOC(Addrblock); + p->tag = TADDR; + p->vtype = TYCHAR; + p->vstg = STGAUTO; + p->memoffset = ICON(0); + fmtname(vname, p); + q = ALLOC(Addrblock); + q->tag = TADDR; + q->vtype = TYCHAR; + q->vstg = STGAUTO; + q->ntempelt = 1; + q->memoffset = ICON(0); + q->uname_tag = UNAM_IDENT; + sprintf(q->user.ident, "fmt_%ld", labelval->stateno); + putout(mkexpr(OPASSIGN, (expptr)p, (expptr)q)); + } + +} /* exassign */ + + + +exarif(expr, neglab, zerlab, poslab) +expptr expr; +struct Labelblock *neglab, *zerlab, *poslab; +{ + register int lm, lz, lp; + + lm = neglab->stateno; + lz = zerlab->stateno; + lp = poslab->stateno; + expr = fixtype(expr); + + if( ! ONEOF(expr->headblock.vtype, MSKINT|MSKREAL) ) + { + err("invalid type of arithmetic if expression"); + frexpr(expr); + } + else + { + if (lm == lz && lz == lp) + exgoto (neglab); + else if(lm == lz) + exar2(OPLE, expr, neglab, poslab); + else if(lm == lp) + exar2(OPNE, expr, neglab, zerlab); + else if(lz == lp) + exar2(OPGE, expr, zerlab, neglab); + else { + expptr t; + + if (!addressable (expr)) { + t = (expptr) mktmp(expr -> headblock.vtype, ENULL); + expr = mkexpr (OPASSIGN, cpexpr (t), expr); + } else + t = (expptr) cpexpr (expr); + + p1_if(putx(fixtype(mkexpr (OPLT, expr, ICON (0))))); + exgoto(neglab); + p1_elif (mkexpr (OPEQ, t, ICON (0))); + exgoto(zerlab); + p1_else (); + exgoto(poslab); + p1else_end (); + } /* else */ + } +} + + + +/* exar2 -- Do arithmetic IF for only 2 distinct labels; if !(e.op.0) + goto l2 else goto l1. If this seems backwards, that's because it is, + in order to make the 1 pass algorithm work. */ + + LOCAL void +exar2(op, e, l1, l2) + int op; + expptr e; + struct Labelblock *l1, *l2; +{ + expptr comp; + + comp = mkexpr (op, e, ICON (0)); + p1_if(putx(fixtype(comp))); + exgoto(l1); + p1_else (); + exgoto(l2); + p1else_end (); +} + + +/* exreturn -- return the value in p from a SUBROUTINE call -- used to + implement the alternate return mechanism */ + +exreturn(p) +register expptr p; +{ + if(procclass != CLPROC) + warn("RETURN statement in main or block data"); + if(p && (proctype!=TYSUBR || procclass!=CLPROC) ) + { + err("alternate return in nonsubroutine"); + p = 0; + } + + if (p || proctype == TYSUBR) { + if (p == ENULL) p = ICON (0); + p = mkconv (TYLONG, fixtype (p)); + p1_subr_ret (p); + } /* if p || proctype == TYSUBR */ + else + p1_subr_ret((expptr)retslot); +} + + +exasgoto(labvar) +Namep labvar; +{ + register Addrp p; + void p1_asgoto(); + + p = mkplace(labvar); + if( ! ISINT(p->vtype) ) + err("assigned goto variable must be integer"); + else { + p1_asgoto (p); + } /* else */ +} diff --git a/usr.bin/f2c/expr.c b/usr.bin/f2c/expr.c new file mode 100644 index 000000000000..eeccf42de50d --- /dev/null +++ b/usr.bin/f2c/expr.c @@ -0,0 +1,3042 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "output.h" +#include "names.h" + +LOCAL void conspower(), consbinop(), zdiv(); +LOCAL expptr fold(), mkpower(), stfcall(); +#ifndef stfcall_MAX +#define stfcall_MAX 144 +#endif + +typedef struct { double dreal, dimag; } dcomplex; + +extern char dflttype[26]; +extern int htype; + +/* little routines to create constant blocks */ + +Constp mkconst(t) +register int t; +{ + register Constp p; + + p = ALLOC(Constblock); + p->tag = TCONST; + p->vtype = t; + return(p); +} + + +/* mklogcon -- Make Logical Constant */ + +expptr mklogcon(l) +register int l; +{ + register Constp p; + + p = mkconst(tylog); + p->Const.ci = l; + return( (expptr) p ); +} + + + +/* mkintcon -- Make Integer Constant */ + +expptr mkintcon(l) +ftnint l; +{ + register Constp p; + + p = mkconst(tyint); + p->Const.ci = l; + return( (expptr) p ); +} + + + + +/* mkaddcon -- Make Address Constant, given integer value */ + +expptr mkaddcon(l) +register long l; +{ + register Constp p; + + p = mkconst(TYADDR); + p->Const.ci = l; + return( (expptr) p ); +} + + + +/* mkrealcon -- Make Real Constant. The type t is assumed + to be TYREAL or TYDREAL */ + +expptr mkrealcon(t, d) + register int t; + char *d; +{ + register Constp p; + + p = mkconst(t); + p->Const.cds[0] = cds(d,CNULL); + p->vstg = 1; + return( (expptr) p ); +} + + +/* mkbitcon -- Make bit constant. Reads the input string, which is + assumed to correctly specify a number in base 2^shift (where shift + is the input parameter). shift may not exceed 4, i.e. only binary, + quad, octal and hex bases may be input. Constants may not exceed 32 + bits, or whatever the size of (struct Constblock).ci may be. */ + +expptr mkbitcon(shift, leng, s) +int shift; +int leng; +char *s; +{ + register Constp p; + register long x; + + p = mkconst(TYLONG); + x = 0; + while(--leng >= 0) + if(*s != ' ') + x = (x << shift) | hextoi(*s++); + /* mwm wanted to change the type to short for short constants, + * but this is dangerous -- there is no syntax for long constants + * with small values. + */ + p->Const.ci = x; + return( (expptr) p ); +} + + + + + +/* mkstrcon -- Make string constant. Allocates storage and initializes + the memory for a copy of the input Fortran-string. */ + +expptr mkstrcon(l,v) +int l; +register char *v; +{ + register Constp p; + register char *s; + + p = mkconst(TYCHAR); + p->vleng = ICON(l); + p->Const.ccp = s = (char *) ckalloc(l+1); + p->Const.ccp1.blanks = 0; + while(--l >= 0) + *s++ = *v++; + *s = '\0'; + return( (expptr) p ); +} + + + +/* mkcxcon -- Make complex contsant. A complex number is a pair of + values, each of which may be integer, real or double. */ + +expptr mkcxcon(realp,imagp) +register expptr realp, imagp; +{ + int rtype, itype; + register Constp p; + expptr errnode(); + + rtype = realp->headblock.vtype; + itype = imagp->headblock.vtype; + + if( ISCONST(realp) && ISNUMERIC(rtype) && ISCONST(imagp) && ISNUMERIC(itype) ) + { + p = mkconst( (rtype==TYDREAL||itype==TYDREAL) + ? TYDCOMPLEX : tycomplex); + if (realp->constblock.vstg || imagp->constblock.vstg) { + p->vstg = 1; + p->Const.cds[0] = ISINT(rtype) + ? string_num("", realp->constblock.Const.ci) + : realp->constblock.vstg + ? realp->constblock.Const.cds[0] + : dtos(realp->constblock.Const.cd[0]); + p->Const.cds[1] = ISINT(itype) + ? string_num("", imagp->constblock.Const.ci) + : imagp->constblock.vstg + ? imagp->constblock.Const.cds[0] + : dtos(imagp->constblock.Const.cd[0]); + } + else { + p->Const.cd[0] = ISINT(rtype) + ? realp->constblock.Const.ci + : realp->constblock.Const.cd[0]; + p->Const.cd[1] = ISINT(itype) + ? imagp->constblock.Const.ci + : imagp->constblock.Const.cd[0]; + } + } + else + { + err("invalid complex constant"); + p = (Constp)errnode(); + } + + frexpr(realp); + frexpr(imagp); + return( (expptr) p ); +} + + +/* errnode -- Allocate a new error block */ + +expptr errnode() +{ + struct Errorblock *p; + p = ALLOC(Errorblock); + p->tag = TERROR; + p->vtype = TYERROR; + return( (expptr) p ); +} + + + + + +/* mkconv -- Make type conversion. Cast expression p into type t. + Note that casting to a character copies only the first sizeof(char) + bytes. */ + +expptr mkconv(t, p) +register int t; +register expptr p; +{ + register expptr q; + register int pt, charwarn = 1; + expptr opconv(); + + if (t >= 100) { + t -= 100; + charwarn = 0; + } + if(t==TYUNKNOWN || t==TYERROR) + badtype("mkconv", t); + pt = p->headblock.vtype; + +/* Casting to the same type is a no-op */ + + if(t == pt) + return(p); + +/* If we're casting a constant which is not in the literal table ... */ + + else if( ISCONST(p) && pt!=TYADDR && pt != TYCHAR) + { + if (ISINT(t) && ISINT(pt) || ISREAL(t) && ISREAL(pt)) { + /* avoid trouble with -i2 */ + p->headblock.vtype = t; + return p; + } + q = (expptr) mkconst(t); + consconv(t, &q->constblock, &p->constblock ); + frexpr(p); + } + else { + if (pt == TYCHAR && t != TYADDR && charwarn + && (!halign || p->tag != TADDR + || p->addrblock.uname_tag != UNAM_CONST)) + warn( + "ichar([first char. of] char. string) assumed for conversion to numeric"); + q = opconv(p, t); + } + + if(t == TYCHAR) + q->constblock.vleng = ICON(1); + return(q); +} + + + +/* opconv -- Convert expression p to type t using the main + expression evaluator; returns an OPCONV expression, I think 14-jun-88 mwm */ + +expptr opconv(p, t) +expptr p; +int t; +{ + register expptr q; + + if (t == TYSUBR) + err("illegal use of subroutine name"); + q = mkexpr(OPCONV, p, ENULL); + q->headblock.vtype = t; + return(q); +} + + + +/* addrof -- Create an ADDR expression operation */ + +expptr addrof(p) +expptr p; +{ + return( mkexpr(OPADDR, p, ENULL) ); +} + + + +/* cpexpr - Returns a new copy of input expression p */ + +tagptr cpexpr(p) +register tagptr p; +{ + register tagptr e; + int tag; + register chainp ep, pp; + tagptr cpblock(); + +/* This table depends on the ordering of the T macros, e.g. TNAME */ + + static int blksize[ ] = + { + 0, + sizeof(struct Nameblock), + sizeof(struct Constblock), + sizeof(struct Exprblock), + sizeof(struct Addrblock), + sizeof(struct Primblock), + sizeof(struct Listblock), + sizeof(struct Impldoblock), + sizeof(struct Errorblock) + }; + + if(p == NULL) + return(NULL); + +/* TNAMEs are special, and don't get copied. Each name in the current + symbol table has a unique TNAME structure. */ + + if( (tag = p->tag) == TNAME) + return(p); + + e = cpblock(blksize[p->tag], (char *)p); + + switch(tag) + { + case TCONST: + if(e->constblock.vtype == TYCHAR) + { + e->constblock.Const.ccp = + copyn((int)e->constblock.vleng->constblock.Const.ci+1, + e->constblock.Const.ccp); + e->constblock.vleng = + (expptr) cpexpr(e->constblock.vleng); + } + case TERROR: + break; + + case TEXPR: + e->exprblock.leftp = (expptr) cpexpr(p->exprblock.leftp); + e->exprblock.rightp = (expptr) cpexpr(p->exprblock.rightp); + break; + + case TLIST: + if(pp = p->listblock.listp) + { + ep = e->listblock.listp = + mkchain((char *)cpexpr((tagptr)pp->datap), CHNULL); + for(pp = pp->nextp ; pp ; pp = pp->nextp) + ep = ep->nextp = + mkchain((char *)cpexpr((tagptr)pp->datap), + CHNULL); + } + break; + + case TADDR: + e->addrblock.vleng = (expptr) cpexpr(e->addrblock.vleng); + e->addrblock.memoffset = (expptr)cpexpr(e->addrblock.memoffset); + e->addrblock.istemp = NO; + break; + + case TPRIM: + e->primblock.argsp = (struct Listblock *) + cpexpr((expptr)e->primblock.argsp); + e->primblock.fcharp = (expptr) cpexpr(e->primblock.fcharp); + e->primblock.lcharp = (expptr) cpexpr(e->primblock.lcharp); + break; + + default: + badtag("cpexpr", tag); + } + + return(e); +} + +/* frexpr -- Free expression -- frees up memory used by expression p */ + +frexpr(p) +register tagptr p; +{ + register chainp q; + + if(p == NULL) + return; + + switch(p->tag) + { + case TCONST: + if( ISCHAR(p) ) + { + free( (charptr) (p->constblock.Const.ccp) ); + frexpr(p->constblock.vleng); + } + break; + + case TADDR: + if (p->addrblock.vtype > TYERROR) /* i/o block */ + break; + frexpr(p->addrblock.vleng); + frexpr(p->addrblock.memoffset); + break; + + case TERROR: + break; + +/* TNAME blocks don't get free'd - probably because they're pointed to in + the hash table. 14-Jun-88 -- mwm */ + + case TNAME: + return; + + case TPRIM: + frexpr((expptr)p->primblock.argsp); + frexpr(p->primblock.fcharp); + frexpr(p->primblock.lcharp); + break; + + case TEXPR: + frexpr(p->exprblock.leftp); + if(p->exprblock.rightp) + frexpr(p->exprblock.rightp); + break; + + case TLIST: + for(q = p->listblock.listp ; q ; q = q->nextp) + frexpr((tagptr)q->datap); + frchain( &(p->listblock.listp) ); + break; + + default: + badtag("frexpr", p->tag); + } + + free( (charptr) p ); +} + + void +wronginf(np) + Namep np; +{ + int c, k; + warn1("fixing wrong type inferred for %.65s", np->fvarname); + np->vinftype = 0; + c = letter(np->fvarname[0]); + if ((np->vtype = impltype[c]) == TYCHAR + && (k = implleng[c])) + np->vleng = ICON(k); + } + +/* fix up types in expression; replace subtrees and convert + names to address blocks */ + +expptr fixtype(p) +register tagptr p; +{ + + if(p == 0) + return(0); + + switch(p->tag) + { + case TCONST: + if(ONEOF(p->constblock.vtype,MSKINT|MSKLOGICAL|MSKADDR| + MSKREAL) ) + return( (expptr) p); + + return( (expptr) putconst((Constp)p) ); + + case TADDR: + p->addrblock.memoffset = fixtype(p->addrblock.memoffset); + return( (expptr) p); + + case TERROR: + return( (expptr) p); + + default: + badtag("fixtype", p->tag); + +/* This case means that fixexpr can't call fixtype with any expr, + only a subexpr of its parameter. */ + + case TEXPR: + return( fixexpr((Exprp)p) ); + + case TLIST: + return( (expptr) p ); + + case TPRIM: + if(p->primblock.argsp && p->primblock.namep->vclass!=CLVAR) + { + if(p->primblock.namep->vtype == TYSUBR) + { + err("function invocation of subroutine"); + return( errnode() ); + } + else { + if (p->primblock.namep->vinftype) + wronginf(p->primblock.namep); + return( mkfunct(p) ); + } + } + +/* The lack of args makes p a function name, substring reference + or variable name. */ + + else return mklhs((struct Primblock *) p, keepsubs); + } +} + + + int +badchleng(p) register expptr p; +{ + if (!p->headblock.vleng) { + if (p->headblock.tag == TADDR + && p->addrblock.uname_tag == UNAM_NAME) + errstr("bad use of character*(*) variable %.60s", + p->addrblock.user.name->fvarname); + else + err("Bad use of character*(*)"); + return 1; + } + return 0; + } + + + static expptr +cplenexpr(p) + expptr p; +{ + expptr rv; + + if (badchleng(p)) + return ICON(1); + rv = cpexpr(p->headblock.vleng); + if (ISCONST(p) && p->constblock.vtype == TYCHAR) + rv->constblock.Const.ci += p->constblock.Const.ccp1.blanks; + return rv; + } + + +/* special case tree transformations and cleanups of expression trees. + Parameter p should have a TEXPR tag at its root, else an error is + returned */ + +expptr fixexpr(p) +register Exprp p; +{ + expptr lp; + register expptr rp; + register expptr q; + int opcode, ltype, rtype, ptype, mtype; + + if( ISERROR(p) ) + return( (expptr) p ); + else if(p->tag != TEXPR) + badtag("fixexpr", p->tag); + opcode = p->opcode; + +/* First set the types of the left and right subexpressions */ + + lp = p->leftp; + if (!ISCONST(lp) || lp->constblock.vtype != TYCHAR) + lp = p->leftp = fixtype(lp); + ltype = lp->headblock.vtype; + + if(opcode==OPASSIGN && lp->tag!=TADDR) + { + err("left side of assignment must be variable"); + frexpr((expptr)p); + return( errnode() ); + } + + if(rp = p->rightp) + { + if (!ISCONST(rp) || rp->constblock.vtype != TYCHAR) + rp = p->rightp = fixtype(rp); + rtype = rp->headblock.vtype; + } + else + rtype = 0; + + if(ltype==TYERROR || rtype==TYERROR) + { + frexpr((expptr)p); + return( errnode() ); + } + +/* Now work on the whole expression */ + + /* force folding if possible */ + + if( ISCONST(lp) && (rp==NULL || ISCONST(rp)) ) + { + q = opcode == OPCONV && lp->constblock.vtype == p->vtype + ? lp : mkexpr(opcode, lp, rp); + +/* mkexpr is expected to reduce constant expressions */ + + if( ISCONST(q) ) { + p->leftp = p->rightp = 0; + frexpr((expptr)p); + return(q); + } + free( (charptr) q ); /* constants did not fold */ + } + + if( (ptype = cktype(opcode, ltype, rtype)) == TYERROR) + { + frexpr((expptr)p); + return( errnode() ); + } + + if (ltype == TYCHAR && ISCONST(lp)) + p->leftp = lp = (expptr)putconst((Constp)lp); + if (rtype == TYCHAR && ISCONST(rp)) + p->rightp = rp = (expptr)putconst((Constp)rp); + + switch(opcode) + { + case OPCONCAT: + if(p->vleng == NULL) + p->vleng = mkexpr(OPPLUS, cplenexpr(lp), + cplenexpr(rp) ); + break; + + case OPASSIGN: + if (rtype == TYREAL || ISLOGICAL(ptype)) + break; + case OPPLUSEQ: + case OPSTAREQ: + if(ltype == rtype) + break; + if( ! ISCONST(rp) && ISREAL(ltype) && ISREAL(rtype) ) + break; + if( ISCOMPLEX(ltype) || ISCOMPLEX(rtype) ) + break; + if( ONEOF(ltype, MSKADDR|MSKINT) && ONEOF(rtype, MSKADDR|MSKINT) + && typesize[ltype]>=typesize[rtype] ) + break; + +/* Cast the right hand side to match the type of the expression */ + + p->rightp = fixtype( mkconv(ptype, rp) ); + break; + + case OPSLASH: + if( ISCOMPLEX(rtype) ) + { + p = (Exprp) call2(ptype, + +/* Handle double precision complex variables */ + + ptype == TYCOMPLEX ? "c_div" : "z_div", + mkconv(ptype, lp), mkconv(ptype, rp) ); + break; + } + case OPPLUS: + case OPMINUS: + case OPSTAR: + case OPMOD: + if(ptype==TYDREAL && ( (ltype==TYREAL && ! ISCONST(lp) ) || + (rtype==TYREAL && ! ISCONST(rp) ) )) + break; + if( ISCOMPLEX(ptype) ) + break; + +/* Cast both sides of the expression to match the type of the whole + expression. */ + + if(ltype != ptype && (ltype < TYINT1 || ptype > TYDREAL)) + p->leftp = fixtype(mkconv(ptype,lp)); + if(rtype != ptype && (rtype < TYINT1 || ptype > TYDREAL)) + p->rightp = fixtype(mkconv(ptype,rp)); + break; + + case OPPOWER: + return( mkpower((expptr)p) ); + + case OPLT: + case OPLE: + case OPGT: + case OPGE: + case OPEQ: + case OPNE: + if(ltype == rtype) + break; + if (htype) { + if (ltype == TYCHAR) { + p->leftp = fixtype(mkconv(rtype,lp)); + break; + } + if (rtype == TYCHAR) { + p->rightp = fixtype(mkconv(ltype,rp)); + break; + } + } + mtype = cktype(OPMINUS, ltype, rtype); + if(mtype==TYDREAL && ( (ltype==TYREAL && ! ISCONST(lp)) || + (rtype==TYREAL && ! ISCONST(rp)) )) + break; + if( ISCOMPLEX(mtype) ) + break; + if(ltype != mtype) + p->leftp = fixtype(mkconv(mtype,lp)); + if(rtype != mtype) + p->rightp = fixtype(mkconv(mtype,rp)); + break; + + case OPCONV: + ptype = cktype(OPCONV, p->vtype, ltype); + if(lp->tag==TEXPR && lp->exprblock.opcode==OPCOMMA + && !ISCOMPLEX(ptype)) + { + lp->exprblock.rightp = + fixtype( mkconv(ptype, lp->exprblock.rightp) ); + free( (charptr) p ); + p = (Exprp) lp; + } + break; + + case OPADDR: + if(lp->tag==TEXPR && lp->exprblock.opcode==OPADDR) + Fatal("addr of addr"); + break; + + case OPCOMMA: + case OPQUEST: + case OPCOLON: + break; + + case OPMIN: + case OPMAX: + case OPMIN2: + case OPMAX2: + case OPDMIN: + case OPDMAX: + case OPABS: + case OPDABS: + ptype = p->vtype; + break; + + default: + break; + } + + p->vtype = ptype; + return((expptr) p); +} + + +/* fix an argument list, taking due care for special first level cases */ + +fixargs(doput, p0) +int doput; /* doput is true if constants need to be passed by reference */ +struct Listblock *p0; +{ + register chainp p; + register tagptr q, t; + register int qtag; + int nargs; + Addrp mkscalar(); + + nargs = 0; + if(p0) + for(p = p0->listp ; p ; p = p->nextp) + { + ++nargs; + q = (tagptr)p->datap; + qtag = q->tag; + if(qtag == TCONST) + { + +/* Call putconst() to store values in a constant table. Since even + constants must be passed by reference, this can optimize on the storage + required */ + + p->datap = doput ? (char *)putconst((Constp)q) + : (char *)q; + } + +/* Take a function name and turn it into an Addr. This only happens when + nothing else has figured out the function beforehand */ + + else if(qtag==TPRIM && q->primblock.argsp==0 && + q->primblock.namep->vclass==CLPROC && + q->primblock.namep->vprocclass != PTHISPROC) + p->datap = (char *)mkaddr(q->primblock.namep); + + else if(qtag==TPRIM && q->primblock.argsp==0 && + q->primblock.namep->vdim!=NULL) + p->datap = (char *)mkscalar(q->primblock.namep); + + else if(qtag==TPRIM && q->primblock.argsp==0 && + q->primblock.namep->vdovar && + (t = (tagptr) memversion(q->primblock.namep)) ) + p->datap = (char *)fixtype(t); + else + p->datap = (char *)fixtype(q); + } + return(nargs); +} + + + +/* mkscalar -- only called by fixargs above, and by some routines in + io.c */ + +Addrp mkscalar(np) +register Namep np; +{ + register Addrp ap; + + vardcl(np); + ap = mkaddr(np); + + /* The prolog causes array arguments to point to the + * (0,...,0) element, unless subscript checking is on. + */ + if( !checksubs && np->vstg==STGARG) + { + register struct Dimblock *dp; + dp = np->vdim; + frexpr(ap->memoffset); + ap->memoffset = mkexpr(OPSTAR, + (np->vtype==TYCHAR ? + cpexpr(np->vleng) : + (tagptr)ICON(typesize[np->vtype]) ), + cpexpr(dp->baseoffset) ); + } + return(ap); +} + + + static void +adjust_arginfo(np) /* adjust arginfo to omit the length arg for the + arg that we now know to be a character-valued + function */ + register Namep np; +{ + struct Entrypoint *ep; + register chainp args; + Argtypes *at; + + for(ep = entries; ep; ep = ep->entnextp) + for(args = ep->arglist; args; args = args->nextp) + if (np == (Namep)args->datap + && (at = ep->entryname->arginfo)) + --at->nargs; + } + + + +expptr mkfunct(p0) + expptr p0; +{ + register struct Primblock *p = (struct Primblock *)p0; + struct Entrypoint *ep; + Addrp ap; + Extsym *extp; + register Namep np; + register expptr q; + expptr intrcall(); + extern chainp new_procs; + int k, nargs; + int class; + + if(p->tag != TPRIM) + return( errnode() ); + + np = p->namep; + class = np->vclass; + + + if(class == CLUNKNOWN) + { + np->vclass = class = CLPROC; + if(np->vstg == STGUNKNOWN) + { + if(np->vtype!=TYSUBR && (k = intrfunct(np->fvarname)) + && (zflag || !(*(struct Intrpacked *)&k).f4 + || dcomplex_seen)) + { + np->vstg = STGINTR; + np->vardesc.varno = k; + np->vprocclass = PINTRINSIC; + } + else + { + extp = mkext(np->fvarname, + addunder(np->cvarname)); + extp->extstg = STGEXT; + np->vstg = STGEXT; + np->vardesc.varno = extp - extsymtab; + np->vprocclass = PEXTERNAL; + } + } + else if(np->vstg==STGARG) + { + if(np->vtype == TYCHAR) { + adjust_arginfo(np); + if (np->vpassed) { + char wbuf[160], *who; + who = np->fvarname; + sprintf(wbuf, "%s%s%s\n\t%s%s%s", + "Character-valued dummy procedure ", + who, " not declared EXTERNAL.", + "Code may be wrong for previous function calls having ", + who, " as a parameter."); + warn(wbuf); + } + } + np->vprocclass = PEXTERNAL; + } + } + + if(class != CLPROC) { + if (np->vstg == STGCOMMON) + fatalstr( + "Cannot invoke common variable %.50s as a function.", + np->fvarname); + fatali("invalid class code %d for function", class); + } + +/* F77 doesn't allow subscripting of function calls */ + + if(p->fcharp || p->lcharp) + { + err("no substring of function call"); + goto error; + } + impldcl(np); + np->vimpltype = 0; /* invoking as function ==> inferred type */ + np->vcalled = 1; + nargs = fixargs( np->vprocclass!=PINTRINSIC, p->argsp); + + switch(np->vprocclass) + { + case PEXTERNAL: + if(np->vtype == TYUNKNOWN) + { + dclerr("attempt to use untyped function", np); + np->vtype = dflttype[letter(np->fvarname[0])]; + } + ap = mkaddr(np); + if (!extsymtab[np->vardesc.varno].extseen) { + new_procs = mkchain((char *)np, new_procs); + extsymtab[np->vardesc.varno].extseen = 1; + } +call: + q = mkexpr(OPCALL, (expptr)ap, (expptr)p->argsp); + q->exprblock.vtype = np->vtype; + if(np->vleng) + q->exprblock.vleng = (expptr) cpexpr(np->vleng); + break; + + case PINTRINSIC: + q = intrcall(np, p->argsp, nargs); + break; + + case PSTFUNCT: + q = stfcall(np, p->argsp); + break; + + case PTHISPROC: + warn("recursive call"); + +/* entries is the list of multiple entry points */ + + for(ep = entries ; ep ; ep = ep->entnextp) + if(ep->enamep == np) + break; + if(ep == NULL) + Fatal("mkfunct: impossible recursion"); + + ap = builtin(np->vtype, ep->entryname->cextname, -2); + /* the negative last arg prevents adding */ + /* this name to the list of used builtins */ + goto call; + + default: + fatali("mkfunct: impossible vprocclass %d", + (int) (np->vprocclass) ); + } + free( (charptr) p ); + return(q); + +error: + frexpr((expptr)p); + return( errnode() ); +} + + + +LOCAL expptr stfcall(np, actlist) +Namep np; +struct Listblock *actlist; +{ + register chainp actuals; + int nargs; + chainp oactp, formals; + int type; + expptr Ln, Lq, q, q1, rhs, ap; + Namep tnp; + register struct Rplblock *rp; + struct Rplblock *tlist; + static int inv_count; + + if (++inv_count > stfcall_MAX) + Fatal("Loop invoking recursive statement function?"); + if(actlist) + { + actuals = actlist->listp; + free( (charptr) actlist); + } + else + actuals = NULL; + oactp = actuals; + + nargs = 0; + tlist = NULL; + if( (type = np->vtype) == TYUNKNOWN) + { + dclerr("attempt to use untyped statement function", np); + type = np->vtype = dflttype[letter(np->fvarname[0])]; + } + formals = (chainp) np->varxptr.vstfdesc->datap; + rhs = (expptr) (np->varxptr.vstfdesc->nextp); + + /* copy actual arguments into temporaries */ + while(actuals!=NULL && formals!=NULL) + { + rp = ALLOC(Rplblock); + rp->rplnp = tnp = (Namep) formals->datap; + ap = fixtype((tagptr)actuals->datap); + if(tnp->vtype==ap->headblock.vtype && tnp->vtype!=TYCHAR + && (ap->tag==TCONST || ap->tag==TADDR) ) + { + +/* If actuals are constants or variable names, no temporaries are required */ + rp->rplvp = (expptr) ap; + rp->rplxp = NULL; + rp->rpltag = ap->tag; + } + else { + rp->rplvp = (expptr) mktmp(tnp->vtype, tnp->vleng); + rp -> rplxp = NULL; + putexpr ( mkexpr(OPASSIGN, cpexpr(rp->rplvp), ap)); + if((rp->rpltag = rp->rplvp->tag) == TERROR) + err("disagreement of argument types in statement function call"); + } + rp->rplnextp = tlist; + tlist = rp; + actuals = actuals->nextp; + formals = formals->nextp; + ++nargs; + } + + if(actuals!=NULL || formals!=NULL) + err("statement function definition and argument list differ"); + + /* + now push down names involved in formal argument list, then + evaluate rhs of statement function definition in this environment +*/ + + if(tlist) /* put tlist in front of the rpllist */ + { + for(rp = tlist; rp->rplnextp; rp = rp->rplnextp) + ; + rp->rplnextp = rpllist; + rpllist = tlist; + } + +/* So when the expression finally gets evaled, that evaluator must read + from the globl rpllist 14-jun-88 mwm */ + + q = (expptr) mkconv(type, fixtype(cpexpr(rhs)) ); + + /* get length right of character-valued statement functions... */ + if (type == TYCHAR + && (Ln = np->vleng) + && q->tag != TERROR + && (Lq = q->exprblock.vleng) + && (Lq->tag != TCONST + || Ln->constblock.Const.ci != Lq->constblock.Const.ci)) { + q1 = (expptr) mktmp(type, Ln); + putexpr ( mkexpr(OPASSIGN, cpexpr(q1), q)); + q = q1; + } + + /* now generate the tree ( t1=a1, (t2=a2,... , f))))) */ + while(--nargs >= 0) + { + if(rpllist->rplxp) + q = mkexpr(OPCOMMA, rpllist->rplxp, q); + rp = rpllist->rplnextp; + frexpr(rpllist->rplvp); + free((char *)rpllist); + rpllist = rp; + } + frchain( &oactp ); + --inv_count; + return(q); +} + + +static int replaced; + +/* mkplace -- Figure out the proper storage class for the input name and + return an addrp with the appropriate stuff */ + +Addrp mkplace(np) +register Namep np; +{ + register Addrp s; + register struct Rplblock *rp; + int regn; + + /* is name on the replace list? */ + + for(rp = rpllist ; rp ; rp = rp->rplnextp) + { + if(np == rp->rplnp) + { + replaced = 1; + if(rp->rpltag == TNAME) + { + np = (Namep) (rp->rplvp); + break; + } + else return( (Addrp) cpexpr(rp->rplvp) ); + } + } + + /* is variable a DO index in a register ? */ + + if(np->vdovar && ( (regn = inregister(np)) >= 0) ) + if(np->vtype == TYERROR) + return((Addrp) errnode() ); + else + { + s = ALLOC(Addrblock); + s->tag = TADDR; + s->vstg = STGREG; + s->vtype = TYIREG; + s->memno = regn; + s->memoffset = ICON(0); + s -> uname_tag = UNAM_NAME; + s -> user.name = np; + return(s); + } + + if (np->vclass == CLPROC && np->vprocclass != PTHISPROC) + errstr("external %.60s used as a variable", np->fvarname); + vardcl(np); + return(mkaddr(np)); +} + + static expptr +subskept(p,a) +struct Primblock *p; +Addrp a; +{ + expptr ep; + struct Listblock *Lb; + chainp cp; + + if (a->uname_tag != UNAM_NAME) + erri("subskept: uname_tag %d", a->uname_tag); + a->user.name->vrefused = 1; + a->user.name->visused = 1; + a->uname_tag = UNAM_REF; + Lb = (struct Listblock *)cpexpr((tagptr)p->argsp); + for(cp = Lb->listp; cp; cp = cp->nextp) + cp->datap = (char *)putx(fixtype((tagptr)cp->datap)); + if (a->vtype == TYCHAR) { + ep = p->fcharp ? mkexpr(OPMINUS, cpexpr(p->fcharp), ICON(1)) + : ICON(0); + Lb->listp = mkchain((char *)ep, Lb->listp); + } + return (expptr)Lb; + } + + static int doing_vleng; + +/* mklhs -- Compute the actual address of the given expression; account + for array subscripts, stack offset, and substring offsets. The f -> C + translator will need this only to worry about the subscript stuff */ + +expptr mklhs(p, subkeep) +register struct Primblock *p; int subkeep; +{ + expptr suboffset(); + register Addrp s; + Namep np; + + if(p->tag != TPRIM) + return( (expptr) p ); + np = p->namep; + + replaced = 0; + s = mkplace(np); + if(s->tag!=TADDR || s->vstg==STGREG) + { + free( (charptr) p ); + return( (expptr) s ); + } + s->parenused = p->parenused; + + /* compute the address modified by subscripts */ + + if (!replaced) + s->memoffset = (subkeep && np->vdim + && (np->vdim->ndim > 1 || np->vtype == TYCHAR + && (!ISCONST(np->vleng) + || np->vleng->constblock.Const.ci != 1))) + ? subskept(p,s) + : mkexpr(OPPLUS, s->memoffset, suboffset(p) ); + frexpr((expptr)p->argsp); + p->argsp = NULL; + + /* now do substring part */ + + if(p->fcharp || p->lcharp) + { + if(np->vtype != TYCHAR) + errstr("substring of noncharacter %s", np->fvarname); + else { + if(p->lcharp == NULL) + p->lcharp = (expptr) cpexpr(s->vleng); + if(p->fcharp) { + doing_vleng = 1; + s->vleng = fixtype(mkexpr(OPMINUS, + p->lcharp, + mkexpr(OPMINUS, p->fcharp, ICON(1) ))); + doing_vleng = 0; + } + else { + frexpr(s->vleng); + s->vleng = p->lcharp; + } + } + } + + s->vleng = fixtype( s->vleng ); + s->memoffset = fixtype( s->memoffset ); + free( (charptr) p ); + return( (expptr) s ); +} + + + + + +/* deregister -- remove a register allocation from the list; assumes that + names are deregistered in stack order (LIFO order - Last In First Out) */ + +deregister(np) +Namep np; +{ + if(nregvar>0 && regnamep[nregvar-1]==np) + { + --nregvar; + } +} + + + + +/* memversion -- moves a DO index REGISTER into a memory location; other + objects are passed through untouched */ + +Addrp memversion(np) +register Namep np; +{ + register Addrp s; + + if(np->vdovar==NO || (inregister(np)<0) ) + return(NULL); + np->vdovar = NO; + s = mkplace(np); + np->vdovar = YES; + return(s); +} + + + +/* inregister -- looks for the input name in the global list regnamep */ + +inregister(np) +register Namep np; +{ + register int i; + + for(i = 0 ; i < nregvar ; ++i) + if(regnamep[i] == np) + return( regnum[i] ); + return(-1); +} + + + +/* suboffset -- Compute the offset from the start of the array, given the + subscripts as arguments */ + +expptr suboffset(p) +register struct Primblock *p; +{ + int n; + expptr si, size; + chainp cp; + expptr e, e1, offp, prod; + expptr subcheck(); + struct Dimblock *dimp; + expptr sub[MAXDIM+1]; + register Namep np; + + np = p->namep; + offp = ICON(0); + n = 0; + if(p->argsp) + for(cp = p->argsp->listp ; cp ; cp = cp->nextp) + { + si = fixtype(cpexpr((tagptr)cp->datap)); + if (!ISINT(si->headblock.vtype)) { + NOEXT("non-integer subscript"); + si = mkconv(TYLONG, si); + } + sub[n++] = si; + if(n > maxdim) + { + erri("more than %d subscripts", maxdim); + break; + } + } + + dimp = np->vdim; + if(n>0 && dimp==NULL) + errstr("subscripts on scalar variable %.68s", np->fvarname); + else if(dimp && dimp->ndim!=n) + errstr("wrong number of subscripts on %.68s", np->fvarname); + else if(n > 0) + { + prod = sub[--n]; + while( --n >= 0) + prod = mkexpr(OPPLUS, sub[n], + mkexpr(OPSTAR, prod, cpexpr(dimp->dims[n].dimsize)) ); + if(checksubs || np->vstg!=STGARG) + prod = mkexpr(OPMINUS, prod, cpexpr(dimp->baseoffset)); + +/* Add in the run-time bounds check */ + + if(checksubs) + prod = subcheck(np, prod); + size = np->vtype == TYCHAR ? + (expptr) cpexpr(np->vleng) : ICON(typesize[np->vtype]); + prod = mkexpr(OPSTAR, prod, size); + offp = mkexpr(OPPLUS, offp, prod); + } + +/* Check for substring indicator */ + + if(p->fcharp && np->vtype==TYCHAR) { + e = p->fcharp; + e1 = mkexpr(OPMINUS, cpexpr(e), ICON(1)); + if (!ISCONST(e) && (e->tag != TPRIM || e->primblock.argsp)) { + e = (expptr)mktmp(TYLONG, ENULL); + putout(putassign(cpexpr(e), e1)); + p->fcharp = mkexpr(OPPLUS, cpexpr(e), ICON(1)); + e1 = e; + } + offp = mkexpr(OPPLUS, offp, e1); + } + return(offp); +} + + + + +expptr subcheck(np, p) +Namep np; +register expptr p; +{ + struct Dimblock *dimp; + expptr t, checkvar, checkcond, badcall; + + dimp = np->vdim; + if(dimp->nelt == NULL) + return(p); /* don't check arrays with * bounds */ + np->vlastdim = 0; + if( ISICON(p) ) + { + +/* check for negative (constant) offset */ + + if(p->constblock.Const.ci < 0) + goto badsub; + if( ISICON(dimp->nelt) ) + +/* see if constant offset exceeds the array declaration */ + + if(p->constblock.Const.ci < dimp->nelt->constblock.Const.ci) + return(p); + else + goto badsub; + } + +/* We know that the subscript offset p or dimp -> nelt is not a constant. + Now find a register to use for run-time bounds checking */ + + if(p->tag==TADDR && p->addrblock.vstg==STGREG) + { + checkvar = (expptr) cpexpr(p); + t = p; + } + else { + checkvar = (expptr) mktmp(p->headblock.vtype, ENULL); + t = mkexpr(OPASSIGN, cpexpr(checkvar), p); + } + checkcond = mkexpr(OPLT, t, cpexpr(dimp->nelt) ); + if( ! ISICON(p) ) + checkcond = mkexpr(OPAND, checkcond, + mkexpr(OPLE, ICON(0), cpexpr(checkvar)) ); + +/* Construct the actual test */ + + badcall = call4(p->headblock.vtype, "s_rnge", + mkstrcon(strlen(np->fvarname), np->fvarname), + mkconv(TYLONG, cpexpr(checkvar)), + mkstrcon(strlen(procname), procname), + ICON(lineno) ); + badcall->exprblock.opcode = OPCCALL; + p = mkexpr(OPQUEST, checkcond, + mkexpr(OPCOLON, checkvar, badcall)); + + return(p); + +badsub: + frexpr(p); + errstr("subscript on variable %s out of range", np->fvarname); + return ( ICON(0) ); +} + + + + +Addrp mkaddr(p) +register Namep p; +{ + Extsym *extp; + register Addrp t; + Addrp intraddr(); + int k; + + switch( p->vstg) + { + case STGAUTO: + if(p->vclass == CLPROC && p->vprocclass == PTHISPROC) + return (Addrp) cpexpr((expptr)xretslot[p->vtype]); + goto other; + + case STGUNKNOWN: + if(p->vclass != CLPROC) + break; /* Error */ + extp = mkext(p->fvarname, addunder(p->cvarname)); + extp->extstg = STGEXT; + p->vstg = STGEXT; + p->vardesc.varno = extp - extsymtab; + p->vprocclass = PEXTERNAL; + if ((extp->exproto || infertypes) + && (p->vtype == TYUNKNOWN || p->vimpltype) + && (k = extp->extype)) + inferdcl(p, k); + + + case STGCOMMON: + case STGEXT: + case STGBSS: + case STGINIT: + case STGEQUIV: + case STGARG: + case STGLENG: + other: + t = ALLOC(Addrblock); + t->tag = TADDR; + + t->vclass = p->vclass; + t->vtype = p->vtype; + t->vstg = p->vstg; + t->memno = p->vardesc.varno; + t->memoffset = ICON(p->voffset); + if (p->vdim) + t->isarray = 1; + if(p->vleng) + { + t->vleng = (expptr) cpexpr(p->vleng); + if( ISICON(t->vleng) ) + t->varleng = t->vleng->constblock.Const.ci; + } + +/* Keep the original name around for the C code generation */ + + t -> uname_tag = UNAM_NAME; + t -> user.name = p; + return(t); + + case STGINTR: + + return ( intraddr (p)); + } + badstg("mkaddr", p->vstg); + /* NOT REACHED */ return 0; +} + + + + +/* mkarg -- create storage for a new parameter. This is called when a + function returns a string (for the return value, which is the first + parameter), or when a variable-length string is passed to a function. */ + +Addrp mkarg(type, argno) +int type, argno; +{ + register Addrp p; + + p = ALLOC(Addrblock); + p->tag = TADDR; + p->vtype = type; + p->vclass = CLVAR; + +/* TYLENG is the type of the field holding the length of a character string */ + + p->vstg = (type==TYLENG ? STGLENG : STGARG); + p->memno = argno; + return(p); +} + + + + +/* mkprim -- Create a PRIM (primary/primitive) block consisting of a + Nameblock (or Paramblock), arguments (actual params or array + subscripts) and substring bounds. Requires that v have lots of + extra (uninitialized) storage, since it could be a paramblock or + nameblock */ + +expptr mkprim(v0, args, substr) + Namep v0; + struct Listblock *args; + chainp substr; +{ + typedef union { + struct Paramblock paramblock; + struct Nameblock nameblock; + struct Headblock headblock; + } *Primu; + register Primu v = (Primu)v0; + register struct Primblock *p; + + if(v->headblock.vclass == CLPARAM) + { + +/* v is to be a Paramblock */ + + if(args || substr) + { + errstr("no qualifiers on parameter name %s", + v->paramblock.fvarname); + frexpr((expptr)args); + if(substr) + { + frexpr((tagptr)substr->datap); + frexpr((tagptr)substr->nextp->datap); + frchain(&substr); + } + frexpr((expptr)v); + return( errnode() ); + } + return( (expptr) cpexpr(v->paramblock.paramval) ); + } + + p = ALLOC(Primblock); + p->tag = TPRIM; + p->vtype = v->nameblock.vtype; + +/* v is to be a Nameblock */ + + p->namep = (Namep) v; + p->argsp = args; + if(substr) + { + p->fcharp = (expptr) substr->datap; + p->lcharp = (expptr) substr->nextp->datap; + frchain(&substr); + } + return( (expptr) p); +} + + + +/* vardcl -- attempt to fill out the Name template for variable v. + This function is called on identifiers known to be variables or + recursive references to the same function */ + +vardcl(v) +register Namep v; +{ + struct Dimblock *t; + expptr neltp; + extern int doing_stmtfcn; + + if(v->vclass == CLUNKNOWN) { + v->vclass = CLVAR; + if (v->vinftype) { + v->vtype = TYUNKNOWN; + if (v->vdcldone) { + v->vdcldone = 0; + impldcl(v); + } + } + } + if(v->vdcldone) + return; + if(v->vclass == CLNAMELIST) + return; + + if(v->vtype == TYUNKNOWN) + impldcl(v); + else if(v->vclass!=CLVAR && v->vprocclass!=PTHISPROC) + { + dclerr("used as variable", v); + return; + } + if(v->vstg==STGUNKNOWN) { + if (doing_stmtfcn) { + /* neither declare this variable if its only use */ + /* is in defining a stmt function, nor complain */ + /* that it is never used */ + v->vimpldovar = 1; + return; + } + v->vstg = implstg[ letter(v->fvarname[0]) ]; + v->vimplstg = 1; + } + +/* Compute the actual storage location, i.e. offsets from base addresses, + possibly the stack pointer */ + + switch(v->vstg) + { + case STGBSS: + v->vardesc.varno = ++lastvarno; + break; + case STGAUTO: + if(v->vclass==CLPROC && v->vprocclass==PTHISPROC) + break; + if(t = v->vdim) + if( (neltp = t->nelt) && ISCONST(neltp) ) ; + else + dclerr("adjustable automatic array", v); + break; + + default: + break; + } + v->vdcldone = YES; +} + + + +/* Set the implicit type declaration of parameter p based on its first + letter */ + +impldcl(p) +register Namep p; +{ + register int k; + int type; + ftnint leng; + + if(p->vdcldone || (p->vclass==CLPROC && p->vprocclass==PINTRINSIC) ) + return; + if(p->vtype == TYUNKNOWN) + { + k = letter(p->fvarname[0]); + type = impltype[ k ]; + leng = implleng[ k ]; + if(type == TYUNKNOWN) + { + if(p->vclass == CLPROC) + return; + dclerr("attempt to use undefined variable", p); + type = dflttype[k]; + leng = 0; + } + settype(p, type, leng); + p->vimpltype = 1; + } +} + + void +inferdcl(np,type) + Namep np; + int type; +{ + int k = impltype[letter(np->fvarname[0])]; + if (k != type) { + np->vinftype = 1; + np->vtype = type; + frexpr(np->vleng); + np->vleng = 0; + } + np->vimpltype = 0; + np->vinfproc = 1; + } + + +#define ICONEQ(z, c) (ISICON(z) && z->constblock.Const.ci==c) +#define COMMUTE { e = lp; lp = rp; rp = e; } + + + +/* mkexpr -- Make expression, and simplify constant subcomponents (tree + order is not preserved). Assumes that lp is nonempty, and uses + fold() to simplify adjacent constants */ + +expptr mkexpr(opcode, lp, rp) +int opcode; +register expptr lp, rp; +{ + register expptr e, e1; + int etype; + int ltype, rtype; + int ltag, rtag; + long L; + + ltype = lp->headblock.vtype; + ltag = lp->tag; + if(rp && opcode!=OPCALL && opcode!=OPCCALL) + { + rtype = rp->headblock.vtype; + rtag = rp->tag; + } + else rtype = 0; + + etype = cktype(opcode, ltype, rtype); + if(etype == TYERROR) + goto error; + + switch(opcode) + { + /* check for multiplication by 0 and 1 and addition to 0 */ + + case OPSTAR: + if( ISCONST(lp) ) + COMMUTE + + if( ISICON(rp) ) + { + if(rp->constblock.Const.ci == 0) + goto retright; + goto mulop; + } + break; + + case OPSLASH: + case OPMOD: + if( ICONEQ(rp, 0) ) + { + err("attempted division by zero"); + rp = ICON(1); + break; + } + if(opcode == OPMOD) + break; + +/* Handle multiplying or dividing by 1, -1 */ + +mulop: + if( ISICON(rp) ) + { + if(rp->constblock.Const.ci == 1) + goto retleft; + + if(rp->constblock.Const.ci == -1) + { + frexpr(rp); + return( mkexpr(OPNEG, lp, ENULL) ); + } + } + +/* Group all constants together. In particular, + + (x * CONST1) * CONST2 ==> x * (CONST1 * CONST2) + (x * CONST1) / CONST2 ==> x * (CONST1 / CONST2) +*/ + + if (lp->tag != TEXPR || !lp->exprblock.rightp + || !ISICON(lp->exprblock.rightp)) + break; + + if (lp->exprblock.opcode == OPLSHIFT) { + L = 1 << lp->exprblock.rightp->constblock.Const.ci; + if (opcode == OPSTAR || ISICON(rp) && + !(L % rp->constblock.Const.ci)) { + lp->exprblock.opcode = OPSTAR; + lp->exprblock.rightp->constblock.Const.ci = L; + } + } + + if (lp->exprblock.opcode == OPSTAR) { + if(opcode == OPSTAR) + e = mkexpr(OPSTAR, lp->exprblock.rightp, rp); + else if(ISICON(rp) && + (lp->exprblock.rightp->constblock.Const.ci % + rp->constblock.Const.ci) == 0) + e = mkexpr(OPSLASH, lp->exprblock.rightp, rp); + else break; + + e1 = lp->exprblock.leftp; + free( (charptr) lp ); + return( mkexpr(OPSTAR, e1, e) ); + } + break; + + + case OPPLUS: + if( ISCONST(lp) ) + COMMUTE + goto addop; + + case OPMINUS: + if( ICONEQ(lp, 0) ) + { + frexpr(lp); + return( mkexpr(OPNEG, rp, ENULL) ); + } + + if( ISCONST(rp) && is_negatable((Constp)rp)) + { + opcode = OPPLUS; + consnegop((Constp)rp); + } + +/* Group constants in an addition expression (also subtraction, since the + subtracted value was negated above). In particular, + + (x + CONST1) + CONST2 ==> x + (CONST1 + CONST2) +*/ + +addop: + if( ISICON(rp) ) + { + if(rp->constblock.Const.ci == 0) + goto retleft; + if( ISPLUSOP(lp) && ISICON(lp->exprblock.rightp) ) + { + e = mkexpr(OPPLUS, lp->exprblock.rightp, rp); + e1 = lp->exprblock.leftp; + free( (charptr) lp ); + return( mkexpr(OPPLUS, e1, e) ); + } + } + if (opcode == OPMINUS && (ISINT(etype) || doing_vleng)) { + /* check for (i [+const]) - (i [+const]) */ + if (lp->tag == TPRIM) + e = lp; + else if (lp->tag == TEXPR && lp->exprblock.opcode == OPPLUS + && lp->exprblock.rightp->tag == TCONST) { + e = lp->exprblock.leftp; + if (e->tag != TPRIM) + break; + } + else + break; + if (e->primblock.argsp) + break; + if (rp->tag == TPRIM) + e1 = rp; + else if (rp->tag == TEXPR && rp->exprblock.opcode == OPPLUS + && rp->exprblock.rightp->tag == TCONST) { + e1 = rp->exprblock.leftp; + if (e1->tag != TPRIM) + break; + } + else + break; + if (e->primblock.namep != e1->primblock.namep + || e1->primblock.argsp) + break; + L = e == lp ? 0 : lp->exprblock.rightp->constblock.Const.ci; + if (e1 != rp) + L -= rp->exprblock.rightp->constblock.Const.ci; + frexpr(lp); + frexpr(rp); + return ICON(L); + } + + break; + + + case OPPOWER: + break; + +/* Eliminate outermost double negations */ + + case OPNEG: + case OPNEG1: + if(ltag==TEXPR && lp->exprblock.opcode==OPNEG) + { + e = lp->exprblock.leftp; + free( (charptr) lp ); + return(e); + } + break; + +/* Eliminate outermost double NOTs */ + + case OPNOT: + if(ltag==TEXPR && lp->exprblock.opcode==OPNOT) + { + e = lp->exprblock.leftp; + free( (charptr) lp ); + return(e); + } + break; + + case OPCALL: + case OPCCALL: + etype = ltype; + if(rp!=NULL && rp->listblock.listp==NULL) + { + free( (charptr) rp ); + rp = NULL; + } + break; + + case OPAND: + case OPOR: + if( ISCONST(lp) ) + COMMUTE + + if( ISCONST(rp) ) + { + if(rp->constblock.Const.ci == 0) + if(opcode == OPOR) + goto retleft; + else + goto retright; + else if(opcode == OPOR) + goto retright; + else + goto retleft; + } + case OPEQV: + case OPNEQV: + + case OPBITAND: + case OPBITOR: + case OPBITXOR: + case OPBITNOT: + case OPLSHIFT: + case OPRSHIFT: + + case OPLT: + case OPGT: + case OPLE: + case OPGE: + case OPEQ: + case OPNE: + + case OPCONCAT: + break; + case OPMIN: + case OPMAX: + case OPMIN2: + case OPMAX2: + case OPDMIN: + case OPDMAX: + + case OPASSIGN: + case OPASSIGNI: + case OPPLUSEQ: + case OPSTAREQ: + case OPMINUSEQ: + case OPSLASHEQ: + case OPMODEQ: + case OPLSHIFTEQ: + case OPRSHIFTEQ: + case OPBITANDEQ: + case OPBITXOREQ: + case OPBITOREQ: + + case OPCONV: + case OPADDR: + case OPWHATSIN: + + case OPCOMMA: + case OPCOMMA_ARG: + case OPQUEST: + case OPCOLON: + case OPDOT: + case OPARROW: + case OPIDENTITY: + case OPCHARCAST: + case OPABS: + case OPDABS: + break; + + default: + badop("mkexpr", opcode); + } + + e = (expptr) ALLOC(Exprblock); + e->exprblock.tag = TEXPR; + e->exprblock.opcode = opcode; + e->exprblock.vtype = etype; + e->exprblock.leftp = lp; + e->exprblock.rightp = rp; + if(ltag==TCONST && (rp==0 || rtag==TCONST) ) + e = fold(e); + return(e); + +retleft: + frexpr(rp); + if (lp->tag == TPRIM) + lp->primblock.parenused = 1; + return(lp); + +retright: + frexpr(lp); + if (rp->tag == TPRIM) + rp->primblock.parenused = 1; + return(rp); + +error: + frexpr(lp); + if(rp && opcode!=OPCALL && opcode!=OPCCALL) + frexpr(rp); + return( errnode() ); +} + +#define ERR(s) { errs = s; goto error; } + +/* cktype -- Check and return the type of the expression */ + +cktype(op, lt, rt) +register int op, lt, rt; +{ + char *errs; + + if(lt==TYERROR || rt==TYERROR) + goto error1; + + if(lt==TYUNKNOWN) + return(TYUNKNOWN); + if(rt==TYUNKNOWN) + +/* If not unary operation, return UNKNOWN */ + + if(!is_unary_op (op) && op != OPCALL && op != OPCCALL) + return(TYUNKNOWN); + + switch(op) + { + case OPPLUS: + case OPMINUS: + case OPSTAR: + case OPSLASH: + case OPPOWER: + case OPMOD: + if( ISNUMERIC(lt) && ISNUMERIC(rt) ) + return( maxtype(lt, rt) ); + ERR("nonarithmetic operand of arithmetic operator") + + case OPNEG: + case OPNEG1: + if( ISNUMERIC(lt) ) + return(lt); + ERR("nonarithmetic operand of negation") + + case OPNOT: + if(ISLOGICAL(lt)) + return(lt); + ERR("NOT of nonlogical") + + case OPAND: + case OPOR: + case OPEQV: + case OPNEQV: + if(ISLOGICAL(lt) && ISLOGICAL(rt)) + return( maxtype(lt, rt) ); + ERR("nonlogical operand of logical operator") + + case OPLT: + case OPGT: + case OPLE: + case OPGE: + case OPEQ: + case OPNE: + if(lt==TYCHAR || rt==TYCHAR || ISLOGICAL(lt) || ISLOGICAL(rt)) + { + if(lt != rt){ + if (htype + && (lt == TYCHAR && ISNUMERIC(rt) + || rt == TYCHAR && ISNUMERIC(lt))) + return TYLOGICAL; + ERR("illegal comparison") + } + } + + else if( ISCOMPLEX(lt) || ISCOMPLEX(rt) ) + { + if(op!=OPEQ && op!=OPNE) + ERR("order comparison of complex data") + } + + else if( ! ISNUMERIC(lt) || ! ISNUMERIC(rt) ) + ERR("comparison of nonarithmetic data") + return(TYLOGICAL); + + case OPCONCAT: + if(lt==TYCHAR && rt==TYCHAR) + return(TYCHAR); + ERR("concatenation of nonchar data") + + case OPCALL: + case OPCCALL: + case OPIDENTITY: + return(lt); + + case OPADDR: + case OPCHARCAST: + return(TYADDR); + + case OPCONV: + if(rt == 0) + return(0); + if(lt==TYCHAR && ISINT(rt) ) + return(TYCHAR); + if (ISLOGICAL(lt) && ISLOGICAL(rt)) + return lt; + case OPASSIGN: + case OPASSIGNI: + case OPMINUSEQ: + case OPPLUSEQ: + case OPSTAREQ: + case OPSLASHEQ: + case OPMODEQ: + case OPLSHIFTEQ: + case OPRSHIFTEQ: + case OPBITANDEQ: + case OPBITXOREQ: + case OPBITOREQ: + if( ISINT(lt) && rt==TYCHAR) + return(lt); + if (ISLOGICAL(lt) && ISLOGICAL(rt) && op == OPASSIGN) + return lt; + if(lt==TYCHAR || rt==TYCHAR || ISLOGICAL(lt) || ISLOGICAL(rt)) + if((op!=OPASSIGN && op != OPPLUSEQ && op != OPMINUSEQ) + || (lt!=rt)) + { + ERR("impossible conversion") + } + return(lt); + + case OPMIN: + case OPMAX: + case OPDMIN: + case OPDMAX: + case OPMIN2: + case OPMAX2: + case OPBITOR: + case OPBITAND: + case OPBITXOR: + case OPBITNOT: + case OPLSHIFT: + case OPRSHIFT: + case OPWHATSIN: + case OPABS: + case OPDABS: + return(lt); + + case OPCOMMA: + case OPCOMMA_ARG: + case OPQUEST: + case OPCOLON: /* Only checks the rightmost type because + of C language definition (rightmost + comma-expr is the value of the expr) */ + return(rt); + + case OPDOT: + case OPARROW: + return (lt); + break; + default: + badop("cktype", op); + } +error: + err(errs); +error1: + return(TYERROR); +} + +/* fold -- simplifies constant expressions; it assumes that e -> leftp and + e -> rightp are TCONST or NULL */ + + LOCAL expptr +fold(e) + register expptr e; +{ + Constp p; + register expptr lp, rp; + int etype, mtype, ltype, rtype, opcode; + int i, bl, ll, lr; + char *q, *s; + struct Constblock lcon, rcon; + long L; + double d; + + opcode = e->exprblock.opcode; + etype = e->exprblock.vtype; + + lp = e->exprblock.leftp; + ltype = lp->headblock.vtype; + rp = e->exprblock.rightp; + + if(rp == 0) + switch(opcode) + { + case OPNOT: + lp->constblock.Const.ci = ! lp->constblock.Const.ci; + retlp: + e->exprblock.leftp = 0; + frexpr(e); + return(lp); + + case OPBITNOT: + lp->constblock.Const.ci = ~ lp->constblock.Const.ci; + goto retlp; + + case OPNEG: + case OPNEG1: + consnegop((Constp)lp); + goto retlp; + + case OPCONV: + case OPADDR: + return(e); + + case OPABS: + case OPDABS: + switch(ltype) { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + if ((L = lp->constblock.Const.ci) < 0) + lp->constblock.Const.ci = -L; + goto retlp; + case TYREAL: + case TYDREAL: + if (lp->constblock.vstg) { + s = lp->constblock.Const.cds[0]; + if (*s == '-') + lp->constblock.Const.cds[0] = s + 1; + goto retlp; + } + if ((d = lp->constblock.Const.cd[0]) < 0.) + lp->constblock.Const.cd[0] = -d; + case TYCOMPLEX: + case TYDCOMPLEX: + return e; /* lazy way out */ + } + default: + badop("fold", opcode); + } + + rtype = rp->headblock.vtype; + + p = ALLOC(Constblock); + p->tag = TCONST; + p->vtype = etype; + p->vleng = e->exprblock.vleng; + + switch(opcode) + { + case OPCOMMA: + case OPCOMMA_ARG: + case OPQUEST: + case OPCOLON: + return(e); + + case OPAND: + p->Const.ci = lp->constblock.Const.ci && + rp->constblock.Const.ci; + break; + + case OPOR: + p->Const.ci = lp->constblock.Const.ci || + rp->constblock.Const.ci; + break; + + case OPEQV: + p->Const.ci = lp->constblock.Const.ci == + rp->constblock.Const.ci; + break; + + case OPNEQV: + p->Const.ci = lp->constblock.Const.ci != + rp->constblock.Const.ci; + break; + + case OPBITAND: + p->Const.ci = lp->constblock.Const.ci & + rp->constblock.Const.ci; + break; + + case OPBITOR: + p->Const.ci = lp->constblock.Const.ci | + rp->constblock.Const.ci; + break; + + case OPBITXOR: + p->Const.ci = lp->constblock.Const.ci ^ + rp->constblock.Const.ci; + break; + + case OPLSHIFT: + p->Const.ci = lp->constblock.Const.ci << + rp->constblock.Const.ci; + break; + + case OPRSHIFT: + p->Const.ci = lp->constblock.Const.ci >> + rp->constblock.Const.ci; + break; + + case OPCONCAT: + ll = lp->constblock.vleng->constblock.Const.ci; + lr = rp->constblock.vleng->constblock.Const.ci; + bl = lp->constblock.Const.ccp1.blanks; + p->Const.ccp = q = (char *) ckalloc(ll+lr+bl); + p->Const.ccp1.blanks = rp->constblock.Const.ccp1.blanks; + p->vleng = ICON(ll+lr+bl); + s = lp->constblock.Const.ccp; + for(i = 0 ; i < ll ; ++i) + *q++ = *s++; + for(i = 0 ; i < bl ; i++) + *q++ = ' '; + s = rp->constblock.Const.ccp; + for(i = 0; i < lr; ++i) + *q++ = *s++; + break; + + + case OPPOWER: + if( ! ISINT(rtype) ) + return(e); + conspower(p, (Constp)lp, rp->constblock.Const.ci); + break; + + + default: + if(ltype == TYCHAR) + { + lcon.Const.ci = cmpstr(lp->constblock.Const.ccp, + rp->constblock.Const.ccp, + lp->constblock.vleng->constblock.Const.ci, + rp->constblock.vleng->constblock.Const.ci); + rcon.Const.ci = 0; + mtype = tyint; + } + else { + mtype = maxtype(ltype, rtype); + consconv(mtype, &lcon, &lp->constblock); + consconv(mtype, &rcon, &rp->constblock); + } + consbinop(opcode, mtype, p, &lcon, &rcon); + break; + } + + frexpr(e); + return( (expptr) p ); +} + + + +/* assign constant l = r , doing coercion */ + +consconv(lt, lc, rc) + int lt; + register Constp lc, rc; +{ + int rt = rc->vtype; + register union Constant *lv = &lc->Const, *rv = &rc->Const; + + lc->vtype = lt; + if (ONEOF(lt, MSKREAL|MSKCOMPLEX) && ONEOF(rt, MSKREAL|MSKCOMPLEX)) { + memcpy((char *)lv, (char *)rv, sizeof(union Constant)); + lc->vstg = rc->vstg; + if (ISCOMPLEX(lt) && ISREAL(rt)) { + if (rc->vstg) + lv->cds[1] = cds("0",CNULL); + else + lv->cd[1] = 0.; + } + return; + } + lc->vstg = 0; + + switch(lt) + { + +/* Casting to character means just copying the first sizeof (character) + bytes into a new 1 character string. This is weird. */ + + case TYCHAR: + *(lv->ccp = (char *) ckalloc(1)) = rv->ci; + lv->ccp1.blanks = 0; + break; + + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + if(rt == TYCHAR) + lv->ci = rv->ccp[0]; + else if( ISINT(rt) ) + lv->ci = rv->ci; + else lv->ci = rc->vstg ? atof(rv->cds[0]) : rv->cd[0]; + + break; + + case TYCOMPLEX: + case TYDCOMPLEX: + lv->cd[1] = 0.; + lv->cd[0] = rv->ci; + break; + + case TYREAL: + case TYDREAL: + lv->cd[0] = rv->ci; + break; + + case TYLOGICAL: + case TYLOGICAL1: + case TYLOGICAL2: + lv->ci = rv->ci; + break; + } +} + + + +/* Negate constant value -- changes the input node's value */ + +consnegop(p) +register Constp p; +{ + register char *s; + + if (p->vstg) { + if (ISCOMPLEX(p->vtype)) { + s = p->Const.cds[1]; + p->Const.cds[1] = *s == '-' ? s+1 + : *s == '0' ? s : s-1; + } + s = p->Const.cds[0]; + p->Const.cds[0] = *s == '-' ? s+1 + : *s == '0' ? s : s-1; + return; + } + switch(p->vtype) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + p->Const.ci = - p->Const.ci; + break; + + case TYCOMPLEX: + case TYDCOMPLEX: + p->Const.cd[1] = - p->Const.cd[1]; + /* fall through and do the real parts */ + case TYREAL: + case TYDREAL: + p->Const.cd[0] = - p->Const.cd[0]; + break; + default: + badtype("consnegop", p->vtype); + } +} + + + +/* conspower -- Expand out an exponentiation */ + + LOCAL void +conspower(p, ap, n) + Constp p, ap; + ftnint n; +{ + register union Constant *powp = &p->Const; + register int type; + struct Constblock x, x0; + + if (n == 1) { + memcpy((char *)powp, (char *)&ap->Const, sizeof(ap->Const)); + return; + } + + switch(type = ap->vtype) /* pow = 1 */ + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + powp->ci = 1; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + powp->cd[1] = 0; + case TYREAL: + case TYDREAL: + powp->cd[0] = 1; + break; + default: + badtype("conspower", type); + } + + if(n == 0) + return; + switch(type) /* x0 = ap */ + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + x0.Const.ci = ap->Const.ci; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + x0.Const.cd[1] = + ap->vstg ? atof(ap->Const.cds[1]) : ap->Const.cd[1]; + case TYREAL: + case TYDREAL: + x0.Const.cd[0] = + ap->vstg ? atof(ap->Const.cds[0]) : ap->Const.cd[0]; + break; + } + x0.vtype = type; + x0.vstg = 0; + if(n < 0) + { + if( ISINT(type) ) + { + err("integer ** negative number"); + return; + } + else if (!x0.Const.cd[0] + && (!ISCOMPLEX(type) || !x0.Const.cd[1])) { + err("0.0 ** negative number"); + return; + } + n = -n; + consbinop(OPSLASH, type, &x, p, &x0); + } + else + consbinop(OPSTAR, type, &x, p, &x0); + + for( ; ; ) + { + if(n & 01) + consbinop(OPSTAR, type, p, p, &x); + if(n >>= 1) + consbinop(OPSTAR, type, &x, &x, &x); + else + break; + } +} + + + +/* do constant operation cp = a op b -- assumes that ap and bp have data + matching the input type */ + + LOCAL void +zerodiv() +{ Fatal("division by zero during constant evaluation; cannot recover"); } + + LOCAL void +consbinop(opcode, type, cpp, app, bpp) + int opcode, type; + Constp cpp, app, bpp; +{ + register union Constant *ap = &app->Const, + *bp = &bpp->Const, + *cp = &cpp->Const; + int k; + double ad[2], bd[2], temp; + + cpp->vstg = 0; + + if (ONEOF(type, MSKREAL|MSKCOMPLEX)) { + ad[0] = app->vstg ? atof(ap->cds[0]) : ap->cd[0]; + bd[0] = bpp->vstg ? atof(bp->cds[0]) : bp->cd[0]; + if (ISCOMPLEX(type)) { + ad[1] = app->vstg ? atof(ap->cds[1]) : ap->cd[1]; + bd[1] = bpp->vstg ? atof(bp->cds[1]) : bp->cd[1]; + } + } + switch(opcode) + { + case OPPLUS: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp->ci = ap->ci + bp->ci; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + cp->cd[1] = ad[1] + bd[1]; + case TYREAL: + case TYDREAL: + cp->cd[0] = ad[0] + bd[0]; + break; + } + break; + + case OPMINUS: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp->ci = ap->ci - bp->ci; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + cp->cd[1] = ad[1] - bd[1]; + case TYREAL: + case TYDREAL: + cp->cd[0] = ad[0] - bd[0]; + break; + } + break; + + case OPSTAR: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp->ci = ap->ci * bp->ci; + break; + case TYREAL: + case TYDREAL: + cp->cd[0] = ad[0] * bd[0]; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + temp = ad[0] * bd[0] - ad[1] * bd[1] ; + cp->cd[1] = ad[0] * bd[1] + ad[1] * bd[0] ; + cp->cd[0] = temp; + break; + } + break; + case OPSLASH: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + if (!bp->ci) + zerodiv(); + cp->ci = ap->ci / bp->ci; + break; + case TYREAL: + case TYDREAL: + if (!bd[0]) + zerodiv(); + cp->cd[0] = ad[0] / bd[0]; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + if (!bd[0] && !bd[1]) + zerodiv(); + zdiv((dcomplex*)cp, (dcomplex*)ad, (dcomplex*)bd); + break; + } + break; + + case OPMOD: + if( ISINT(type) ) + { + cp->ci = ap->ci % bp->ci; + break; + } + else + Fatal("inline mod of noninteger"); + + case OPMIN2: + case OPDMIN: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp->ci = ap->ci <= bp->ci ? ap->ci : bp->ci; + break; + case TYREAL: + case TYDREAL: + cp->cd[0] = ad[0] <= bd[0] ? ad[0] : bd[0]; + break; + default: + Fatal("inline min of exected type"); + } + break; + + case OPMAX2: + case OPDMAX: + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp->ci = ap->ci >= bp->ci ? ap->ci : bp->ci; + break; + case TYREAL: + case TYDREAL: + cp->cd[0] = ad[0] >= bd[0] ? ad[0] : bd[0]; + break; + default: + Fatal("inline max of exected type"); + } + break; + + default: /* relational ops */ + switch(type) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + if(ap->ci < bp->ci) + k = -1; + else if(ap->ci == bp->ci) + k = 0; + else k = 1; + break; + case TYREAL: + case TYDREAL: + if(ad[0] < bd[0]) + k = -1; + else if(ad[0] == bd[0]) + k = 0; + else k = 1; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + if(ad[0] == bd[0] && + ad[1] == bd[1] ) + k = 0; + else k = 1; + break; + } + + switch(opcode) + { + case OPEQ: + cp->ci = (k == 0); + break; + case OPNE: + cp->ci = (k != 0); + break; + case OPGT: + cp->ci = (k == 1); + break; + case OPLT: + cp->ci = (k == -1); + break; + case OPGE: + cp->ci = (k >= 0); + break; + case OPLE: + cp->ci = (k <= 0); + break; + } + break; + } +} + + + +/* conssgn - returns the sign of a Fortran constant */ + +conssgn(p) +register expptr p; +{ + register char *s; + + if( ! ISCONST(p) ) + Fatal( "sgn(nonconstant)" ); + + switch(p->headblock.vtype) + { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + if(p->constblock.Const.ci > 0) return(1); + if(p->constblock.Const.ci < 0) return(-1); + return(0); + + case TYREAL: + case TYDREAL: + if (p->constblock.vstg) { + s = p->constblock.Const.cds[0]; + if (*s == '-') + return -1; + if (*s == '0') + return 0; + return 1; + } + if(p->constblock.Const.cd[0] > 0) return(1); + if(p->constblock.Const.cd[0] < 0) return(-1); + return(0); + + +/* The sign of a complex number is 0 iff the number is 0 + 0i, else it's 1 */ + + case TYCOMPLEX: + case TYDCOMPLEX: + if (p->constblock.vstg) + return *p->constblock.Const.cds[0] != '0' + && *p->constblock.Const.cds[1] != '0'; + return(p->constblock.Const.cd[0]!=0 || p->constblock.Const.cd[1]!=0); + + default: + badtype( "conssgn", p->constblock.vtype); + } + /* NOT REACHED */ return 0; +} + +char *powint[ ] = { + "pow_ii", +#ifdef TYQUAD + "pow_qi", +#endif + "pow_ri", "pow_di", "pow_ci", "pow_zi" }; + +LOCAL expptr mkpower(p) +register expptr p; +{ + register expptr q, lp, rp; + int ltype, rtype, mtype, tyi; + + lp = p->exprblock.leftp; + rp = p->exprblock.rightp; + ltype = lp->headblock.vtype; + rtype = rp->headblock.vtype; + + if (lp->tag == TADDR) + lp->addrblock.parenused = 0; + + if (rp->tag == TADDR) + rp->addrblock.parenused = 0; + + if(ISICON(rp)) + { + if(rp->constblock.Const.ci == 0) + { + frexpr(p); + if( ISINT(ltype) ) + return( ICON(1) ); + else if (ISREAL (ltype)) + return mkconv (ltype, ICON (1)); + else + return( (expptr) putconst((Constp) + mkconv(ltype, ICON(1))) ); + } + if(rp->constblock.Const.ci < 0) + { + if( ISINT(ltype) ) + { + frexpr(p); + err("integer**negative"); + return( errnode() ); + } + rp->constblock.Const.ci = - rp->constblock.Const.ci; + p->exprblock.leftp = lp + = fixexpr((Exprp)mkexpr(OPSLASH, ICON(1), lp)); + } + if(rp->constblock.Const.ci == 1) + { + frexpr(rp); + free( (charptr) p ); + return(lp); + } + + if( ONEOF(ltype, MSKINT|MSKREAL) ) { + p->exprblock.vtype = ltype; + return(p); + } + } + if( ISINT(rtype) ) + { + if(ltype==TYSHORT && rtype==TYSHORT && (!ISCONST(lp) || tyint==TYSHORT) ) + q = call2(TYSHORT, "pow_hh", lp, rp); + else { + if(ONEOF(ltype,M(TYINT1)|M(TYSHORT))) + { + ltype = TYLONG; + lp = mkconv(TYLONG,lp); + } +#ifdef TYQUAD + if (ltype == TYQUAD) + rp = mkconv(TYQUAD,rp); + else +#endif + rp = mkconv(TYLONG,rp); + if (ISCONST(rp)) { + tyi = tyint; + tyint = TYLONG; + rp = (expptr)putconst((Constp)rp); + tyint = tyi; + } + q = call2(ltype, powint[ltype-TYLONG], lp, rp); + } + } + else if( ISREAL( (mtype = maxtype(ltype,rtype)) )) { + extern int callk_kludge; + callk_kludge = TYDREAL; + q = call2(mtype, "pow_dd", mkconv(TYDREAL,lp), mkconv(TYDREAL,rp)); + callk_kludge = 0; + } + else { + q = call2(TYDCOMPLEX, "pow_zz", + mkconv(TYDCOMPLEX,lp), mkconv(TYDCOMPLEX,rp)); + if(mtype == TYCOMPLEX) + q = mkconv(TYCOMPLEX, q); + } + free( (charptr) p ); + return(q); +} + + +/* Complex Division. Same code as in Runtime Library +*/ + + + LOCAL void +zdiv(c, a, b) + register dcomplex *a, *b, *c; +{ + double ratio, den; + double abr, abi; + + if( (abr = b->dreal) < 0.) + abr = - abr; + if( (abi = b->dimag) < 0.) + abi = - abi; + if( abr <= abi ) + { + if(abi == 0) + Fatal("complex division by zero"); + ratio = b->dreal / b->dimag ; + den = b->dimag * (1 + ratio*ratio); + c->dreal = (a->dreal*ratio + a->dimag) / den; + c->dimag = (a->dimag*ratio - a->dreal) / den; + } + + else + { + ratio = b->dimag / b->dreal ; + den = b->dreal * (1 + ratio*ratio); + c->dreal = (a->dreal + a->dimag*ratio) / den; + c->dimag = (a->dimag - a->dreal*ratio) / den; + } +} diff --git a/usr.bin/f2c/f2c.1 b/usr.bin/f2c/f2c.1 new file mode 100644 index 000000000000..2a59dff8f7a5 --- /dev/null +++ b/usr.bin/f2c/f2c.1 @@ -0,0 +1,336 @@ +. \" Definitions of F, L and LR for the benefit of systems +. \" whose -man lacks them... +.de F +.nh +.if n \%\&\\$1 +.if t \%\&\f(CW\\$1\fR +.hy 14 +.. +.de L +.nh +.if n \%`\\$1' +.if t \%\&\f(CW\\$1\fR +.hy 14 +.. +.de LR +.nh +.if n \%`\\$1'\\$2 +.if t \%\&\f(CW\\$1\fR\\$2 +.hy 14 +.. +.TH F2C 1 +.CT 1 prog_other +.SH NAME +f\^2c \(mi Convert Fortran 77 to C or C++ +.SH SYNOPSIS +.B f\^2c +[ +.I option ... +] +.I file ... +.SH DESCRIPTION +.I F2c +converts Fortran 77 source code in +.I files +with names ending in +.L .f +or +.L .F +to C (or C++) source files in the +current directory, with +.L .c +substituted +for the final +.L .f +or +.LR .F . +If no Fortran files are named, +.I f\^2c +reads Fortran from standard input and +writes C on standard output. +.I File +names that end with +.L .p +or +.L .P +are taken to be prototype +files, as produced by option +.LR -P , +and are read first. +.PP +The following options have the same meaning as in +.IR f\^77 (1). +.TP +.B -C +Compile code to check that subscripts are within declared array bounds. +.TP +.B -I2 +Render INTEGER and LOGICAL as short, +INTEGER\(**4 as long int. Assume the default \fIlibF77\fR +and \fIlibI77\fR: allow only INTEGER\(**4 (and no LOGICAL) +variables in INQUIREs. Option +.L -I4 +confirms the default rendering of INTEGER as long int. +.TP +.B -onetrip +Compile DO loops that are performed at least once if reached. +(Fortran 77 DO loops are not performed at all if the upper limit is smaller than the lower limit.) +.TP +.B -U +Honor the case of variable and external names. Fortran keywords must be in +.I +lower +case. +.TP +.B -u +Make the default type of a variable `undefined' rather than using the default Fortran rules. +.TP +.B -w +Suppress all warning messages. +If the option is +.LR -w66 , +only Fortran 66 compatibility warnings are suppressed. +.PP +The following options are peculiar to +.IR f\^2c . +.TP +.B -A +Produce +.SM ANSI +C. +Default is old-style C. +.TP +.B -a +Make local variables automatic rather than static +unless they appear in a +.SM "DATA, EQUIVALENCE, NAMELIST," +or +.SM SAVE +statement. +.TP +.B -C++ +Output C++ code. +.TP +.B -c +Include original Fortran source as comments. +.TP +.B -E +Declare uninitialized +.SM COMMON +to be +.B Extern +(overridably defined in +.F f2c.h +as +.B extern). +.TP +.B -ec +Place uninitialized +.SM COMMON +blocks in separate files: +.B COMMON /ABC/ +appears in file +.BR abc_com.c . +Option +.LR -e1c +bundles the separate files +into the output file, with comments that give an unbundling +.IR sed (1) +script. +.TP +.B -ext +Complain about +.IR f\^77 (1) +extensions. +.TP +.B -f +Assume free-format input: accept text after column 72 and do not +pad fixed-format lines shorter than 72 characters with blanks. +.TP +.B -72 +Treat text appearing after column 72 as an error. +.TP +.B -g +Include original Fortran line numbers in \f(CW#line\fR lines. +.TP +.B -h +Emulate Fortran 66's treatment of Hollerith: try to align character strings on +word (or, if the option is +.LR -hd , +on double-word) boundaries. +.TP +.B -i2 +Similar to +.BR -I2 , +but assume a modified +.I libF77 +and +.I libI77 +(compiled with +.BR -Df\^2c_i2 ), +so +.SM INTEGER +and +.SM LOGICAL +variables may be assigned by +.SM INQUIRE +and array lengths are stored in short ints. +.TP +.B -kr +Use temporary values to enforce Fortran expression evaluation +where K&R (first edition) parenthesization rules allow rearrangement. +If the option is +.LR -krd , +use double precision temporaries even for single-precision operands. +.TP +.B -P +Write a +.IB file .P +of ANSI (or C++) prototypes +for definitions in each input +.IB file .f +or +.IB file .F . +When reading Fortran from standard input, write prototypes +at the beginning of standard output. Option +.B -Ps +implies +.B -P +and gives exit status 4 if rerunning +.I f\^2c +may change prototypes or declarations. +.TP +.B -p +Supply preprocessor definitions to make common-block members +look like local variables. +.TP +.B -R +Do not promote +.SM REAL +functions and operations to +.SM DOUBLE PRECISION. +Option +.L -!R +confirms the default, which imitates +.IR f\^77 . +.TP +.B -r +Cast values of REAL functions (including intrinsics) to REAL. +.TP +.B -r8 +Promote +.SM REAL +to +.SM DOUBLE PRECISION, COMPLEX +to +.SM DOUBLE COMPLEX. +.TP +.B -s +Preserve multidimensional subscripts. +.TP +.BI -T dir +Put temporary files in directory +.I dir. +.TP +.B -w8 +Suppress warnings when +.SM COMMON +or +.SM EQUIVALENCE +forces odd-word alignment of doubles. +.TP +.BI -W n +Assume +.I n +characters/word (default 4) +when initializing numeric variables with character data. +.TP +.B -z +Do not implicitly recognize +.SM DOUBLE COMPLEX. +.TP +.B -!bs +Do not recognize \fIb\fRack\fIs\fRlash escapes +(\e", \e', \e0, \e\e, \eb, \ef, \en, \er, \et, \ev) in character strings. +.TP +.B -!c +Inhibit C output, but produce +.B -P +output. +.TP +.B -!I +Reject +.B include +statements. +.TP +.B -!i8 +Disallow +.SM INTEGER*8. +.TP +.B -!it +Don't infer types of untyped +.SM EXTERNAL +procedures from use as parameters to previously defined or prototyped +procedures. +.TP +.B -!P +Do not attempt to infer +.SM ANSI +or C++ +prototypes from usage. +.PP +The resulting C invokes the support routines of +.IR f\^77 ; +object code should be loaded by +.I f\^77 +or with +.IR ld (1) +or +.IR cc (1) +options +.BR "-lF77 -lI77 -lm" . +Calling conventions +are those of +.IR f\&77 : +see the reference below. +.br +.SH FILES +.TP +.IB file .[fF] +input file +.TP +.B *.c +output file +.TP +.F /usr/include/f2c.h +header file +.TP +.F /usr/lib/libF77.a +intrinsic function library +.TP +.F /usr/lib/libI77.a +Fortran I/O library +.TP +.F /lib/libc.a +C library, see section 3 +.SH "SEE ALSO" +S. I. Feldman and +P. J. Weinberger, +`A Portable Fortran 77 Compiler', +\fIUNIX Time Sharing System Programmer's Manual\fR, +Tenth Edition, Volume 2, AT&T Bell Laboratories, 1990. +.SH DIAGNOSTICS +The diagnostics produced by +.I f\^2c +are intended to be +self-explanatory. +.SH BUGS +Floating-point constant expressions are simplified in +the floating-point arithmetic of the machine running +.IR f\^2c , +so they are typically accurate to at most 16 or 17 decimal places. +.br +Untypable +.SM EXTERNAL +functions are declared +.BR int . diff --git a/usr.bin/f2c/f2c.h b/usr.bin/f2c/f2c.h new file mode 100644 index 000000000000..fc1e9791fbc7 --- /dev/null +++ b/usr.bin/f2c/f2c.h @@ -0,0 +1,214 @@ +/* f2c.h -- Standard Fortran to C header file */ + +/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." + + - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ + +#ifndef F2C_INCLUDE +#define F2C_INCLUDE + +typedef long int integer; +typedef char *address; +typedef short int shortint; +typedef float real; +typedef double doublereal; +typedef struct { real r, i; } complex; +typedef struct { doublereal r, i; } doublecomplex; +typedef long int logical; +typedef short int shortlogical; +typedef char logical1; +typedef char integer1; +/* typedef long long longint; */ /* system-dependent */ + +#define TRUE_ (1) +#define FALSE_ (0) + +/* Extern is for use with -E */ +#ifndef Extern +#define Extern extern +#endif + +/* I/O stuff */ + +#ifdef f2c_i2 +/* for -i2 */ +typedef short flag; +typedef short ftnlen; +typedef short ftnint; +#else +typedef long flag; +typedef long ftnlen; +typedef long ftnint; +#endif + +/*external read, write*/ +typedef struct +{ flag cierr; + ftnint ciunit; + flag ciend; + char *cifmt; + ftnint cirec; +} cilist; + +/*internal read, write*/ +typedef struct +{ flag icierr; + char *iciunit; + flag iciend; + char *icifmt; + ftnint icirlen; + ftnint icirnum; +} icilist; + +/*open*/ +typedef struct +{ flag oerr; + ftnint ounit; + char *ofnm; + ftnlen ofnmlen; + char *osta; + char *oacc; + char *ofm; + ftnint orl; + char *oblnk; +} olist; + +/*close*/ +typedef struct +{ flag cerr; + ftnint cunit; + char *csta; +} cllist; + +/*rewind, backspace, endfile*/ +typedef struct +{ flag aerr; + ftnint aunit; +} alist; + +/* inquire */ +typedef struct +{ flag inerr; + ftnint inunit; + char *infile; + ftnlen infilen; + ftnint *inex; /*parameters in standard's order*/ + ftnint *inopen; + ftnint *innum; + ftnint *innamed; + char *inname; + ftnlen innamlen; + char *inacc; + ftnlen inacclen; + char *inseq; + ftnlen inseqlen; + char *indir; + ftnlen indirlen; + char *infmt; + ftnlen infmtlen; + char *inform; + ftnint informlen; + char *inunf; + ftnlen inunflen; + ftnint *inrecl; + ftnint *innrec; + char *inblank; + ftnlen inblanklen; +} inlist; + +#define VOID void + +union Multitype { /* for multiple entry points */ + integer1 g; + shortint h; + integer i; + /* longint j; */ + real r; + doublereal d; + complex c; + doublecomplex z; + }; + +typedef union Multitype Multitype; + +typedef long Long; /* No longer used; formerly in Namelist */ + +struct Vardesc { /* for Namelist */ + char *name; + char *addr; + ftnlen *dims; + int type; + }; +typedef struct Vardesc Vardesc; + +struct Namelist { + char *name; + Vardesc **vars; + int nvars; + }; +typedef struct Namelist Namelist; + +#define abs(x) ((x) >= 0 ? (x) : -(x)) +#define dabs(x) (doublereal)abs(x) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#define dmin(a,b) (doublereal)min(a,b) +#define dmax(a,b) (doublereal)max(a,b) + +/* procedure parameter types for -A and -C++ */ + +#define F2C_proc_par_types 1 +#ifdef __cplusplus +typedef int /* Unknown procedure type */ (*U_fp)(...); +typedef shortint (*J_fp)(...); +typedef integer (*I_fp)(...); +typedef real (*R_fp)(...); +typedef doublereal (*D_fp)(...), (*E_fp)(...); +typedef /* Complex */ VOID (*C_fp)(...); +typedef /* Double Complex */ VOID (*Z_fp)(...); +typedef logical (*L_fp)(...); +typedef shortlogical (*K_fp)(...); +typedef /* Character */ VOID (*H_fp)(...); +typedef /* Subroutine */ int (*S_fp)(...); +#else +typedef int /* Unknown procedure type */ (*U_fp)(); +typedef shortint (*J_fp)(); +typedef integer (*I_fp)(); +typedef real (*R_fp)(); +typedef doublereal (*D_fp)(), (*E_fp)(); +typedef /* Complex */ VOID (*C_fp)(); +typedef /* Double Complex */ VOID (*Z_fp)(); +typedef logical (*L_fp)(); +typedef shortlogical (*K_fp)(); +typedef /* Character */ VOID (*H_fp)(); +typedef /* Subroutine */ int (*S_fp)(); +#endif +/* E_fp is for real functions when -R is not specified */ +typedef VOID C_f; /* complex function */ +typedef VOID H_f; /* character function */ +typedef VOID Z_f; /* double complex function */ +typedef doublereal E_f; /* real function with -R not specified */ + +/* undef any lower-case symbols that your C compiler predefines, e.g.: */ + +#ifndef Skip_f2c_Undefs +#undef cray +#undef gcos +#undef mc68010 +#undef mc68020 +#undef mips +#undef pdp11 +#undef sgi +#undef sparc +#undef sun +#undef sun2 +#undef sun3 +#undef sun4 +#undef u370 +#undef u3b +#undef u3b2 +#undef u3b5 +#undef unix +#undef vax +#endif +#endif diff --git a/usr.bin/f2c/format.c b/usr.bin/f2c/format.c new file mode 100644 index 000000000000..80faacceeb41 --- /dev/null +++ b/usr.bin/f2c/format.c @@ -0,0 +1,2225 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* Format.c -- this file takes an intermediate file (generated by pass 1 + of the translator) and some state information about the contents of that + file, and generates C program text. */ + +#include "defs.h" +#include "p1defs.h" +#include "format.h" +#include "output.h" +#include "names.h" +#include "iob.h" + +int c_output_line_length = DEF_C_LINE_LENGTH; + +int last_was_label; /* Boolean used to generate semicolons + when a label terminates a block */ +static char this_proc_name[52]; /* Name of the current procedure. This is + probably too simplistic to handle + multiple entry points */ + +static int p1getd(), p1gets(), p1getf(), get_p1_token(); +static int p1get_const(), p1getn(); +static expptr do_format(), do_p1_name_pointer(), do_p1_const(); +static expptr do_p1_expr(), do_p1_ident(), do_p1_charp(), do_p1_extern(); +static expptr do_p1_head(), do_p1_list(), do_p1_literal(); +static void do_p1_label(), do_p1_asgoto(), do_p1_goto(); +static void do_p1_if(), do_p1_else(), do_p1_elif(), do_p1_endif(); +static void do_p1_endelse(), do_p1_subr_ret(), do_p1_comp_goto(); +static void do_p1_for(), do_p1_end_for(), do_p1_fortran(); +static void do_p1_1while(), do_p1_2while(), do_p1_elseifstart(); +static void do_p1_comment(), do_p1_set_line(); +static expptr do_p1_addr(); +static void proto(); +void list_arg_types(); +chainp length_comp(); +void listargs(); +extern chainp assigned_fmts; +static char filename[P1_FILENAME_MAX]; +extern int gflag; +int gflag1; +extern char *parens; + +start_formatting () +{ + FILE *infile; + static int wrote_one = 0; + extern int usedefsforcommon; + extern char *p1_file, *p1_bakfile; + + this_proc_name[0] = '\0'; + last_was_label = 0; + ei_next = ei_first; + wh_next = wh_first; + + (void) fclose (pass1_file); + if ((infile = fopen (p1_file, binread)) == NULL) + Fatal("start_formatting: couldn't open the intermediate file\n"); + + if (wrote_one) + nice_printf (c_file, "\n"); + + while (!feof (infile)) { + expptr this_expr; + + this_expr = do_format (infile, c_file); + if (this_expr) { + out_and_free_statement (c_file, this_expr); + } /* if this_expr */ + } /* while !feof infile */ + + (void) fclose (infile); + + if (last_was_label) + nice_printf (c_file, ";\n"); + + prev_tab (c_file); + gflag1 = 0; + if (this_proc_name[0]) + nice_printf (c_file, "} /* %s */\n", this_proc_name); + + +/* Write the #undefs for common variable reference */ + + if (usedefsforcommon) { + Extsym *ext; + int did_one = 0; + + for (ext = extsymtab; ext < nextext; ext++) + if (ext -> extstg == STGCOMMON && ext -> used_here) { + ext -> used_here = 0; + if (!did_one) + nice_printf (c_file, "\n"); + wr_abbrevs(c_file, 0, ext->extp); + did_one = 1; + ext -> extp = CHNULL; + } /* if */ + + if (did_one) + nice_printf (c_file, "\n"); + } /* if usedefsforcommon */ + + other_undefs(c_file); + + wrote_one = 1; + +/* For debugging only */ + + if (debugflag && (pass1_file = fopen (p1_bakfile, binwrite))) + if (infile = fopen (p1_file, binread)) { + ffilecopy (infile, pass1_file); + fclose (infile); + fclose (pass1_file); + } /* if infile */ + +/* End of "debugging only" */ + + scrub(p1_file); /* optionally unlink */ + + if ((pass1_file = fopen (p1_file, binwrite)) == NULL) + err ("start_formatting: couldn't reopen the pass1 file"); + +} /* start_formatting */ + + + static void +put_semi(outfile) + FILE *outfile; +{ + nice_printf (outfile, ";\n"); + last_was_label = 0; + } + +#define SEM_CHECK(x) if (last_was_label) put_semi(x) + +/* do_format -- takes an input stream (a file in pass1 format) and writes + the appropriate C code to outfile when possible. When reading an + expression, the expression tree is returned instead. */ + +static expptr do_format (infile, outfile) +FILE *infile, *outfile; +{ + int token_type, was_c_token; + expptr retval = ENULL; + + token_type = get_p1_token (infile); + was_c_token = 1; + switch (token_type) { + case P1_COMMENT: + do_p1_comment (infile, outfile); + was_c_token = 0; + break; + case P1_SET_LINE: + do_p1_set_line (infile); + was_c_token = 0; + break; + case P1_FILENAME: + p1gets(infile, filename, P1_FILENAME_MAX); + was_c_token = 0; + break; + case P1_NAME_POINTER: + retval = do_p1_name_pointer (infile); + break; + case P1_CONST: + retval = do_p1_const (infile); + break; + case P1_EXPR: + retval = do_p1_expr (infile, outfile); + break; + case P1_IDENT: + retval = do_p1_ident(infile); + break; + case P1_CHARP: + retval = do_p1_charp(infile); + break; + case P1_EXTERN: + retval = do_p1_extern (infile); + break; + case P1_HEAD: + gflag1 = 0; + retval = do_p1_head (infile, outfile); + gflag1 = gflag; + break; + case P1_LIST: + retval = do_p1_list (infile, outfile); + break; + case P1_LITERAL: + retval = do_p1_literal (infile); + break; + case P1_LABEL: + do_p1_label (infile, outfile); + /* last_was_label = 1; -- now set in do_p1_label */ + was_c_token = 0; + break; + case P1_ASGOTO: + do_p1_asgoto (infile, outfile); + break; + case P1_GOTO: + do_p1_goto (infile, outfile); + break; + case P1_IF: + do_p1_if (infile, outfile); + break; + case P1_ELSE: + SEM_CHECK(outfile); + do_p1_else (outfile); + break; + case P1_ELIF: + SEM_CHECK(outfile); + do_p1_elif (infile, outfile); + break; + case P1_ENDIF: + SEM_CHECK(outfile); + do_p1_endif (outfile); + break; + case P1_ENDELSE: + SEM_CHECK(outfile); + do_p1_endelse (outfile); + break; + case P1_ADDR: + retval = do_p1_addr (infile, outfile); + break; + case P1_SUBR_RET: + do_p1_subr_ret (infile, outfile); + break; + case P1_COMP_GOTO: + do_p1_comp_goto (infile, outfile); + break; + case P1_FOR: + do_p1_for (infile, outfile); + break; + case P1_ENDFOR: + SEM_CHECK(outfile); + do_p1_end_for (outfile); + break; + case P1_WHILE1START: + do_p1_1while(outfile); + break; + case P1_WHILE2START: + do_p1_2while(infile, outfile); + break; + case P1_PROCODE: + procode(outfile); + break; + case P1_ELSEIFSTART: + SEM_CHECK(outfile); + do_p1_elseifstart(outfile); + break; + case P1_FORTRAN: + do_p1_fortran(infile, outfile); + /* no break; */ + case P1_EOF: + was_c_token = 0; + break; + case P1_UNKNOWN: + Fatal("do_format: Unknown token type in intermediate file"); + break; + default: + Fatal("do_format: Bad token type in intermediate file"); + break; + } /* switch */ + + if (was_c_token) + last_was_label = 0; + return retval; +} /* do_format */ + + + static void +do_p1_comment (infile, outfile) +FILE *infile, *outfile; +{ + extern int c_output_line_length, in_comment; + + char storage[COMMENT_BUFFER_SIZE + 1]; + int length; + + if (!p1gets(infile, storage, COMMENT_BUFFER_SIZE + 1)) + return; + + length = strlen (storage); + + gflag1 = 0; + in_comment = 1; + if (length > c_output_line_length - 6) + margin_printf (outfile, "/*%s*/\n", storage); + else + margin_printf (outfile, length ? "/* %s */\n" : "\n", storage); + in_comment = 0; + gflag1 = gflag; +} /* do_p1_comment */ + + static void +do_p1_set_line (infile) +FILE *infile; +{ + int status; + long new_line_number = -1; + + status = p1getd (infile, &new_line_number); + + if (status == EOF) + err ("do_p1_set_line: Missing line number at end of file\n"); + else if (status == 0 || new_line_number == -1) + errl("do_p1_set_line: Illegal line number in intermediate file: %ld\n", + new_line_number); + else { + lineno = new_line_number; + } +} /* do_p1_set_line */ + + +static expptr do_p1_name_pointer (infile) +FILE *infile; +{ + Namep namep = (Namep) NULL; + int status; + + status = p1getd (infile, (long *) &namep); + + if (status == EOF) + err ("do_p1_name_pointer: Missing pointer at end of file\n"); + else if (status == 0 || namep == (Namep) NULL) + erri ("do_p1_name_pointer: Illegal name pointer in p1 file: '%x'\n", + (int) namep); + + return (expptr) namep; +} /* do_p1_name_pointer */ + + + +static expptr do_p1_const (infile) +FILE *infile; +{ + struct Constblock *c = (struct Constblock *) NULL; + long type = -1; + int status; + + status = p1getd (infile, &type); + + if (status == EOF) + err ("do_p1_const: Missing constant type at end of file\n"); + else if (status == 0) + errl("do_p1_const: Illegal constant type in p1 file: %ld\n", type); + else { + status = p1get_const (infile, (int)type, &c); + + if (status == EOF) { + err ("do_p1_const: Missing constant value at end of file\n"); + c = (struct Constblock *) NULL; + } else if (status == 0) { + err ("do_p1_const: Illegal constant value in p1 file\n"); + c = (struct Constblock *) NULL; + } /* else */ + } /* else */ + return (expptr) c; +} /* do_p1_const */ + + +static expptr do_p1_literal (infile) +FILE *infile; +{ + int status; + long memno; + Addrp addrp; + + status = p1getd (infile, &memno); + + if (status == EOF) + err ("do_p1_literal: Missing memno at end of file"); + else if (status == 0) + err ("do_p1_literal: Missing memno in p1 file"); + else { + struct Literal *litp, *lastlit; + + addrp = ALLOC (Addrblock); + addrp -> tag = TADDR; + addrp -> vtype = TYUNKNOWN; + addrp -> Field = NULL; + + lastlit = litpool + nliterals; + for (litp = litpool; litp < lastlit; litp++) + if (litp -> litnum == memno) { + addrp -> vtype = litp -> littype; + *((union Constant *) &(addrp -> user)) = + *((union Constant *) &(litp -> litval)); + break; + } /* if litp -> litnum == memno */ + + addrp -> memno = memno; + addrp -> vstg = STGMEMNO; + addrp -> uname_tag = UNAM_CONST; + } /* else */ + + return (expptr) addrp; +} /* do_p1_literal */ + + +static void do_p1_label (infile, outfile) +FILE *infile, *outfile; +{ + int status; + ftnint stateno; + char *user_label (); + struct Labelblock *L; + char *fmt; + + status = p1getd (infile, &stateno); + + if (status == EOF) + err ("do_p1_label: Missing label at end of file"); + else if (status == 0) + err ("do_p1_label: Missing label in p1 file "); + else if (stateno < 0) { /* entry */ + margin_printf(outfile, "\n%s:\n", user_label(stateno)); + last_was_label = 1; + } + else { + L = labeltab + stateno; + if (L->labused) { + fmt = "%s:\n"; + last_was_label = 1; + } + else + fmt = "/* %s: */\n"; + margin_printf(outfile, fmt, user_label(L->stateno)); + } /* else */ +} /* do_p1_label */ + + + +static void do_p1_asgoto (infile, outfile) +FILE *infile, *outfile; +{ + expptr expr; + + expr = do_format (infile, outfile); + out_asgoto (outfile, expr); + +} /* do_p1_asgoto */ + + +static void do_p1_goto (infile, outfile) +FILE *infile, *outfile; +{ + int status; + long stateno; + char *user_label (); + + status = p1getd (infile, &stateno); + + if (status == EOF) + err ("do_p1_goto: Missing goto label at end of file"); + else if (status == 0) + err ("do_p1_goto: Missing goto label in p1 file"); + else { + nice_printf (outfile, "goto %s;\n", user_label (stateno)); + } /* else */ +} /* do_p1_goto */ + + +static void do_p1_if (infile, outfile) +FILE *infile, *outfile; +{ + expptr cond; + + do { + cond = do_format (infile, outfile); + } while (cond == ENULL); + + out_if (outfile, cond); +} /* do_p1_if */ + + +static void do_p1_else (outfile) +FILE *outfile; +{ + out_else (outfile); +} /* do_p1_else */ + + +static void do_p1_elif (infile, outfile) +FILE *infile, *outfile; +{ + expptr cond; + + do { + cond = do_format (infile, outfile); + } while (cond == ENULL); + + elif_out (outfile, cond); +} /* do_p1_elif */ + +static void do_p1_endif (outfile) +FILE *outfile; +{ + endif_out (outfile); +} /* do_p1_endif */ + + +static void do_p1_endelse (outfile) +FILE *outfile; +{ + end_else_out (outfile); +} /* do_p1_endelse */ + + +static expptr do_p1_addr (infile, outfile) +FILE *infile, *outfile; +{ + Addrp addrp = (Addrp) NULL; + int status; + + status = p1getn (infile, (int)sizeof(struct Addrblock), (char **) &addrp); + + if (status == EOF) + err ("do_p1_addr: Missing Addrp at end of file"); + else if (status == 0) + err ("do_p1_addr: Missing Addrp in p1 file"); + else if (addrp == (Addrp) NULL) + err ("do_p1_addr: Null addrp in p1 file"); + else if (addrp -> tag != TADDR) + erri ("do_p1_addr: bad tag in p1 file '%d'", addrp -> tag); + else { + addrp -> vleng = do_format (infile, outfile); + addrp -> memoffset = do_format (infile, outfile); + } + + return (expptr) addrp; +} /* do_p1_addr */ + + + +static void do_p1_subr_ret (infile, outfile) +FILE *infile, *outfile; +{ + expptr retval; + + nice_printf (outfile, "return "); + retval = do_format (infile, outfile); + if (!multitype) + if (retval) + expr_out (outfile, retval); + + nice_printf (outfile, ";\n"); +} /* do_p1_subr_ret */ + + + +static void do_p1_comp_goto (infile, outfile) +FILE *infile, *outfile; +{ + expptr index; + expptr labels; + + index = do_format (infile, outfile); + + if (index == ENULL) { + err ("do_p1_comp_goto: no expression for computed goto"); + return; + } /* if index == ENULL */ + + labels = do_format (infile, outfile); + + if (labels && labels -> tag != TLIST) + erri ("do_p1_comp_goto: expected list, got tag '%d'", labels -> tag); + else + compgoto_out (outfile, index, labels); +} /* do_p1_comp_goto */ + + +static void do_p1_for (infile, outfile) +FILE *infile, *outfile; +{ + expptr init, test, inc; + + init = do_format (infile, outfile); + test = do_format (infile, outfile); + inc = do_format (infile, outfile); + + out_for (outfile, init, test, inc); +} /* do_p1_for */ + +static void do_p1_end_for (outfile) +FILE *outfile; +{ + out_end_for (outfile); +} /* do_p1_end_for */ + + + static void +do_p1_fortran(infile, outfile) + FILE *infile, *outfile; +{ + char buf[P1_STMTBUFSIZE]; + if (!p1gets(infile, buf, P1_STMTBUFSIZE)) + return; + /* bypass nice_printf nonsense */ + fprintf(outfile, "/*< %s >*/\n", buf+1); /* + 1 to skip by '$' */ + } + + +static expptr do_p1_expr (infile, outfile) +FILE *infile, *outfile; +{ + int status; + long opcode, type; + struct Exprblock *result = (struct Exprblock *) NULL; + + status = p1getd (infile, &opcode); + + if (status == EOF) + err ("do_p1_expr: Missing expr opcode at end of file"); + else if (status == 0) + err ("do_p1_expr: Missing expr opcode in p1 file"); + else { + + status = p1getd (infile, &type); + + if (status == EOF) + err ("do_p1_expr: Missing expr type at end of file"); + else if (status == 0) + err ("do_p1_expr: Missing expr type in p1 file"); + else if (opcode == 0) + return ENULL; + else { + result = ALLOC (Exprblock); + + result -> tag = TEXPR; + result -> vtype = type; + result -> opcode = opcode; + result -> vleng = do_format (infile, outfile); + + if (is_unary_op (opcode)) + result -> leftp = do_format (infile, outfile); + else if (is_binary_op (opcode)) { + result -> leftp = do_format (infile, outfile); + result -> rightp = do_format (infile, outfile); + } else + errl("do_p1_expr: Illegal opcode %ld", opcode); + } /* else */ + } /* else */ + + return (expptr) result; +} /* do_p1_expr */ + + +static expptr do_p1_ident(infile) +FILE *infile; +{ + Addrp addrp; + int status; + long vtype, vstg; + + addrp = ALLOC (Addrblock); + addrp -> tag = TADDR; + + status = p1getd (infile, &vtype); + if (status == EOF) + err ("do_p1_ident: Missing identifier type at end of file\n"); + else if (status == 0 || vtype < 0 || vtype >= NTYPES) + errl("do_p1_ident: Bad type in intermediate file: %ld\n", vtype); + else + addrp -> vtype = vtype; + + status = p1getd (infile, &vstg); + if (status == EOF) + err ("do_p1_ident: Missing identifier storage at end of file\n"); + else if (status == 0 || vstg < 0 || vstg > STGNULL) + errl("do_p1_ident: Bad storage in intermediate file: %ld\n", vtype); + else + addrp -> vstg = vstg; + + status = p1gets(infile, addrp->user.ident, IDENT_LEN); + + if (status == EOF) + err ("do_p1_ident: Missing ident string at end of file"); + else if (status == 0) + err ("do_p1_ident: Missing ident string in intermediate file"); + addrp->uname_tag = UNAM_IDENT; + return (expptr) addrp; +} /* do_p1_ident */ + +static expptr do_p1_charp(infile) +FILE *infile; +{ + Addrp addrp; + int status; + long vtype, vstg; + char buf[64]; + + addrp = ALLOC (Addrblock); + addrp -> tag = TADDR; + + status = p1getd (infile, &vtype); + if (status == EOF) + err ("do_p1_ident: Missing identifier type at end of file\n"); + else if (status == 0 || vtype < 0 || vtype >= NTYPES) + errl("do_p1_ident: Bad type in intermediate file: %ld\n", vtype); + else + addrp -> vtype = vtype; + + status = p1getd (infile, &vstg); + if (status == EOF) + err ("do_p1_ident: Missing identifier storage at end of file\n"); + else if (status == 0 || vstg < 0 || vstg > STGNULL) + errl("do_p1_ident: Bad storage in intermediate file: %ld\n", vtype); + else + addrp -> vstg = vstg; + + status = p1gets(infile, buf, (int)sizeof(buf)); + + if (status == EOF) + err ("do_p1_ident: Missing charp ident string at end of file"); + else if (status == 0) + err ("do_p1_ident: Missing charp ident string in intermediate file"); + addrp->uname_tag = UNAM_CHARP; + addrp->user.Charp = strcpy(mem(strlen(buf)+1,0), buf); + return (expptr) addrp; +} + + +static expptr do_p1_extern (infile) +FILE *infile; +{ + Addrp addrp; + + addrp = ALLOC (Addrblock); + if (addrp) { + int status; + + addrp->tag = TADDR; + addrp->vstg = STGEXT; + addrp->uname_tag = UNAM_EXTERN; + status = p1getd (infile, &(addrp -> memno)); + if (status == EOF) + err ("do_p1_extern: Missing memno at end of file"); + else if (status == 0) + err ("do_p1_extern: Missing memno in intermediate file"); + if (addrp->vtype = extsymtab[addrp->memno].extype) + addrp->vclass = CLPROC; + } /* if addrp */ + + return (expptr) addrp; +} /* do_p1_extern */ + + + +static expptr do_p1_head (infile, outfile) +FILE *infile, *outfile; +{ + int status; + int add_n_; + long class; + char storage[256]; + + status = p1getd (infile, &class); + if (status == EOF) + err ("do_p1_head: missing header class at end of file"); + else if (status == 0) + err ("do_p1_head: missing header class in p1 file"); + else { + status = p1gets (infile, storage, (int)sizeof(storage)); + if (status == EOF || status == 0) + storage[0] = '\0'; + } /* else */ + + if (class == CLPROC || class == CLMAIN) { + chainp lengths; + + add_n_ = nentry > 1; + lengths = length_comp(entries, add_n_); + + if (!add_n_ && protofile && class != CLMAIN) + protowrite(protofile, proctype, storage, entries, lengths); + + if (class == CLMAIN) + nice_printf (outfile, "/* Main program */ "); + else + nice_printf(outfile, "%s ", multitype ? "VOID" + : c_type_decl(proctype, 1)); + + nice_printf(outfile, add_n_ ? "%s0_" : "%s", storage); + if (!Ansi) { + listargs(outfile, entries, add_n_, lengths); + nice_printf (outfile, "\n"); + } + list_arg_types (outfile, entries, lengths, add_n_, "\n"); + nice_printf (outfile, "{\n"); + frchain(&lengths); + next_tab (outfile); + strcpy(this_proc_name, storage); + list_decls (outfile); + + } else if (class == CLBLOCK) + next_tab (outfile); + else + errl("do_p1_head: got class %ld", class); + + return NULL; +} /* do_p1_head */ + + +static expptr do_p1_list (infile, outfile) +FILE *infile, *outfile; +{ + long tag, type, count; + int status; + expptr result; + + status = p1getd (infile, &tag); + if (status == EOF) + err ("do_p1_list: missing list tag at end of file"); + else if (status == 0) + err ("do_p1_list: missing list tag in p1 file"); + else { + status = p1getd (infile, &type); + if (status == EOF) + err ("do_p1_list: missing list type at end of file"); + else if (status == 0) + err ("do_p1_list: missing list type in p1 file"); + else { + status = p1getd (infile, &count); + if (status == EOF) + err ("do_p1_list: missing count at end of file"); + else if (status == 0) + err ("do_p1_list: missing count in p1 file"); + } /* else */ + } /* else */ + + result = (expptr) ALLOC (Listblock); + if (result) { + chainp pointer; + + result -> tag = tag; + result -> listblock.vtype = type; + +/* Assume there will be enough data */ + + if (count--) { + pointer = result->listblock.listp = + mkchain((char *)do_format(infile, outfile), CHNULL); + while (count--) { + pointer -> nextp = + mkchain((char *)do_format(infile, outfile), CHNULL); + pointer = pointer -> nextp; + } /* while (count--) */ + } /* if (count) */ + } /* if (result) */ + + return result; +} /* do_p1_list */ + + +chainp length_comp(e, add_n) /* get lengths of characters args */ + struct Entrypoint *e; + int add_n; +{ + chainp lengths; + chainp args, args1; + Namep arg, np; + int nchargs; + Argtypes *at; + Atype *a; + extern int init_ac[TYSUBR+1]; + + if (!e) + return 0; /* possible only with errors */ + args = args1 = add_n ? allargs : e->arglist; + nchargs = 0; + for (lengths = NULL; args; args = args -> nextp) + if (arg = (Namep)args->datap) { + if (arg->vclass == CLUNKNOWN) + arg->vclass = CLVAR; + if (arg->vtype == TYCHAR && arg->vclass != CLPROC) { + lengths = mkchain((char *)arg, lengths); + nchargs++; + } + } + if (!add_n && (np = e->enamep)) { + /* one last check -- by now we know all we ever will + * about external args... + */ + save_argtypes(e->arglist, &e->entryname->arginfo, + &np->arginfo, 0, np->fvarname, STGEXT, nchargs, + np->vtype, 1); + at = e->entryname->arginfo; + a = at->atypes + init_ac[np->vtype]; + for(; args1; a++, args1 = args1->nextp) { + frchain(&a->cp); + if (arg = (Namep)args1->datap) + switch(arg->vclass) { + case CLPROC: + if (arg->vimpltype + && a->type >= 300) + a->type = TYUNKNOWN + 200; + break; + case CLUNKNOWN: + a->type %= 100; + } + } + } + return revchain(lengths); + } + +void listargs(outfile, entryp, add_n_, lengths) + FILE *outfile; + struct Entrypoint *entryp; + int add_n_; + chainp lengths; +{ + chainp args; + char *s; + Namep arg; + int did_one = 0; + + nice_printf (outfile, "("); + + if (add_n_) { + nice_printf(outfile, "n__"); + did_one = 1; + args = allargs; + } + else { + if (!entryp) + return; /* possible only with errors */ + args = entryp->arglist; + } + + if (multitype) + { + nice_printf(outfile, ", ret_val"); + did_one = 1; + args = allargs; + } + else if (ONEOF(proctype, MSKCOMPLEX|MSKCHAR)) + { + s = xretslot[proctype]->user.ident; + nice_printf(outfile, did_one ? ", %s" : "%s", + *s == '(' /*)*/ ? "r_v" : s); + did_one = 1; + if (proctype == TYCHAR) + nice_printf (outfile, ", ret_val_len"); + } + for (; args; args = args -> nextp) + if (arg = (Namep)args->datap) { + nice_printf (outfile, "%s", did_one ? ", " : ""); + out_name (outfile, arg); + did_one = 1; + } + + for (args = lengths; args; args = args -> nextp) + nice_printf(outfile, ", %s", + new_arg_length((Namep)args->datap)); + nice_printf (outfile, ")"); +} /* listargs */ + + +void list_arg_types(outfile, entryp, lengths, add_n_, finalnl) +FILE *outfile; +struct Entrypoint *entryp; +chainp lengths; +int add_n_; +char *finalnl; +{ + chainp args; + int last_type = -1, last_class = -1; + int did_one = 0, done_one, is_ext; + char *s, *sep = "", *sep1; + + if (outfile == (FILE *) NULL) { + err ("list_arg_types: null output file"); + return; + } else if (entryp == (struct Entrypoint *) NULL) { + err ("list_arg_types: null procedure entry pointer"); + return; + } /* else */ + + if (Ansi) { + done_one = 0; + sep1 = ", "; + nice_printf(outfile, "(" /*)*/); + } + else { + done_one = 1; + sep1 = ";\n"; + } + args = entryp->arglist; + if (add_n_) { + nice_printf(outfile, "int n__"); + did_one = done_one; + sep = sep1; + args = allargs; + } + if (multitype) { + nice_printf(outfile, "%sMultitype *ret_val", sep); + did_one = done_one; + sep = sep1; + } + else if (ONEOF (proctype, MSKCOMPLEX|MSKCHAR)) { + s = xretslot[proctype]->user.ident; + nice_printf(outfile, "%s%s *%s", sep, c_type_decl(proctype, 0), + *s == '(' /*)*/ ? "r_v" : s); + did_one = done_one; + sep = sep1; + if (proctype == TYCHAR) + nice_printf (outfile, "%sftnlen ret_val_len", sep); + } /* if ONEOF proctype */ + for (; args; args = args -> nextp) { + Namep arg = (Namep) args->datap; + +/* Scalars are passed by reference, and arrays will have their lower bound + adjusted, so nearly everything is printed with a star in front. The + exception is character lengths, which are passed by value. */ + + if (arg) { + int type = arg -> vtype, class = arg -> vclass; + + if (class == CLPROC) + if (arg->vimpltype) + type = Castargs ? TYUNKNOWN : TYSUBR; + else if (type == TYREAL && forcedouble && !Castargs) + type = TYDREAL; + + if (type == last_type && class == last_class && did_one) + nice_printf (outfile, ", "); + else + if ((is_ext = class == CLPROC) && Castargs) + nice_printf(outfile, "%s%s ", sep, + usedcasts[type] = casttypes[type]); + else + nice_printf(outfile, "%s%s ", sep, + c_type_decl(type, is_ext)); + if (class == CLPROC) + if (Castargs) + out_name(outfile, arg); + else { + nice_printf(outfile, "(*"); + out_name(outfile, arg); + nice_printf(outfile, ") %s", parens); + } + else { + nice_printf (outfile, "*"); + out_name (outfile, arg); + } + + last_type = type; + last_class = class; + did_one = done_one; + sep = sep1; + } /* if (arg) */ + } /* for args = entryp -> arglist */ + + for (args = lengths; args; args = args -> nextp) + nice_printf(outfile, "%sftnlen %s", sep, + new_arg_length((Namep)args->datap)); + if (did_one) + nice_printf (outfile, ";\n"); + else if (Ansi) + nice_printf(outfile, + /*((*/ sep != sep1 && Ansi == 1 ? "void)%s" : ")%s", + finalnl); +} /* list_arg_types */ + + static void +write_formats(outfile) + FILE *outfile; +{ + register struct Labelblock *lp; + int first = 1; + char *fs; + + for(lp = labeltab ; lp < highlabtab ; ++lp) + if (lp->fmtlabused) { + if (first) { + first = 0; + nice_printf(outfile, "/* Format strings */\n"); + } + nice_printf(outfile, "static char fmt_%ld[] = \"", + lp->stateno); + if (!(fs = lp->fmtstring)) + fs = ""; + nice_printf(outfile, "%s\";\n", fs); + } + if (!first) + nice_printf(outfile, "\n"); + } + + static void +write_ioblocks(outfile) + FILE *outfile; +{ + register iob_data *L; + register char *f, **s, *sep; + + nice_printf(outfile, "/* Fortran I/O blocks */\n"); + L = iob_list = (iob_data *)revchain((chainp)iob_list); + do { + nice_printf(outfile, "static %s %s = { ", + L->type, L->name); + sep = 0; + for(s = L->fields; f = *s; s++) { + if (sep) + nice_printf(outfile, sep); + sep = ", "; + if (*f == '"') { /* kludge */ + nice_printf(outfile, "\""); + nice_printf(outfile, "%s\"", f+1); + } + else + nice_printf(outfile, "%s", f); + } + nice_printf(outfile, " };\n"); + } + while(L = L->next); + nice_printf(outfile, "\n\n"); + } + + static void +write_assigned_fmts(outfile) + FILE *outfile; +{ + register chainp cp; + Namep np; + int did_one = 0; + + cp = assigned_fmts = revchain(assigned_fmts); + nice_printf(outfile, "/* Assigned format variables */\nchar "); + do { + np = (Namep)cp->datap; + if (did_one) + nice_printf(outfile, ", "); + did_one = 1; + nice_printf(outfile, "*%s_fmt", np->fvarname); + } + while(cp = cp->nextp); + nice_printf(outfile, ";\n\n"); + } + + static char * +to_upper(s) + register char *s; +{ + static char buf[64]; + register char *t = buf; + register int c; + while(*t++ = (c = *s++) >= 'a' && c <= 'z' ? c + 'A' - 'a' : c); + return buf; + } + + +/* This routine creates static structures representing a namelist. + Declarations of the namelist and related structures are: + + struct Vardesc { + char *name; + char *addr; + ftnlen *dims; /* laid out as struct dimensions below *//* + int type; + }; + typedef struct Vardesc Vardesc; + + struct Namelist { + char *name; + Vardesc **vars; + int nvars; + }; + + struct dimensions + { + ftnlen numberofdimensions; + ftnlen numberofelements + ftnlen baseoffset; + ftnlen span[numberofdimensions-1]; + }; + + If dims is not null, then the corner element of the array is at + addr. However, the element with subscripts (i1,...,in) is at + addr + sizeoftype * (i1+span[0]*(i2+span[1]*...) - dimp->baseoffset) +*/ + + static void +write_namelists(nmch, outfile) + chainp nmch; + FILE *outfile; +{ + Namep var; + struct Hashentry *entry; + struct Dimblock *dimp; + int i, nd, type; + char *comma, *name; + register chainp q; + register Namep v; + extern int typeconv[]; + + nice_printf(outfile, "/* Namelist stuff */\n\n"); + for (entry = hashtab; entry < lasthash; ++entry) { + if (!(v = entry->varp) || !v->vnamelist) + continue; + type = v->vtype; + name = v->cvarname; + if (dimp = v->vdim) { + nd = dimp->ndim; + nice_printf(outfile, + "static ftnlen %s_dims[] = { %d, %ld, %ld", + name, nd, + dimp->nelt->constblock.Const.ci, + dimp->baseoffset->constblock.Const.ci); + for(i = 0, --nd; i < nd; i++) + nice_printf(outfile, ", %ld", + dimp->dims[i].dimsize->constblock.Const.ci); + nice_printf(outfile, " };\n"); + } + nice_printf(outfile, "static Vardesc %s_dv = { \"%s\", %s", + name, to_upper(v->fvarname), + type == TYCHAR ? "" + : (dimp || oneof_stg(v,v->vstg, + M(STGEQUIV)|M(STGCOMMON))) + ? "(char *)" : "(char *)&"); + out_name(outfile, v); + nice_printf(outfile, dimp ? ", %s_dims" : ", (ftnlen *)0", name); + nice_printf(outfile, ", %ld };\n", + type != TYCHAR ? (long)typeconv[type] + : -v->vleng->constblock.Const.ci); + } + + do { + var = (Namep)nmch->datap; + name = var->cvarname; + nice_printf(outfile, "\nstatic Vardesc *%s_vl[] = ", name); + comma = "{"; + i = 0; + for(q = var->varxptr.namelist ; q ; q = q->nextp) { + v = (Namep)q->datap; + if (!v->vnamelist) + continue; + i++; + nice_printf(outfile, "%s &%s_dv", comma, v->cvarname); + comma = ","; + } + nice_printf(outfile, " };\n"); + nice_printf(outfile, + "static Namelist %s = { \"%s\", %s_vl, %d };\n", + name, to_upper(var->fvarname), name, i); + } + while(nmch = nmch->nextp); + nice_printf(outfile, "\n"); + } + +/* fixextype tries to infer from usage in previous procedures + the type of an external procedure declared + external and passed as an argument but never typed or invoked. + */ + + static int +fixexttype(var) + Namep var; +{ + Extsym *e; + int type, type1; + extern void changedtype(); + + type = var->vtype; + e = &extsymtab[var->vardesc.varno]; + if ((type1 = e->extype) && type == TYUNKNOWN) + return var->vtype = type1; + if (var->visused) { + if (e->exused && type != type1) + changedtype(var); + e->exused = 1; + e->extype = type; + } + return type; + } + + static void +ref_defs(outfile, refdefs) FILE *outfile; chainp refdefs; +{ + chainp cp; + int eb, i, j, n; + struct Dimblock *dimp; + long L; + expptr b, vl; + Namep var; + char *amp, *comma; + + ind_printf(0, outfile, "\n"); + for(cp = refdefs = revchain(refdefs); cp; cp = cp->nextp) { + var = (Namep)cp->datap; + cp->datap = 0; + amp = "_subscr"; + if (!(eb = var->vsubscrused)) { + var->vrefused = 0; + if (!ISCOMPLEX(var->vtype)) + amp = "_ref"; + } + def_start(outfile, var->cvarname, amp, CNULL); + dimp = var->vdim; + vl = 0; + comma = "("; + amp = ""; + if (var->vtype == TYCHAR) { + amp = "&"; + vl = var->vleng; + if (ISCONST(vl) && vl->constblock.Const.ci == 1) + vl = 0; + nice_printf(outfile, "%sa_0", comma); + comma = ","; + } + n = dimp->ndim; + for(i = 1; i <= n; i++, comma = ",") + nice_printf(outfile, "%sa_%d", comma, i); + nice_printf(outfile, ") %s", amp); + if (var->vsubscrused) + var->vsubscrused = 0; + else if (!ISCOMPLEX(var->vtype)) { + out_name(outfile, var); + nice_printf(outfile, "[%s", vl ? "(" : ""); + } + for(j = 2; j < n; j++) + nice_printf(outfile, "("); + while(--i > 1) { + nice_printf(outfile, "(a_%d)%s*", i, i == n ? "" : ")"); + expr_out(outfile, cpexpr(dimp->dims[i-2].dimsize)); + nice_printf(outfile, " + "); + } + nice_printf(outfile, "a_1"); + if (var->vtype == TYCHAR) { + if (vl) { + nice_printf(outfile, ")*"); + expr_out(outfile, cpexpr(vl)); + } + nice_printf(outfile, " + a_0"); + } + if (var->vstg != STGARG && (b = dimp->baseoffset)) { + b = cpexpr(b); + if (var->vtype == TYCHAR) + b = mkexpr(OPSTAR, cpexpr(var->vleng), b); + nice_printf(outfile, " - "); + expr_out(outfile, b); + } + if (ISCOMPLEX(var->vtype)) { + ind_printf(0, outfile, "\n"); + def_start(outfile, var->cvarname, "_ref", CNULL); + comma = "("; + for(i = 1; i <= n; i++, comma = ",") + nice_printf(outfile, "%sa_%d", comma, i); + nice_printf(outfile, ") %s[%s_subscr", + var->cvarname, var->cvarname); + comma = "("; + for(i = 1; i <= n; i++, comma = ",") + nice_printf(outfile, "%sa_%d", comma, i); + nice_printf(outfile, ")"); + } + ind_printf(0, outfile, "]\n" + eb); + } + nice_printf(outfile, "\n"); + frchain(&refdefs); + } + +list_decls (outfile) +FILE *outfile; +{ + extern chainp used_builtins; + extern struct Hashentry *hashtab; + extern ftnint wr_char_len(); + struct Hashentry *entry; + int write_header = 1; + int last_class = -1, last_stg = -1; + Namep var; + int Alias, Define, did_one, last_type, type; + extern int def_equivs, useauto; + extern chainp new_vars; /* Compiler-generated locals */ + chainp namelists = 0, refdefs = 0; + char *ctype; + int useauto1 = useauto && !saveall; + long x; + extern int hsize; + +/* First write out the statically initialized data */ + + if (initfile) + list_init_data(&initfile, initfname, outfile); + +/* Next come formats */ + write_formats(outfile); + +/* Now write out the system-generated identifiers */ + + if (new_vars || nequiv) { + chainp args, next_var, this_var; + chainp nv[TYVOID], nv1[TYVOID]; + int i, j; + Addrp Var; + Namep arg; + + /* zap unused dimension variables */ + + for(args = allargs; args; args = args->nextp) { + arg = (Namep)args->datap; + if (this_var = arg->vlastdim) { + frexpr((tagptr)this_var->datap); + this_var->datap = 0; + } + } + + /* sort new_vars by type, skipping entries just zapped */ + + for(i = TYADDR; i < TYVOID; i++) + nv[i] = 0; + for(this_var = new_vars; this_var; this_var = next_var) { + next_var = this_var->nextp; + if (Var = (Addrp)this_var->datap) { + if (!(this_var->nextp = nv[j = Var->vtype])) + nv1[j] = this_var; + nv[j] = this_var; + } + else { + this_var->nextp = 0; + frchain(&this_var); + } + } + new_vars = 0; + for(i = TYVOID; --i >= TYADDR;) + if (this_var = nv[i]) { + nv1[i]->nextp = new_vars; + new_vars = this_var; + } + + /* write the declarations */ + + did_one = 0; + last_type = -1; + + for (this_var = new_vars; this_var; this_var = this_var -> nextp) { + Var = (Addrp) this_var->datap; + + if (Var == (Addrp) NULL) + err ("list_decls: null variable"); + else if (Var -> tag != TADDR) + erri ("list_decls: bad tag on new variable '%d'", + Var -> tag); + + type = nv_type (Var); + if (Var->vstg == STGINIT + || Var->uname_tag == UNAM_IDENT + && *Var->user.ident == ' ' + && multitype) + continue; + if (!did_one) + nice_printf (outfile, "/* System generated locals */\n"); + + if (last_type == type && did_one) + nice_printf (outfile, ", "); + else { + if (did_one) + nice_printf (outfile, ";\n"); + nice_printf (outfile, "%s ", + c_type_decl (type, Var -> vclass == CLPROC)); + } /* else */ + +/* Character type is really a string type. Put out a '*' for parameters + with unknown length and functions returning character */ + + if (Var -> vtype == TYCHAR && (!ISICON ((Var -> vleng)) + || Var -> vclass == CLPROC)) + nice_printf (outfile, "*"); + + write_nv_ident(outfile, (Addrp)this_var->datap); + if (Var -> vtype == TYCHAR && Var->vclass != CLPROC && + ISICON((Var -> vleng)) + && (i = Var->vleng->constblock.Const.ci) > 0) + nice_printf (outfile, "[%d]", i); + + did_one = 1; + last_type = nv_type (Var); + } /* for this_var */ + +/* Handle the uninitialized equivalences */ + + do_uninit_equivs (outfile, &did_one); + + if (did_one) + nice_printf (outfile, ";\n\n"); + } /* if new_vars */ + +/* Write out builtin declarations */ + + if (used_builtins) { + chainp cp; + Extsym *es; + + last_type = -1; + did_one = 0; + + nice_printf (outfile, "/* Builtin functions */"); + + for (cp = used_builtins; cp; cp = cp -> nextp) { + Addrp e = (Addrp)cp->datap; + + switch(type = e->vtype) { + case TYDREAL: + case TYREAL: + /* if (forcedouble || e->dbl_builtin) */ + /* libF77 currently assumes everything double */ + type = TYDREAL; + ctype = "double"; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + type = TYVOID; + /* no break */ + default: + ctype = c_type_decl(type, 0); + } + + if (did_one && last_type == type) + nice_printf(outfile, ", "); + else + nice_printf(outfile, "%s\n%s ", did_one ? ";" : "", ctype); + + extern_out(outfile, es = &extsymtab[e -> memno]); + proto(outfile, es->arginfo, es->fextname); + last_type = type; + did_one = 1; + } /* for cp = used_builtins */ + + nice_printf (outfile, ";\n\n"); + } /* if used_builtins */ + + last_type = -1; + for (entry = hashtab; entry < lasthash; ++entry) { + var = entry -> varp; + + if (var) { + int procclass = var -> vprocclass; + char *comment = NULL; + int stg = var -> vstg; + int class = var -> vclass; + type = var -> vtype; + + if (var->vrefused) + refdefs = mkchain((char *)var, refdefs); + if (var->vsubscrused) + if (ISCOMPLEX(var->vtype)) + var->vsubscrused = 0; + else + refdefs = mkchain((char *)var, refdefs); + if (ONEOF(stg, M(STGARG)|M(STGLENG)|M(STGINIT))) + continue; + + if (useauto1 && stg == STGBSS && !var->vsave) + stg = STGAUTO; + + switch (class) { + case CLVAR: + break; + case CLPROC: + switch(procclass) { + case PTHISPROC: + extsymtab[var->vardesc.varno].extype = type; + continue; + case PSTFUNCT: + case PINTRINSIC: + continue; + case PUNKNOWN: + err ("list_decls: unknown procedure class"); + continue; + case PEXTERNAL: + if (stg == STGUNKNOWN) { + warn1( + "%.64s declared EXTERNAL but never used.", + var->fvarname); + /* to retain names declared EXTERNAL */ + /* but not referenced, change + /* "continue" to "stg = STGEXT" */ + continue; + } + else + type = fixexttype(var); + } + break; + case CLUNKNOWN: + /* declared but never used */ + continue; + case CLPARAM: + continue; + case CLNAMELIST: + if (var->visused) + namelists = mkchain((char *)var, namelists); + continue; + default: + erri("list_decls: can't handle class '%d' yet", + class); + Fatal(var->fvarname); + continue; + } /* switch */ + + /* Might be equivalenced to a common. If not, don't process */ + if (stg == STGCOMMON && !var->vcommequiv) + continue; + +/* Only write the header if system-generated locals, builtins, or + uninitialized equivs were already output */ + + if (write_header == 1 && (new_vars || nequiv || used_builtins) + && oneof_stg ( var, stg, + M(STGBSS)|M(STGEXT)|M(STGAUTO)|M(STGCOMMON)|M(STGEQUIV))) { + nice_printf (outfile, "/* Local variables */\n"); + write_header = 2; + } + + + Alias = oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON)); + if (Define = (Alias && def_equivs)) { + if (!write_header) + nice_printf(outfile, ";\n"); + def_start(outfile, var->cvarname, CNULL, "("); + goto Alias1; + } + else if (type == last_type && class == last_class && + stg == last_stg && !write_header) + nice_printf (outfile, ", "); + else { + if (!write_header && ONEOF(stg, M(STGBSS)| + M(STGEXT)|M(STGAUTO)|M(STGEQUIV)|M(STGCOMMON))) + nice_printf (outfile, ";\n"); + + switch (stg) { + case STGARG: + case STGLENG: + /* Part of the argument list, don't write them out + again */ + continue; /* Go back to top of the loop */ + case STGBSS: + case STGEQUIV: + case STGCOMMON: + nice_printf (outfile, "static "); + break; + case STGEXT: + nice_printf (outfile, "extern "); + break; + case STGAUTO: + break; + case STGINIT: + case STGUNKNOWN: + /* Don't want to touch the initialized data, that will + be handled elsewhere. Unknown data have + already been complained about, so skip them */ + continue; + default: + erri("list_decls: can't handle storage class %d", + stg); + continue; + } /* switch */ + + if (type == TYCHAR && halign && class != CLPROC + && ISICON(var->vleng)) { + nice_printf(outfile, "struct { %s fill; char val", + halign); + x = wr_char_len(outfile, var->vdim, + var->vleng->constblock.Const.ci, 1); + if (x %= hsize) + nice_printf(outfile, "; char fill2[%ld]", + hsize - x); + nice_printf(outfile, "; } %s_st;\n", var->cvarname); + def_start(outfile, var->cvarname, CNULL, var->cvarname); + ind_printf(0, outfile, "_st.val\n"); + last_type = -1; + write_header = 2; + continue; + } + nice_printf(outfile, "%s ", + c_type_decl(type, class == CLPROC)); + } /* else */ + +/* Character type is really a string type. Put out a '*' for variable + length strings, and also for equivalences */ + + if (type == TYCHAR && class != CLPROC + && (!var->vleng || !ISICON (var -> vleng)) + || oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON))) + nice_printf (outfile, "*%s", var->cvarname); + else { + nice_printf (outfile, "%s", var->cvarname); + if (class == CLPROC) { + Argtypes *at; + if (!(at = var->arginfo) + && var->vprocclass == PEXTERNAL) + at = extsymtab[var->vardesc.varno].arginfo; + proto(outfile, at, var->fvarname); + } + else if (type == TYCHAR && ISICON ((var -> vleng))) + wr_char_len(outfile, var->vdim, + (int)var->vleng->constblock.Const.ci, 0); + else if (var -> vdim && + !oneof_stg (var, stg, M(STGEQUIV)|M(STGCOMMON))) + comment = wr_ardecls(outfile, var->vdim, 1L); + } + + if (comment) + nice_printf (outfile, "%s", comment); + Alias1: + if (Alias) { + char *amp, *lp, *name, *rp; + char *equiv_name (); + ftnint voff = var -> voffset; + int et0, expr_type, k; + Extsym *E; + struct Equivblock *eb; + char buf[16]; + +/* We DON'T want to use oneof_stg here, because we need to distinguish + between them */ + + if (stg == STGEQUIV) { + name = equiv_name(k = var->vardesc.varno, CNULL); + eb = eqvclass + k; + if (eb->eqvinit) { + amp = "&"; + et0 = TYERROR; + } + else { + amp = ""; + et0 = eb->eqvtype; + } + expr_type = et0; + } + else { + E = &extsymtab[var->vardesc.varno]; + sprintf(name = buf, "%s%d", E->cextname, E->curno); + expr_type = type; + et0 = -1; + amp = "&"; + } /* else */ + + if (!Define) + nice_printf (outfile, " = "); + if (voff) { + k = typesize[type]; + switch((int)(voff % k)) { + case 0: + voff /= k; + expr_type = type; + break; + case SZSHORT: + case SZSHORT+SZLONG: + expr_type = TYSHORT; + voff /= SZSHORT; + break; + case SZLONG: + expr_type = TYLONG; + voff /= SZLONG; + break; + default: + expr_type = TYCHAR; + } + } + + if (expr_type == type) { + lp = rp = ""; + if (et0 == -1 && !voff) + goto cast; + } + else { + lp = "("; + rp = ")"; + cast: + nice_printf(outfile, "(%s *)", c_type_decl(type, 0)); + } + +/* Now worry about computing the offset */ + + if (voff) { + if (expr_type == et0) + nice_printf (outfile, "%s%s + %ld%s", + lp, name, voff, rp); + else + nice_printf(outfile, "%s(%s *)%s%s + %ld%s", lp, + c_type_decl (expr_type, 0), amp, + name, voff, rp); + } else + nice_printf(outfile, "%s%s", amp, name); +/* Always put these at the end of the line */ + last_type = last_class = last_stg = -1; + write_header = 0; + if (Define) { + ind_printf(0, outfile, ")\n"); + write_header = 2; + } + continue; + } + write_header = 0; + last_type = type; + last_class = class; + last_stg = stg; + } /* if (var) */ + } /* for (entry = hashtab */ + + if (!write_header) + nice_printf (outfile, ";\n\n"); + else if (write_header == 2) + nice_printf(outfile, "\n"); + +/* Next, namelists, which may reference equivs */ + + if (namelists) { + write_namelists(namelists = revchain(namelists), outfile); + frchain(&namelists); + } + +/* Finally, ioblocks (which may reference equivs and namelists) */ + if (iob_list) + write_ioblocks(outfile); + if (assigned_fmts) + write_assigned_fmts(outfile); + + if (refdefs) + ref_defs(outfile, refdefs); + +} /* list_decls */ + +do_uninit_equivs (outfile, did_one) +FILE *outfile; +int *did_one; +{ + extern int nequiv; + struct Equivblock *eqv, *lasteqv = eqvclass + nequiv; + int k, last_type = -1, t; + + for (eqv = eqvclass; eqv < lasteqv; eqv++) + if (!eqv -> eqvinit && eqv -> eqvtop != eqv -> eqvbottom) { + if (!*did_one) + nice_printf (outfile, "/* System generated locals */\n"); + t = eqv->eqvtype; + if (last_type == t) + nice_printf (outfile, ", "); + else { + if (*did_one) + nice_printf (outfile, ";\n"); + nice_printf (outfile, "static %s ", c_type_decl(t, 0)); + k = typesize[t]; + } /* else */ + nice_printf(outfile, "%s", equiv_name((int)(eqv - eqvclass), CNULL)); + nice_printf(outfile, "[%ld]", + (eqv->eqvtop - eqv->eqvbottom + k - 1) / k); + last_type = t; + *did_one = 1; + } /* if !eqv -> eqvinit */ +} /* do_uninit_equivs */ + + +/* wr_ardecls -- Writes the brackets and size for an array + declaration. Because of the inner workings of the compiler, + multi-dimensional arrays get mapped directly into a one-dimensional + array, so we have to compute the size of the array here. When the + dimension is greater than 1, a string comment about the original size + is returned */ + +char *wr_ardecls(outfile, dimp, size) +FILE *outfile; +struct Dimblock *dimp; +long size; +{ + int i, k; + static char buf[1000]; + + if (dimp == (struct Dimblock *) NULL) + return NULL; + + sprintf(buf, "\t/* was "); /* would like to say k = sprintf(...), but */ + k = strlen(buf); /* BSD doesn't return char transmitted count */ + + for (i = 0; i < dimp -> ndim; i++) { + expptr this_size = dimp -> dims[i].dimsize; + + if (!ISICON (this_size)) + err ("wr_ardecls: nonconstant array size"); + else { + size *= this_size -> constblock.Const.ci; + sprintf(buf+k, "[%ld]", this_size -> constblock.Const.ci); + k += strlen(buf+k); /* BSD prevents combining this with prev stmt */ + } /* else */ + } /* for i = 0 */ + + nice_printf (outfile, "[%ld]", size); + strcat(buf+k, " */"); + + return (i > 1) ? buf : NULL; +} /* wr_ardecls */ + + + +/* ---------------------------------------------------------------------- + + The following routines read from the p1 intermediate file. If + that format changes, only these routines need be changed + + ---------------------------------------------------------------------- */ + +static int get_p1_token (infile) +FILE *infile; +{ + int token = P1_UNKNOWN; + +/* NOT PORTABLE!! */ + + if (fscanf (infile, "%d", &token) == EOF) + return P1_EOF; + +/* Skip over the ": " */ + + if (getc (infile) != '\n') + getc (infile); + + return token; +} /* get_p1_token */ + + + +/* Returns a (null terminated) string from the input file */ + +static int p1gets (fp, str, size) +FILE *fp; +char *str; +int size; +{ + char *fgets (); + char c; + + if (str == NULL) + return 0; + + if ((c = getc (fp)) != ' ') + ungetc (c, fp); + + if (fgets (str, size, fp)) { + int length; + + str[size - 1] = '\0'; + length = strlen (str); + +/* Get rid of the newline */ + + if (str[length - 1] == '\n') + str[length - 1] = '\0'; + return 1; + + } else if (feof (fp)) + return EOF; + else + return 0; +} /* p1gets */ + + +static int p1get_const (infile, type, resultp) +FILE *infile; +int type; +struct Constblock **resultp; +{ + int status; + struct Constblock *result; + + if (type != TYCHAR) { + *resultp = result = ALLOC(Constblock); + result -> tag = TCONST; + result -> vtype = type; + } + + switch (type) { + case TYINT1: + case TYSHORT: + case TYLONG: + case TYLOGICAL: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYLOGICAL1: + case TYLOGICAL2: + status = p1getd (infile, &(result -> Const.ci)); + break; + case TYREAL: + case TYDREAL: + status = p1getf(infile, &result->Const.cds[0]); + result->vstg = 1; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + status = p1getf(infile, &result->Const.cds[0]); + if (status && status != EOF) + status = p1getf(infile, &result->Const.cds[1]); + result->vstg = 1; + break; + case TYCHAR: + status = fscanf(infile, "%lx", resultp); + break; + default: + erri ("p1get_const: bad constant type '%d'", type); + status = 0; + break; + } /* switch */ + + return status; +} /* p1get_const */ + +static int p1getd (infile, result) +FILE *infile; +long *result; +{ + return fscanf (infile, "%ld", result); +} /* p1getd */ + + static int +p1getf(infile, result) + FILE *infile; + char **result; +{ + + char buf[1324]; + register int k; + + k = fscanf (infile, "%s", buf); + if (k < 1) + k = EOF; + else + strcpy(*result = mem(strlen(buf)+1,0), buf); + return k; +} + +static int p1getn (infile, count, result) +FILE *infile; +int count; +char **result; +{ + + char *bufptr; + extern ptr ckalloc (); + + bufptr = (char *) ckalloc (count); + + if (result) + *result = bufptr; + + for (; !feof (infile) && count > 0; count--) + *bufptr++ = getc (infile); + + return feof (infile) ? EOF : 1; +} /* p1getn */ + + static void +proto(outfile, at, fname) + FILE *outfile; + Argtypes *at; + char *fname; +{ + int i, j, k, n; + char *comma; + Atype *atypes; + Namep np; + chainp cp; + extern void bad_atypes(); + + if (at) { + /* Correct types that we learn on the fly, e.g. + subroutine gotcha(foo) + external foo + call zap(...,foo,...) + call foo(...) + */ + atypes = at->atypes; + n = at->defined ? at->dnargs : at->nargs; + for(i = 0; i++ < n; atypes++) { + if (!(cp = atypes->cp)) + continue; + j = atypes->type; + do { + np = (Namep)cp->datap; + k = np->vtype; + if (np->vclass == CLPROC) { + if (!np->vimpltype && k) + k += 200; + else { + if (j >= 300) + j = TYUNKNOWN + 200; + continue; + } + } + if (j == k) + continue; + if (j >= 300 + || j == 200 && k >= 200) + j = k; + else { + if (at->nargs >= 0) + bad_atypes(at,fname,i,j,k,""," and"); + goto break2; + } + } + while(cp = cp->nextp); + atypes->type = j; + frchain(&atypes->cp); + } + } + break2: + if (parens) { + nice_printf(outfile, parens); + return; + } + + if (!at || (n = at-> defined ? at->dnargs : at->nargs) < 0) { + nice_printf(outfile, Ansi == 1 ? "()" : "(...)"); + return; + } + + if (n == 0) { + nice_printf(outfile, Ansi == 1 ? "(void)" : "()"); + return; + } + + atypes = at->atypes; + nice_printf(outfile, "("); + comma = ""; + for(; --n >= 0; atypes++) { + k = atypes->type; + if (k == TYADDR) + nice_printf(outfile, "%schar **", comma); + else if (k >= 200) { + k -= 200; + nice_printf(outfile, "%s%s", comma, + usedcasts[k] = casttypes[k]); + } + else if (k >= 100) + nice_printf(outfile, + k == TYCHAR + 100 ? "%s%s *" : "%s%s", + comma, c_type_decl(k-100, 0)); + else + nice_printf(outfile, "%s%s *", comma, + c_type_decl(k, 0)); + comma = ", "; + } + nice_printf(outfile, ")"); + } + + void +protowrite(protofile, type, name, e, lengths) + FILE *protofile; + char *name; + struct Entrypoint *e; + chainp lengths; +{ + extern char used_rets[]; + int asave; + + if (!(asave = Ansi)) + Castargs = Ansi = 1; + nice_printf(protofile, "extern %s %s", protorettypes[type], name); + list_arg_types(protofile, e, lengths, 0, ";\n"); + used_rets[type] = 1; + if (!(Ansi = asave)) + Castargs = 0; + } + + static void +do_p1_1while(outfile) + FILE *outfile; +{ + if (*wh_next) { + nice_printf(outfile, + "for(;;) { /* while(complicated condition) */\n" /*}*/ ); + next_tab(outfile); + } + else + nice_printf(outfile, "while(" /*)*/ ); + } + + static void +do_p1_2while(infile, outfile) + FILE *infile, *outfile; +{ + expptr test; + + test = do_format(infile, outfile); + if (*wh_next) + nice_printf(outfile, "if (!("); + expr_out(outfile, test); + if (*wh_next++) + nice_printf(outfile, "))\n\tbreak;\n"); + else { + nice_printf(outfile, /*(*/ ") {\n"); + next_tab(outfile); + } + } + + static void +do_p1_elseifstart(outfile) + FILE *outfile; +{ + if (*ei_next++) { + prev_tab(outfile); + nice_printf(outfile, /*{*/ + "} else /* if(complicated condition) */ {\n" /*}*/ ); + next_tab(outfile); + } + } diff --git a/usr.bin/f2c/format.h b/usr.bin/f2c/format.h new file mode 100644 index 000000000000..a88c038929f5 --- /dev/null +++ b/usr.bin/f2c/format.h @@ -0,0 +1,10 @@ +#define DEF_C_LINE_LENGTH 77 +/* actual max will be 79 */ + +extern int c_output_line_length; /* max # chars per line in C source + code */ + +char *wr_ardecls (/* FILE *, struct Dimblock * */); +void list_init_data (), wr_one_init (), wr_output_values (); +int do_init_data (); +chainp data_value (); diff --git a/usr.bin/f2c/formatdata.c b/usr.bin/f2c/formatdata.c new file mode 100644 index 000000000000..541472a9400e --- /dev/null +++ b/usr.bin/f2c/formatdata.c @@ -0,0 +1,1081 @@ +/**************************************************************** +Copyright 1990, 1991, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "output.h" +#include "names.h" +#include "format.h" + +#define MAX_INIT_LINE 100 +#define NAME_MAX 64 + +static int memno2info(); + +extern char *initbname; +extern void def_start(); + +void list_init_data(Infile, Inname, outfile) + FILE **Infile, *outfile; + char *Inname; +{ + FILE *sortfp; + int status; + + fclose(*Infile); + *Infile = 0; + + if (status = dsort(Inname, sortfname)) + fatali ("sort failed, status %d", status); + + scrub(Inname); /* optionally unlink Inname */ + + if ((sortfp = fopen(sortfname, textread)) == NULL) + Fatal("Couldn't open sorted initialization data"); + + do_init_data(outfile, sortfp); + fclose(sortfp); + scrub(sortfname); + +/* Insert a blank line after any initialized data */ + + nice_printf (outfile, "\n"); + + if (debugflag && infname) + /* don't back block data file up -- it won't be overwritten */ + backup(initfname, initbname); +} /* list_init_data */ + + + +/* do_init_data -- returns YES when at least one declaration has been + written */ + +int do_init_data(outfile, infile) +FILE *outfile, *infile; +{ + char varname[NAME_MAX], ovarname[NAME_MAX]; + ftnint offset; + ftnint type; + int vargroup; /* 0 --> init, 1 --> equiv, 2 --> common */ + int did_one = 0; /* True when one has been output */ + chainp values = CHNULL; /* Actual data values */ + int keepit = 0; + Namep np; + + ovarname[0] = '\0'; + + while (rdname (infile, &vargroup, varname) && rdlong (infile, &offset) + && rdlong (infile, &type)) { + if (strcmp (varname, ovarname)) { + + /* If this is a new variable name, the old initialization has been + completed */ + + wr_one_init(outfile, ovarname, &values, keepit); + + strcpy (ovarname, varname); + values = CHNULL; + if (vargroup == 0) { + if (memno2info(atoi(varname+2), &np)) { + if (((Addrp)np)->uname_tag != UNAM_NAME) { + err("do_init_data: expected NAME"); + goto Keep; + } + np = ((Addrp)np)->user.name; + } + if (!(keepit = np->visused) && !np->vimpldovar) + warn1("local variable %s never used", + np->fvarname); + } + else { + Keep: + keepit = 1; + } + if (keepit && !did_one) { + nice_printf (outfile, "/* Initialized data */\n\n"); + did_one = YES; + } + } /* if strcmp */ + + values = mkchain((char *)data_value(infile, offset, (int)type), values); + } /* while */ + +/* Write out the last declaration */ + + wr_one_init (outfile, ovarname, &values, keepit); + + return did_one; +} /* do_init_data */ + + + ftnint +wr_char_len(outfile, dimp, n, extra1) + FILE *outfile; + int n; + struct Dimblock *dimp; + int extra1; +{ + int i, nd; + expptr e; + ftnint rv; + + if (!dimp) { + nice_printf (outfile, extra1 ? "[%d+1]" : "[%d]", n); + return n + extra1; + } + nice_printf(outfile, "[%d", n); + nd = dimp->ndim; + rv = n; + for(i = 0; i < nd; i++) { + e = dimp->dims[i].dimsize; + if (!ISICON (e)) + err ("wr_char_len: nonconstant array size"); + else { + nice_printf(outfile, "*%ld", e->constblock.Const.ci); + rv *= e->constblock.Const.ci; + } + } + /* extra1 allows for stupid C compilers that complain about + * too many initializers in + * char x[2] = "ab"; + */ + nice_printf(outfile, extra1 ? "+1]" : "]"); + return extra1 ? rv+1 : rv; + } + + static int ch_ar_dim = -1; /* length of each element of char string array */ + static int eqvmemno; /* kludge */ + + static void +write_char_init(outfile, Values, namep) + FILE *outfile; + chainp *Values; + Namep namep; +{ + struct Equivblock *eqv; + long size; + struct Dimblock *dimp; + int i, nd, type; + expptr ds; + + if (!namep) + return; + if(nequiv >= maxequiv) + many("equivalences", 'q', maxequiv); + eqv = &eqvclass[nequiv]; + eqv->eqvbottom = 0; + type = namep->vtype; + size = type == TYCHAR + ? namep->vleng->constblock.Const.ci + : typesize[type]; + if (dimp = namep->vdim) + for(i = 0, nd = dimp->ndim; i < nd; i++) { + ds = dimp->dims[i].dimsize; + if (!ISICON(ds)) + err("write_char_values: nonconstant array size"); + else + size *= ds->constblock.Const.ci; + } + *Values = revchain(*Values); + eqv->eqvtop = size; + eqvmemno = ++lastvarno; + eqv->eqvtype = type; + wr_equiv_init(outfile, nequiv, Values, 0); + def_start(outfile, namep->cvarname, CNULL, ""); + if (type == TYCHAR) + ind_printf(0, outfile, "((char *)&equiv_%d)\n\n", eqvmemno); + else + ind_printf(0, outfile, dimp + ? "((%s *)&equiv_%d)\n\n" : "(*(%s *)&equiv_%d)\n\n", + c_type_decl(type,0), eqvmemno); + } + +/* wr_one_init -- outputs the initialization of the variable pointed to + by info. When is_addr is true, info is an Addrp; otherwise, + treat it as a Namep */ + +void wr_one_init (outfile, varname, Values, keepit) +FILE *outfile; +char *varname; +chainp *Values; +int keepit; +{ + static int memno; + static union { + Namep name; + Addrp addr; + } info; + Namep namep; + int is_addr, size, type; + ftnint last, loc; + int is_scalar = 0; + char *array_comment = NULL, *name; + chainp cp, values; + extern char datachar[]; + static int e1[3] = {1, 0, 1}; + ftnint x; + extern int hsize; + + if (!keepit) + goto done; + if (varname == NULL || varname[1] != '.') + goto badvar; + +/* Get back to a meaningful representation; find the given memno in one + of the appropriate tables (user-generated variables in the hash table, + system-generated variables in a separate list */ + + memno = atoi(varname + 2); + switch(varname[0]) { + case 'q': + /* Must subtract eqvstart when the source file + * contains more than one procedure. + */ + wr_equiv_init(outfile, eqvmemno = memno - eqvstart, Values, 0); + goto done; + case 'Q': + /* COMMON initialization (BLOCK DATA) */ + wr_equiv_init(outfile, memno, Values, 1); + goto done; + case 'v': + break; + default: + badvar: + errstr("wr_one_init: unknown variable name '%s'", varname); + goto done; + } + + is_addr = memno2info (memno, &info.name); + if (info.name == (Namep) NULL) { + err ("wr_one_init -- unknown variable"); + return; + } + if (is_addr) { + if (info.addr -> uname_tag != UNAM_NAME) { + erri ("wr_one_init -- couldn't get name pointer; tag is %d", + info.addr -> uname_tag); + namep = (Namep) NULL; + nice_printf (outfile, " /* bad init data */"); + } else + namep = info.addr -> user.name; + } else + namep = info.name; + + /* check for character initialization */ + + *Values = values = revchain(*Values); + type = info.name->vtype; + if (type == TYCHAR) { + for(last = 0; values; values = values->nextp) { + cp = (chainp)values->datap; + loc = (ftnint)cp->datap; + if (loc > last) { + write_char_init(outfile, Values, namep); + goto done; + } + last = (int)cp->nextp->datap == TYBLANK + ? loc + (int)cp->nextp->nextp->datap + : loc + 1; + } + if (halign && info.name->tag == TNAME) { + nice_printf(outfile, "static struct { %s fill; char val", + halign); + x = wr_char_len(outfile, namep->vdim, ch_ar_dim = + info.name -> vleng -> constblock.Const.ci, 1); + if (x %= hsize) + nice_printf(outfile, "; char fill2[%ld]", hsize - x); + name = info.name->cvarname; + nice_printf(outfile, "; } %s_st = { 0,", name); + wr_output_values(outfile, namep, *Values); + nice_printf(outfile, " };\n"); + ch_ar_dim = -1; + def_start(outfile, name, CNULL, name); + ind_printf(0, outfile, "_st.val\n"); + goto done; + } + } + else { + size = typesize[type]; + loc = 0; + for(; values; values = values->nextp) { + if ((int)((chainp)values->datap)->nextp->datap == TYCHAR) { + write_char_init(outfile, Values, namep); + goto done; + } + last = ((long) ((chainp) values->datap)->datap) / size; + if (last - loc > 4) { + write_char_init(outfile, Values, namep); + goto done; + } + loc = last; + } + } + values = *Values; + + nice_printf (outfile, "static %s ", c_type_decl (type, 0)); + + if (is_addr) + write_nv_ident (outfile, info.addr); + else + out_name (outfile, info.name); + + if (namep) + is_scalar = namep -> vdim == (struct Dimblock *) NULL; + + if (namep && !is_scalar) + array_comment = type == TYCHAR + ? 0 : wr_ardecls(outfile, namep->vdim, 1L); + + if (type == TYCHAR) + if (ISICON (info.name -> vleng)) + +/* We'll make single strings one character longer, so that we can use the + standard C initialization. All this does is pad an extra zero onto the + end of the string */ + wr_char_len(outfile, namep->vdim, ch_ar_dim = + info.name -> vleng -> constblock.Const.ci, e1[Ansi]); + else + err ("variable length character initialization"); + + if (array_comment) + nice_printf (outfile, "%s", array_comment); + + nice_printf (outfile, " = "); + wr_output_values (outfile, namep, values); + ch_ar_dim = -1; + nice_printf (outfile, ";\n"); + done: + frchain(Values); +} /* wr_one_init */ + + + + +chainp data_value (infile, offset, type) +FILE *infile; +ftnint offset; +int type; +{ + char line[MAX_INIT_LINE + 1], *pointer; + chainp vals, prev_val; +#ifndef atol + long atol(); +#endif + char *newval; + + if (fgets (line, MAX_INIT_LINE, infile) == NULL) { + err ("data_value: error reading from intermediate file"); + return CHNULL; + } /* if fgets */ + +/* Get rid of the trailing newline */ + + if (line[0]) + line[strlen (line) - 1] = '\0'; + +#define iswhite(x) (isspace (x) || (x) == ',') + + pointer = line; + prev_val = vals = CHNULL; + + while (*pointer) { + register char *end_ptr, old_val; + +/* Move pointer to the start of the next word */ + + while (*pointer && iswhite (*pointer)) + pointer++; + if (*pointer == '\0') + break; + +/* Move end_ptr to the end of the current word */ + + for (end_ptr = pointer + 1; *end_ptr && !iswhite (*end_ptr); + end_ptr++) + ; + + old_val = *end_ptr; + *end_ptr = '\0'; + +/* Add this value to the end of the list */ + + if (ONEOF(type, MSKREAL|MSKCOMPLEX)) + newval = cpstring(pointer); + else + newval = (char *)atol(pointer); + if (vals) { + prev_val->nextp = mkchain(newval, CHNULL); + prev_val = prev_val -> nextp; + } else + prev_val = vals = mkchain(newval, CHNULL); + *end_ptr = old_val; + pointer = end_ptr; + } /* while *pointer */ + + return mkchain((char *)offset, mkchain((char *)LONG_CAST type, vals)); +} /* data_value */ + + static void +overlapping() +{ + extern char *filename0; + static int warned = 0; + + if (warned) + return; + warned = 1; + + fprintf(stderr, "Error"); + if (filename0) + fprintf(stderr, " in file %s", filename0); + fprintf(stderr, ": overlapping initializations\n"); + nerr++; + } + + static void make_one_const(); + static long charlen; + +void wr_output_values (outfile, namep, values) +FILE *outfile; +Namep namep; +chainp values; +{ + int type = TYUNKNOWN; + struct Constblock Const; + static expptr Vlen; + + if (namep) + type = namep -> vtype; + +/* Handle array initializations away from scalars */ + + if (namep && namep -> vdim) + wr_array_init (outfile, namep -> vtype, values); + + else if (values->nextp && type != TYCHAR) + overlapping(); + + else { + make_one_const(type, &Const.Const, values); + Const.vtype = type; + Const.vstg = ONEOF(type, MSKREAL|MSKCOMPLEX) != 0; + if (type== TYCHAR) { + if (!Vlen) + Vlen = ICON(0); + Const.vleng = Vlen; + Vlen->constblock.Const.ci = charlen; + out_const (outfile, &Const); + free (Const.Const.ccp); + } + else + out_const (outfile, &Const); + } + } + + +wr_array_init (outfile, type, values) +FILE *outfile; +int type; +chainp values; +{ + int size = typesize[type]; + long index, main_index = 0; + int k; + + if (type == TYCHAR) { + nice_printf(outfile, "\""); + k = 0; + if (Ansi != 1) + ch_ar_dim = -1; + } + else + nice_printf (outfile, "{ "); + while (values) { + struct Constblock Const; + + index = ((long) ((chainp) values->datap)->datap) / size; + while (index > main_index) { + +/* Fill with zeros. The structure shorthand works because the compiler + will expand the "0" in braces to fill the size of the entire structure + */ + + switch (type) { + case TYREAL: + case TYDREAL: + nice_printf (outfile, "0.0,"); + break; + case TYCOMPLEX: + case TYDCOMPLEX: + nice_printf (outfile, "{0},"); + break; + case TYCHAR: + nice_printf(outfile, " "); + break; + default: + nice_printf (outfile, "0,"); + break; + } /* switch */ + main_index++; + } /* while index > main_index */ + + if (index < main_index) + overlapping(); + else switch (type) { + case TYCHAR: + { int this_char; + + if (k == ch_ar_dim) { + nice_printf(outfile, "\" \""); + k = 0; + } + this_char = (int) ((chainp) values->datap)-> + nextp->nextp->datap; + if ((int)((chainp)values->datap)->nextp->datap == TYBLANK) { + main_index += this_char; + k += this_char; + while(--this_char >= 0) + nice_printf(outfile, " "); + values = values -> nextp; + continue; + } + nice_printf(outfile, str_fmt[this_char], this_char); + k++; + } /* case TYCHAR */ + break; + + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYREAL: + case TYDREAL: + case TYLOGICAL: + case TYLOGICAL1: + case TYLOGICAL2: + case TYCOMPLEX: + case TYDCOMPLEX: + make_one_const(type, &Const.Const, values); + Const.vtype = type; + Const.vstg = ONEOF(type, MSKREAL|MSKCOMPLEX) != 0; + out_const(outfile, &Const); + break; + default: + erri("wr_array_init: bad type '%d'", type); + break; + } /* switch */ + values = values->nextp; + + main_index++; + if (values && type != TYCHAR) + nice_printf (outfile, ","); + } /* while values */ + + if (type == TYCHAR) { + nice_printf(outfile, "\""); + } + else + nice_printf (outfile, " }"); +} /* wr_array_init */ + + + static void +make_one_const(type, storage, values) + int type; + union Constant *storage; + chainp values; +{ + union Constant *Const; + register char **L; + + if (type == TYCHAR) { + char *str, *str_ptr; + chainp v, prev; + int b = 0, k, main_index = 0; + +/* Find the max length of init string, by finding the highest offset + value stored in the list of initial values */ + + for(k = 1, prev = CHNULL, v = values; v; prev = v, v = v->nextp) + ; + if (prev != CHNULL) + k = ((int) (((chainp) prev->datap)->datap)) + 2; + /* + 2 above for null char at end */ + str = Alloc (k); + for (str_ptr = str; values; str_ptr++) { + int index = (int) (((chainp) values->datap)->datap); + + if (index < main_index) + overlapping(); + while (index > main_index++) + *str_ptr++ = ' '; + + k = (int) (((chainp) values->datap)->nextp->nextp->datap); + if ((int)((chainp)values->datap)->nextp->datap == TYBLANK) { + b = k; + break; + } + *str_ptr = k; + values = values -> nextp; + } /* for str_ptr */ + *str_ptr = '\0'; + Const = storage; + Const -> ccp = str; + Const -> ccp1.blanks = b; + charlen = str_ptr - str; + } else { + int i = 0; + chainp vals; + + vals = ((chainp)values->datap)->nextp->nextp; + if (vals) { + L = (char **)storage; + do L[i++] = vals->datap; + while(vals = vals->nextp); + } + + } /* else */ + +} /* make_one_const */ + + + +rdname (infile, vargroupp, name) +FILE *infile; +int *vargroupp; +char *name; +{ + register int i, c; + + c = getc (infile); + + if (feof (infile)) + return NO; + + *vargroupp = c - '0'; + for (i = 1;; i++) { + if (i >= NAME_MAX) + Fatal("rdname: oversize name"); + c = getc (infile); + if (feof (infile)) + return NO; + if (c == '\t') + break; + *name++ = c; + } + *name = 0; + return YES; +} /* rdname */ + +rdlong (infile, n) +FILE *infile; +ftnint *n; +{ + register int c; + + for (c = getc (infile); !feof (infile) && isspace (c); c = getc (infile)) + ; + + if (feof (infile)) + return NO; + + for (*n = 0; isdigit (c); c = getc (infile)) + *n = 10 * (*n) + c - '0'; + return YES; +} /* rdlong */ + + + static int +memno2info (memno, info) + int memno; + Namep *info; +{ + chainp this_var; + extern chainp new_vars; + extern struct Hashentry *hashtab, *lasthash; + struct Hashentry *entry; + + for (this_var = new_vars; this_var; this_var = this_var -> nextp) { + Addrp var = (Addrp) this_var->datap; + + if (var == (Addrp) NULL) + Fatal("memno2info: null variable"); + else if (var -> tag != TADDR) + Fatal("memno2info: bad tag"); + if (memno == var -> memno) { + *info = (Namep) var; + return 1; + } /* if memno == var -> memno */ + } /* for this_var = new_vars */ + + for (entry = hashtab; entry < lasthash; ++entry) { + Namep var = entry -> varp; + + if (var && var -> vardesc.varno == memno && var -> vstg == STGINIT) { + *info = (Namep) var; + return 0; + } /* if entry -> vardesc.varno == memno */ + } /* for entry = hashtab */ + + Fatal("memno2info: couldn't find memno"); + return 0; +} /* memno2info */ + + static chainp +do_string(outfile, v, nloc) + FILEP outfile; + register chainp v; + ftnint *nloc; +{ + register chainp cp, v0; + ftnint dloc, k, loc; + unsigned long uk; + char buf[8], *comma; + + nice_printf(outfile, "{"); + cp = (chainp)v->datap; + loc = (ftnint)cp->datap; + comma = ""; + for(v0 = v;;) { + switch((int)cp->nextp->datap) { + case TYBLANK: + k = (ftnint)cp->nextp->nextp->datap; + loc += k; + while(--k >= 0) { + nice_printf(outfile, "%s' '", comma); + comma = ", "; + } + break; + case TYCHAR: + uk = (ftnint)cp->nextp->nextp->datap; + sprintf(buf, chr_fmt[uk], uk); + nice_printf(outfile, "%s'%s'", comma, buf); + comma = ", "; + loc++; + break; + default: + goto done; + } + v0 = v; + if (!(v = v->nextp)) + break; + cp = (chainp)v->datap; + dloc = (ftnint)cp->datap; + if (loc != dloc) + break; + } + done: + nice_printf(outfile, "}"); + *nloc = loc; + return v0; + } + + static chainp +Ado_string(outfile, v, nloc) + FILEP outfile; + register chainp v; + ftnint *nloc; +{ + register chainp cp, v0; + ftnint dloc, k, loc; + + nice_printf(outfile, "\""); + cp = (chainp)v->datap; + loc = (ftnint)cp->datap; + for(v0 = v;;) { + switch((int)cp->nextp->datap) { + case TYBLANK: + k = (ftnint)cp->nextp->nextp->datap; + loc += k; + while(--k >= 0) + nice_printf(outfile, " "); + break; + case TYCHAR: + k = (ftnint)cp->nextp->nextp->datap; + nice_printf(outfile, str_fmt[k], k); + loc++; + break; + default: + goto done; + } + v0 = v; + if (!(v = v->nextp)) + break; + cp = (chainp)v->datap; + dloc = (ftnint)cp->datap; + if (loc != dloc) + break; + } + done: + nice_printf(outfile, "\""); + *nloc = loc; + return v0; + } + + static char * +Len(L,type) + long L; + int type; +{ + static char buf[24]; + if (L == 1 && type != TYCHAR) + return ""; + sprintf(buf, "[%ld]", L); + return buf; + } + +wr_equiv_init(outfile, memno, Values, iscomm) + FILE *outfile; + int memno; + chainp *Values; + int iscomm; +{ + struct Equivblock *eqv; + char *equiv_name (); + int btype, curtype, dtype, filltype, filltype1, j, k, wasblank, xtype; + static char Blank[] = ""; + register char *comma = Blank; + register chainp cp, v; + chainp sentinel, values, v1, vlast; + ftnint L, L1, dL, dloc, loc, loc0; + union Constant Const; + char imag_buf[50], real_buf[50]; + int szshort = typesize[TYSHORT]; + static char typepref[] = {0, 0, TYINT1, TYSHORT, TYLONG, +#ifdef TYQUAD + TYQUAD, +#endif + TYREAL, TYDREAL, TYREAL, TYDREAL, + TYLOGICAL1, TYLOGICAL2, + TYLOGICAL, TYCHAR}; + static char basetype[] = {0, 0, TYCHAR, TYSHORT, TYLONG, +#ifdef TYQUAD + TYDREAL, +#endif + TYLONG, TYDREAL, TYLONG, TYDREAL, + TYCHAR, TYSHORT, + TYLONG, TYCHAR}; + extern int htype; + char *z; + + /* add sentinel */ + if (iscomm) { + L = extsymtab[memno].maxleng; + xtype = extsymtab[memno].extype; + } + else { + eqv = &eqvclass[memno]; + L = eqv->eqvtop - eqv->eqvbottom; + xtype = eqv->eqvtype; + } + + if (halign && typealign[typepref[xtype]] < typealign[htype]) + xtype = htype; + *Values = values = revchain(vlast = *Values); + + if (xtype != TYCHAR) { + + /* unless the data include a value of the appropriate + * type, we add an extra element in an attempt + * to force correct alignment */ + + btype = basetype[xtype]; + loc = 0; + for(v = *Values;;v = v->nextp) { + if (!v) { + dtype = typepref[xtype]; + z = ISREAL(dtype) ? cpstring("0.") : (char *)0; + k = typesize[dtype]; + if (j = L % k) + L += k - j; + v = mkchain((char *)L, + mkchain((char *)LONG_CAST dtype, + mkchain(z, CHNULL))); + vlast = vlast->nextp = + mkchain((char *)v, CHNULL); + L += k; + break; + } + cp = (chainp)v->datap; + if (basetype[(int)cp->nextp->datap] == btype) + break; + dloc = (ftnint)cp->datap; + L1 = dloc - loc; + if (L1 > 0 + && !(L1 % szshort) + && !(loc % szshort) + && btype <= type_choice[L1/szshort % 4] + && btype <= type_choice[loc/szshort % 4]) + break; + dtype = (int)cp->nextp->datap; + loc = dloc + dtype == TYBLANK + ? (ftnint)cp->nextp->nextp->datap + : typesize[dtype]; + } + } + sentinel = mkchain((char *)L, mkchain((char *)TYERROR,CHNULL)); + vlast->nextp = mkchain((char *)sentinel, CHNULL); + + /* use doublereal fillers only if there are doublereal values */ + + k = TYLONG; + for(v = values; v; v = v->nextp) + if (ONEOF((int)((chainp)v->datap)->nextp->datap, + M(TYDREAL)|M(TYDCOMPLEX))) { + k = TYDREAL; + break; + } + type_choice[0] = k; + + nice_printf(outfile, "%sstruct {\n", iscomm ? "" : "static "); + next_tab(outfile); + loc = loc0 = k = 0; + curtype = -1; + for(v = values; v; v = v->nextp) { + cp = (chainp)v->datap; + dloc = (ftnint)cp->datap; + L = dloc - loc; + if (L < 0) { + overlapping(); + if ((int)cp->nextp->datap != TYERROR) { + v1 = cp; + frchain(&v1); + v->datap = 0; + } + continue; + } + dtype = (int)cp->nextp->datap; + if (dtype == TYBLANK) { + dtype = TYCHAR; + wasblank = 1; + } + else + wasblank = 0; + if (curtype != dtype || L > 0) { + if (curtype != -1) { + L1 = (loc - loc0)/dL; + nice_printf(outfile, "%s e_%d%s;\n", + typename[curtype], ++k, + Len(L1,curtype)); + } + curtype = dtype; + loc0 = dloc; + } + if (L > 0) { + if (xtype == TYCHAR) + filltype = TYCHAR; + else { + filltype = L % szshort ? TYCHAR + : type_choice[L/szshort % 4]; + filltype1 = loc % szshort ? TYCHAR + : type_choice[loc/szshort % 4]; + if (typesize[filltype] > typesize[filltype1]) + filltype = filltype1; + } + L1 = L / typesize[filltype]; + nice_printf(outfile, "%s fill_%d[%ld];\n", + typename[filltype], ++k, L1); + loc = dloc; + } + if (wasblank) { + loc += (ftnint)cp->nextp->nextp->datap; + dL = 1; + } + else { + dL = typesize[dtype]; + loc += dL; + } + } + nice_printf(outfile, "} %s = { ", iscomm + ? extsymtab[memno].cextname + : equiv_name(eqvmemno, CNULL)); + loc = 0; + for(v = values; ; v = v->nextp) { + cp = (chainp)v->datap; + if (!cp) + continue; + dtype = (int)cp->nextp->datap; + if (dtype == TYERROR) + break; + dloc = (ftnint)cp->datap; + if (dloc > loc) { + nice_printf(outfile, "%s{0}", comma); + comma = ", "; + loc = dloc; + } + if (comma != Blank) + nice_printf(outfile, ", "); + comma = ", "; + if (dtype == TYCHAR || dtype == TYBLANK) { + v = Ansi == 1 ? Ado_string(outfile, v, &loc) + : do_string(outfile, v, &loc); + continue; + } + make_one_const(dtype, &Const, v); + switch(dtype) { + case TYLOGICAL: + case TYLOGICAL2: + case TYLOGICAL1: + if (Const.ci < 0 || Const.ci > 1) + errl( + "wr_equiv_init: unexpected logical value %ld", + Const.ci); + nice_printf(outfile, + Const.ci ? "TRUE_" : "FALSE_"); + break; + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + nice_printf(outfile, "%ld", Const.ci); + break; + case TYREAL: + nice_printf(outfile, "%s", + flconst(real_buf, Const.cds[0])); + break; + case TYDREAL: + nice_printf(outfile, "%s", Const.cds[0]); + break; + case TYCOMPLEX: + nice_printf(outfile, "%s, %s", + flconst(real_buf, Const.cds[0]), + flconst(imag_buf, Const.cds[1])); + break; + case TYDCOMPLEX: + nice_printf(outfile, "%s, %s", + Const.cds[0], Const.cds[1]); + break; + default: + erri("unexpected type %d in wr_equiv_init", + dtype); + } + loc += typesize[dtype]; + } + nice_printf(outfile, " };\n\n"); + prev_tab(outfile); + frchain(&sentinel); + } diff --git a/usr.bin/f2c/ftypes.h b/usr.bin/f2c/ftypes.h new file mode 100644 index 000000000000..80d2deba2db7 --- /dev/null +++ b/usr.bin/f2c/ftypes.h @@ -0,0 +1,51 @@ + +/* variable types (stored in the vtype field of expptr) + * numeric assumptions: + * int < reals < complexes + * TYDREAL-TYREAL = TYDCOMPLEX-TYCOMPLEX + */ + +#ifdef NO_TYQUAD +#undef TYQUAD +#define TYQUAD_inc 0 +#else +#define TYQUAD 5 +#define TYQUAD_inc 1 +#endif + +#define TYUNKNOWN 0 +#define TYADDR 1 +#define TYINT1 2 +#define TYSHORT 3 +#define TYLONG 4 +/* #define TYQUAD 5 */ +#define TYREAL (5+TYQUAD_inc) +#define TYDREAL (6+TYQUAD_inc) +#define TYCOMPLEX (7+TYQUAD_inc) +#define TYDCOMPLEX (8+TYQUAD_inc) +#define TYLOGICAL1 (9+TYQUAD_inc) +#define TYLOGICAL2 (10+TYQUAD_inc) +#define TYLOGICAL (11+TYQUAD_inc) +#define TYCHAR (12+TYQUAD_inc) +#define TYSUBR (13+TYQUAD_inc) +#define TYERROR (14+TYQUAD_inc) +#define TYCILIST (15+TYQUAD_inc) +#define TYICILIST (16+TYQUAD_inc) +#define TYOLIST (17+TYQUAD_inc) +#define TYCLLIST (18+TYQUAD_inc) +#define TYALIST (19+TYQUAD_inc) +#define TYINLIST (20+TYQUAD_inc) +#define TYVOID (21+TYQUAD_inc) +#define TYLABEL (22+TYQUAD_inc) +#define TYFTNLEN (23+TYQUAD_inc) +/* TYVOID is not in any tables. */ + +/* NTYPES, NTYPES0 -- Total number of types, used to allocate tables indexed by + type. Such tables can include the size (in bytes) of objects of a given + type, or labels for returning objects of different types from procedures + (see array rtvlabels) */ + +#define NTYPES TYVOID +#define NTYPES0 TYCILIST +#define TYBLANK TYSUBR /* Huh? */ + diff --git a/usr.bin/f2c/gram.dcl b/usr.bin/f2c/gram.dcl new file mode 100644 index 000000000000..9a25c25afe27 --- /dev/null +++ b/usr.bin/f2c/gram.dcl @@ -0,0 +1,394 @@ +spec: dcl + | common + | external + | intrinsic + | equivalence + | data + | implicit + | namelist + | SSAVE + { NO66("SAVE statement"); + saveall = YES; } + | SSAVE savelist + { NO66("SAVE statement"); } + | SFORMAT + { fmtstmt(thislabel); setfmt(thislabel); } + | SPARAM in_dcl SLPAR paramlist SRPAR + { NO66("PARAMETER statement"); } + ; + +dcl: type opt_comma name in_dcl new_dcl dims lengspec + { settype($3, $1, $7); + if(ndim>0) setbound($3,ndim,dims); + } + | dcl SCOMMA name dims lengspec + { settype($3, $1, $5); + if(ndim>0) setbound($3,ndim,dims); + } + | dcl SSLASHD datainit vallist SSLASHD + { if (new_dcl == 2) { + err("attempt to give DATA in type-declaration"); + new_dcl = 1; + } + } + ; + +new_dcl: { new_dcl = 2; } ; + +type: typespec lengspec + { varleng = $2; } + ; + +typespec: typename + { varleng = ($1<0 || ONEOF($1,M(TYLOGICAL)|M(TYLONG)) + ? 0 : typesize[$1]); + vartype = $1; } + ; + +typename: SINTEGER { $$ = TYLONG; } + | SREAL { $$ = tyreal; } + | SCOMPLEX { ++complex_seen; $$ = tycomplex; } + | SDOUBLE { $$ = TYDREAL; } + | SDCOMPLEX { ++dcomplex_seen; NOEXT("DOUBLE COMPLEX statement"); $$ = TYDCOMPLEX; } + | SLOGICAL { $$ = TYLOGICAL; } + | SCHARACTER { NO66("CHARACTER statement"); $$ = TYCHAR; } + | SUNDEFINED { $$ = TYUNKNOWN; } + | SDIMENSION { $$ = TYUNKNOWN; } + | SAUTOMATIC { NOEXT("AUTOMATIC statement"); $$ = - STGAUTO; } + | SSTATIC { NOEXT("STATIC statement"); $$ = - STGBSS; } + ; + +lengspec: + { $$ = varleng; } + | SSTAR intonlyon expr intonlyoff + { + expptr p; + p = $3; + NO66("length specification *n"); + if( ! ISICON(p) || p->constblock.Const.ci <= 0 ) + { + $$ = 0; + dclerr("length must be a positive integer constant", + NPNULL); + } + else { + if (vartype == TYCHAR) + $$ = p->constblock.Const.ci; + else switch((int)p->constblock.Const.ci) { + case 1: $$ = 1; break; + case 2: $$ = typesize[TYSHORT]; break; + case 4: $$ = typesize[TYLONG]; break; + case 8: $$ = typesize[TYDREAL]; break; + case 16: $$ = typesize[TYDCOMPLEX]; break; + default: + dclerr("invalid length",NPNULL); + $$ = varleng; + } + } + } + | SSTAR intonlyon SLPAR SSTAR SRPAR intonlyoff + { NO66("length specification *(*)"); $$ = -1; } + ; + +common: SCOMMON in_dcl var + { incomm( $$ = comblock("") , $3 ); } + | SCOMMON in_dcl comblock var + { $$ = $3; incomm($3, $4); } + | common opt_comma comblock opt_comma var + { $$ = $3; incomm($3, $5); } + | common SCOMMA var + { incomm($1, $3); } + ; + +comblock: SCONCAT + { $$ = comblock(""); } + | SSLASH SNAME SSLASH + { $$ = comblock(token); } + ; + +external: SEXTERNAL in_dcl name + { setext($3); } + | external SCOMMA name + { setext($3); } + ; + +intrinsic: SINTRINSIC in_dcl name + { NO66("INTRINSIC statement"); setintr($3); } + | intrinsic SCOMMA name + { setintr($3); } + ; + +equivalence: SEQUIV in_dcl equivset + | equivalence SCOMMA equivset + ; + +equivset: SLPAR equivlist SRPAR + { + struct Equivblock *p; + if(nequiv >= maxequiv) + many("equivalences", 'q', maxequiv); + p = & eqvclass[nequiv++]; + p->eqvinit = NO; + p->eqvbottom = 0; + p->eqvtop = 0; + p->equivs = $2; + } + ; + +equivlist: lhs + { $$=ALLOC(Eqvchain); + $$->eqvitem.eqvlhs = (struct Primblock *)$1; + } + | equivlist SCOMMA lhs + { $$=ALLOC(Eqvchain); + $$->eqvitem.eqvlhs = (struct Primblock *) $3; + $$->eqvnextp = $1; + } + ; + +data: SDATA in_data datalist + | data opt_comma datalist + ; + +in_data: + { if(parstate == OUTSIDE) + { + newproc(); + startproc(ESNULL, CLMAIN); + } + if(parstate < INDATA) + { + enddcl(); + parstate = INDATA; + datagripe = 1; + } + } + ; + +datalist: datainit datavarlist SSLASH datapop vallist SSLASH + { ftnint junk; + if(nextdata(&junk) != NULL) + err("too few initializers"); + frdata($2); + frrpl(); + } + ; + +datainit: /* nothing */ { frchain(&datastack); curdtp = 0; } ; + +datapop: /* nothing */ { pop_datastack(); } ; + +vallist: { toomanyinit = NO; } val + | vallist SCOMMA val + ; + +val: value + { dataval(ENULL, $1); } + | simple SSTAR value + { dataval($1, $3); } + ; + +value: simple + | addop simple + { if( $1==OPMINUS && ISCONST($2) ) + consnegop((Constp)$2); + $$ = $2; + } + | complex_const + ; + +savelist: saveitem + | savelist SCOMMA saveitem + ; + +saveitem: name + { int k; + $1->vsave = YES; + k = $1->vstg; + if( ! ONEOF(k, M(STGUNKNOWN)|M(STGBSS)|M(STGINIT)) ) + dclerr("can only save static variables", $1); + } + | comblock + ; + +paramlist: paramitem + | paramlist SCOMMA paramitem + ; + +paramitem: name SEQUALS expr + { if($1->vclass == CLUNKNOWN) + make_param((struct Paramblock *)$1, $3); + else dclerr("cannot make into parameter", $1); + } + ; + +var: name dims + { if(ndim>0) setbound($1, ndim, dims); } + ; + +datavar: lhs + { Namep np; + np = ( (struct Primblock *) $1) -> namep; + vardcl(np); + if(np->vstg == STGCOMMON) + extsymtab[np->vardesc.varno].extinit = YES; + else if(np->vstg==STGEQUIV) + eqvclass[np->vardesc.varno].eqvinit = YES; + else if(np->vstg!=STGINIT && np->vstg!=STGBSS) + dclerr("inconsistent storage classes", np); + $$ = mkchain((char *)$1, CHNULL); + } + | SLPAR datavarlist SCOMMA dospec SRPAR + { chainp p; struct Impldoblock *q; + pop_datastack(); + q = ALLOC(Impldoblock); + q->tag = TIMPLDO; + (q->varnp = (Namep) ($4->datap))->vimpldovar = 1; + p = $4->nextp; + if(p) { q->implb = (expptr)(p->datap); p = p->nextp; } + if(p) { q->impub = (expptr)(p->datap); p = p->nextp; } + if(p) { q->impstep = (expptr)(p->datap); } + frchain( & ($4) ); + $$ = mkchain((char *)q, CHNULL); + q->datalist = hookup($2, $$); + } + ; + +datavarlist: datavar + { if (!datastack) + curdtp = 0; + datastack = mkchain((char *)curdtp, datastack); + curdtp = $1; curdtelt = 0; + } + | datavarlist SCOMMA datavar + { $$ = hookup($1, $3); } + ; + +dims: + { ndim = 0; } + | SLPAR dimlist SRPAR + ; + +dimlist: { ndim = 0; } dim + | dimlist SCOMMA dim + ; + +dim: ubound + { + if(ndim == maxdim) + err("too many dimensions"); + else if(ndim < maxdim) + { dims[ndim].lb = 0; + dims[ndim].ub = $1; + } + ++ndim; + } + | expr SCOLON ubound + { + if(ndim == maxdim) + err("too many dimensions"); + else if(ndim < maxdim) + { dims[ndim].lb = $1; + dims[ndim].ub = $3; + } + ++ndim; + } + ; + +ubound: SSTAR + { $$ = 0; } + | expr + ; + +labellist: label + { nstars = 1; labarray[0] = $1; } + | labellist SCOMMA label + { if(nstars < maxlablist) labarray[nstars++] = $3; } + ; + +label: SICON + { $$ = execlab( convci(toklen, token) ); } + ; + +implicit: SIMPLICIT in_dcl implist + { NO66("IMPLICIT statement"); } + | implicit SCOMMA implist + ; + +implist: imptype SLPAR letgroups SRPAR + | imptype + { if (vartype != TYUNKNOWN) + dclerr("-- expected letter range",NPNULL); + setimpl(vartype, varleng, 'a', 'z'); } + ; + +imptype: { needkwd = 1; } type + /* { vartype = $2; } */ + ; + +letgroups: letgroup + | letgroups SCOMMA letgroup + ; + +letgroup: letter + { setimpl(vartype, varleng, $1, $1); } + | letter SMINUS letter + { setimpl(vartype, varleng, $1, $3); } + ; + +letter: SNAME + { if(toklen!=1 || token[0]<'a' || token[0]>'z') + { + dclerr("implicit item must be single letter", NPNULL); + $$ = 0; + } + else $$ = token[0]; + } + ; + +namelist: SNAMELIST + | namelist namelistentry + ; + +namelistentry: SSLASH name SSLASH namelistlist + { + if($2->vclass == CLUNKNOWN) + { + $2->vclass = CLNAMELIST; + $2->vtype = TYINT; + $2->vstg = STGBSS; + $2->varxptr.namelist = $4; + $2->vardesc.varno = ++lastvarno; + } + else dclerr("cannot be a namelist name", $2); + } + ; + +namelistlist: name + { $$ = mkchain((char *)$1, CHNULL); } + | namelistlist SCOMMA name + { $$ = hookup($1, mkchain((char *)$3, CHNULL)); } + ; + +in_dcl: + { switch(parstate) + { + case OUTSIDE: newproc(); + startproc(ESNULL, CLMAIN); + case INSIDE: parstate = INDCL; + case INDCL: break; + + case INDATA: + if (datagripe) { + errstr( + "Statement order error: declaration after DATA", + CNULL); + datagripe = 0; + } + break; + + default: + dclerr("declaration among executables", NPNULL); + } + } + ; diff --git a/usr.bin/f2c/gram.exec b/usr.bin/f2c/gram.exec new file mode 100644 index 000000000000..0dc6010d15f3 --- /dev/null +++ b/usr.bin/f2c/gram.exec @@ -0,0 +1,143 @@ +exec: iffable + | SDO end_spec intonlyon label intonlyoff opt_comma dospecw + { + if($4->labdefined) + execerr("no backward DO loops", CNULL); + $4->blklevel = blklevel+1; + exdo($4->labelno, NPNULL, $7); + } + | SDO end_spec opt_comma dospecw + { + exdo((int)(ctls - ctlstack - 2), NPNULL, $4); + NOEXT("DO without label"); + } + | SENDDO + { exenddo(NPNULL); } + | logif iffable + { exendif(); thiswasbranch = NO; } + | logif STHEN + | SELSEIF end_spec SLPAR expr SRPAR STHEN + { exelif($4); lastwasbranch = NO; } + | SELSE end_spec + { exelse(); lastwasbranch = NO; } + | SENDIF end_spec + { exendif(); lastwasbranch = NO; } + ; + +logif: SLOGIF end_spec SLPAR expr SRPAR + { exif($4); } + ; + +dospec: name SEQUALS exprlist + { $$ = mkchain((char *)$1, $3); } + ; + +dospecw: dospec + | SWHILE SLPAR expr SRPAR + { $$ = mkchain(CNULL, (chainp)$3); } + ; + +iffable: let lhs SEQUALS expr + { exequals((struct Primblock *)$2, $4); } + | SASSIGN end_spec assignlabel STO name + { exassign($5, $3); } + | SCONTINUE end_spec + | goto + | io + { inioctl = NO; } + | SARITHIF end_spec SLPAR expr SRPAR label SCOMMA label SCOMMA label + { exarif($4, $6, $8, $10); thiswasbranch = YES; } + | call + { excall($1, LBNULL, 0, labarray); } + | call SLPAR SRPAR + { excall($1, LBNULL, 0, labarray); } + | call SLPAR callarglist SRPAR + { if(nstars < maxlablist) + excall($1, mklist(revchain($3)), nstars, labarray); + else + many("alternate returns", 'l', maxlablist); + } + | SRETURN end_spec opt_expr + { exreturn($3); thiswasbranch = YES; } + | stop end_spec opt_expr + { exstop($1, $3); thiswasbranch = $1; } + ; + +assignlabel: SICON + { $$ = mklabel( convci(toklen, token) ); } + ; + +let: SLET + { if(parstate == OUTSIDE) + { + newproc(); + startproc(ESNULL, CLMAIN); + } + } + ; + +goto: SGOTO end_spec label + { exgoto($3); thiswasbranch = YES; } + | SASGOTO end_spec name + { exasgoto($3); thiswasbranch = YES; } + | SASGOTO end_spec name opt_comma SLPAR labellist SRPAR + { exasgoto($3); thiswasbranch = YES; } + | SCOMPGOTO end_spec SLPAR labellist SRPAR opt_comma expr + { if(nstars < maxlablist) + putcmgo(putx(fixtype($7)), nstars, labarray); + else + many("labels in computed GOTO list", 'l', maxlablist); + } + ; + +opt_comma: + | SCOMMA + ; + +call: SCALL end_spec name + { nstars = 0; $$ = $3; } + ; + +callarglist: callarg + { $$ = $1 ? mkchain((char *)$1,CHNULL) : CHNULL; } + | callarglist SCOMMA callarg + { $$ = $3 ? mkchain((char *)$3, $1) : $1; } + ; + +callarg: expr + | SSTAR label + { if(nstars < maxlablist) labarray[nstars++] = $2; $$ = 0; } + ; + +stop: SPAUSE + { $$ = 0; } + | SSTOP + { $$ = 2; } + ; + +exprlist: expr + { $$ = mkchain((char *)$1, CHNULL); } + | exprlist SCOMMA expr + { $$ = hookup($1, mkchain((char *)$3,CHNULL) ); } + ; + +end_spec: + { if(parstate == OUTSIDE) + { + newproc(); + startproc(ESNULL, CLMAIN); + } + +/* This next statement depends on the ordering of the state table encoding */ + + if(parstate < INDATA) enddcl(); + } + ; + +intonlyon: + { intonly = YES; } + ; + +intonlyoff: + { intonly = NO; } + ; diff --git a/usr.bin/f2c/gram.expr b/usr.bin/f2c/gram.expr new file mode 100644 index 000000000000..1ef18e5022da --- /dev/null +++ b/usr.bin/f2c/gram.expr @@ -0,0 +1,142 @@ +funarglist: + { $$ = 0; } + | funargs + { $$ = revchain($1); } + ; + +funargs: expr + { $$ = mkchain((char *)$1, CHNULL); } + | funargs SCOMMA expr + { $$ = mkchain((char *)$3, $1); } + ; + + +expr: uexpr + | SLPAR expr SRPAR { $$ = $2; if ($$->tag == TPRIM) + $$->primblock.parenused = 1; } + | complex_const + ; + +uexpr: lhs + | simple_const + | expr addop expr %prec SPLUS + { $$ = mkexpr($2, $1, $3); } + | expr SSTAR expr + { $$ = mkexpr(OPSTAR, $1, $3); } + | expr SSLASH expr + { $$ = mkexpr(OPSLASH, $1, $3); } + | expr SPOWER expr + { $$ = mkexpr(OPPOWER, $1, $3); } + | addop expr %prec SSTAR + { if($1 == OPMINUS) + $$ = mkexpr(OPNEG, $2, ENULL); + else $$ = $2; + } + | expr relop expr %prec SEQ + { $$ = mkexpr($2, $1, $3); } + | expr SEQV expr + { NO66(".EQV. operator"); + $$ = mkexpr(OPEQV, $1,$3); } + | expr SNEQV expr + { NO66(".NEQV. operator"); + $$ = mkexpr(OPNEQV, $1, $3); } + | expr SOR expr + { $$ = mkexpr(OPOR, $1, $3); } + | expr SAND expr + { $$ = mkexpr(OPAND, $1, $3); } + | SNOT expr + { $$ = mkexpr(OPNOT, $2, ENULL); } + | expr SCONCAT expr + { NO66("concatenation operator //"); + $$ = mkexpr(OPCONCAT, $1, $3); } + ; + +addop: SPLUS { $$ = OPPLUS; } + | SMINUS { $$ = OPMINUS; } + ; + +relop: SEQ { $$ = OPEQ; } + | SGT { $$ = OPGT; } + | SLT { $$ = OPLT; } + | SGE { $$ = OPGE; } + | SLE { $$ = OPLE; } + | SNE { $$ = OPNE; } + ; + +lhs: name + { $$ = mkprim($1, LBNULL, CHNULL); } + | name substring + { NO66("substring operator :"); + $$ = mkprim($1, LBNULL, $2); } + | name SLPAR funarglist SRPAR + { $$ = mkprim($1, mklist($3), CHNULL); } + | name SLPAR funarglist SRPAR substring + { NO66("substring operator :"); + $$ = mkprim($1, mklist($3), $5); } + ; + +substring: SLPAR opt_expr SCOLON opt_expr SRPAR + { $$ = mkchain((char *)$2, mkchain((char *)$4,CHNULL)); } + ; + +opt_expr: + { $$ = 0; } + | expr + ; + +simple: name + { if($1->vclass == CLPARAM) + $$ = (expptr) cpexpr( + ( (struct Paramblock *) ($1) ) -> paramval); + } + | simple_const + ; + +simple_const: STRUE { $$ = mklogcon(1); } + | SFALSE { $$ = mklogcon(0); } + | SHOLLERITH { $$ = mkstrcon(toklen, token); } + | SICON = { $$ = mkintcon( convci(toklen, token) ); } + | SRCON = { $$ = mkrealcon(tyreal, token); } + | SDCON = { $$ = mkrealcon(TYDREAL, token); } + | bit_const + ; + +complex_const: SLPAR uexpr SCOMMA uexpr SRPAR + { $$ = mkcxcon($2,$4); } + ; + +bit_const: SHEXCON + { NOEXT("hex constant"); + $$ = mkbitcon(4, toklen, token); } + | SOCTCON + { NOEXT("octal constant"); + $$ = mkbitcon(3, toklen, token); } + | SBITCON + { NOEXT("binary constant"); + $$ = mkbitcon(1, toklen, token); } + ; + +fexpr: unpar_fexpr + | SLPAR fexpr SRPAR + { $$ = $2; } + ; + +unpar_fexpr: lhs + | simple_const + | fexpr addop fexpr %prec SPLUS + { $$ = mkexpr($2, $1, $3); } + | fexpr SSTAR fexpr + { $$ = mkexpr(OPSTAR, $1, $3); } + | fexpr SSLASH fexpr + { $$ = mkexpr(OPSLASH, $1, $3); } + | fexpr SPOWER fexpr + { $$ = mkexpr(OPPOWER, $1, $3); } + | addop fexpr %prec SSTAR + { if($1 == OPMINUS) + $$ = mkexpr(OPNEG, $2, ENULL); + else $$ = $2; + } + | fexpr SCONCAT fexpr + { NO66("concatenation operator //"); + $$ = mkexpr(OPCONCAT, $1, $3); } + ; diff --git a/usr.bin/f2c/gram.head b/usr.bin/f2c/gram.head new file mode 100644 index 000000000000..4af7dc7a1a99 --- /dev/null +++ b/usr.bin/f2c/gram.head @@ -0,0 +1,300 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories, Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +%{ +#include "defs.h" +#include "p1defs.h" + +static int nstars; /* Number of labels in an + alternate return CALL */ +static int datagripe; +static int ndim; +static int vartype; +int new_dcl; +static ftnint varleng; +static struct Dims dims[MAXDIM+1]; +extern struct Labelblock **labarray; /* Labels in an alternate + return CALL */ +extern int maxlablist; + +/* The next two variables are used to verify that each statement might be reached + during runtime. lastwasbranch is tested only in the defintion of the + stat: nonterminal. */ + +int lastwasbranch = NO; +static int thiswasbranch = NO; +extern ftnint yystno; +extern flag intonly; +static chainp datastack; +extern long laststfcn, thisstno; +extern int can_include; /* for netlib */ + +ftnint convci(); +Addrp nextdata(); +expptr mklogcon(), mkaddcon(), mkrealcon(), mkstrcon(), mkbitcon(); +expptr mkcxcon(); +struct Listblock *mklist(); +struct Listblock *mklist(); +struct Impldoblock *mkiodo(); +Extsym *comblock(); +#define ESNULL (Extsym *)0 +#define NPNULL (Namep)0 +#define LBNULL (struct Listblock *)0 +extern void freetemps(), make_param(); + + static void +pop_datastack() { + chainp d0 = datastack; + if (d0->datap) + curdtp = (chainp)d0->datap; + datastack = d0->nextp; + d0->nextp = 0; + frchain(&d0); + } + +%} + +/* Specify precedences and associativities. */ + +%union { + int ival; + ftnint lval; + char *charpval; + chainp chval; + tagptr tagval; + expptr expval; + struct Labelblock *labval; + struct Nameblock *namval; + struct Eqvchain *eqvval; + Extsym *extval; + } + +%left SCOMMA +%nonassoc SCOLON +%right SEQUALS +%left SEQV SNEQV +%left SOR +%left SAND +%left SNOT +%nonassoc SLT SGT SLE SGE SEQ SNE +%left SCONCAT +%left SPLUS SMINUS +%left SSTAR SSLASH +%right SPOWER + +%start program +%type <labval> thislabel label assignlabel +%type <tagval> other inelt +%type <ival> type typespec typename dcl letter addop relop stop nameeq +%type <lval> lengspec +%type <charpval> filename +%type <chval> datavar datavarlist namelistlist funarglist funargs +%type <chval> dospec dospecw +%type <chval> callarglist arglist args exprlist inlist outlist out2 substring +%type <namval> name arg call var +%type <expval> lhs expr uexpr opt_expr fexpr unpar_fexpr +%type <expval> ubound simple value callarg complex_const simple_const bit_const +%type <extval> common comblock entryname progname +%type <eqvval> equivlist + +%% + +program: + | program stat SEOS + ; + +stat: thislabel entry + { +/* stat: is the nonterminal for Fortran statements */ + + lastwasbranch = NO; } + | thislabel spec + | thislabel exec + { /* forbid further statement function definitions... */ + if (parstate == INDATA && laststfcn != thisstno) + parstate = INEXEC; + thisstno++; + if($1 && ($1->labelno==dorange)) + enddo($1->labelno); + if(lastwasbranch && thislabel==NULL) + warn("statement cannot be reached"); + lastwasbranch = thiswasbranch; + thiswasbranch = NO; + if($1) + { + if($1->labtype == LABFORMAT) + err("label already that of a format"); + else + $1->labtype = LABEXEC; + } + freetemps(); + } + | thislabel SINCLUDE filename + { if (can_include) + doinclude( $3 ); + else { + fprintf(diagfile, "Cannot open file %s\n", $3); + done(1); + } + } + | thislabel SEND end_spec + { if ($1) + lastwasbranch = NO; + endproc(); /* lastwasbranch = NO; -- set in endproc() */ + } + | thislabel SUNKNOWN + { extern void unclassifiable(); + unclassifiable(); + +/* flline flushes the current line, ignoring the rest of the text there */ + + flline(); } + | error + { flline(); needkwd = NO; inioctl = NO; + yyerrok; yyclearin; } + ; + +thislabel: SLABEL + { + if(yystno != 0) + { + $$ = thislabel = mklabel(yystno); + if( ! headerdone ) { + if (procclass == CLUNKNOWN) + procclass = CLMAIN; + puthead(CNULL, procclass); + } + if(thislabel->labdefined) + execerr("label %s already defined", + convic(thislabel->stateno) ); + else { + if(thislabel->blklevel!=0 && thislabel->blklevel<blklevel + && thislabel->labtype!=LABFORMAT) + warn1("there is a branch to label %s from outside block", + convic( (ftnint) (thislabel->stateno) ) ); + thislabel->blklevel = blklevel; + thislabel->labdefined = YES; + if(thislabel->labtype != LABFORMAT) + p1_label((long)(thislabel - labeltab)); + } + } + else $$ = thislabel = NULL; + } + ; + +entry: SPROGRAM new_proc progname + {startproc($3, CLMAIN); } + | SPROGRAM new_proc progname progarglist + { warn("ignoring arguments to main program"); + /* hashclear(); */ + startproc($3, CLMAIN); } + | SBLOCK new_proc progname + { if($3) NO66("named BLOCKDATA"); + startproc($3, CLBLOCK); } + | SSUBROUTINE new_proc entryname arglist + { entrypt(CLPROC, TYSUBR, (ftnint) 0, $3, $4); } + | SFUNCTION new_proc entryname arglist + { entrypt(CLPROC, TYUNKNOWN, (ftnint) 0, $3, $4); } + | type SFUNCTION new_proc entryname arglist + { entrypt(CLPROC, $1, varleng, $4, $5); } + | SENTRY entryname arglist + { if(parstate==OUTSIDE || procclass==CLMAIN + || procclass==CLBLOCK) + execerr("misplaced entry statement", CNULL); + entrypt(CLENTRY, 0, (ftnint) 0, $2, $3); + } + ; + +new_proc: + { newproc(); } + ; + +entryname: name + { $$ = newentry($1, 1); } + ; + +name: SNAME + { $$ = mkname(token); } + ; + +progname: { $$ = NULL; } + | entryname + ; + +progarglist: + SLPAR SRPAR + | SLPAR progargs SRPAR + ; + +progargs: progarg + | progargs SCOMMA progarg + ; + +progarg: SNAME + | SNAME SEQUALS SNAME + ; + +arglist: + { $$ = 0; } + | SLPAR SRPAR + { NO66(" () argument list"); + $$ = 0; } + | SLPAR args SRPAR + {$$ = $2; } + ; + +args: arg + { $$ = ($1 ? mkchain((char *)$1,CHNULL) : CHNULL ); } + | args SCOMMA arg + { if($3) $1 = $$ = mkchain((char *)$3, $1); } + ; + +arg: name + { if($1->vstg!=STGUNKNOWN && $1->vstg!=STGARG) + dclerr("name declared as argument after use", $1); + $1->vstg = STGARG; + } + | SSTAR + { NO66("altenate return argument"); + +/* substars means that '*'ed formal parameters should be replaced. + This is used to specify alternate return labels; in theory, only + parameter slots which have '*' should accept the statement labels. + This compiler chooses to ignore the '*'s in the formal declaration, and + always return the proper value anyway. + + This variable is only referred to in proc.c */ + + $$ = 0; substars = YES; } + ; + + + +filename: SHOLLERITH + { + char *s; + s = copyn(toklen+1, token); + s[toklen] = '\0'; + $$ = s; + } + ; diff --git a/usr.bin/f2c/gram.io b/usr.bin/f2c/gram.io new file mode 100644 index 000000000000..f1a6649aacd7 --- /dev/null +++ b/usr.bin/f2c/gram.io @@ -0,0 +1,173 @@ + /* Input/Output Statements */ + +io: io1 + { endio(); } + ; + +io1: iofmove ioctl + | iofmove unpar_fexpr + { ioclause(IOSUNIT, $2); endioctl(); } + | iofmove SSTAR + { ioclause(IOSUNIT, ENULL); endioctl(); } + | iofmove SPOWER + { ioclause(IOSUNIT, IOSTDERR); endioctl(); } + | iofctl ioctl + | read ioctl + { doio(CHNULL); } + | read infmt + { doio(CHNULL); } + | read ioctl inlist + { doio(revchain($3)); } + | read infmt SCOMMA inlist + { doio(revchain($4)); } + | read ioctl SCOMMA inlist + { doio(revchain($4)); } + | write ioctl + { doio(CHNULL); } + | write ioctl outlist + { doio(revchain($3)); } + | print + { doio(CHNULL); } + | print SCOMMA outlist + { doio(revchain($3)); } + ; + +iofmove: fmkwd end_spec in_ioctl + ; + +fmkwd: SBACKSPACE + { iostmt = IOBACKSPACE; } + | SREWIND + { iostmt = IOREWIND; } + | SENDFILE + { iostmt = IOENDFILE; } + ; + +iofctl: ctlkwd end_spec in_ioctl + ; + +ctlkwd: SINQUIRE + { iostmt = IOINQUIRE; } + | SOPEN + { iostmt = IOOPEN; } + | SCLOSE + { iostmt = IOCLOSE; } + ; + +infmt: unpar_fexpr + { + ioclause(IOSUNIT, ENULL); + ioclause(IOSFMT, $1); + endioctl(); + } + | SSTAR + { + ioclause(IOSUNIT, ENULL); + ioclause(IOSFMT, ENULL); + endioctl(); + } + ; + +ioctl: SLPAR fexpr SRPAR + { + ioclause(IOSUNIT, $2); + endioctl(); + } + | SLPAR ctllist SRPAR + { endioctl(); } + ; + +ctllist: ioclause + | ctllist SCOMMA ioclause + ; + +ioclause: fexpr + { ioclause(IOSPOSITIONAL, $1); } + | SSTAR + { ioclause(IOSPOSITIONAL, ENULL); } + | SPOWER + { ioclause(IOSPOSITIONAL, IOSTDERR); } + | nameeq expr + { ioclause($1, $2); } + | nameeq SSTAR + { ioclause($1, ENULL); } + | nameeq SPOWER + { ioclause($1, IOSTDERR); } + ; + +nameeq: SNAMEEQ + { $$ = iocname(); } + ; + +read: SREAD end_spec in_ioctl + { iostmt = IOREAD; } + ; + +write: SWRITE end_spec in_ioctl + { iostmt = IOWRITE; } + ; + +print: SPRINT end_spec fexpr in_ioctl + { + iostmt = IOWRITE; + ioclause(IOSUNIT, ENULL); + ioclause(IOSFMT, $3); + endioctl(); + } + | SPRINT end_spec SSTAR in_ioctl + { + iostmt = IOWRITE; + ioclause(IOSUNIT, ENULL); + ioclause(IOSFMT, ENULL); + endioctl(); + } + ; + +inlist: inelt + { $$ = mkchain((char *)$1, CHNULL); } + | inlist SCOMMA inelt + { $$ = mkchain((char *)$3, $1); } + ; + +inelt: lhs + { $$ = (tagptr) $1; } + | SLPAR inlist SCOMMA dospec SRPAR + { $$ = (tagptr) mkiodo($4,revchain($2)); } + ; + +outlist: uexpr + { $$ = mkchain((char *)$1, CHNULL); } + | other + { $$ = mkchain((char *)$1, CHNULL); } + | out2 + ; + +out2: uexpr SCOMMA uexpr + { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); } + | uexpr SCOMMA other + { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); } + | other SCOMMA uexpr + { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); } + | other SCOMMA other + { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); } + | out2 SCOMMA uexpr + { $$ = mkchain((char *)$3, $1); } + | out2 SCOMMA other + { $$ = mkchain((char *)$3, $1); } + ; + +other: complex_const + { $$ = (tagptr) $1; } + | SLPAR expr SRPAR + { $$ = (tagptr) $2; } + | SLPAR uexpr SCOMMA dospec SRPAR + { $$ = (tagptr) mkiodo($4, mkchain((char *)$2, CHNULL) ); } + | SLPAR other SCOMMA dospec SRPAR + { $$ = (tagptr) mkiodo($4, mkchain((char *)$2, CHNULL) ); } + | SLPAR out2 SCOMMA dospec SRPAR + { $$ = (tagptr) mkiodo($4, revchain($2)); } + ; + +in_ioctl: + { startioctl(); } + ; diff --git a/usr.bin/f2c/index b/usr.bin/f2c/index new file mode 100644 index 000000000000..09422b31ccb7 --- /dev/null +++ b/usr.bin/f2c/index @@ -0,0 +1,135 @@ +# ====== index for f2c/src ====== + +file f2c/src/all +for bundle of complete f2c source + +# NOTE: "all from f2c/src" is the complete f2c source (sans libraries). +# The remaining files in this directory are the component modules +# of "all from f2c/src", so you can request just the modules that +# have changed since last you updated your f2c source. You can +# tell what has changed by looking at the timestamps at the end +# of "readme from f2c". + +file f2c/src/notice + +file f2c/src/readme + +file f2c/src/cds.c + +file f2c/src/changes + +file f2c/src/data.c + +file f2c/src/defines.h + +file f2c/src/defs.h + +file f2c/src/equiv.c + +file f2c/src/error.c + +file f2c/src/exec.c + +file f2c/src/expr.c + +file f2c/src/f2c.1 + +file f2c/src/f2c.1t + +file f2c/src/f2c.h + +file f2c/src/fc + +file f2c/src/format.c + +file f2c/src/format.h + +file f2c/src/formatdata.c + +file f2c/src/ftypes.h + +file f2c/src/gram.c + +file f2c/src/gram.dcl + +file f2c/src/gram.exec + +file f2c/src/gram.expr + +file f2c/src/gram.head + +file f2c/src/gram.io + +file f2c/src/init.c + +file f2c/src/intr.c + +file f2c/src/io.c + +file f2c/src/iob.h + +file f2c/src/lex.c + +file f2c/src/machdefs.h + +file f2c/src/main.c + +file f2c/src/makefile + +file f2c/src/malloc.c + +file f2c/src/mem.c + +file f2c/src/memset.c + +file f2c/src/misc.c + +file f2c/src/names.c + +file f2c/src/names.h + +file f2c/src/niceprintf.c + +file f2c/src/niceprintf.h + +file f2c/src/notice + +file f2c/src/output.c + +file f2c/src/output.h + +file f2c/src/p1defs.h + +file f2c/src/p1output.c + +file f2c/src/parse.h + +file f2c/src/parse_args.c + +file f2c/src/pccdefs.h + +file f2c/src/pread.c + +file f2c/src/proc.c + +file f2c/src/put.c + +file f2c/src/putpcc.c + +file f2c/src/readme + +file f2c/src/sysdep.c + +file f2c/src/sysdep.h + +file f2c/src/tokens + +file f2c/src/usignal.h + +file f2c/src/vax.c + +file f2c/src/version.c + +file f2c/src/xsum.c + +file f2c/src/xsum0.out diff --git a/usr.bin/f2c/index.html b/usr.bin/f2c/index.html new file mode 100644 index 000000000000..f93c66cc8f65 --- /dev/null +++ b/usr.bin/f2c/index.html @@ -0,0 +1,142 @@ +<TITLE>f2c/src/index</TITLE><UL> +<PRE> +====== index for f2c/src ====== +</PRE> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/all.Z">f2c/src/all</A><MENU> +<LI><EM>for: </EM>bundle of complete f2c source +</MENU> +<PRE> +NOTE: "all from f2c/src" is the complete f2c source (sans libraries). +The remaining files in this directory are the component modules +of "all from f2c/src", so you can request just the modules that +have changed since last you updated your f2c source. You can +tell what has changed by looking at the timestamps at the end +of "readme from f2c". +</PRE> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/notice.Z">f2c/src/notice</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/readme.Z">f2c/src/readme</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/cds.c.Z">f2c/src/cds.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/changes.Z">f2c/src/changes</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/data.c.Z">f2c/src/data.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/defines.h.Z">f2c/src/defines.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/defs.h.Z">f2c/src/defs.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/equiv.c.Z">f2c/src/equiv.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/error.c.Z">f2c/src/error.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/exec.c.Z">f2c/src/exec.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/expr.c.Z">f2c/src/expr.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/f2c.1.Z">f2c/src/f2c.1</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/f2c.1t.Z">f2c/src/f2c.1t</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/f2c.h.Z">f2c/src/f2c.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/fc.Z">f2c/src/fc</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/format.c.Z">f2c/src/format.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/format.h.Z">f2c/src/format.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/formatdata.c.Z">f2c/src/formatdata.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/ftypes.h.Z">f2c/src/ftypes.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.c.Z">f2c/src/gram.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.dcl.Z">f2c/src/gram.dcl</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.exec.Z">f2c/src/gram.exec</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.expr.Z">f2c/src/gram.expr</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.head.Z">f2c/src/gram.head</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/gram.io.Z">f2c/src/gram.io</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/init.c.Z">f2c/src/init.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/intr.c.Z">f2c/src/intr.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/io.c.Z">f2c/src/io.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/iob.h.Z">f2c/src/iob.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/lex.c.Z">f2c/src/lex.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/machdefs.h.Z">f2c/src/machdefs.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/main.c.Z">f2c/src/main.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/makefile.Z">f2c/src/makefile</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/malloc.c.Z">f2c/src/malloc.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/mem.c.Z">f2c/src/mem.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/memset.c.Z">f2c/src/memset.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/misc.c.Z">f2c/src/misc.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/names.c.Z">f2c/src/names.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/names.h.Z">f2c/src/names.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/niceprintf.c.Z">f2c/src/niceprintf.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/niceprintf.h.Z">f2c/src/niceprintf.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/notice.Z">f2c/src/notice</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/output.c.Z">f2c/src/output.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/output.h.Z">f2c/src/output.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/p1defs.h.Z">f2c/src/p1defs.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/p1output.c.Z">f2c/src/p1output.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/parse.h.Z">f2c/src/parse.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/parse_args.c.Z">f2c/src/parse_args.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/pccdefs.h.Z">f2c/src/pccdefs.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/pread.c.Z">f2c/src/pread.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/proc.c.Z">f2c/src/proc.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/put.c.Z">f2c/src/put.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/putpcc.c.Z">f2c/src/putpcc.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/readme.Z">f2c/src/readme</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/sysdep.c.Z">f2c/src/sysdep.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/sysdep.h.Z">f2c/src/sysdep.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/tokens.Z">f2c/src/tokens</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/usignal.h.Z">f2c/src/usignal.h</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/vax.c.Z">f2c/src/vax.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/version.c.Z">f2c/src/version.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/xsum.c.Z">f2c/src/xsum.c</A><MENU> +</MENU> +<LI><EM>file: </EM><A HREF="ftp://netlib.att.com/netlib/f2c/src/xsum0.out.Z">f2c/src/xsum0.out</A><MENU> +</MENU> +<P><LI><A HREF="ftp://netlib.att.com/netlib/bib/thesaurus.Z">glossary/thesaurus of terms used in this index</A> +</UL> +<P><A HREF="ftp://netlib.att.com/netlib/bib/ericjack.Z">Eric and Jack</EM> diff --git a/usr.bin/f2c/init.c b/usr.bin/f2c/init.c new file mode 100644 index 000000000000..67bcd1e3e599 --- /dev/null +++ b/usr.bin/f2c/init.c @@ -0,0 +1,509 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "output.h" +#include "iob.h" + +/* State required for the C output */ +char *fl_fmt_string; /* Float format string */ +char *db_fmt_string; /* Double format string */ +char *cm_fmt_string; /* Complex format string */ +char *dcm_fmt_string; /* Double complex format string */ + +chainp new_vars = CHNULL; /* List of newly created locals in this + function. These may have identifiers + which have underscores and more than VL + characters */ +chainp used_builtins = CHNULL; /* List of builtins used by this function. + These are all Addrps with UNAM_EXTERN + */ +chainp assigned_fmts = CHNULL; /* assigned formats */ +chainp allargs; /* union of args in all entry points */ +chainp earlylabs; /* labels seen before enddcl() */ +char main_alias[52]; /* PROGRAM name, if any is given */ +int tab_size = 4; + + +FILEP infile; +FILEP diagfile; + +FILEP c_file; +FILEP pass1_file; +FILEP initfile; +FILEP blkdfile; + + +char token[MAXTOKENLEN+2]; +int toklen; +long lineno; /* Current line in the input file, NOT the + Fortran statement label number */ +char *infname; +int needkwd; +struct Labelblock *thislabel = NULL; +int nerr; +int nwarn; + +flag saveall; +flag substars; +int parstate = OUTSIDE; +flag headerdone = NO; +int blklevel; +int doin_setbound; +int impltype[26]; +ftnint implleng[26]; +int implstg[26]; + +int tyint = TYLONG ; +int tylogical = TYLONG; +int tylog = TYLOGICAL; +int typesize[NTYPES] = { + 1, SZADDR, 1, SZSHORT, SZLONG, +#ifdef TYQUAD + 2*SZLONG, +#endif + SZLONG, 2*SZLONG, + 2*SZLONG, 4*SZLONG, 1, SZSHORT, SZLONG, 1, 1, 0, + 4*SZLONG + SZADDR, /* sizeof(cilist) */ + 4*SZLONG + 2*SZADDR, /* sizeof(icilist) */ + 4*SZLONG + 5*SZADDR, /* sizeof(olist) */ + 2*SZLONG + SZADDR, /* sizeof(cllist) */ + 2*SZLONG, /* sizeof(alist) */ + 11*SZLONG + 15*SZADDR /* sizeof(inlist) */ + }; + +int typealign[NTYPES] = { + 1, ALIADDR, 1, ALISHORT, ALILONG, +#ifdef TYQUAD + ALIDOUBLE, +#endif + ALILONG, ALIDOUBLE, + ALILONG, ALIDOUBLE, 1, ALISHORT, ALILONG, 1, 1, 1, + ALILONG, ALILONG, ALILONG, ALILONG, ALILONG, ALILONG}; + +int type_choice[4] = { TYDREAL, TYSHORT, TYLONG, TYSHORT }; + +char *typename[] = { + "<<unknown>>", + "address", + "integer1", + "shortint", + "integer", +#ifdef TYQUAD + "longint", +#endif + "real", + "doublereal", + "complex", + "doublecomplex", + "logical1", + "shortlogical", + "logical", + "char" /* character */ + }; + +int type_pref[NTYPES] = { 0, 0, 3, 5, 7, +#ifdef TYQUAD + 10, +#endif + 8, 11, 9, 12, 1, 4, 6, 2 }; + +char *protorettypes[] = { + "?", "??", "integer1", "shortint", "integer", +#ifdef TYQUAD + "longint", +#endif + "real", "doublereal", + "C_f", "Z_f", "logical1", "shortlogical", "logical", "H_f", "int" + }; + +char *casttypes[TYSUBR+1] = { + "U_fp", "??bug??", "I1_fp", + "J_fp", "I_fp", +#ifdef TYQUAD + "Q_fp", +#endif + "R_fp", "D_fp", "C_fp", "Z_fp", + "L1_fp", "L2_fp", "L_fp", "H_fp", "S_fp" + }; +char *usedcasts[TYSUBR+1]; + +char *dfltarg[] = { + 0, 0, "(integer1 *)0", + "(shortint *)0", "(integer *)0", +#ifdef TYQUAD + "(longint *)0", +#endif + "(real *)0", + "(doublereal *)0", "(complex *)0", "(doublecomplex *)0", + "(logical1 *)0","(shortlogical *)0)", "(logical *)0", "(char *)0" + }; + +static char *dflt0proc[] = { + 0, 0, "(integer1 (*)())0", + "(shortint (*)())0", "(integer (*)())0", +#ifdef TYQUAD + "(longint (*)())0", +#endif + "(real (*)())0", + "(doublereal (*)())0", "(complex (*)())0", "(doublecomplex (*)())0", + "(logical1 (*)())0", "(shortlogical (*)())0", + "(logical (*)())0", "(char (*)())0", "(int (*)())0" + }; + +char *dflt1proc[] = { "(U_fp)0", "(??bug??)0", "(I1_fp)0", + "(J_fp)0", "(I_fp)0", +#ifdef TYQUAD + "(Q_fp)0", +#endif + "(R_fp)0", "(D_fp)0", "(C_fp)0", "(Z_fp)0", + "(L1_fp)0","(L2_fp)0", + "(L_fp)0", "(H_fp)0", "(S_fp)0" + }; + +char **dfltproc = dflt0proc; + +static char Bug[] = "bug"; + +char *ftn_types[] = { "external", "??", "integer*1", + "integer*2", "integer", +#ifdef TYQUAD + "integer*8", +#endif + "real", + "double precision", "complex", "double complex", + "logical*1", "logical*2", + "logical", "character", "subroutine", + Bug,Bug,Bug,Bug,Bug,Bug,Bug,Bug,Bug, "ftnlen" + }; + +int init_ac[TYSUBR+1] = { 0,0,0,0,0,0,0, +#ifdef TYQUAD + 0, +#endif + 1, 1, 0, 0, 0, 2}; + +int proctype = TYUNKNOWN; +char *procname; +int rtvlabel[NTYPES0]; +Addrp retslot; /* Holds automatic variable which was + allocated the function return value + */ +Addrp xretslot[NTYPES0]; /* for multiple entry points */ +int cxslot = -1; +int chslot = -1; +int chlgslot = -1; +int procclass = CLUNKNOWN; +int nentry; +int nallargs; +int nallchargs; +flag multitype; +ftnint procleng; +long lastiolabno; +int lastlabno; +int lastvarno; +int lastargslot; +int autonum[TYVOID]; +char *av_pfix[TYVOID] = {"??TYUNKNOWN??", "a","i1","s","i", +#ifdef TYQUAD + "i8", +#endif + "r","d","q","z","L1","L2","L","ch", + "??TYSUBR??", "??TYERROR??","ci", "ici", + "o", "cl", "al", "ioin" }; + +extern int maxctl; +struct Ctlframe *ctls; +struct Ctlframe *ctlstack; +struct Ctlframe *lastctl; + +Namep regnamep[MAXREGVAR]; +int highregvar; +int nregvar; + +extern int maxext; +Extsym *extsymtab; +Extsym *nextext; +Extsym *lastext; + +extern int maxequiv; +struct Equivblock *eqvclass; + +extern int maxhash; +struct Hashentry *hashtab; +struct Hashentry *lasthash; + +extern int maxstno; /* Maximum number of statement labels */ +struct Labelblock *labeltab; +struct Labelblock *labtabend; +struct Labelblock *highlabtab; + +int maxdim = MAXDIM; +struct Rplblock *rpllist = NULL; +struct Chain *curdtp = NULL; +flag toomanyinit; +ftnint curdtelt; +chainp templist[TYVOID]; +chainp holdtemps; +int dorange = 0; +struct Entrypoint *entries = NULL; + +chainp chains = NULL; + +flag inioctl; +int iostmt; +int nioctl; +int nequiv = 0; +int eqvstart = 0; +int nintnames = 0; +extern int maxlablist; +struct Labelblock **labarray; + +struct Literal *litpool; +int nliterals; + +char dflttype[26]; +char hextoi_tab[Table_size], Letters[Table_size]; +char *ei_first, *ei_next, *ei_last; +char *wh_first, *wh_next, *wh_last; + +#define ALLOCN(n,x) (struct x *) ckalloc((n)*sizeof(struct x)) + +fileinit() +{ + register char *s; + register int i, j; + extern void fmt_init(), mem_init(), np_init(); + + lastiolabno = 100000; + lastlabno = 0; + lastvarno = 0; + nliterals = 0; + nerr = 0; + + infile = stdin; + + memset(dflttype, tyreal, 26); + memset(dflttype + 'i' - 'a', tyint, 6); + memset(hextoi_tab, 16, sizeof(hextoi_tab)); + for(i = 0, s = "0123456789abcdef"; *s; i++, s++) + hextoi(*s) = i; + for(i = 10, s = "ABCDEF"; *s; i++, s++) + hextoi(*s) = i; + for(j = 0, s = "abcdefghijklmnopqrstuvwxyz"; i = *s++; j++) + Letters[i] = Letters[i+'A'-'a'] = j; + + ctls = ALLOCN(maxctl+1, Ctlframe); + extsymtab = ALLOCN(maxext, Extsym); + eqvclass = ALLOCN(maxequiv, Equivblock); + hashtab = ALLOCN(maxhash, Hashentry); + labeltab = ALLOCN(maxstno, Labelblock); + litpool = ALLOCN(maxliterals, Literal); + labarray = (struct Labelblock **)ckalloc(maxlablist* + sizeof(struct Labelblock *)); + fmt_init(); + mem_init(); + np_init(); + + ctlstack = ctls++; + lastctl = ctls + maxctl; + nextext = extsymtab; + lastext = extsymtab + maxext; + lasthash = hashtab + maxhash; + labtabend = labeltab + maxstno; + highlabtab = labeltab; + main_alias[0] = '\0'; + if (forcedouble) + dfltproc[TYREAL] = dfltproc[TYDREAL]; + +/* Initialize the routines for providing C output */ + + out_init (); +} + +hashclear() /* clear hash table */ +{ + register struct Hashentry *hp; + register Namep p; + register struct Dimblock *q; + register int i; + + for(hp = hashtab ; hp < lasthash ; ++hp) + if(p = hp->varp) + { + frexpr(p->vleng); + if(q = p->vdim) + { + for(i = 0 ; i < q->ndim ; ++i) + { + frexpr(q->dims[i].dimsize); + frexpr(q->dims[i].dimexpr); + } + frexpr(q->nelt); + frexpr(q->baseoffset); + frexpr(q->basexpr); + free( (charptr) q); + } + if(p->vclass == CLNAMELIST) + frchain( &(p->varxptr.namelist) ); + free( (charptr) p); + hp->varp = NULL; + } + } + +procinit() +{ + register struct Labelblock *lp; + struct Chain *cp; + int i; + struct memblock; + extern struct memblock *curmemblock, *firstmemblock; + extern char *mem_first, *mem_next, *mem_last, *mem0_last; + extern void frexchain(); + + curmemblock = firstmemblock; + mem_next = mem_first; + mem_last = mem0_last; + ei_next = ei_first = ei_last = 0; + wh_next = wh_first = wh_last = 0; + iob_list = 0; + for(i = 0; i < 9; i++) + io_structs[i] = 0; + + parstate = OUTSIDE; + headerdone = NO; + blklevel = 1; + saveall = NO; + substars = NO; + nwarn = 0; + thislabel = NULL; + needkwd = 0; + + proctype = TYUNKNOWN; + procname = "MAIN_"; + procclass = CLUNKNOWN; + nentry = 0; + nallargs = nallchargs = 0; + multitype = NO; + retslot = NULL; + for(i = 0; i < NTYPES0; i++) { + frexpr((expptr)xretslot[i]); + xretslot[i] = 0; + } + cxslot = -1; + chslot = -1; + chlgslot = -1; + procleng = 0; + blklevel = 1; + lastargslot = 0; + + for(lp = labeltab ; lp < labtabend ; ++lp) + lp->stateno = 0; + + hashclear(); + +/* Clear the list of newly generated identifiers from the previous + function */ + + frexchain(&new_vars); + frexchain(&used_builtins); + frchain(&assigned_fmts); + frchain(&allargs); + frchain(&earlylabs); + + nintnames = 0; + highlabtab = labeltab; + + ctlstack = ctls - 1; + for(i = TYADDR; i < TYVOID; i++) { + for(cp = templist[i]; cp ; cp = cp->nextp) + free( (charptr) (cp->datap) ); + frchain(templist + i); + autonum[i] = 0; + } + holdtemps = NULL; + dorange = 0; + nregvar = 0; + highregvar = 0; + entries = NULL; + rpllist = NULL; + inioctl = NO; + eqvstart += nequiv; + nequiv = 0; + dcomplex_seen = 0; + + for(i = 0 ; i<NTYPES0 ; ++i) + rtvlabel[i] = 0; + + if(undeftype) + setimpl(TYUNKNOWN, (ftnint) 0, 'a', 'z'); + else + { + setimpl(tyreal, (ftnint) 0, 'a', 'z'); + setimpl(tyint, (ftnint) 0, 'i', 'n'); + } + setimpl(-STGBSS, (ftnint) 0, 'a', 'z'); /* set class */ + setlog(); +} + + + + +setimpl(type, length, c1, c2) +int type; +ftnint length; +int c1, c2; +{ + int i; + char buff[100]; + + if(c1==0 || c2==0) + return; + + if(c1 > c2) { + sprintf(buff, "characters out of order in implicit:%c-%c", c1, c2); + err(buff); + } + else { + c1 = letter(c1); + c2 = letter(c2); + if(type < 0) + for(i = c1 ; i<=c2 ; ++i) + implstg[i] = - type; + else { + type = lengtype(type, length); + if(type == TYCHAR) { + if (length < 0) { + err("length (*) in implicit"); + length = 1; + } + } + else if (type != TYLONG) + length = 0; + for(i = c1 ; i<=c2 ; ++i) { + impltype[i] = type; + implleng[i] = length; + } + } + } + } diff --git a/usr.bin/f2c/intr.c b/usr.bin/f2c/intr.c new file mode 100644 index 000000000000..210047fef04f --- /dev/null +++ b/usr.bin/f2c/intr.c @@ -0,0 +1,854 @@ +/**************************************************************** +Copyright 1990, 1992 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "names.h" + +void cast_args (); + +union + { + int ijunk; + struct Intrpacked bits; + } packed; + +struct Intrbits + { + char intrgroup /* :3 */; + char intrstuff /* result type or number of generics */; + char intrno /* :7 */; + char dblcmplx; + char dblintrno; /* for -r8 */ + }; + +/* List of all intrinsic functions. */ + +LOCAL struct Intrblock + { + char intrfname[8]; + struct Intrbits intrval; + } intrtab[ ] = +{ +"int", { INTRCONV, TYLONG }, +"real", { INTRCONV, TYREAL, 1 }, + /* 1 ==> real(TYDCOMPLEX) yields TYDREAL */ +"dble", { INTRCONV, TYDREAL }, +"cmplx", { INTRCONV, TYCOMPLEX }, +"dcmplx", { INTRCONV, TYDCOMPLEX, 0, 1 }, +"ifix", { INTRCONV, TYLONG }, +"idint", { INTRCONV, TYLONG }, +"float", { INTRCONV, TYREAL }, +"dfloat", { INTRCONV, TYDREAL }, +"sngl", { INTRCONV, TYREAL }, +"ichar", { INTRCONV, TYLONG }, +"iachar", { INTRCONV, TYLONG }, +"char", { INTRCONV, TYCHAR }, +"achar", { INTRCONV, TYCHAR }, + +/* any MAX or MIN can be used with any types; the compiler will cast them + correctly. So rules against bad syntax in these expressions are not + enforced */ + +"max", { INTRMAX, TYUNKNOWN }, +"max0", { INTRMAX, TYLONG }, +"amax0", { INTRMAX, TYREAL }, +"max1", { INTRMAX, TYLONG }, +"amax1", { INTRMAX, TYREAL }, +"dmax1", { INTRMAX, TYDREAL }, + +"and", { INTRBOOL, TYUNKNOWN, OPBITAND }, +"or", { INTRBOOL, TYUNKNOWN, OPBITOR }, +"xor", { INTRBOOL, TYUNKNOWN, OPBITXOR }, +"not", { INTRBOOL, TYUNKNOWN, OPBITNOT }, +"lshift", { INTRBOOL, TYUNKNOWN, OPLSHIFT }, +"rshift", { INTRBOOL, TYUNKNOWN, OPRSHIFT }, + +"min", { INTRMIN, TYUNKNOWN }, +"min0", { INTRMIN, TYLONG }, +"amin0", { INTRMIN, TYREAL }, +"min1", { INTRMIN, TYLONG }, +"amin1", { INTRMIN, TYREAL }, +"dmin1", { INTRMIN, TYDREAL }, + +"aint", { INTRGEN, 2, 0 }, +"dint", { INTRSPEC, TYDREAL, 1 }, + +"anint", { INTRGEN, 2, 2 }, +"dnint", { INTRSPEC, TYDREAL, 3 }, + +"nint", { INTRGEN, 4, 4 }, +"idnint", { INTRGEN, 2, 6 }, + +"abs", { INTRGEN, 6, 8 }, +"iabs", { INTRGEN, 2, 9 }, +"dabs", { INTRSPEC, TYDREAL, 11 }, +"cabs", { INTRSPEC, TYREAL, 12, 0, 13 }, +"zabs", { INTRSPEC, TYDREAL, 13, 1 }, + +"mod", { INTRGEN, 4, 14 }, +"amod", { INTRSPEC, TYREAL, 16, 0, 17 }, +"dmod", { INTRSPEC, TYDREAL, 17 }, + +"sign", { INTRGEN, 4, 18 }, +"isign", { INTRGEN, 2, 19 }, +"dsign", { INTRSPEC, TYDREAL, 21 }, + +"dim", { INTRGEN, 4, 22 }, +"idim", { INTRGEN, 2, 23 }, +"ddim", { INTRSPEC, TYDREAL, 25 }, + +"dprod", { INTRSPEC, TYDREAL, 26 }, + +"len", { INTRSPEC, TYLONG, 27 }, +"index", { INTRSPEC, TYLONG, 29 }, + +"imag", { INTRGEN, 2, 31 }, +"aimag", { INTRSPEC, TYREAL, 31, 0, 32 }, +"dimag", { INTRSPEC, TYDREAL, 32 }, + +"conjg", { INTRGEN, 2, 33 }, +"dconjg", { INTRSPEC, TYDCOMPLEX, 34, 1 }, + +"sqrt", { INTRGEN, 4, 35 }, +"dsqrt", { INTRSPEC, TYDREAL, 36 }, +"csqrt", { INTRSPEC, TYCOMPLEX, 37, 0, 38 }, +"zsqrt", { INTRSPEC, TYDCOMPLEX, 38, 1 }, + +"exp", { INTRGEN, 4, 39 }, +"dexp", { INTRSPEC, TYDREAL, 40 }, +"cexp", { INTRSPEC, TYCOMPLEX, 41, 0, 42 }, +"zexp", { INTRSPEC, TYDCOMPLEX, 42, 1 }, + +"log", { INTRGEN, 4, 43 }, +"alog", { INTRSPEC, TYREAL, 43, 0, 44 }, +"dlog", { INTRSPEC, TYDREAL, 44 }, +"clog", { INTRSPEC, TYCOMPLEX, 45, 0, 46 }, +"zlog", { INTRSPEC, TYDCOMPLEX, 46, 1 }, + +"log10", { INTRGEN, 2, 47 }, +"alog10", { INTRSPEC, TYREAL, 47, 0, 48 }, +"dlog10", { INTRSPEC, TYDREAL, 48 }, + +"sin", { INTRGEN, 4, 49 }, +"dsin", { INTRSPEC, TYDREAL, 50 }, +"csin", { INTRSPEC, TYCOMPLEX, 51, 0, 52 }, +"zsin", { INTRSPEC, TYDCOMPLEX, 52, 1 }, + +"cos", { INTRGEN, 4, 53 }, +"dcos", { INTRSPEC, TYDREAL, 54 }, +"ccos", { INTRSPEC, TYCOMPLEX, 55, 0, 56 }, +"zcos", { INTRSPEC, TYDCOMPLEX, 56, 1 }, + +"tan", { INTRGEN, 2, 57 }, +"dtan", { INTRSPEC, TYDREAL, 58 }, + +"asin", { INTRGEN, 2, 59 }, +"dasin", { INTRSPEC, TYDREAL, 60 }, + +"acos", { INTRGEN, 2, 61 }, +"dacos", { INTRSPEC, TYDREAL, 62 }, + +"atan", { INTRGEN, 2, 63 }, +"datan", { INTRSPEC, TYDREAL, 64 }, + +"atan2", { INTRGEN, 2, 65 }, +"datan2", { INTRSPEC, TYDREAL, 66 }, + +"sinh", { INTRGEN, 2, 67 }, +"dsinh", { INTRSPEC, TYDREAL, 68 }, + +"cosh", { INTRGEN, 2, 69 }, +"dcosh", { INTRSPEC, TYDREAL, 70 }, + +"tanh", { INTRGEN, 2, 71 }, +"dtanh", { INTRSPEC, TYDREAL, 72 }, + +"lge", { INTRSPEC, TYLOGICAL, 73}, +"lgt", { INTRSPEC, TYLOGICAL, 75}, +"lle", { INTRSPEC, TYLOGICAL, 77}, +"llt", { INTRSPEC, TYLOGICAL, 79}, + +#if 0 +"epbase", { INTRCNST, 4, 0 }, +"epprec", { INTRCNST, 4, 4 }, +"epemin", { INTRCNST, 2, 8 }, +"epemax", { INTRCNST, 2, 10 }, +"eptiny", { INTRCNST, 2, 12 }, +"ephuge", { INTRCNST, 4, 14 }, +"epmrsp", { INTRCNST, 2, 18 }, +#endif + +"fpexpn", { INTRGEN, 4, 81 }, +"fpabsp", { INTRGEN, 2, 85 }, +"fprrsp", { INTRGEN, 2, 87 }, +"fpfrac", { INTRGEN, 2, 89 }, +"fpmake", { INTRGEN, 2, 91 }, +"fpscal", { INTRGEN, 2, 93 }, + +"" }; + + +LOCAL struct Specblock + { + char atype; /* Argument type; every arg must have + this type */ + char rtype; /* Result type */ + char nargs; /* Number of arguments */ + char spxname[8]; /* Name of the function in Fortran */ + char othername; /* index into callbyvalue table */ + } spectab[ ] = +{ + { TYREAL,TYREAL,1,"r_int" }, + { TYDREAL,TYDREAL,1,"d_int" }, + + { TYREAL,TYREAL,1,"r_nint" }, + { TYDREAL,TYDREAL,1,"d_nint" }, + + { TYREAL,TYSHORT,1,"h_nint" }, + { TYREAL,TYLONG,1,"i_nint" }, + + { TYDREAL,TYSHORT,1,"h_dnnt" }, + { TYDREAL,TYLONG,1,"i_dnnt" }, + + { TYREAL,TYREAL,1,"r_abs" }, + { TYSHORT,TYSHORT,1,"h_abs" }, + { TYLONG,TYLONG,1,"i_abs" }, + { TYDREAL,TYDREAL,1,"d_abs" }, + { TYCOMPLEX,TYREAL,1,"c_abs" }, + { TYDCOMPLEX,TYDREAL,1,"z_abs" }, + + { TYSHORT,TYSHORT,2,"h_mod" }, + { TYLONG,TYLONG,2,"i_mod" }, + { TYREAL,TYREAL,2,"r_mod" }, + { TYDREAL,TYDREAL,2,"d_mod" }, + + { TYREAL,TYREAL,2,"r_sign" }, + { TYSHORT,TYSHORT,2,"h_sign" }, + { TYLONG,TYLONG,2,"i_sign" }, + { TYDREAL,TYDREAL,2,"d_sign" }, + + { TYREAL,TYREAL,2,"r_dim" }, + { TYSHORT,TYSHORT,2,"h_dim" }, + { TYLONG,TYLONG,2,"i_dim" }, + { TYDREAL,TYDREAL,2,"d_dim" }, + + { TYREAL,TYDREAL,2,"d_prod" }, + + { TYCHAR,TYSHORT,1,"h_len" }, + { TYCHAR,TYLONG,1,"i_len" }, + + { TYCHAR,TYSHORT,2,"h_indx" }, + { TYCHAR,TYLONG,2,"i_indx" }, + + { TYCOMPLEX,TYREAL,1,"r_imag" }, + { TYDCOMPLEX,TYDREAL,1,"d_imag" }, + { TYCOMPLEX,TYCOMPLEX,1,"r_cnjg" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"d_cnjg" }, + + { TYREAL,TYREAL,1,"r_sqrt", 1 }, + { TYDREAL,TYDREAL,1,"d_sqrt", 1 }, + { TYCOMPLEX,TYCOMPLEX,1,"c_sqrt" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"z_sqrt" }, + + { TYREAL,TYREAL,1,"r_exp", 2 }, + { TYDREAL,TYDREAL,1,"d_exp", 2 }, + { TYCOMPLEX,TYCOMPLEX,1,"c_exp" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"z_exp" }, + + { TYREAL,TYREAL,1,"r_log", 3 }, + { TYDREAL,TYDREAL,1,"d_log", 3 }, + { TYCOMPLEX,TYCOMPLEX,1,"c_log" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"z_log" }, + + { TYREAL,TYREAL,1,"r_lg10" }, + { TYDREAL,TYDREAL,1,"d_lg10" }, + + { TYREAL,TYREAL,1,"r_sin", 4 }, + { TYDREAL,TYDREAL,1,"d_sin", 4 }, + { TYCOMPLEX,TYCOMPLEX,1,"c_sin" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"z_sin" }, + + { TYREAL,TYREAL,1,"r_cos", 5 }, + { TYDREAL,TYDREAL,1,"d_cos", 5 }, + { TYCOMPLEX,TYCOMPLEX,1,"c_cos" }, + { TYDCOMPLEX,TYDCOMPLEX,1,"z_cos" }, + + { TYREAL,TYREAL,1,"r_tan", 6 }, + { TYDREAL,TYDREAL,1,"d_tan", 6 }, + + { TYREAL,TYREAL,1,"r_asin", 7 }, + { TYDREAL,TYDREAL,1,"d_asin", 7 }, + + { TYREAL,TYREAL,1,"r_acos", 8 }, + { TYDREAL,TYDREAL,1,"d_acos", 8 }, + + { TYREAL,TYREAL,1,"r_atan", 9 }, + { TYDREAL,TYDREAL,1,"d_atan", 9 }, + + { TYREAL,TYREAL,2,"r_atn2", 10 }, + { TYDREAL,TYDREAL,2,"d_atn2", 10 }, + + { TYREAL,TYREAL,1,"r_sinh", 11 }, + { TYDREAL,TYDREAL,1,"d_sinh", 11 }, + + { TYREAL,TYREAL,1,"r_cosh", 12 }, + { TYDREAL,TYDREAL,1,"d_cosh", 12 }, + + { TYREAL,TYREAL,1,"r_tanh", 13 }, + { TYDREAL,TYDREAL,1,"d_tanh", 13 }, + + { TYCHAR,TYLOGICAL,2,"hl_ge" }, + { TYCHAR,TYLOGICAL,2,"l_ge" }, + + { TYCHAR,TYLOGICAL,2,"hl_gt" }, + { TYCHAR,TYLOGICAL,2,"l_gt" }, + + { TYCHAR,TYLOGICAL,2,"hl_le" }, + { TYCHAR,TYLOGICAL,2,"l_le" }, + + { TYCHAR,TYLOGICAL,2,"hl_lt" }, + { TYCHAR,TYLOGICAL,2,"l_lt" }, + + { TYREAL,TYSHORT,1,"hr_expn" }, + { TYREAL,TYLONG,1,"ir_expn" }, + { TYDREAL,TYSHORT,1,"hd_expn" }, + { TYDREAL,TYLONG,1,"id_expn" }, + + { TYREAL,TYREAL,1,"r_absp" }, + { TYDREAL,TYDREAL,1,"d_absp" }, + + { TYREAL,TYDREAL,1,"r_rrsp" }, + { TYDREAL,TYDREAL,1,"d_rrsp" }, + + { TYREAL,TYREAL,1,"r_frac" }, + { TYDREAL,TYDREAL,1,"d_frac" }, + + { TYREAL,TYREAL,2,"r_make" }, + { TYDREAL,TYDREAL,2,"d_make" }, + + { TYREAL,TYREAL,2,"r_scal" }, + { TYDREAL,TYDREAL,2,"d_scal" }, + { 0 } +} ; + +#if 0 +LOCAL struct Incstblock + { + char atype; + char rtype; + char constno; + } consttab[ ] = +{ + { TYSHORT, TYLONG, 0 }, + { TYLONG, TYLONG, 1 }, + { TYREAL, TYLONG, 2 }, + { TYDREAL, TYLONG, 3 }, + + { TYSHORT, TYLONG, 4 }, + { TYLONG, TYLONG, 5 }, + { TYREAL, TYLONG, 6 }, + { TYDREAL, TYLONG, 7 }, + + { TYREAL, TYLONG, 8 }, + { TYDREAL, TYLONG, 9 }, + + { TYREAL, TYLONG, 10 }, + { TYDREAL, TYLONG, 11 }, + + { TYREAL, TYREAL, 0 }, + { TYDREAL, TYDREAL, 1 }, + + { TYSHORT, TYLONG, 12 }, + { TYLONG, TYLONG, 13 }, + { TYREAL, TYREAL, 2 }, + { TYDREAL, TYDREAL, 3 }, + + { TYREAL, TYREAL, 4 }, + { TYDREAL, TYDREAL, 5 } +}; +#endif + +char *callbyvalue[ ] = + {0, + "sqrt", + "exp", + "log", + "sin", + "cos", + "tan", + "asin", + "acos", + "atan", + "atan2", + "sinh", + "cosh", + "tanh" + }; + + void +r8fix() /* adjust tables for -r8 */ +{ + register struct Intrblock *I; + register struct Specblock *S; + + for(I = intrtab; I->intrfname[0]; I++) + if (I->intrval.intrgroup != INTRGEN) + switch(I->intrval.intrstuff) { + case TYREAL: + I->intrval.intrstuff = TYDREAL; + I->intrval.intrno = I->intrval.dblintrno; + break; + case TYCOMPLEX: + I->intrval.intrstuff = TYDCOMPLEX; + I->intrval.intrno = I->intrval.dblintrno; + I->intrval.dblcmplx = 1; + } + + for(S = spectab; S->atype; S++) + switch(S->atype) { + case TYCOMPLEX: + S->atype = TYDCOMPLEX; + if (S->rtype == TYREAL) + S->rtype = TYDREAL; + else if (S->rtype == TYCOMPLEX) + S->rtype = TYDCOMPLEX; + switch(S->spxname[0]) { + case 'r': + S->spxname[0] = 'd'; + break; + case 'c': + S->spxname[0] = 'z'; + break; + default: + Fatal("r8fix bug"); + } + break; + case TYREAL: + S->atype = TYDREAL; + switch(S->rtype) { + case TYREAL: + S->rtype = TYDREAL; + if (S->spxname[0] != 'r') + Fatal("r8fix bug"); + S->spxname[0] = 'd'; + case TYDREAL: /* d_prod */ + break; + + case TYSHORT: + if (!strcmp(S->spxname, "hr_expn")) + S->spxname[1] = 'd'; + else if (!strcmp(S->spxname, "h_nint")) + strcpy(S->spxname, "h_dnnt"); + else Fatal("r8fix bug"); + break; + + case TYLONG: + if (!strcmp(S->spxname, "ir_expn")) + S->spxname[1] = 'd'; + else if (!strcmp(S->spxname, "i_nint")) + strcpy(S->spxname, "i_dnnt"); + else Fatal("r8fix bug"); + break; + + default: + Fatal("r8fix bug"); + } + } + } + +expptr intrcall(np, argsp, nargs) +Namep np; +struct Listblock *argsp; +int nargs; +{ + int i, rettype; + Addrp ap; + register struct Specblock *sp; + register struct Chain *cp; + expptr Inline(), mkcxcon(), mkrealcon(); + expptr q, ep; + int mtype; + int op; + int f1field, f2field, f3field; + + packed.ijunk = np->vardesc.varno; + f1field = packed.bits.f1; + f2field = packed.bits.f2; + f3field = packed.bits.f3; + if(nargs == 0) + goto badnargs; + + mtype = 0; + for(cp = argsp->listp ; cp ; cp = cp->nextp) + { + ep = (expptr)cp->datap; + if( ISCONST(ep) && ep->headblock.vtype==TYSHORT ) + cp->datap = (char *) mkconv(tyint, ep); + mtype = maxtype(mtype, ep->headblock.vtype); + } + + switch(f1field) + { + case INTRBOOL: + op = f3field; + if( ! ONEOF(mtype, MSKINT|MSKLOGICAL) ) + goto badtype; + if(op == OPBITNOT) + { + if(nargs != 1) + goto badnargs; + q = mkexpr(OPBITNOT, (expptr)argsp->listp->datap, ENULL); + } + else + { + if(nargs != 2) + goto badnargs; + q = mkexpr(op, (expptr)argsp->listp->datap, + (expptr)argsp->listp->nextp->datap); + } + frchain( &(argsp->listp) ); + free( (charptr) argsp); + return(q); + + case INTRCONV: + rettype = f2field; + switch(rettype) { + case TYLONG: + rettype = tyint; + break; + case TYLOGICAL: + rettype = tylog; + } + if( ISCOMPLEX(rettype) && nargs==2) + { + expptr qr, qi; + qr = (expptr) argsp->listp->datap; + qi = (expptr) argsp->listp->nextp->datap; + if(ISCONST(qr) && ISCONST(qi)) + q = mkcxcon(qr,qi); + else q = mkexpr(OPCONV,mkconv(rettype-2,qr), + mkconv(rettype-2,qi)); + } + else if(nargs == 1) { + if (f3field && ((Exprp)argsp->listp->datap)->vtype + == TYDCOMPLEX) + rettype = TYDREAL; + q = mkconv(rettype+100, (expptr)argsp->listp->datap); + if (q->tag == TADDR) + q->addrblock.parenused = 1; + } + else goto badnargs; + + q->headblock.vtype = rettype; + frchain(&(argsp->listp)); + free( (charptr) argsp); + return(q); + + +#if 0 + case INTRCNST: + +/* Machine-dependent f77 stuff that f2c omits: + +intcon contains + radix for short int + radix for long int + radix for single precision + radix for double precision + precision for short int + precision for long int + precision for single precision + precision for double precision + emin for single precision + emin for double precision + emax for single precision + emax for double prcision + largest short int + largest long int + +realcon contains + tiny for single precision + tiny for double precision + huge for single precision + huge for double precision + mrsp (epsilon) for single precision + mrsp (epsilon) for double precision +*/ + { register struct Incstblock *cstp; + extern ftnint intcon[14]; + extern double realcon[6]; + + cstp = consttab + f3field; + for(i=0 ; i<f2field ; ++i) + if(cstp->atype == mtype) + goto foundconst; + else + ++cstp; + goto badtype; + +foundconst: + switch(cstp->rtype) + { + case TYLONG: + return(mkintcon(intcon[cstp->constno])); + + case TYREAL: + case TYDREAL: + return(mkrealcon(cstp->rtype, + realcon[cstp->constno]) ); + + default: + Fatal("impossible intrinsic constant"); + } + } +#endif + + case INTRGEN: + sp = spectab + f3field; + if(no66flag) + if(sp->atype == mtype) + goto specfunct; + else err66("generic function"); + + for(i=0; i<f2field ; ++i) + if(sp->atype == mtype) + goto specfunct; + else + ++sp; + warn1 ("bad argument type to intrinsic %s", np->fvarname); + +/* Made this a warning rather than an error so things like "log (5) ==> + log (5.0)" can be accommodated. When none of these cases matches, the + argument is cast up to the first type in the spectab list; this first + type is assumed to be the "smallest" type, e.g. REAL before DREAL + before COMPLEX, before DCOMPLEX */ + + sp = spectab + f3field; + mtype = sp -> atype; + goto specfunct; + + case INTRSPEC: + sp = spectab + f3field; +specfunct: + if(tyint==TYLONG && ONEOF(sp->rtype,M(TYSHORT)|M(TYLOGICAL)) + && (sp+1)->atype==sp->atype) + ++sp; + + if(nargs != sp->nargs) + goto badnargs; + if(mtype != sp->atype) + goto badtype; + +/* NOTE!! I moved fixargs (YES) into the ELSE branch so that constants in + the inline expression wouldn't get put into the constant table */ + + fixargs (NO, argsp); + cast_args (mtype, argsp -> listp); + + if(q = Inline((int)(sp-spectab), mtype, argsp->listp)) + { + frchain( &(argsp->listp) ); + free( (charptr) argsp); + } else { + + if(sp->othername) { + /* C library routines that return double... */ + /* sp->rtype might be TYREAL */ + ap = builtin(sp->rtype, + callbyvalue[sp->othername], 1); + q = fixexpr((Exprp) + mkexpr(OPCCALL, (expptr)ap, (expptr)argsp) ); + } else { + fixargs(YES, argsp); + ap = builtin(sp->rtype, sp->spxname, 0); + q = fixexpr((Exprp) + mkexpr(OPCALL, (expptr)ap, (expptr)argsp) ); + } /* else */ + } /* else */ + return(q); + + case INTRMIN: + case INTRMAX: + if(nargs < 2) + goto badnargs; + if( ! ONEOF(mtype, MSKINT|MSKREAL) ) + goto badtype; + argsp->vtype = mtype; + q = mkexpr( (f1field==INTRMIN ? OPMIN : OPMAX), (expptr)argsp, ENULL); + + q->headblock.vtype = mtype; + rettype = f2field; + if(rettype == TYLONG) + rettype = tyint; + else if(rettype == TYUNKNOWN) + rettype = mtype; + return( mkconv(rettype, q) ); + + default: + fatali("intrcall: bad intrgroup %d", f1field); + } +badnargs: + errstr("bad number of arguments to intrinsic %s", np->fvarname); + goto bad; + +badtype: + errstr("bad argument type to intrinsic %s", np->fvarname); + +bad: + return( errnode() ); +} + + + + +intrfunct(s) +char *s; +{ + register struct Intrblock *p; + + for(p = intrtab; p->intrval.intrgroup!=INTREND ; ++p) + { + if( !strcmp(s, p->intrfname) ) + { + packed.bits.f1 = p->intrval.intrgroup; + packed.bits.f2 = p->intrval.intrstuff; + packed.bits.f3 = p->intrval.intrno; + packed.bits.f4 = p->intrval.dblcmplx; + return(packed.ijunk); + } + } + + return(0); +} + + + + + +Addrp intraddr(np) +Namep np; +{ + Addrp q; + register struct Specblock *sp; + int f3field; + + if(np->vclass!=CLPROC || np->vprocclass!=PINTRINSIC) + fatalstr("intraddr: %s is not intrinsic", np->fvarname); + packed.ijunk = np->vardesc.varno; + f3field = packed.bits.f3; + + switch(packed.bits.f1) + { + case INTRGEN: + /* imag, log, and log10 arent specific functions */ + if(f3field==31 || f3field==43 || f3field==47) + goto bad; + + case INTRSPEC: + sp = spectab + f3field; + if (tyint == TYLONG + && (sp->rtype == TYSHORT || sp->rtype == TYLOGICAL)) + ++sp; + q = builtin(sp->rtype, sp->spxname, + sp->othername ? 1 : 0); + return(q); + + case INTRCONV: + case INTRMIN: + case INTRMAX: + case INTRBOOL: + case INTRCNST: +bad: + errstr("cannot pass %s as actual", np->fvarname); + return((Addrp)errnode()); + } + fatali("intraddr: impossible f1=%d\n", (int) packed.bits.f1); + /* NOT REACHED */ return 0; +} + + + +void cast_args (maxtype, args) +int maxtype; +chainp args; +{ + for (; args; args = args -> nextp) { + expptr e = (expptr) args->datap; + if (e -> headblock.vtype != maxtype) + if (e -> tag == TCONST) + args->datap = (char *) mkconv(maxtype, e); + else { + Addrp temp = mktmp(maxtype, ENULL); + + puteq(cpexpr((expptr)temp), e); + args->datap = (char *)temp; + } /* else */ + } /* for */ +} /* cast_args */ + + + +expptr Inline(fno, type, args) +int fno; +int type; +struct Chain *args; +{ + register expptr q, t, t1; + + switch(fno) + { + case 8: /* real abs */ + case 9: /* short int abs */ + case 10: /* long int abs */ + case 11: /* double precision abs */ + if( addressable(q = (expptr) args->datap) ) + { + t = q; + q = NULL; + } + else + t = (expptr) mktmp(type,ENULL); + t1 = mkexpr(type == TYREAL && forcedouble ? OPDABS : OPABS, + cpexpr(t), ENULL); + if(q) + t1 = mkexpr(OPCOMMA, mkexpr(OPASSIGN, cpexpr(t),q), t1); + frexpr(t); + return(t1); + + case 26: /* dprod */ + q = mkexpr(OPSTAR, mkconv(TYDREAL,(expptr)args->datap), + (expptr)args->nextp->datap); + return(q); + + case 27: /* len of character string */ + q = (expptr) cpexpr(((tagptr)args->datap)->headblock.vleng); + frexpr((expptr)args->datap); + return(q); + + case 14: /* half-integer mod */ + case 15: /* mod */ + return mkexpr(OPMOD, (expptr) args->datap, + (expptr) args->nextp->datap); + } + return(NULL); +} diff --git a/usr.bin/f2c/io.c b/usr.bin/f2c/io.c new file mode 100644 index 000000000000..761876ca00b1 --- /dev/null +++ b/usr.bin/f2c/io.c @@ -0,0 +1,1420 @@ +/**************************************************************** +Copyright 1990, 1991, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* Routines to generate code for I/O statements. + Some corrections and improvements due to David Wasley, U. C. Berkeley +*/ + +/* TEMPORARY */ +#define TYIOINT TYLONG +#define SZIOINT SZLONG + +#include "defs.h" +#include "names.h" +#include "iob.h" + +extern int inqmask; + +LOCAL void dofclose(), dofinquire(), dofinquire(), dofmove(), dofopen(), + doiolist(), ioset(), ioseta(), iosetc(), iosetip(), iosetlc(), + putio(), putiocall(); + +iob_data *iob_list; +Addrp io_structs[9]; + +LOCAL char ioroutine[12]; + +LOCAL long ioendlab; +LOCAL long ioerrlab; +LOCAL int endbit; +LOCAL int errbit; +LOCAL long jumplab; +LOCAL long skiplab; +LOCAL int ioformatted; +LOCAL int statstruct = NO; +LOCAL struct Labelblock *skiplabel; +Addrp ioblkp; + +#define UNFORMATTED 0 +#define FORMATTED 1 +#define LISTDIRECTED 2 +#define NAMEDIRECTED 3 + +#define V(z) ioc[z].iocval + +#define IOALL 07777 + +LOCAL struct Ioclist +{ + char *iocname; + int iotype; + expptr iocval; +} +ioc[ ] = +{ + { "", 0 }, + { "unit", IOALL }, + { "fmt", M(IOREAD) | M(IOWRITE) }, + { "err", IOALL }, + { "end", M(IOREAD) }, + { "iostat", IOALL }, + { "rec", M(IOREAD) | M(IOWRITE) }, + { "recl", M(IOOPEN) | M(IOINQUIRE) }, + { "file", M(IOOPEN) | M(IOINQUIRE) }, + { "status", M(IOOPEN) | M(IOCLOSE) }, + { "access", M(IOOPEN) | M(IOINQUIRE) }, + { "form", M(IOOPEN) | M(IOINQUIRE) }, + { "blank", M(IOOPEN) | M(IOINQUIRE) }, + { "exist", M(IOINQUIRE) }, + { "opened", M(IOINQUIRE) }, + { "number", M(IOINQUIRE) }, + { "named", M(IOINQUIRE) }, + { "name", M(IOINQUIRE) }, + { "sequential", M(IOINQUIRE) }, + { "direct", M(IOINQUIRE) }, + { "formatted", M(IOINQUIRE) }, + { "unformatted", M(IOINQUIRE) }, + { "nextrec", M(IOINQUIRE) }, + { "nml", M(IOREAD) | M(IOWRITE) } +}; + +#define NIOS (sizeof(ioc)/sizeof(struct Ioclist) - 1) + +/* #define IOSUNIT 1 */ +/* #define IOSFMT 2 */ +#define IOSERR 3 +#define IOSEND 4 +#define IOSIOSTAT 5 +#define IOSREC 6 +#define IOSRECL 7 +#define IOSFILE 8 +#define IOSSTATUS 9 +#define IOSACCESS 10 +#define IOSFORM 11 +#define IOSBLANK 12 +#define IOSEXISTS 13 +#define IOSOPENED 14 +#define IOSNUMBER 15 +#define IOSNAMED 16 +#define IOSNAME 17 +#define IOSSEQUENTIAL 18 +#define IOSDIRECT 19 +#define IOSFORMATTED 20 +#define IOSUNFORMATTED 21 +#define IOSNEXTREC 22 +#define IOSNML 23 + +#define IOSTP V(IOSIOSTAT) + + +/* offsets in generated structures */ + +#define SZFLAG SZIOINT + +/* offsets for external READ and WRITE statements */ + +#define XERR 0 +#define XUNIT SZFLAG +#define XEND SZFLAG + SZIOINT +#define XFMT 2*SZFLAG + SZIOINT +#define XREC 2*SZFLAG + SZIOINT + SZADDR + +/* offsets for internal READ and WRITE statements */ + +#define XIUNIT SZFLAG +#define XIEND SZFLAG + SZADDR +#define XIFMT 2*SZFLAG + SZADDR +#define XIRLEN 2*SZFLAG + 2*SZADDR +#define XIRNUM 2*SZFLAG + 2*SZADDR + SZIOINT +#define XIREC 2*SZFLAG + 2*SZADDR + 2*SZIOINT + +/* offsets for OPEN statements */ + +#define XFNAME SZFLAG + SZIOINT +#define XFNAMELEN SZFLAG + SZIOINT + SZADDR +#define XSTATUS SZFLAG + 2*SZIOINT + SZADDR +#define XACCESS SZFLAG + 2*SZIOINT + 2*SZADDR +#define XFORMATTED SZFLAG + 2*SZIOINT + 3*SZADDR +#define XRECLEN SZFLAG + 2*SZIOINT + 4*SZADDR +#define XBLANK SZFLAG + 3*SZIOINT + 4*SZADDR + +/* offset for CLOSE statement */ + +#define XCLSTATUS SZFLAG + SZIOINT + +/* offsets for INQUIRE statement */ + +#define XFILE SZFLAG + SZIOINT +#define XFILELEN SZFLAG + SZIOINT + SZADDR +#define XEXISTS SZFLAG + 2*SZIOINT + SZADDR +#define XOPEN SZFLAG + 2*SZIOINT + 2*SZADDR +#define XNUMBER SZFLAG + 2*SZIOINT + 3*SZADDR +#define XNAMED SZFLAG + 2*SZIOINT + 4*SZADDR +#define XNAME SZFLAG + 2*SZIOINT + 5*SZADDR +#define XNAMELEN SZFLAG + 2*SZIOINT + 6*SZADDR +#define XQACCESS SZFLAG + 3*SZIOINT + 6*SZADDR +#define XQACCLEN SZFLAG + 3*SZIOINT + 7*SZADDR +#define XSEQ SZFLAG + 4*SZIOINT + 7*SZADDR +#define XSEQLEN SZFLAG + 4*SZIOINT + 8*SZADDR +#define XDIRECT SZFLAG + 5*SZIOINT + 8*SZADDR +#define XDIRLEN SZFLAG + 5*SZIOINT + 9*SZADDR +#define XFORM SZFLAG + 6*SZIOINT + 9*SZADDR +#define XFORMLEN SZFLAG + 6*SZIOINT + 10*SZADDR +#define XFMTED SZFLAG + 7*SZIOINT + 10*SZADDR +#define XFMTEDLEN SZFLAG + 7*SZIOINT + 11*SZADDR +#define XUNFMT SZFLAG + 8*SZIOINT + 11*SZADDR +#define XUNFMTLEN SZFLAG + 8*SZIOINT + 12*SZADDR +#define XQRECL SZFLAG + 9*SZIOINT + 12*SZADDR +#define XNEXTREC SZFLAG + 9*SZIOINT + 13*SZADDR +#define XQBLANK SZFLAG + 9*SZIOINT + 14*SZADDR +#define XQBLANKLEN SZFLAG + 9*SZIOINT + 15*SZADDR + +LOCAL char *cilist_names[] = { + "cilist", + "cierr", + "ciunit", + "ciend", + "cifmt", + "cirec" + }; +LOCAL char *icilist_names[] = { + "icilist", + "icierr", + "iciunit", + "iciend", + "icifmt", + "icirlen", + "icirnum" + }; +LOCAL char *olist_names[] = { + "olist", + "oerr", + "ounit", + "ofnm", + "ofnmlen", + "osta", + "oacc", + "ofm", + "orl", + "oblnk" + }; +LOCAL char *cllist_names[] = { + "cllist", + "cerr", + "cunit", + "csta" + }; +LOCAL char *alist_names[] = { + "alist", + "aerr", + "aunit" + }; +LOCAL char *inlist_names[] = { + "inlist", + "inerr", + "inunit", + "infile", + "infilen", + "inex", + "inopen", + "innum", + "innamed", + "inname", + "innamlen", + "inacc", + "inacclen", + "inseq", + "inseqlen", + "indir", + "indirlen", + "infmt", + "infmtlen", + "inform", + "informlen", + "inunf", + "inunflen", + "inrecl", + "innrec", + "inblank", + "inblanklen" + }; + +LOCAL char **io_fields; + +#define zork(n,t) n, sizeof(n)/sizeof(char *) - 1, t + +LOCAL io_setup io_stuff[] = { + zork(cilist_names, TYCILIST), /* external read/write */ + zork(inlist_names, TYINLIST), /* inquire */ + zork(olist_names, TYOLIST), /* open */ + zork(cllist_names, TYCLLIST), /* close */ + zork(alist_names, TYALIST), /* rewind */ + zork(alist_names, TYALIST), /* backspace */ + zork(alist_names, TYALIST), /* endfile */ + zork(icilist_names,TYICILIST), /* internal read */ + zork(icilist_names,TYICILIST) /* internal write */ + }; + +#undef zork + + +fmtstmt(lp) +register struct Labelblock *lp; +{ + if(lp == NULL) + { + execerr("unlabeled format statement" , CNULL); + return(-1); + } + if(lp->labtype == LABUNKNOWN) + { + lp->labtype = LABFORMAT; + lp->labelno = newlabel(); + } + else if(lp->labtype != LABFORMAT) + { + execerr("bad format number", CNULL); + return(-1); + } + return(lp->labelno); +} + + +setfmt(lp) +struct Labelblock *lp; +{ + int n; + char *s0, *lexline(); + register char *s, *se, *t; + register k; + + s0 = s = lexline(&n); + se = t = s + n; + + /* warn of trivial errors, e.g. " 11 CONTINUE" (one too few spaces) */ + /* following FORMAT... */ + + if (n <= 0) + warn("No (...) after FORMAT"); + else if (*s != '(') + warni("%c rather than ( after FORMAT", *s); + else if (se[-1] != ')') { + *se = 0; + while(--t > s && *t != ')') ; + if (t <= s) + warn("No ) at end of FORMAT statement"); + else if (se - t > 30) + warn1("Extraneous text at end of FORMAT: ...%s", se-12); + else + warn1("Extraneous text at end of FORMAT: %s", t+1); + t = se; + } + + /* fix MYQUOTES (\002's) and \\'s */ + + while(s < se) + switch(*s++) { + case 2: + t += 3; break; + case '"': + case '\\': + t++; break; + } + s = s0; + if (lp) { + lp->fmtstring = t = mem((int)(t - s + 1), 0); + while(s < se) + switch(k = *s++) { + case 2: + t[0] = '\\'; + t[1] = '0'; + t[2] = '0'; + t[3] = '2'; + t += 4; + break; + case '"': + case '\\': + *t++ = '\\'; + /* no break */ + default: + *t++ = k; + } + *t = 0; + } + flline(); +} + + + +startioctl() +{ + register int i; + + inioctl = YES; + nioctl = 0; + ioformatted = UNFORMATTED; + for(i = 1 ; i<=NIOS ; ++i) + V(i) = NULL; +} + + static long +newiolabel() { + long rv; + rv = ++lastiolabno; + skiplabel = mklabel(rv); + skiplabel->labdefined = 1; + return rv; + } + + +endioctl() +{ + int i; + expptr p; + struct io_setup *ios; + + inioctl = NO; + + /* set up for error recovery */ + + ioerrlab = ioendlab = skiplab = jumplab = 0; + + if(p = V(IOSEND)) + if(ISICON(p)) + execlab(ioendlab = p->constblock.Const.ci); + else + err("bad end= clause"); + + if(p = V(IOSERR)) + if(ISICON(p)) + execlab(ioerrlab = p->constblock.Const.ci); + else + err("bad err= clause"); + + if(IOSTP) + if(IOSTP->tag!=TADDR || ! ISINT(IOSTP->addrblock.vtype) ) + { + err("iostat must be an integer variable"); + frexpr(IOSTP); + IOSTP = NULL; + } + + if(iostmt == IOREAD) + { + if(IOSTP) + { + if(ioerrlab && ioendlab && ioerrlab==ioendlab) + jumplab = ioerrlab; + else + skiplab = jumplab = newiolabel(); + } + else { + if(ioerrlab && ioendlab && ioerrlab!=ioendlab) + { + IOSTP = (expptr) mktmp(TYINT, ENULL); + skiplab = jumplab = newiolabel(); + } + else + jumplab = (ioerrlab ? ioerrlab : ioendlab); + } + } + else if(iostmt == IOWRITE) + { + if(IOSTP && !ioerrlab) + skiplab = jumplab = newiolabel(); + else + jumplab = ioerrlab; + } + else + jumplab = ioerrlab; + + endbit = IOSTP!=NULL || ioendlab!=0; /* for use in startrw() */ + errbit = IOSTP!=NULL || ioerrlab!=0; + if (jumplab && !IOSTP) + IOSTP = (expptr) mktmp(TYINT, ENULL); + + if(iostmt!=IOREAD && iostmt!=IOWRITE) + { + ios = io_stuff + iostmt; + io_fields = ios->fields; + ioblkp = io_structs[iostmt]; + if(ioblkp == NULL) + io_structs[iostmt] = ioblkp = + autovar(1, ios->type, ENULL, ""); + ioset(TYIOINT, XERR, ICON(errbit)); + } + + switch(iostmt) + { + case IOOPEN: + dofopen(); + break; + + case IOCLOSE: + dofclose(); + break; + + case IOINQUIRE: + dofinquire(); + break; + + case IOBACKSPACE: + dofmove("f_back"); + break; + + case IOREWIND: + dofmove("f_rew"); + break; + + case IOENDFILE: + dofmove("f_end"); + break; + + case IOREAD: + case IOWRITE: + startrw(); + break; + + default: + fatali("impossible iostmt %d", iostmt); + } + for(i = 1 ; i<=NIOS ; ++i) + if(i!=IOSIOSTAT && V(i)!=NULL) + frexpr(V(i)); +} + + + +iocname() +{ + register int i; + int found, mask; + + found = 0; + mask = M(iostmt); + for(i = 1 ; i <= NIOS ; ++i) + if(!strcmp(ioc[i].iocname, token)) + if(ioc[i].iotype & mask) + return(i); + else { + found = i; + break; + } + if(found) { + if (iostmt == IOOPEN && !strcmp(ioc[i].iocname, "name")) { + NOEXT("open with \"name=\" treated as \"file=\""); + for(i = 1; strcmp(ioc[i].iocname, "file"); i++); + return i; + } + errstr("invalid control %s for statement", ioc[found].iocname); + } + else + errstr("unknown iocontrol %s", token); + return(IOSBAD); +} + + +ioclause(n, p) +register int n; +register expptr p; +{ + struct Ioclist *iocp; + + ++nioctl; + if(n == IOSBAD) + return; + if(n == IOSPOSITIONAL) + { + n = nioctl; + if (n == IOSFMT) { + if (iostmt == IOOPEN) { + n = IOSFILE; + NOEXT("file= specifier omitted from open"); + } + else if (iostmt < IOREAD) + goto illegal; + } + else if(n > IOSFMT) + { + illegal: + err("illegal positional iocontrol"); + return; + } + } + else if (n == IOSNML) + n = IOSFMT; + + if(p == NULL) + { + if(n == IOSUNIT) + p = (expptr) (iostmt==IOREAD ? IOSTDIN : IOSTDOUT); + else if(n != IOSFMT) + { + err("illegal * iocontrol"); + return; + } + } + if(n == IOSFMT) + ioformatted = (p==NULL ? LISTDIRECTED : FORMATTED); + + iocp = & ioc[n]; + if(iocp->iocval == NULL) + { + if(n!=IOSFMT && ( n!=IOSUNIT || (p && p->headblock.vtype!=TYCHAR) ) ) + p = fixtype(p); + else if (p && p->tag == TPRIM + && p->primblock.namep->vclass == CLUNKNOWN) { + /* kludge made necessary by attempt to infer types + * for untyped external parameters: given an error + * in calling sequences, an integer argument might + * tentatively be assumed TYCHAR; this would otherwise + * be corrected too late in startrw after startrw + * had decided this to be an internal file. + */ + vardcl(p->primblock.namep); + p->primblock.vtype = p->primblock.namep->vtype; + } + iocp->iocval = p; + } + else + errstr("iocontrol %s repeated", iocp->iocname); +} + +/* io list item */ + +doio(list) +chainp list; +{ + expptr call0(); + + if(ioformatted == NAMEDIRECTED) + { + if(list) + err("no I/O list allowed in NAMELIST read/write"); + } + else + { + doiolist(list); + ioroutine[0] = 'e'; + if (skiplab || ioroutine[4] == 'l') + jumplab = 0; + putiocall( call0(TYINT, ioroutine) ); + } +} + + + + + + LOCAL void +doiolist(p0) + chainp p0; +{ + chainp p; + register tagptr q; + register expptr qe; + register Namep qn; + Addrp tp, mkscalar(); + int range; + extern char *ohalign; + + for (p = p0 ; p ; p = p->nextp) + { + q = (tagptr)p->datap; + if(q->tag == TIMPLDO) + { + exdo(range=newlabel(), (Namep)0, + q->impldoblock.impdospec); + doiolist(q->impldoblock.datalist); + enddo(range); + free( (charptr) q); + } + else { + if(q->tag==TPRIM && q->primblock.argsp==NULL + && q->primblock.namep->vdim!=NULL) + { + vardcl(qn = q->primblock.namep); + if(qn->vdim->nelt) { + putio( fixtype(cpexpr(qn->vdim->nelt)), + (expptr)mkscalar(qn) ); + qn->vlastdim = 0; + } + else + err("attempt to i/o array of unknown size"); + } + else if(q->tag==TPRIM && q->primblock.argsp==NULL && + (qe = (expptr) memversion(q->primblock.namep)) ) + putio(ICON(1),qe); + else if (ISCONST(q) && q->constblock.vtype == TYCHAR) { + halign = 0; + putio(ICON(1), qe = fixtype(cpexpr(q))); + halign = ohalign; + } + else if(((qe = fixtype(cpexpr(q)))->tag==TADDR && + (qe->addrblock.uname_tag != UNAM_CONST || + !ISCOMPLEX(qe -> addrblock.vtype))) || + (qe -> tag == TCONST && !ISCOMPLEX(qe -> + headblock.vtype))) { + if (qe -> tag == TCONST) + qe = (expptr) putconst((Constp)qe); + putio(ICON(1), qe); + } + else if(qe->headblock.vtype != TYERROR) + { + if(iostmt == IOWRITE) + { + ftnint lencat(); + expptr qvl; + qvl = NULL; + if( ISCHAR(qe) ) + { + qvl = (expptr) + cpexpr(qe->headblock.vleng); + tp = mktmp(qe->headblock.vtype, + ICON(lencat(qe))); + } + else + tp = mktmp(qe->headblock.vtype, + qe->headblock.vleng); + puteq( cpexpr((expptr)tp), qe); + if(qvl) /* put right length on block */ + { + frexpr(tp->vleng); + tp->vleng = qvl; + } + putio(ICON(1), (expptr)tp); + } + else + err("non-left side in READ list"); + } + frexpr(q); + } + } + frchain( &p0 ); +} + + int iocalladdr = TYADDR; /* for fixing TYADDR in saveargtypes */ + int typeconv[TYERROR+1] = { +#ifdef TYQUAD + 0, 1, 11, 2, 3, 14, 4, 5, 6, 7, 12, 13, 8, 9, 10, 15 +#else + 0, 1, 11, 2, 3, 4, 5, 6, 7, 12, 13, 8, 9, 10, 14 +#endif + }; + + LOCAL void +putio(nelt, addr) + expptr nelt; + register expptr addr; +{ + int type; + register expptr q; + register Addrp c = 0; + + type = addr->headblock.vtype; + if(ioformatted!=LISTDIRECTED && ISCOMPLEX(type) ) + { + nelt = mkexpr(OPSTAR, ICON(2), nelt); + type -= (TYCOMPLEX-TYREAL); + } + + /* pass a length with every item. for noncharacter data, fake one */ + if(type != TYCHAR) + { + + if( ISCONST(addr) ) + addr = (expptr) putconst((Constp)addr); + c = ALLOC(Addrblock); + c->tag = TADDR; + c->vtype = TYLENG; + c->vstg = STGAUTO; + c->ntempelt = 1; + c->isarray = 1; + c->memoffset = ICON(0); + c->uname_tag = UNAM_IDENT; + c->charleng = 1; + sprintf(c->user.ident, "(ftnlen)sizeof(%s)", typename[type]); + addr = mkexpr(OPCHARCAST, addr, ENULL); + } + + nelt = fixtype( mkconv(tyioint,nelt) ); + if(ioformatted == LISTDIRECTED) { + expptr mc = mkconv(tyioint, ICON(typeconv[type])); + q = c ? call4(TYINT, "do_lio", mc, nelt, addr, (expptr)c) + : call3(TYINT, "do_lio", mc, nelt, addr); + } + else { + char *s = ioformatted==FORMATTED ? "do_fio" : "do_uio"; + q = c ? call3(TYINT, s, nelt, addr, (expptr)c) + : call2(TYINT, s, nelt, addr); + } + iocalladdr = TYCHAR; + putiocall(q); + iocalladdr = TYADDR; +} + + + + +endio() +{ + extern void p1_label(); + + if(skiplab) + { + if (ioformatted != NAMEDIRECTED) + p1_label((long)(skiplabel - labeltab)); + if(ioendlab) { + exif( mkexpr(OPLT, cpexpr(IOSTP), ICON(0))); + exgoto(execlab(ioendlab)); + exendif(); + } + if(ioerrlab) { + exif( mkexpr(iostmt==IOREAD||iostmt==IOWRITE + ? OPGT : OPNE, + cpexpr(IOSTP), ICON(0))); + exgoto(execlab(ioerrlab)); + exendif(); + } + } + + if(IOSTP) + frexpr(IOSTP); +} + + + + LOCAL void +putiocall(q) + register expptr q; +{ + int tyintsave; + + tyintsave = tyint; + tyint = tyioint; /* for -I2 and -i2 */ + + if(IOSTP) + { + q->headblock.vtype = TYINT; + q = fixexpr((Exprp)mkexpr(OPASSIGN, cpexpr(IOSTP), q)); + } + putexpr(q); + if(jumplab) { + exif(mkexpr(OPNE, cpexpr(IOSTP), ICON(0))); + exgoto(execlab(jumplab)); + exendif(); + } + tyint = tyintsave; +} + + void +fmtname(np, q) + Namep np; + register Addrp q; +{ + register int k; + register char *s, *t; + extern chainp assigned_fmts; + + if (!np->vfmt_asg) { + np->vfmt_asg = 1; + assigned_fmts = mkchain((char *)np, assigned_fmts); + } + k = strlen(s = np->fvarname); + if (k < IDENT_LEN - 4) { + q->uname_tag = UNAM_IDENT; + t = q->user.ident; + } + else { + q->uname_tag = UNAM_CHARP; + q->user.Charp = t = mem(k + 5,0); + } + sprintf(t, "%s_fmt", s); + } + +LOCAL Addrp asg_addr(p) + union Expression *p; +{ + register Addrp q; + + if (p->tag != TPRIM) + badtag("asg_addr", p->tag); + q = ALLOC(Addrblock); + q->tag = TADDR; + q->vtype = TYCHAR; + q->vstg = STGAUTO; + q->ntempelt = 1; + q->isarray = 0; + q->memoffset = ICON(0); + fmtname(p->primblock.namep, q); + return q; + } + +startrw() +{ + register expptr p; + register Namep np; + register Addrp unitp, fmtp, recp; + register expptr nump; + Addrp mkscalar(); + expptr mkaddcon(); + int iostmt1; + flag intfile, sequential, ok, varfmt; + struct io_setup *ios; + + /* First look at all the parameters and determine what is to be done */ + + ok = YES; + statstruct = YES; + + intfile = NO; + if(p = V(IOSUNIT)) + { + if( ISINT(p->headblock.vtype) ) { + int_unit: + unitp = (Addrp) cpexpr(p); + } + else if(p->headblock.vtype == TYCHAR) + { + if (nioctl == 1 && iostmt == IOREAD) { + /* kludge to recognize READ(format expr) */ + V(IOSFMT) = p; + V(IOSUNIT) = p = (expptr) IOSTDIN; + ioformatted = FORMATTED; + goto int_unit; + } + intfile = YES; + if(p->tag==TPRIM && p->primblock.argsp==NULL && + (np = p->primblock.namep)->vdim!=NULL) + { + vardcl(np); + if(nump = np->vdim->nelt) + { + nump = fixtype(cpexpr(nump)); + if( ! ISCONST(nump) ) { + statstruct = NO; + np->vlastdim = 0; + } + } + else + { + err("attempt to use internal unit array of unknown size"); + ok = NO; + nump = ICON(1); + } + unitp = mkscalar(np); + } + else { + nump = ICON(1); + unitp = (Addrp /*pjw */) fixtype(cpexpr(p)); + } + if(! isstatic((expptr)unitp) ) + statstruct = NO; + } + else { + err("unit specifier not of type integer or character"); + ok = NO; + } + } + else + { + err("bad unit specifier"); + ok = NO; + } + + sequential = YES; + if(p = V(IOSREC)) + if( ISINT(p->headblock.vtype) ) + { + recp = (Addrp) cpexpr(p); + sequential = NO; + } + else { + err("bad REC= clause"); + ok = NO; + } + else + recp = NULL; + + + varfmt = YES; + fmtp = NULL; + if(p = V(IOSFMT)) + { + if(p->tag==TPRIM && p->primblock.argsp==NULL) + { + np = p->primblock.namep; + if(np->vclass == CLNAMELIST) + { + ioformatted = NAMEDIRECTED; + fmtp = (Addrp) fixtype(p); + V(IOSFMT) = (expptr)fmtp; + if (skiplab) + jumplab = 0; + goto endfmt; + } + vardcl(np); + if(np->vdim) + { + if( ! ONEOF(np->vstg, MSKSTATIC) ) + statstruct = NO; + fmtp = mkscalar(np); + goto endfmt; + } + if( ISINT(np->vtype) ) /* ASSIGNed label */ + { + statstruct = NO; + varfmt = YES; + fmtp = asg_addr(p); + goto endfmt; + } + } + p = V(IOSFMT) = fixtype(p); + if(p->headblock.vtype == TYCHAR + /* Since we allow write(6,n) */ + /* we may as well allow write(6,n(2)) */ + || p->tag == TADDR && ISINT(p->addrblock.vtype)) + { + if( ! isstatic(p) ) + statstruct = NO; + fmtp = (Addrp) cpexpr(p); + } + else if( ISICON(p) ) + { + struct Labelblock *lp; + lp = mklabel(p->constblock.Const.ci); + if (fmtstmt(lp) > 0) + { + fmtp = (Addrp)mkaddcon(lp->stateno); + /* lp->stateno for names fmt_nnn */ + lp->fmtlabused = 1; + varfmt = NO; + } + else + ioformatted = UNFORMATTED; + } + else { + err("bad format descriptor"); + ioformatted = UNFORMATTED; + ok = NO; + } + } + else + fmtp = NULL; + +endfmt: + if(intfile) { + if (ioformatted==UNFORMATTED) { + err("unformatted internal I/O not allowed"); + ok = NO; + } + if (recp) { + err("direct internal I/O not allowed"); + ok = NO; + } + } + if(!sequential && ioformatted==LISTDIRECTED) + { + err("direct list-directed I/O not allowed"); + ok = NO; + } + if(!sequential && ioformatted==NAMEDIRECTED) + { + err("direct namelist I/O not allowed"); + ok = NO; + } + + if( ! ok ) { + statstruct = NO; + return; + } + + /* + Now put out the I/O structure, statically if all the clauses + are constants, dynamically otherwise +*/ + + if (intfile) { + ios = io_stuff + iostmt; + iostmt1 = IOREAD; + } + else { + ios = io_stuff; + iostmt1 = 0; + } + io_fields = ios->fields; + if(statstruct) + { + ioblkp = ALLOC(Addrblock); + ioblkp->tag = TADDR; + ioblkp->vtype = ios->type; + ioblkp->vclass = CLVAR; + ioblkp->vstg = STGINIT; + ioblkp->memno = ++lastvarno; + ioblkp->memoffset = ICON(0); + ioblkp -> uname_tag = UNAM_IDENT; + new_iob_data(ios, + temp_name("io_", lastvarno, ioblkp->user.ident)); } + else if(!(ioblkp = io_structs[iostmt1])) + io_structs[iostmt1] = ioblkp = + autovar(1, ios->type, ENULL, ""); + + ioset(TYIOINT, XERR, ICON(errbit)); + if(iostmt == IOREAD) + ioset(TYIOINT, (intfile ? XIEND : XEND), ICON(endbit) ); + + if(intfile) + { + ioset(TYIOINT, XIRNUM, nump); + ioset(TYIOINT, XIRLEN, cpexpr(unitp->vleng) ); + ioseta(XIUNIT, unitp); + } + else + ioset(TYIOINT, XUNIT, (expptr) unitp); + + if(recp) + ioset(TYIOINT, /* intfile ? XIREC : */ XREC, (expptr) recp); + + if(varfmt) + ioseta( intfile ? XIFMT : XFMT , fmtp); + else + ioset(TYADDR, intfile ? XIFMT : XFMT, (expptr) fmtp); + + ioroutine[0] = 's'; + ioroutine[1] = '_'; + ioroutine[2] = iostmt==IOREAD ? 'r' : 'w'; + ioroutine[3] = "ds"[sequential]; + ioroutine[4] = "ufln"[ioformatted]; + ioroutine[5] = "ei"[intfile]; + ioroutine[6] = '\0'; + + putiocall( call1(TYINT, ioroutine, cpexpr((expptr)ioblkp) )); + + if(statstruct) + { + frexpr((expptr)ioblkp); + statstruct = NO; + ioblkp = 0; /* unnecessary */ + } +} + + + + LOCAL void +dofopen() +{ + register expptr p; + + if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) ) + ioset(TYIOINT, XUNIT, cpexpr(p) ); + else + err("bad unit in open"); + if( (p = V(IOSFILE)) ) + if(p->headblock.vtype == TYCHAR) + ioset(TYIOINT, XFNAMELEN, cpexpr(p->headblock.vleng) ); + else + err("bad file in open"); + + iosetc(XFNAME, p); + + if(p = V(IOSRECL)) + if( ISINT(p->headblock.vtype) ) + ioset(TYIOINT, XRECLEN, cpexpr(p) ); + else + err("bad recl"); + else + ioset(TYIOINT, XRECLEN, ICON(0) ); + + iosetc(XSTATUS, V(IOSSTATUS)); + iosetc(XACCESS, V(IOSACCESS)); + iosetc(XFORMATTED, V(IOSFORM)); + iosetc(XBLANK, V(IOSBLANK)); + + putiocall( call1(TYINT, "f_open", cpexpr((expptr)ioblkp) )); +} + + + LOCAL void +dofclose() +{ + register expptr p; + + if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) ) + { + ioset(TYIOINT, XUNIT, cpexpr(p) ); + iosetc(XCLSTATUS, V(IOSSTATUS)); + putiocall( call1(TYINT, "f_clos", cpexpr((expptr)ioblkp)) ); + } + else + err("bad unit in close statement"); +} + + + LOCAL void +dofinquire() +{ + register expptr p; + if(p = V(IOSUNIT)) + { + if( V(IOSFILE) ) + err("inquire by unit or by file, not both"); + ioset(TYIOINT, XUNIT, cpexpr(p) ); + } + else if( ! V(IOSFILE) ) + err("must inquire by unit or by file"); + iosetlc(IOSFILE, XFILE, XFILELEN); + iosetip(IOSEXISTS, XEXISTS); + iosetip(IOSOPENED, XOPEN); + iosetip(IOSNUMBER, XNUMBER); + iosetip(IOSNAMED, XNAMED); + iosetlc(IOSNAME, XNAME, XNAMELEN); + iosetlc(IOSACCESS, XQACCESS, XQACCLEN); + iosetlc(IOSSEQUENTIAL, XSEQ, XSEQLEN); + iosetlc(IOSDIRECT, XDIRECT, XDIRLEN); + iosetlc(IOSFORM, XFORM, XFORMLEN); + iosetlc(IOSFORMATTED, XFMTED, XFMTEDLEN); + iosetlc(IOSUNFORMATTED, XUNFMT, XUNFMTLEN); + iosetip(IOSRECL, XQRECL); + iosetip(IOSNEXTREC, XNEXTREC); + iosetlc(IOSBLANK, XQBLANK, XQBLANKLEN); + + putiocall( call1(TYINT, "f_inqu", cpexpr((expptr)ioblkp) )); +} + + + + LOCAL void +dofmove(subname) + char *subname; +{ + register expptr p; + + if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) ) + { + ioset(TYIOINT, XUNIT, cpexpr(p) ); + putiocall( call1(TYINT, subname, cpexpr((expptr)ioblkp) )); + } + else + err("bad unit in I/O motion statement"); +} + +static int ioset_assign = OPASSIGN; + + LOCAL void +ioset(type, offset, p) + int type, offset; + register expptr p; +{ + offset /= SZLONG; + if(statstruct && ISCONST(p)) { + register char *s; + switch(type) { + case TYADDR: /* stmt label */ + s = "fmt_"; + break; + case TYIOINT: + s = ""; + break; + default: + badtype("ioset", type); + } + iob_list->fields[offset] = + string_num(s, p->constblock.Const.ci); + frexpr(p); + } + else { + register Addrp q; + + q = ALLOC(Addrblock); + q->tag = TADDR; + q->vtype = type; + q->vstg = STGAUTO; + q->ntempelt = 1; + q->isarray = 0; + q->memoffset = ICON(0); + q->uname_tag = UNAM_IDENT; + sprintf(q->user.ident, "%s.%s", + statstruct ? iob_list->name : ioblkp->user.ident, + io_fields[offset + 1]); + if (type == TYADDR && p->tag == TCONST + && p->constblock.vtype == TYADDR) { + /* kludge */ + register Addrp p1; + p1 = ALLOC(Addrblock); + p1->tag = TADDR; + p1->vtype = type; + p1->vstg = STGAUTO; /* wrong, but who cares? */ + p1->ntempelt = 1; + p1->isarray = 0; + p1->memoffset = ICON(0); + p1->uname_tag = UNAM_IDENT; + sprintf(p1->user.ident, "fmt_%ld", + p->constblock.Const.ci); + frexpr(p); + p = (expptr)p1; + } + if (type == TYADDR && p->headblock.vtype == TYCHAR) + q->vtype = TYCHAR; + putexpr(mkexpr(ioset_assign, (expptr)q, p)); + } +} + + + + + LOCAL void +iosetc(offset, p) + int offset; + register expptr p; +{ + extern Addrp putchop(); + + if(p == NULL) + ioset(TYADDR, offset, ICON(0) ); + else if(p->headblock.vtype == TYCHAR) { + p = putx(fixtype((expptr)putchop(cpexpr(p)))); + ioset(TYADDR, offset, addrof(p)); + } + else + err("non-character control clause"); +} + + + + LOCAL void +ioseta(offset, p) + int offset; + register Addrp p; +{ + char *s, *s1; + static char who[] = "ioseta"; + expptr e, mo; + Namep np; + ftnint ci; + int k; + char buf[24], buf1[24]; + Extsym *comm; + extern int usedefsforcommon; + + if(statstruct) + { + if (!p) + return; + if (p->tag != TADDR) + badtag(who, p->tag); + offset /= SZLONG; + switch(p->uname_tag) { + case UNAM_NAME: + mo = p->memoffset; + if (mo->tag != TCONST) + badtag("ioseta/memoffset", mo->tag); + np = p->user.name; + np->visused = 1; + ci = mo->constblock.Const.ci - np->voffset; + if (np->vstg == STGCOMMON + && !np->vcommequiv + && !usedefsforcommon) { + comm = &extsymtab[np->vardesc.varno]; + sprintf(buf, "%d.", comm->curno); + k = strlen(buf) + strlen(comm->cextname) + + strlen(np->cvarname); + if (ci) { + sprintf(buf1, "+%ld", ci); + k += strlen(buf1); + } + else + buf1[0] = 0; + s = mem(k + 1, 0); + sprintf(s, "%s%s%s%s", comm->cextname, buf, + np->cvarname, buf1); + } + else if (ci) { + sprintf(buf,"%ld", ci); + s1 = p->user.name->cvarname; + k = strlen(buf) + strlen(s1); + sprintf(s = mem(k+2,0), "%s+%s", s1, buf); + } + else + s = cpstring(np->cvarname); + break; + case UNAM_CONST: + s = tostring(p->user.Const.ccp1.ccp0, + (int)p->vleng->constblock.Const.ci); + break; + default: + badthing("uname_tag", who, p->uname_tag); + } + /* kludge for Hollerith */ + if (p->vtype != TYCHAR) { + s1 = mem(strlen(s)+10,0); + sprintf(s1, "(char *)%s%s", p->isarray ? "" : "&", s); + s = s1; + } + iob_list->fields[offset] = s; + } + else { + if (!p) + e = ICON(0); + else if (p->vtype != TYCHAR) { + NOEXT("non-character variable as format or internal unit"); + e = mkexpr(OPCHARCAST, (expptr)p, ENULL); + } + else + e = addrof((expptr)p); + ioset(TYADDR, offset, e); + } +} + + + + + LOCAL void +iosetip(i, offset) + int i, offset; +{ + register expptr p; + + if(p = V(i)) + if(p->tag==TADDR && + ONEOF(p->addrblock.vtype, inqmask) ) { + ioset_assign = OPASSIGNI; + ioset(TYADDR, offset, addrof(cpexpr(p)) ); + ioset_assign = OPASSIGN; + } + else + errstr("impossible inquire parameter %s", ioc[i].iocname); + else + ioset(TYADDR, offset, ICON(0) ); +} + + + + LOCAL void +iosetlc(i, offp, offl) + int i, offp, offl; +{ + register expptr p; + if( (p = V(i)) && p->headblock.vtype==TYCHAR) + ioset(TYIOINT, offl, cpexpr(p->headblock.vleng) ); + iosetc(offp, p); +} diff --git a/usr.bin/f2c/iob.h b/usr.bin/f2c/iob.h new file mode 100644 index 000000000000..9f2269bbe9f3 --- /dev/null +++ b/usr.bin/f2c/iob.h @@ -0,0 +1,24 @@ +struct iob_data { + struct iob_data *next; + char *type; + char *name; + char *fields[1]; + }; +struct io_setup { + char **fields; + int nelt, type; + }; + +struct defines { + struct defines *next; + char defname[1]; + }; + +typedef struct iob_data iob_data; +typedef struct io_setup io_setup; +typedef struct defines defines; + +extern iob_data *iob_list; +extern struct Addrblock *io_structs[9]; +extern void def_start(), new_iob_data(), other_undefs(); +extern char *tostring(); diff --git a/usr.bin/f2c/lex.c b/usr.bin/f2c/lex.c new file mode 100644 index 000000000000..a9900be900c3 --- /dev/null +++ b/usr.bin/f2c/lex.c @@ -0,0 +1,1577 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "tokdefs.h" +#include "p1defs.h" + +#ifdef NO_EOF_CHAR_CHECK +#undef EOF_CHAR +#else +#ifndef EOF_CHAR +#define EOF_CHAR 26 /* ASCII control-Z */ +#endif +#endif + +#define BLANK ' ' +#define MYQUOTE (2) +#define SEOF 0 + +/* card types */ + +#define STEOF 1 +#define STINITIAL 2 +#define STCONTINUE 3 + +/* lex states */ + +#define NEWSTMT 1 +#define FIRSTTOKEN 2 +#define OTHERTOKEN 3 +#define RETEOS 4 + + +LOCAL int stkey; /* Type of the current statement (DO, END, IF, etc) */ +extern char token[]; /* holds the actual token text */ +static int needwkey; +ftnint yystno; +flag intonly; +extern int new_dcl; +LOCAL long int stno; +LOCAL long int nxtstno; /* Statement label */ +LOCAL int parlev; /* Parentheses level */ +LOCAL int parseen; +LOCAL int expcom; +LOCAL int expeql; +LOCAL char *nextch; +LOCAL char *lastch; +LOCAL char *nextcd = NULL; +LOCAL char *endcd; +LOCAL long prevlin; +LOCAL long thislin; +LOCAL int code; /* Card type; INITIAL, CONTINUE or EOF */ +LOCAL int lexstate = NEWSTMT; +LOCAL char *sbuf; /* Main buffer for Fortran source input. */ +LOCAL char *send; /* Was = sbuf+20*66 with sbuf[1390]. */ +LOCAL int maxcont; +LOCAL int nincl = 0; /* Current number of include files */ +LOCAL long firstline; +LOCAL char *laststb, *stb0; +extern int addftnsrc; +static char **linestart; +LOCAL int ncont; +LOCAL char comstart[Table_size]; +#define USC (unsigned char *) + +static char anum_buf[Table_size]; +#define isalnum_(x) anum_buf[x] +#define isalpha_(x) (anum_buf[x] == 1) + +#define COMMENT_BUF_STORE 4088 + +typedef struct comment_buf { + struct comment_buf *next; + char *last; + char buf[COMMENT_BUF_STORE]; + } comment_buf; +static comment_buf *cbfirst, *cbcur; +static char *cbinit, *cbnext, *cblast; +static void flush_comments(); +extern flag use_bs; + + +/* Comment buffering data + + Comments are kept in a list until the statement before them has + been parsed. This list is implemented with the above comment_buf + structure and the pointers cbnext and cblast. + + The comments are stored with terminating NULL, and no other + intervening space. The last few bytes of each block are likely to + remain unused. +*/ + +/* struct Inclfile holds the state information for each include file */ +struct Inclfile +{ + struct Inclfile *inclnext; + FILEP inclfp; + char *inclname; + int incllno; + char *incllinp; + int incllen; + int inclcode; + ftnint inclstno; +}; + +LOCAL struct Inclfile *inclp = NULL; +struct Keylist { + char *keyname; + int keyval; + char notinf66; +}; +struct Punctlist { + char punchar; + int punval; +}; +struct Fmtlist { + char fmtchar; + int fmtval; +}; +struct Dotlist { + char *dotname; + int dotval; + }; +LOCAL struct Keylist *keystart[26], *keyend[26]; + +/* KEYWORD AND SPECIAL CHARACTER TABLES +*/ + +static struct Punctlist puncts[ ] = +{ + '(', SLPAR, + ')', SRPAR, + '=', SEQUALS, + ',', SCOMMA, + '+', SPLUS, + '-', SMINUS, + '*', SSTAR, + '/', SSLASH, + '$', SCURRENCY, + ':', SCOLON, + '<', SLT, + '>', SGT, + 0, 0 }; + +LOCAL struct Dotlist dots[ ] = +{ + "and.", SAND, + "or.", SOR, + "not.", SNOT, + "true.", STRUE, + "false.", SFALSE, + "eq.", SEQ, + "ne.", SNE, + "lt.", SLT, + "le.", SLE, + "gt.", SGT, + "ge.", SGE, + "neqv.", SNEQV, + "eqv.", SEQV, + 0, 0 }; + +LOCAL struct Keylist keys[ ] = +{ + { "assign", SASSIGN }, + { "automatic", SAUTOMATIC, YES }, + { "backspace", SBACKSPACE }, + { "blockdata", SBLOCK }, + { "call", SCALL }, + { "character", SCHARACTER, YES }, + { "close", SCLOSE, YES }, + { "common", SCOMMON }, + { "complex", SCOMPLEX }, + { "continue", SCONTINUE }, + { "data", SDATA }, + { "dimension", SDIMENSION }, + { "doubleprecision", SDOUBLE }, + { "doublecomplex", SDCOMPLEX, YES }, + { "elseif", SELSEIF, YES }, + { "else", SELSE, YES }, + { "endfile", SENDFILE }, + { "endif", SENDIF, YES }, + { "enddo", SENDDO, YES }, + { "end", SEND }, + { "entry", SENTRY, YES }, + { "equivalence", SEQUIV }, + { "external", SEXTERNAL }, + { "format", SFORMAT }, + { "function", SFUNCTION }, + { "goto", SGOTO }, + { "implicit", SIMPLICIT, YES }, + { "include", SINCLUDE, YES }, + { "inquire", SINQUIRE, YES }, + { "intrinsic", SINTRINSIC, YES }, + { "integer", SINTEGER }, + { "logical", SLOGICAL }, + { "namelist", SNAMELIST, YES }, + { "none", SUNDEFINED, YES }, + { "open", SOPEN, YES }, + { "parameter", SPARAM, YES }, + { "pause", SPAUSE }, + { "print", SPRINT }, + { "program", SPROGRAM, YES }, + { "punch", SPUNCH, YES }, + { "read", SREAD }, + { "real", SREAL }, + { "return", SRETURN }, + { "rewind", SREWIND }, + { "save", SSAVE, YES }, + { "static", SSTATIC, YES }, + { "stop", SSTOP }, + { "subroutine", SSUBROUTINE }, + { "then", STHEN, YES }, + { "undefined", SUNDEFINED, YES }, + { "while", SWHILE, YES }, + { "write", SWRITE }, + { 0, 0 } +}; + +LOCAL void analyz(), crunch(), store_comment(); +LOCAL int getcd(), getcds(), getkwd(), gettok(); +LOCAL char *stbuf[3]; + +inilex(name) +char *name; +{ + stbuf[0] = Alloc(3*P1_STMTBUFSIZE); + stbuf[1] = stbuf[0] + P1_STMTBUFSIZE; + stbuf[2] = stbuf[1] + P1_STMTBUFSIZE; + nincl = 0; + inclp = NULL; + doinclude(name); + lexstate = NEWSTMT; + return(NO); +} + + + +/* throw away the rest of the current line */ +flline() +{ + lexstate = RETEOS; +} + + + +char *lexline(n) +int *n; +{ + *n = (lastch - nextch) + 1; + return(nextch); +} + + + + + +doinclude(name) +char *name; +{ + FILEP fp; + struct Inclfile *t; + char *lastslash, *s, *s0, *temp; + int k; + + if(inclp) + { + inclp->incllno = thislin; + inclp->inclcode = code; + inclp->inclstno = nxtstno; + if(nextcd) + inclp->incllinp = copyn(inclp->incllen = endcd-nextcd , nextcd); + else + inclp->incllinp = 0; + } + nextcd = NULL; + + if(++nincl >= MAXINCLUDES) + Fatal("includes nested too deep"); + if(name[0] == '\0') + fp = stdin; + else if(name[0] == '/' || inclp == NULL +#ifdef MSDOS + || name[0] == '\\' + || name[1] == ':' +#endif + ) + fp = fopen(name, textread); + else { + lastslash = NULL; + s = s0 = inclp->inclname; +#ifdef MSDOS + if (s[1] == ':') + lastslash = s + 1; +#endif + for(; *s ; ++s) + if(*s == '/' +#ifdef MSDOS + || *s == '\\' +#endif + ) + lastslash = s; + if(lastslash) { + k = lastslash - s0 + 1; + temp = Alloc(k + strlen(name) + 1); + strncpy(temp, s0, k); + strcpy(temp+k, name); + name = temp; + } + fp = fopen(name, textread); + } + if (fp) + { + t = inclp; + inclp = ALLOC(Inclfile); + inclp->inclnext = t; + prevlin = thislin = 0; + infname = inclp->inclname = name; + infile = inclp->inclfp = fp; + } + else + { + fprintf(diagfile, "Cannot open file %s\n", name); + done(1); + } +} + + + + +LOCAL popinclude() +{ + struct Inclfile *t; + register char *p; + register int k; + + if(infile != stdin) + clf(&infile, infname, 1); /* Close the input file */ + free(infname); + + --nincl; + t = inclp->inclnext; + free( (charptr) inclp); + inclp = t; + if(inclp == NULL) { + infname = 0; + return(NO); + } + + infile = inclp->inclfp; + infname = inclp->inclname; + prevlin = thislin = inclp->incllno; + code = inclp->inclcode; + stno = nxtstno = inclp->inclstno; + if(inclp->incllinp) + { + endcd = nextcd = sbuf; + k = inclp->incllen; + p = inclp->incllinp; + while(--k >= 0) + *endcd++ = *p++; + free( (charptr) (inclp->incllinp) ); + } + else + nextcd = NULL; + return(YES); +} + + +static char *lastfile = "??", *lastfile0 = "?"; +static char fbuf[P1_FILENAME_MAX]; + +void p1_line_number (line_number) +long line_number; +{ + if (lastfile != lastfile0) { + p1puts(P1_FILENAME, fbuf); + lastfile0 = lastfile; + } + fprintf(pass1_file, "%d: %ld\n", P1_SET_LINE, line_number); + } + + static void +putlineno() +{ + static long lastline; + extern int gflag; + register char *s0, *s1; + + if (gflag) { + if (lastline) + p1_line_number(lastline); + lastline = firstline; + if (lastfile != infname) + if (lastfile = infname) { + strncpy(fbuf, lastfile, sizeof(fbuf)); + fbuf[sizeof(fbuf)-1] = 0; + } + else + fbuf[0] = 0; + } + if (addftnsrc) { + if (laststb && *laststb) { + for(s1 = laststb; *s1; s1++) { + for(s0 = s1; *s1 != '\n'; s1++) + if (*s1 == '*' && s1[1] == '/') + *s1 = '+'; + *s1 = 0; + p1puts(P1_FORTRAN, s0); + } + *laststb = 0; /* prevent trouble after EOF */ + } + laststb = stb0; + } + } + + +yylex() +{ + static int tokno; + int retval; + + switch(lexstate) + { + case NEWSTMT : /* need a new statement */ + retval = getcds(); + putlineno(); + if(retval == STEOF) { + retval = SEOF; + break; + } /* if getcds() == STEOF */ + crunch(); + tokno = 0; + lexstate = FIRSTTOKEN; + yystno = stno; + stno = nxtstno; + toklen = 0; + retval = SLABEL; + break; + +first: + case FIRSTTOKEN : /* first step on a statement */ + analyz(); + lexstate = OTHERTOKEN; + tokno = 1; + retval = stkey; + break; + + case OTHERTOKEN : /* return next token */ + if(nextch > lastch) + goto reteos; + ++tokno; + if( (stkey==SLOGIF || stkey==SELSEIF) && parlev==0 && tokno>3) + goto first; + + if(stkey==SASSIGN && tokno==3 && nextch<lastch && + nextch[0]=='t' && nextch[1]=='o') + { + nextch+=2; + retval = STO; + break; + } + retval = gettok(); + break; + +reteos: + case RETEOS: + lexstate = NEWSTMT; + retval = SEOS; + break; + default: + fatali("impossible lexstate %d", lexstate); + break; + } + + if (retval == SEOF) + flush_comments (); + + return retval; +} + + LOCAL void +contmax() +{ + lineno = thislin; + many("continuation lines", 'C', maxcontin); + } + +/* Get Cards. + + Returns STEOF or STINITIAL, never STCONTINUE. Any continuation cards get +merged into one long card (hence the size of the buffer named sbuf) */ + + LOCAL int +getcds() +{ + register char *p, *q; + + flush_comments (); +top: + if(nextcd == NULL) + { + code = getcd( nextcd = sbuf, 1 ); + stno = nxtstno; + prevlin = thislin; + } + if(code == STEOF) + if( popinclude() ) + goto top; + else + return(STEOF); + + if(code == STCONTINUE) + { + lineno = thislin; + nextcd = NULL; + goto top; + } + +/* Get rid of unused space at the head of the buffer */ + + if(nextcd > sbuf) + { + q = nextcd; + p = sbuf; + while(q < endcd) + *p++ = *q++; + endcd = p; + } + +/* Be aware that the input (i.e. the string at the address nextcd) is NOT + NULL-terminated */ + +/* This loop merges all continuations into one long statement, AND puts the next + card to be read at the end of the buffer (i.e. it stores the look-ahead card + when there's room) */ + + ncont = 0; + for(;;) { + nextcd = endcd; + if (ncont >= maxcont || nextcd+66 > send) + contmax(); + linestart[ncont++] = nextcd; + if ((code = getcd(nextcd,0)) != STCONTINUE) + break; + if (ncont == 20 && noextflag) { + lineno = thislin; + errext("more than 19 continuation lines"); + } + } + nextch = sbuf; + lastch = nextcd - 1; + + lineno = prevlin; + prevlin = thislin; + return(STINITIAL); +} + + static void +bang(a,b,c,d,e) /* save ! comments */ + char *a, *b, *c; + register char *d, *e; +{ + char buf[COMMENT_BUFFER_SIZE + 1]; + register char *p, *pe; + + p = buf; + pe = buf + COMMENT_BUFFER_SIZE; + *pe = 0; + while(a < b) + if (!(*p++ = *a++)) + p[-1] = 0; + if (b < c) + *p++ = '\t'; + while(d < e) { + if (!(*p++ = *d++)) + p[-1] = ' '; + if (p == pe) { + store_comment(buf); + p = buf; + } + } + if (p > buf) { + while(--p >= buf && *p == ' '); + p[1] = 0; + store_comment(buf); + } + } + + +/* getcd - Get next input card + + This function reads the next input card from global file pointer infile. +It assumes that b points to currently empty storage somewhere in sbuf */ + + LOCAL int +getcd(b, nocont) + register char *b; +{ + register int c; + register char *p, *bend; + int speclin; /* Special line - true when the line is allowed + to have more than 66 characters (e.g. the + "&" shorthand for continuation, use of a "\t" + to skip part of the label columns) */ + static char a[6]; /* Statement label buffer */ + static char *aend = a+6; + static char *stb, *stbend; + static int nst; + char *atend, *endcd0; + extern int warn72; + char buf72[24]; + int amp, i; + char storage[COMMENT_BUFFER_SIZE + 1]; + char *pointer; + long L; + +top: + endcd = b; + bend = b+66; + amp = speclin = NO; + atend = aend; + +/* Handle the continuation shorthand of "&" in the first column, which stands + for " x" */ + + if( (c = getc(infile)) == '&') + { + a[0] = c; + a[1] = 0; + a[5] = 'x'; + amp = speclin = YES; + bend = send; + p = aend; + } + +/* Handle the Comment cards (a 'C', 'c', '*', or '!' in the first column). */ + + else if(comstart[c & 0xfff]) + { + if (feof (infile) +#ifdef EOF_CHAR + || c == EOF_CHAR +#endif + ) + return STEOF; + + if (c == '#') { + *endcd++ = c; + while((c = getc(infile)) != '\n') + if (c == EOF) + return STEOF; + else if (endcd < bend) + *endcd++ = c; + ++thislin; + *endcd = 0; + if (b[1] == ' ') + p = b + 2; + else if (!strncmp(b,"#line ",6)) + p = b + 6; + else { + bad_cpp: + errstr("Bad # line: \"%s\"", b); + goto top; + } + if (*p < '1' || *p > '9') + goto bad_cpp; + L = *p - '1'; /* bias down 1 */ + while((c = *++p) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (c != ' ' || *++p != '"') + goto bad_cpp; + bend = p; + while(*++p != '"') + if (!*p) + goto bad_cpp; + *p = 0; + i = p - bend++; + thislin = L; + if (!infname || strcmp(infname, bend)) { + if (infname) + free(infname); + infname = Alloc(i); + strcpy(infname, bend); + if (inclp) + inclp->inclname = infname; + } + goto top; + } + + storage[COMMENT_BUFFER_SIZE] = c = '\0'; + pointer = storage; + while( !feof (infile) && (*pointer++ = c = getc(infile)) != '\n') { + +/* Handle obscure end of file conditions on many machines */ + + if (feof (infile) && (c == '\377' || c == EOF)) { + pointer--; + break; + } /* if (feof (infile)) */ + + if (c == '\0') + *(pointer - 1) = ' '; + + if (pointer == &storage[COMMENT_BUFFER_SIZE]) { + store_comment (storage); + pointer = storage; + } /* if (pointer == BUFFER_SIZE) */ + } /* while */ + + if (pointer > storage) { + if (c == '\n') + +/* Get rid of the newline */ + + pointer[-1] = 0; + else + *pointer = 0; + + store_comment (storage); + } /* if */ + + if (feof (infile)) + if (c != '\n') /* To allow the line index to + increment correctly */ + return STEOF; + + ++thislin; + goto top; + } + + else if(c != EOF) + { + +/* Load buffer a with the statement label */ + + /* a tab in columns 1-6 skips to column 7 */ + ungetc(c, infile); + for(p=a; p<aend && (c=getc(infile)) != '\n' && c!=EOF; ) + if(c == '\t') + +/* The tab character translates into blank characters in the statement label */ + + { + atend = p; + while(p < aend) + *p++ = BLANK; + speclin = YES; + bend = send; + } + else + *p++ = c; + } + +/* By now we've read either a continuation character or the statement label + field */ + + if(c == EOF) + return(STEOF); + +/* The next 'if' block handles lines that have fewer than 7 characters */ + + if(c == '\n') + { + while(p < aend) + *p++ = BLANK; + +/* Blank out the buffer on lines which are not longer than 66 characters */ + + endcd0 = endcd; + if( ! speclin ) + while(endcd < bend) + *endcd++ = BLANK; + } + else { /* read body of line */ + if (warn72 & 2) { + speclin = YES; + bend = send; + } + while( endcd<bend && (c=getc(infile)) != '\n' && c!=EOF ) + *endcd++ = c; + if(c == EOF) + return(STEOF); + +/* Drop any extra characters on the input card; this usually means those after + column 72 */ + + if(c != '\n') + { + i = 0; + while( (c=getc(infile)) != '\n' && c != EOF) + if (i < 23) + buf72[i++] = c; + if (warn72 && i && !speclin) { + buf72[i] = 0; + if (i >= 23) + strcpy(buf72+20, "..."); + lineno = thislin + 1; + errstr("text after column 72: %s", buf72); + } + if(c == EOF) + return(STEOF); + } + + endcd0 = endcd; + if( ! speclin ) + while(endcd < bend) + *endcd++ = BLANK; + } + +/* The flow of control usually gets to this line (unless an earlier RETURN has + been taken) */ + + ++thislin; + + /* Fortran 77 specifies that a 0 in column 6 */ + /* does not signify continuation */ + + if( !isspace(a[5]) && a[5]!='0') { + if (!amp) + for(p = a; p < aend;) + if (*p++ == '!' && p != aend) + goto initcheck; + if (addftnsrc && stb) { + if (stbend > stb + 7) { /* otherwise forget col 1-6 */ + /* kludge around funny p1gets behavior */ + *stb++ = '$'; + if (amp) + *stb++ = '&'; + else + for(p = a; p < atend;) + *stb++ = *p++; + } + if (endcd0 - b > stbend - stb) { + if (stb > stbend) + stb = stbend; + endcd0 = b + (stbend - stb); + } + for(p = b; p < endcd0;) + *stb++ = *p++; + *stb++ = '\n'; + *stb = 0; + } + if (nocont) { + lineno = thislin; + errstr("illegal continuation card (starts \"%.6s\")",a); + } + else if (!amp && strncmp(a," ",5)) { + lineno = thislin; + errstr("labeled continuation line (starts \"%.6s\")",a); + } + return(STCONTINUE); + } +initcheck: + for(p=a; p<atend; ++p) + if( !isspace(*p) ) { + if (*p++ != '!') + goto initline; + bang(p, atend, aend, b, endcd); + goto top; + } + for(p = b ; p<endcd ; ++p) + if( !isspace(*p) ) { + if (*p++ != '!') + goto initline; + bang(a, a, a, p, endcd); + goto top; + } + +/* Skip over blank cards by reading the next one right away */ + + goto top; + +initline: + if (addftnsrc) { + nst = (nst+1)%3; + if (!laststb && stb0) + laststb = stb0; + stb0 = stb = stbuf[nst]; + *stb++ = '$'; /* kludge around funny p1gets behavior */ + stbend = stb + sizeof(stbuf[0])-2; + for(p = a; p < atend;) + *stb++ = *p++; + if (atend < aend) + *stb++ = '\t'; + for(p = b; p < endcd0;) + *stb++ = *p++; + *stb++ = '\n'; + *stb = 0; + } + +/* Set nxtstno equal to the integer value of the statement label */ + + nxtstno = 0; + bend = a + 5; + for(p = a ; p < bend ; ++p) + if( !isspace(*p) ) + if(isdigit(*p)) + nxtstno = 10*nxtstno + (*p - '0'); + else if (*p == '!') { + if (!addftnsrc) + bang(p+1,atend,aend,b,endcd); + endcd = b; + break; + } + else { + lineno = thislin; + errstr( + "nondigit in statement label field \"%.5s\"", a); + nxtstno = 0; + break; + } + firstline = thislin; + return(STINITIAL); +} + + +/* crunch -- deletes all space characters, folds the backslash chars and + Hollerith strings, quotes the Fortran strings */ + + LOCAL void +crunch() +{ + register char *i, *j, *j0, *j1, *prvstr; + int k, ten, nh, nh0, quote; + + /* i is the next input character to be looked at + j is the next output character */ + + new_dcl = needwkey = parlev = parseen = 0; + expcom = 0; /* exposed ','s */ + expeql = 0; /* exposed equal signs */ + j = sbuf; + prvstr = sbuf; + k = 0; + for(i=sbuf ; i<=lastch ; ++i) + { + if(isspace(*i) ) + continue; + if (*i == '!') { + while(i >= linestart[k]) + if (++k >= maxcont) + contmax(); + j0 = linestart[k]; + if (!addftnsrc) + bang(sbuf,sbuf,sbuf,i+1,j0); + i = j0-1; + continue; + } + +/* Keep everything in a quoted string */ + + if(*i=='\'' || *i=='"') + { + int len = 0; + + quote = *i; + *j = MYQUOTE; /* special marker */ + for(;;) + { + if(++i > lastch) + { + err("unbalanced quotes; closing quote supplied"); + if (j >= lastch) + j = lastch - 1; + break; + } + if(*i == quote) + if(i<lastch && i[1]==quote) ++i; + else break; + else if(*i=='\\' && i<lastch && use_bs) { + ++i; + *i = escapes[*(unsigned char *)i]; + } + if (len < MAXTOKENLEN) + *++j = *i; + else if (len == MAXTOKENLEN) + erri + ("String too long, truncating to %d chars", MAXTOKENLEN); + len++; + } /* for (;;) */ + + j[1] = MYQUOTE; + j += 2; + prvstr = j; + } + else if( (*i=='h' || *i=='H') && j>prvstr) /* test for Hollerith strings */ + { + j0 = j - 1; + if( ! isdigit(*j0)) goto copychar; + nh = *j0 - '0'; + ten = 10; + j1 = prvstr; + if (j1+4 < j) + j1 = j-4; + for(;;) { + if (j0-- <= j1) + goto copychar; + if( ! isdigit(*j0 ) ) break; + nh += ten * (*j0-'0'); + ten*=10; + } + /* a hollerith must be preceded by a punctuation mark. + '*' is possible only as repetition factor in a data statement + not, in particular, in character*2h +*/ + + if( !(*j0=='*'&&sbuf[0]=='d') && *j0!='/' + && *j0!='(' && *j0!=',' && *j0!='=' && *j0!='.') + goto copychar; + nh0 = nh; + if(i+nh > lastch || nh > MAXTOKENLEN) + { + erri("%dH too big", nh); + nh = lastch - i; + if (nh > MAXTOKENLEN) + nh = MAXTOKENLEN; + nh0 = -1; + } + j0[1] = MYQUOTE; /* special marker */ + j = j0 + 1; + while(nh-- > 0) + { + if (++i > lastch) { + hol_overflow: + if (nh0 >= 0) + erri("escapes make %dH too big", + nh0); + break; + } + if(*i == '\\' && use_bs) { + if (++i > lastch) + goto hol_overflow; + *i = escapes[*(unsigned char *)i]; + } + *++j = *i; + } + j[1] = MYQUOTE; + j+=2; + prvstr = j; + } + else { + if(*i == '(') parseen = ++parlev; + else if(*i == ')') --parlev; + else if(parlev == 0) + if(*i == '=') expeql = 1; + else if(*i == ',') expcom = 1; +copychar: /*not a string or space -- copy, shifting case if necessary */ + if(shiftcase && isupper(*i)) + *j++ = tolower(*i); + else *j++ = *i; + } + } + lastch = j - 1; + nextch = sbuf; +} + + LOCAL void +analyz() +{ + register char *i; + + if(parlev != 0) + { + err("unbalanced parentheses, statement skipped"); + stkey = SUNKNOWN; + lastch = sbuf - 1; /* prevent double error msg */ + return; + } + if(nextch+2<=lastch && nextch[0]=='i' && nextch[1]=='f' && nextch[2]=='(') + { + /* assignment or if statement -- look at character after balancing paren */ + parlev = 1; + for(i=nextch+3 ; i<=lastch; ++i) + if(*i == (MYQUOTE)) + { + while(*++i != MYQUOTE) + ; + } + else if(*i == '(') + ++parlev; + else if(*i == ')') + { + if(--parlev == 0) + break; + } + if(i >= lastch) + stkey = SLOGIF; + else if(i[1] == '=') + stkey = SLET; + else if( isdigit(i[1]) ) + stkey = SARITHIF; + else stkey = SLOGIF; + if(stkey != SLET) + nextch += 2; + } + else if(expeql) /* may be an assignment */ + { + if(expcom && nextch<lastch && + nextch[0]=='d' && nextch[1]=='o') + { + stkey = SDO; + nextch += 2; + } + else stkey = SLET; + } + else if (parseen && nextch + 7 < lastch + && nextch[2] != 'u' /* screen out "double..." early */ + && nextch[0] == 'd' && nextch[1] == 'o' + && ((nextch[2] >= '0' && nextch[2] <= '9') + || nextch[2] == ',' + || nextch[2] == 'w')) + { + stkey = SDO; + nextch += 2; + needwkey = 1; + } + /* otherwise search for keyword */ + else { + stkey = getkwd(); + if(stkey==SGOTO && lastch>=nextch) + if(nextch[0]=='(') + stkey = SCOMPGOTO; + else if(isalpha_(* USC nextch)) + stkey = SASGOTO; + } + parlev = 0; +} + + + + LOCAL int +getkwd() +{ + register char *i, *j; + register struct Keylist *pk, *pend; + int k; + + if(! isalpha_(* USC nextch) ) + return(SUNKNOWN); + k = letter(nextch[0]); + if(pk = keystart[k]) + for(pend = keyend[k] ; pk<=pend ; ++pk ) + { + i = pk->keyname; + j = nextch; + while(*++i==*++j && *i!='\0') + ; + if(*i=='\0' && j<=lastch+1) + { + nextch = j; + if(no66flag && pk->notinf66) + errstr("Not a Fortran 66 keyword: %s", + pk->keyname); + return(pk->keyval); + } + } + return(SUNKNOWN); +} + +initkey() +{ + register struct Keylist *p; + register int i,j; + register char *s; + + for(i = 0 ; i<26 ; ++i) + keystart[i] = NULL; + + for(p = keys ; p->keyname ; ++p) { + j = letter(p->keyname[0]); + if(keystart[j] == NULL) + keystart[j] = p; + keyend[j] = p; + } + i = (maxcontin + 2) * 66; + sbuf = (char *)ckalloc(i + 70); + send = sbuf + i; + maxcont = maxcontin + 1; + linestart = (char **)ckalloc(maxcont*sizeof(char*)); + comstart['c'] = comstart['C'] = comstart['*'] = comstart['!'] = + comstart['#'] = 1; +#ifdef EOF_CHAR + comstart[EOF_CHAR] = 1; +#endif + s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + while(i = *s++) + anum_buf[i] = 1; + s = "0123456789"; + while(i = *s++) + anum_buf[i] = 2; + } + + LOCAL int +hexcheck(key) + int key; +{ + register int radix; + register char *p; + char *kind; + + switch(key) { + case 'z': + case 'Z': + case 'x': + case 'X': + radix = 16; + key = SHEXCON; + kind = "hexadecimal"; + break; + case 'o': + case 'O': + radix = 8; + key = SOCTCON; + kind = "octal"; + break; + case 'b': + case 'B': + radix = 2; + key = SBITCON; + kind = "binary"; + break; + default: + err("bad bit identifier"); + return(SNAME); + } + for(p = token; *p; p++) + if (hextoi(*p) >= radix) { + errstr("invalid %s character", kind); + break; + } + return key; + } + +/* gettok -- moves the right amount of text from nextch into the token + buffer. token initially contains garbage (leftovers from the prev token) */ + + LOCAL int +gettok() +{ +int havdot, havexp, havdbl; + int radix, val; + struct Punctlist *pp; + struct Dotlist *pd; + register int ch; + + char *i, *j, *n1, *p; + + ch = * USC nextch; + if(ch == (MYQUOTE)) + { + ++nextch; + p = token; + while(*nextch != MYQUOTE) + *p++ = *nextch++; + toklen = p - token; + *p = 0; + /* allow octal, binary, hex constants of the form 'abc'x (etc.) */ + if (++nextch <= lastch && isalpha_(val = * USC nextch)) { + ++nextch; + return hexcheck(val); + } + return (SHOLLERITH); + } + + if(needkwd) + { + needkwd = 0; + return( getkwd() ); + } + + for(pp=puncts; pp->punchar; ++pp) + if(ch == pp->punchar) { + val = pp->punval; + if (++nextch <= lastch) + switch(ch) { + case '/': + if (*nextch == '/') { + nextch++; + val = SCONCAT; + } + else if (new_dcl && parlev == 0) + val = SSLASHD; + return val; + case '*': + if (*nextch == '*') { + nextch++; + return SPOWER; + } + break; + case '<': + if (*nextch == '=') { + nextch++; + val = SLE; + } + if (*nextch == '>') { + nextch++; + val = SNE; + } + goto extchk; + case '=': + if (*nextch == '=') { + nextch++; + val = SEQ; + goto extchk; + } + break; + case '>': + if (*nextch == '=') { + nextch++; + val = SGE; + } + extchk: + NOEXT("Fortran 8x comparison operator"); + return val; + } + else if (ch == '/' && new_dcl && parlev == 0) + return SSLASHD; + switch(val) { + case SLPAR: + ++parlev; + break; + case SRPAR: + --parlev; + } + return(val); + } + if(ch == '.') + if(nextch >= lastch) goto badchar; + else if(isdigit(nextch[1])) goto numconst; + else { + for(pd=dots ; (j=pd->dotname) ; ++pd) + { + for(i=nextch+1 ; i<=lastch ; ++i) + if(*i != *j) break; + else if(*i != '.') ++j; + else { + nextch = i+1; + return(pd->dotval); + } + } + goto badchar; + } + if( isalpha_(ch) ) + { + p = token; + *p++ = *nextch++; + while(nextch<=lastch) + if( isalnum_(* USC nextch) ) + *p++ = *nextch++; + else break; + toklen = p - token; + *p = 0; + if (needwkey) { + needwkey = 0; + if (toklen == 5 + && nextch <= lastch && *nextch == '(' /*)*/ + && !strcmp(token,"while")) + return(SWHILE); + } + if(inioctl && nextch<=lastch && *nextch=='=') + { + ++nextch; + return(SNAMEEQ); + } + if(toklen>8 && eqn(8,token,"function") + && isalpha_(* USC (token+8)) && + nextch<lastch && nextch[0]=='(' && + (nextch[1]==')' || isalpha_(* USC (nextch+1))) ) + { + nextch -= (toklen - 8); + return(SFUNCTION); + } + + if(toklen > 50) + { + char buff[100]; + sprintf(buff, toklen >= 60 + ? "name %.56s... too long, truncated to %.*s" + : "name %s too long, truncated to %.*s", + token, 50, token); + err(buff); + toklen = 50; + token[50] = '\0'; + } + if(toklen==1 && *nextch==MYQUOTE) { + val = token[0]; + ++nextch; + for(p = token ; *nextch!=MYQUOTE ; ) + *p++ = *nextch++; + ++nextch; + toklen = p - token; + *p = 0; + return hexcheck(val); + } + return(SNAME); + } + + if (isdigit(ch)) { + + /* Check for NAG's special hex constant */ + + if (nextch[1] == '#' && nextch < lastch + || nextch[2] == '#' && isdigit(nextch[1] + && lastch - nextch >= 2)) { + + radix = atoi (nextch); + if (*++nextch != '#') + nextch++; + if (radix != 2 && radix != 8 && radix != 16) { + erri("invalid base %d for constant, defaulting to hex", + radix); + radix = 16; + } /* if */ + if (++nextch > lastch) + goto badchar; + for (p = token; hextoi(*nextch) < radix;) { + *p++ = *nextch++; + if (nextch > lastch) + break; + } + toklen = p - token; + *p = 0; + return (radix == 16) ? SHEXCON : ((radix == 8) ? SOCTCON : + SBITCON); + } + } + else + goto badchar; +numconst: + havdot = NO; + havexp = NO; + havdbl = NO; + for(n1 = nextch ; nextch<=lastch ; ++nextch) + { + if(*nextch == '.') + if(havdot) break; + else if(nextch+2<=lastch && isalpha_(* USC (nextch+1)) + && isalpha_(* USC (nextch+2))) + break; + else havdot = YES; + else if( !intonly && (*nextch=='d' || *nextch=='e') ) + { + p = nextch; + havexp = YES; + if(*nextch == 'd') + havdbl = YES; + if(nextch<lastch) + if(nextch[1]=='+' || nextch[1]=='-') + ++nextch; + if( ! isdigit(*++nextch) ) + { + nextch = p; + havdbl = havexp = NO; + break; + } + for(++nextch ; + nextch<=lastch && isdigit(* USC nextch); + ++nextch); + break; + } + else if( ! isdigit(* USC nextch) ) + break; + } + p = token; + i = n1; + while(i < nextch) + *p++ = *i++; + toklen = p - token; + *p = 0; + if(havdbl) return(SDCON); + if(havdot || havexp) return(SRCON); + return(SICON); +badchar: + sbuf[0] = *nextch++; + return(SUNKNOWN); +} + +/* Comment buffering code */ + + static void +store_comment(str) + char *str; +{ + int len; + comment_buf *ncb; + + if (nextcd == sbuf) { + flush_comments(); + p1_comment(str); + return; + } + len = strlen(str) + 1; + if (cbnext + len > cblast) { + if (!cbcur || !(ncb = cbcur->next)) { + ncb = (comment_buf *) Alloc(sizeof(comment_buf)); + if (cbcur) { + cbcur->last = cbnext; + cbcur->next = ncb; + } + else { + cbfirst = ncb; + cbinit = ncb->buf; + } + ncb->next = 0; + } + cbcur = ncb; + cbnext = ncb->buf; + cblast = cbnext + COMMENT_BUF_STORE; + } + strcpy(cbnext, str); + cbnext += len; + } + + static void +flush_comments() +{ + register char *s, *s1; + register comment_buf *cb; + if (cbnext == cbinit) + return; + cbcur->last = cbnext; + for(cb = cbfirst;; cb = cb->next) { + for(s = cb->buf; s < cb->last; s = s1) { + /* compute s1 = new s value first, since */ + /* p1_comment may insert nulls into s */ + s1 = s + strlen(s) + 1; + p1_comment(s); + } + if (cb == cbcur) + break; + } + cbcur = cbfirst; + cbnext = cbinit; + cblast = cbnext + COMMENT_BUF_STORE; + } + + void +unclassifiable() +{ + register char *s, *se; + + s = sbuf; + se = lastch; + if (se < sbuf) + return; + lastch = s - 1; + if (se - s > 10) + se = s + 10; + for(; s < se; s++) + if (*s == MYQUOTE) { + se = s; + break; + } + *se = 0; + errstr("unclassifiable statement (starts \"%s\")", sbuf); + } diff --git a/usr.bin/f2c/machdefs.h b/usr.bin/f2c/machdefs.h new file mode 100644 index 000000000000..3ab8961f0a2c --- /dev/null +++ b/usr.bin/f2c/machdefs.h @@ -0,0 +1,31 @@ +#define TYLENG TYLONG /* char string length field */ + +#define TYINT TYLONG +#define SZADDR 4 +#define SZSHORT 2 +#define SZINT 4 + +#define SZLONG 4 +#define SZLENG SZLONG + +#define SZDREAL 8 + +/* Alignment restrictions */ + +#define ALIADDR SZADDR +#define ALISHORT SZSHORT +#define ALILONG 4 +#define ALIDOUBLE 8 +#define ALIINT ALILONG +#define ALILENG ALILONG + +#define BLANKCOMMON "_BLNK__" /* Name for the unnamed + common block; this is unique + because of underscores */ + +#define LABELFMT "%s:\n" + +#define MAXREGVAR 4 +#define TYIREG TYLONG +#define MSKIREG (M(TYSHORT)|M(TYLONG)) /* allowed types of DO indicies + which can be put in registers */ diff --git a/usr.bin/f2c/main.c b/usr.bin/f2c/main.c new file mode 100644 index 000000000000..899e955944c0 --- /dev/null +++ b/usr.bin/f2c/main.c @@ -0,0 +1,620 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +extern char F2C_version[]; + +#include "defs.h" +#include "parse.h" + +int complex_seen, dcomplex_seen; + +LOCAL int Max_ftn_files; + +char **ftn_files; +int current_ftn_file = 0; + +flag ftn66flag = NO; +flag nowarnflag = NO; +flag noextflag = NO; +flag no66flag = NO; /* Must also set noextflag to this + same value */ +flag zflag = YES; /* recognize double complex intrinsics */ +flag debugflag = NO; +flag onetripflag = NO; +flag shiftcase = YES; +flag undeftype = NO; +flag checksubs = NO; +flag r8flag = NO; +flag use_bs = YES; +flag keepsubs = NO; +#ifdef TYQUAD +flag use_tyquad = YES; +#endif +int tyreal = TYREAL; +int tycomplex = TYCOMPLEX; +extern void r8fix(), read_Pfiles(); + +int maxregvar = MAXREGVAR; /* if maxregvar > MAXREGVAR, error */ +int maxequiv = MAXEQUIV; +int maxext = MAXEXT; +int maxstno = MAXSTNO; +int maxctl = MAXCTL; +int maxhash = MAXHASH; +int maxliterals = MAXLITERALS; +int maxcontin = MAXCONTIN; +int maxlablist = MAXLABLIST; +int extcomm, ext1comm, useauto; +int can_include = YES; /* so we can disable includes for netlib */ + +static char *def_i2 = ""; + +static int useshortints = NO; /* YES => tyint = TYSHORT */ +static int uselongints = NO; /* YES => tyint = TYLONG */ +int addftnsrc = NO; /* Include ftn source in output */ +int usedefsforcommon = NO; /* Use #defines for common reference */ +int forcedouble = YES; /* force real functions to double */ +int Ansi = NO; +int def_equivs = YES; +int tyioint = TYLONG; +int szleng = SZLENG; +int inqmask = M(TYLONG)|M(TYLOGICAL); +int wordalign = NO; +int forcereal = NO; +int warn72 = NO; +static int skipC, skipversion; +char *file_name, *filename0, *parens; +int Castargs = 1; +static int Castargs1; +static int typedefs = 0; +int chars_per_wd, gflag, protostatus; +int infertypes = 1; +char used_rets[TYSUBR+1]; +extern char *tmpdir; +static int h0align = 0; +char *halign, *ohalign; +int krparens = NO; +int hsize; /* for padding under -h */ +int htype; /* for wr_equiv_init under -h */ +char *o_coutput = 0; + +#define f2c_entry(swit,count,type,store,size) \ + p_entry ("-", swit, 0, count, type, store, size) + +static arg_info table[] = { + f2c_entry ("o", P_ONE_ARG, P_STRING, &o_coutput, YES), + f2c_entry ("w66", P_NO_ARGS, P_INT, &ftn66flag, YES), + f2c_entry ("w", P_NO_ARGS, P_INT, &nowarnflag, YES), + f2c_entry ("66", P_NO_ARGS, P_INT, &no66flag, YES), + f2c_entry ("d", P_ONE_ARG, P_INT, &debugflag, YES), + f2c_entry ("1", P_NO_ARGS, P_INT, &onetripflag, YES), + f2c_entry ("onetrip", P_NO_ARGS, P_INT, &onetripflag, YES), + f2c_entry ("I2", P_NO_ARGS, P_INT, &useshortints, YES), + f2c_entry ("I4", P_NO_ARGS, P_INT, &uselongints, YES), + f2c_entry ("U", P_NO_ARGS, P_INT, &shiftcase, NO), + f2c_entry ("u", P_NO_ARGS, P_INT, &undeftype, YES), + f2c_entry ("O", P_ONE_ARG, P_INT, &maxregvar, 0), + f2c_entry ("C", P_NO_ARGS, P_INT, &checksubs, YES), + f2c_entry ("Nq", P_ONE_ARG, P_INT, &maxequiv, 0), + f2c_entry ("Nx", P_ONE_ARG, P_INT, &maxext, 0), + f2c_entry ("Ns", P_ONE_ARG, P_INT, &maxstno, 0), + f2c_entry ("Nc", P_ONE_ARG, P_INT, &maxctl, 0), + f2c_entry ("Nn", P_ONE_ARG, P_INT, &maxhash, 0), + f2c_entry ("NL", P_ONE_ARG, P_INT, &maxliterals, 0), + f2c_entry ("NC", P_ONE_ARG, P_INT, &maxcontin, 0), + f2c_entry ("Nl", P_ONE_ARG, P_INT, &maxlablist, 0), + f2c_entry ("c", P_NO_ARGS, P_INT, &addftnsrc, YES), + f2c_entry ("p", P_NO_ARGS, P_INT, &usedefsforcommon, YES), + f2c_entry ("R", P_NO_ARGS, P_INT, &forcedouble, NO), + f2c_entry ("!R", P_NO_ARGS, P_INT, &forcedouble, YES), + f2c_entry ("A", P_NO_ARGS, P_INT, &Ansi, YES), + f2c_entry ("ext", P_NO_ARGS, P_INT, &noextflag, YES), + f2c_entry ("z", P_NO_ARGS, P_INT, &zflag, NO), + f2c_entry ("a", P_NO_ARGS, P_INT, &useauto, YES), + f2c_entry ("r8", P_NO_ARGS, P_INT, &r8flag, YES), + f2c_entry ("i2", P_NO_ARGS, P_INT, &tyioint, NO), + f2c_entry ("w8", P_NO_ARGS, P_INT, &wordalign, YES), + f2c_entry ("!I", P_NO_ARGS, P_INT, &can_include, NO), + f2c_entry ("W", P_ONE_ARG, P_INT, &chars_per_wd, 0), + f2c_entry ("g", P_NO_ARGS, P_INT, &gflag, YES), + f2c_entry ("T", P_ONE_ARG, P_STRING, &tmpdir, 0), + f2c_entry ("E", P_NO_ARGS, P_INT, &extcomm, 1), + f2c_entry ("e1c", P_NO_ARGS, P_INT, &ext1comm, 1), + f2c_entry ("ec", P_NO_ARGS, P_INT, &ext1comm, 2), + f2c_entry ("C++", P_NO_ARGS, P_INT, &Ansi, 2), + f2c_entry ("P", P_NO_ARGS, P_INT, &Castargs, 3), + f2c_entry ("Ps", P_NO_ARGS, P_INT, &protostatus, 1), + f2c_entry ("!P", P_NO_ARGS, P_INT, &Castargs, 0), + f2c_entry ("!c", P_NO_ARGS, P_INT, &skipC, 1), + f2c_entry ("!it", P_NO_ARGS, P_INT, &infertypes, 0), + f2c_entry ("h", P_NO_ARGS, P_INT, &h0align, 1), + f2c_entry ("hd", P_NO_ARGS, P_INT, &h0align, 2), + f2c_entry ("kr", P_NO_ARGS, P_INT, &krparens, 1), + f2c_entry ("krd", P_NO_ARGS, P_INT, &krparens, 2), + f2c_entry ("!bs", P_NO_ARGS, P_INT, &use_bs, NO), + f2c_entry ("r", P_NO_ARGS, P_INT, &forcereal, YES), + f2c_entry ("72", P_NO_ARGS, P_INT, &warn72, 1), + f2c_entry ("f", P_NO_ARGS, P_INT, &warn72, 2), + f2c_entry ("s", P_NO_ARGS, P_INT, &keepsubs, 1), +#ifdef TYQUAD + f2c_entry ("!i8", P_NO_ARGS, P_INT, &use_tyquad, NO), +#endif + + /* options omitted from man pages */ + + /* -ev ==> implement equivalence with initialized pointers */ + f2c_entry ("ev", P_NO_ARGS, P_INT, &def_equivs, NO), + + /* -!it used to be the default when -it was more agressive */ + + f2c_entry ("it", P_NO_ARGS, P_INT, &infertypes, 1), + + /* -Pd is similar to -P, but omits :ref: lines */ + f2c_entry ("Pd", P_NO_ARGS, P_INT, &Castargs, 2), + + /* -t ==> emit typedefs (under -A or -C++) for procedure + argument types used. This is meant for netlib's + f2c service, so -A and -C++ will work with older + versions of f2c.h + */ + f2c_entry ("t", P_NO_ARGS, P_INT, &typedefs, 1), + + /* -!V ==> omit version msg (to facilitate using diff in + regression testing) + */ + f2c_entry ("!V", P_NO_ARGS, P_INT, &skipversion, 1) + +}; /* table */ + +extern char *c_functions; /* "c_functions" */ +extern char *coutput; /* "c_output" */ +extern char *initfname; /* "raw_data" */ +extern char *blkdfname; /* "block_data" */ +extern char *p1_file; /* "p1_file" */ +extern char *p1_bakfile; /* "p1_file.BAK" */ +extern char *sortfname; /* "init_file" */ +extern char *proto_fname; /* "proto_file" */ +FILE *protofile; + +extern void list_init_data(), set_tmp_names(), sigcatch(), Un_link_all(); +extern char *c_name(); + + +set_externs () +{ + static char *hset[3] = { 0, "integer", "doublereal" }; + +/* Adjust the global flags according to the command line parameters */ + + if (chars_per_wd > 0) { + typesize[TYADDR] = typesize[TYLONG] = typesize[TYREAL] = + typesize[TYLOGICAL] = chars_per_wd; + typesize[TYINT1] = typesize[TYLOGICAL1] = 1; + typesize[TYDREAL] = typesize[TYCOMPLEX] = chars_per_wd << 1; + typesize[TYDCOMPLEX] = chars_per_wd << 2; + typesize[TYSHORT] = typesize[TYLOGICAL2] = chars_per_wd >> 1; + typesize[TYCILIST] = 5*chars_per_wd; + typesize[TYICILIST] = 6*chars_per_wd; + typesize[TYOLIST] = 9*chars_per_wd; + typesize[TYCLLIST] = 3*chars_per_wd; + typesize[TYALIST] = 2*chars_per_wd; + typesize[TYINLIST] = 26*chars_per_wd; + } + + if (wordalign) + typealign[TYDREAL] = typealign[TYDCOMPLEX] = typealign[TYREAL]; + if (!tyioint) { + tyioint = TYSHORT; + szleng = typesize[TYSHORT]; + def_i2 = "#define f2c_i2 1\n"; + inqmask = M(TYSHORT)|M(TYLOGICAL); + goto checklong; + } + else + szleng = typesize[TYLONG]; + if (useshortints) { + inqmask = M(TYLONG); + checklong: + protorettypes[TYLOGICAL] = typename[TYLOGICAL] = "shortlogical"; + typesize[TYLOGICAL] = typesize[TYSHORT]; + casttypes[TYLOGICAL] = "K_fp"; + if (uselongints) + err ("Can't use both long and short ints"); + else { + tyint = tylogical = TYSHORT; + tylog = TYLOGICAL2; + } + } + else if (uselongints) + tyint = TYLONG; + + if (h0align) { + if (tyint == TYLONG && wordalign) + h0align = 1; + ohalign = halign = hset[h0align]; + htype = h0align == 1 ? tyint : TYDREAL; + hsize = typesize[htype]; + } + + if (no66flag) + noextflag = no66flag; + if (noextflag) + zflag = 0; + + if (r8flag) { + tyreal = TYDREAL; + tycomplex = TYDCOMPLEX; + r8fix(); + } + if (forcedouble) { + protorettypes[TYREAL] = "E_f"; + casttypes[TYREAL] = "E_fp"; + } + + if (maxregvar > MAXREGVAR) { + warni("-O%d: too many register variables", maxregvar); + maxregvar = MAXREGVAR; + } /* if maxregvar > MAXREGVAR */ + +/* Check the list of input files */ + + { + int bad, i, cur_max = Max_ftn_files; + + for (i = bad = 0; i < cur_max && ftn_files[i]; i++) + if (ftn_files[i][0] == '-') { + errstr ("Invalid flag '%s'", ftn_files[i]); + bad++; + } + if (bad) + exit(1); + + } /* block */ +} /* set_externs */ + + + static int +comm2dcl() +{ + Extsym *ext; + if (ext1comm) + for(ext = extsymtab; ext < nextext; ext++) + if (ext->extstg == STGCOMMON && !ext->extinit) + return ext1comm; + return 0; + } + + static void +write_typedefs(outfile) + FILE *outfile; +{ + register int i; + register char *s, *p = 0; + static char st[4] = { TYREAL, TYCOMPLEX, TYDCOMPLEX, TYCHAR }; + static char stl[4] = { 'E', 'C', 'Z', 'H' }; + + for(i = 0; i <= TYSUBR; i++) + if (s = usedcasts[i]) { + if (!p) { + p = Ansi == 1 ? "()" : "(...)"; + nice_printf(outfile, + "/* Types for casting procedure arguments: */\ +\n\n#ifndef F2C_proc_par_types\n"); + if (i == 0) { + nice_printf(outfile, + "typedef int /* Unknown procedure type */ (*%s)%s;\n", + s, p); + continue; + } + } + nice_printf(outfile, "typedef %s (*%s)%s;\n", + c_type_decl(i,1), s, p); + } + for(i = !forcedouble; i < 4; i++) + if (used_rets[st[i]]) + nice_printf(outfile, + "typedef %s %c_f; /* %s function */\n", + p = i ? "VOID" : "doublereal", + stl[i], ftn_types[st[i]]); + if (p) + nice_printf(outfile, "#endif\n\n"); + } + + static void +commonprotos(outfile) + register FILE *outfile; +{ + register Extsym *e, *ee; + register Argtypes *at; + Atype *a, *ae; + int k; + extern int proc_protochanges; + + if (!outfile) + return; + for (e = extsymtab, ee = nextext; e < ee; e++) + if (e->extstg == STGCOMMON && e->allextp) + nice_printf(outfile, "/* comlen %s %ld */\n", + e->cextname, e->maxleng); + if (Castargs1 < 3) + return; + + /* -Pr: special comments conveying current knowledge + of external references */ + + k = proc_protochanges; + for (e = extsymtab, ee = nextext; e < ee; e++) + if (e->extstg == STGEXT + && e->cextname != e->fextname) /* not a library function */ + if (at = e->arginfo) { + if ((!e->extinit || at->changes & 1) + /* not defined here or + changed since definition */ + && at->nargs >= 0) { + nice_printf(outfile, "/*:ref: %s %d %d", + e->cextname, e->extype, at->nargs); + a = at->atypes; + for(ae = a + at->nargs; a < ae; a++) + nice_printf(outfile, " %d", a->type); + nice_printf(outfile, " */\n"); + if (at->changes & 1) + k++; + } + } + else if (e->extype) + /* typed external, never invoked */ + nice_printf(outfile, "/*:ref: %s %d :*/\n", + e->cextname, e->extype); + if (k) { + nice_printf(outfile, + "/* Rerunning f2c -P may change prototypes or declarations. */\n"); + if (nerr) + return; + if (protostatus) + done(4); + if (protofile != stdout) { + fprintf(diagfile, + "Rerunning \"f2c -P ... %s %s\" may change prototypes or declarations.\n", + filename0, proto_fname); + fflush(diagfile); + } + } + } + + int retcode = 0; + +main(argc, argv) +int argc; +char **argv; +{ + int c2d, k; + FILE *c_output; + char *cdfilename; + static char stderrbuf[BUFSIZ]; + extern void def_commons(); + extern char **dfltproc, *dflt1proc[]; + extern char link_msg[]; + + diagfile = stderr; + setbuf(stderr, stderrbuf); /* arrange for fast error msgs */ + + Max_ftn_files = argc - 1; + ftn_files = (char **)ckalloc((argc+1)*sizeof(char *)); + + parse_args (argc, argv, table, sizeof(table)/sizeof(arg_info), + ftn_files, Max_ftn_files); + if (!can_include && ext1comm == 2) + ext1comm = 1; + if (ext1comm && !extcomm) + extcomm = 2; + if (protostatus) + Castargs = 3; + Castargs1 = Castargs; + if (!Ansi) { + Castargs = 0; + parens = "()"; + } + else if (!Castargs) + parens = Ansi == 1 ? "()" : "(...)"; + else + dfltproc = dflt1proc; + + set_externs(); + fileinit(); + read_Pfiles(ftn_files); + + for(k = 1; ftn_files[k]; k++) + if (dofork()) + break; + filename0 = file_name = ftn_files[current_ftn_file = k - 1]; + + set_tmp_names(); + sigcatch(); + + c_file = opf(c_functions, textwrite); + pass1_file=opf(p1_file, binwrite); + initkey(); + if (file_name && *file_name) { + if (debugflag != 1) { + if (!o_coutput) + coutput = c_name(file_name,'c'); + else + coutput = o_coutput; + if (Castargs1 >= 2) + proto_fname = c_name(file_name,'P'); + } + cdfilename = coutput; + if (skipC) + coutput = 0; + if (coutput[0] == '-'){ + c_output = stdout; + coutput = 0; + } + else if (!(c_output = fopen(coutput, textwrite))) { + file_name = coutput; + coutput = 0; /* don't delete read-only .c file */ + fatalstr("can't open %.86s", file_name); + } + + if (Castargs1 >= 2 + && !(protofile = fopen(proto_fname, textwrite))) + fatalstr("Can't open %.84s\n", proto_fname); + } + else { + file_name = ""; + cdfilename = "f2c_out.c"; + c_output = stdout; + coutput = 0; + if (Castargs1 >= 2) { + protofile = stdout; + if (!skipC) + printf("#ifdef P_R_O_T_O_T_Y_P_E_S\n"); + } + } + + if(inilex( copys(file_name) )) + done(1); + if (filename0) { + fprintf(diagfile, "%s:\n", file_name); + fflush(diagfile); + } + + procinit(); + if(k = yyparse()) + { + fprintf(diagfile, "Bad parse, return code %d\n", k); + done(1); + } + + commonprotos(protofile); + if (protofile == stdout && !skipC) + printf("#endif\n\n"); + + if (nerr || skipC) + goto C_skipped; + + +/* Write out the declarations which are global to this file */ + + if ((c2d = comm2dcl()) == 1) + nice_printf(c_output, "/*>>>'/dev/null'<<<*/\n\n\ +/* Split this into several files by piping it through\n\n\ +sed \"s/^\\/\\*>>>'\\(.*\\)'<<<\\*\\/\\$/cat >'\\1' <<'\\/*<<<\\1>>>*\\/'/\" | /bin/sh\n\ + */\n\ +/*<<</dev/null>>>*/\n\ +/*>>>'%s'<<<*/\n", cdfilename); + if (gflag) + nice_printf (c_output, "#line 1 \"%s\"\n", file_name); + if (!skipversion) { + nice_printf (c_output, "/* %s -- translated by f2c ", file_name); + nice_printf (c_output, "(version %s).\n", F2C_version); + nice_printf (c_output, + " You must link the resulting object file with the libraries:\n\ + %s (in that order)\n*/\n\n", link_msg); + } + if (Ansi == 2) + nice_printf(c_output, + "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"); + nice_printf (c_output, "%s#include \"f2c.h\"\n\n", def_i2); + if (gflag) + nice_printf (c_output, "#line 1 \"%s\"\n", file_name); + if (Castargs && typedefs) + write_typedefs(c_output); + nice_printf (c_file, "\n"); + fclose (c_file); + c_file = c_output; /* HACK to get the next indenting + to work */ + wr_common_decls (c_output); + if (blkdfile) + list_init_data(&blkdfile, blkdfname, c_output); + wr_globals (c_output); + if ((c_file = fopen (c_functions, textread)) == (FILE *) NULL) + Fatal("main - couldn't reopen c_functions"); + ffilecopy (c_file, c_output); + if (*main_alias) { + nice_printf (c_output, "/* Main program alias */ "); + nice_printf (c_output, "int %s () { MAIN__ ();%s }\n", + main_alias, Ansi ? " return 0;" : ""); + } + if (Ansi == 2) + nice_printf(c_output, + "#ifdef __cplusplus\n\t}\n#endif\n"); + if (c2d) { + if (c2d == 1) + fprintf(c_output, "/*<<<%s>>>*/\n", cdfilename); + else + fclose(c_output); + def_commons(c_output); + } + if (c2d != 2) + fclose (c_output); + + C_skipped: + if(parstate != OUTSIDE) + { + warn("missing final end statement"); + endproc(); + } + done(nerr ? 1 : 0); +} + + +FILEP opf(fn, mode) +char *fn, *mode; +{ + FILEP fp; + if( fp = fopen(fn, mode) ) + return(fp); + + fatalstr("cannot open intermediate file %s", fn); + /* NOT REACHED */ return 0; +} + + +clf(p, what, quit) + FILEP *p; + char *what; + int quit; +{ + if(p!=NULL && *p!=NULL && *p!=stdout) + { + if(ferror(*p)) { + fprintf(stderr, "I/O error on %s\n", what); + if (quit) + done(3); + retcode = 3; + } + fclose(*p); + } + *p = NULL; +} + + +done(k) +int k; +{ + clf(&initfile, "initfile", 0); + clf(&c_file, "c_file", 0); + clf(&pass1_file, "pass1_file", 0); + Un_link_all(k); + exit(k|retcode); +} diff --git a/usr.bin/f2c/malloc.c b/usr.bin/f2c/malloc.c new file mode 100644 index 000000000000..e4414dad9a8b --- /dev/null +++ b/usr.bin/f2c/malloc.c @@ -0,0 +1,142 @@ +/**************************************************************** +Copyright 1990 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#ifndef CRAY +#define STACKMIN 512 +#define MINBLK (2*sizeof(struct mem) + 16) +#define MSTUFF _malloc_stuff_ +#define F MSTUFF.free +#define B MSTUFF.busy +#define SBGULP 8192 +char *memcpy(); + +struct mem { + struct mem *next; + unsigned len; + }; + +struct { + struct mem *free; + char *busy; + } MSTUFF; + +char * +malloc(size) +register unsigned size; +{ + register struct mem *p, *q, *r, *s; + unsigned register k, m; + extern char *sbrk(); + char *top, *top1; + + size = (size+7) & ~7; + r = (struct mem *) &F; + for (p = F, q = 0; p; r = p, p = p->next) { + if ((k = p->len) >= size && (!q || m > k)) { m = k; q = p; s = r; } + } + if (q) { + if (q->len - size >= MINBLK) { /* split block */ + p = (struct mem *) (((char *) (q+1)) + size); + p->next = q->next; + p->len = q->len - size - sizeof(struct mem); + s->next = p; + q->len = size; + } + else s->next = q->next; + } + else { + top = B ? B : (char *)(((long)sbrk(0) + 7) & ~7); + if (F && (char *)(F+1) + F->len == B) + { q = F; F = F->next; } + else q = (struct mem *) top; + top1 = (char *)(q+1) + size; + if (top1 > top) { + if (sbrk((int)(top1-top+SBGULP)) == (char *) -1) + return 0; + r = (struct mem *)top1; + r->len = SBGULP - sizeof(struct mem); + r->next = F; + F = r; + top1 += SBGULP; + } + q->len = size; + B = top1; + } + return (char *) (q+1); + } + +free(f) +char *f; +{ + struct mem *p, *q, *r; + char *pn, *qn; + + if (!f) return; + q = (struct mem *) (f - sizeof(struct mem)); + qn = f + q->len; + for (p = F, r = (struct mem *) &F; ; r = p, p = p->next) { + if (qn == (char *) p) { + q->len += p->len + sizeof(struct mem); + p = p->next; + } + pn = p ? ((char *) (p+1)) + p->len : 0; + if (pn == (char *) q) { + p->len += sizeof(struct mem) + q->len; + q->len = 0; + q->next = p; + r->next = p; + break; + } + if (pn < (char *) q) { + r->next = q; + q->next = p; + break; + } + } + } + +char * +realloc(f, size) +char *f; +unsigned size; +{ + struct mem *p; + char *q, *f1; + unsigned s1; + + if (!f) return malloc(size); + p = (struct mem *) (f - sizeof(struct mem)); + s1 = p->len; + free(f); + if (s1 > size) s1 = size + 7 & ~7; + if (!p->len) { + f1 = (char *)(p->next + 1); + memcpy(f1, f, s1); + f = f1; + } + q = malloc(size); + if (q && q != f) + memcpy(q, f, s1); + return q; + } +#endif diff --git a/usr.bin/f2c/mem.c b/usr.bin/f2c/mem.c new file mode 100644 index 000000000000..940e9c1848c0 --- /dev/null +++ b/usr.bin/f2c/mem.c @@ -0,0 +1,234 @@ +/**************************************************************** +Copyright 1990, 1991 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "iob.h" + +#define MEMBSIZE 32000 +#define GMEMBSIZE 16000 + + extern void exit(); + + char * +gmem(n, round) + int n, round; +{ + static char *last, *next; + char *rv; + if (round) +#ifdef CRAY + if ((long)next & 0xe000000000000000) + next = (char *)(((long)next & 0x1fffffffffffffff) + 1); +#else +#ifdef MSDOS + if ((int)next & 1) + next++; +#else + next = (char *)(((long)next + sizeof(char *)-1) + & ~((long)sizeof(char *)-1)); +#endif +#endif + rv = next; + if ((next += n) > last) { + rv = Alloc(n + GMEMBSIZE); + + next = rv + n; + last = next + GMEMBSIZE; + } + return rv; + } + + struct memblock { + struct memblock *next; + char buf[MEMBSIZE]; + }; + typedef struct memblock memblock; + + static memblock *mem0; + memblock *curmemblock, *firstmemblock; + + char *mem_first, *mem_next, *mem_last, *mem0_last; + + void +mem_init() +{ + curmemblock = firstmemblock = mem0 + = (memblock *)Alloc(sizeof(memblock)); + mem_first = mem0->buf; + mem_next = mem0->buf; + mem_last = mem0->buf + MEMBSIZE; + mem0_last = mem0->buf + MEMBSIZE; + mem0->next = 0; + } + + char * +mem(n, round) + int n, round; +{ + memblock *b; + register char *rv, *s; + + if (round) +#ifdef CRAY + if ((long)mem_next & 0xe000000000000000) + mem_next = (char *)(((long)mem_next & 0x1fffffffffffffff) + 1); +#else +#ifdef MSDOS + if ((int)mem_next & 1) + mem_next++; +#else + mem_next = (char *)(((long)mem_next + sizeof(char *)-1) + & ~((long)sizeof(char *)-1)); +#endif +#endif + rv = mem_next; + s = rv + n; + if (s >= mem_last) { + if (n > MEMBSIZE) { + fprintf(stderr, "mem(%d) failure!\n", n); + exit(1); + } + if (!(b = curmemblock->next)) { + b = (memblock *)Alloc(sizeof(memblock)); + curmemblock->next = b; + b->next = 0; + } + curmemblock = b; + rv = b->buf; + mem_last = rv + sizeof(b->buf); + s = rv + n; + } + mem_next = s; + return rv; + } + + char * +tostring(s,n) + register char *s; + int n; +{ + register char *s1, *se, **sf; + char *rv, *s0; + register int k = n + 2, t; + + sf = str_fmt; + sf['%'] = "%"; + s0 = s; + se = s + n; + for(; s < se; s++) { + t = *(unsigned char *)s; + s1 = sf[t]; + while(*++s1) + k++; + } + sf['%'] = "%%"; + rv = s1 = mem(k,0); + *s1++ = '"'; + for(s = s0; s < se; s++) { + t = *(unsigned char *)s; + sprintf(s1, sf[t], t); + s1 += strlen(s1); + } + *s1 = 0; + return rv; + } + + char * +cpstring(s) + register char *s; +{ + return strcpy(mem(strlen(s)+1,0), s); + } + + void +new_iob_data(ios, name) + register io_setup *ios; + char *name; +{ + register iob_data *iod; + register char **s, **se; + + iod = (iob_data *) + mem(sizeof(iob_data) + ios->nelt*sizeof(char *), 1); + iod->next = iob_list; + iob_list = iod; + iod->type = ios->fields[0]; + iod->name = cpstring(name); + s = iod->fields; + se = s + ios->nelt; + while(s < se) + *s++ = "0"; + *s = 0; + } + + char * +string_num(pfx, n) + char *pfx; + long n; +{ + char buf[32]; + sprintf(buf, "%s%ld", pfx, n); + /* can't trust return type of sprintf -- BSD gets it wrong */ + return strcpy(mem(strlen(buf)+1,0), buf); + } + +static defines *define_list; + + void +def_start(outfile, s1, s2, post) + FILE *outfile; + char *s1, *s2, *post; +{ + defines *d; + int n, n1; + extern int in_define; + + n = n1 = strlen(s1); + if (s2) + n += strlen(s2); + d = (defines *)mem(sizeof(defines)+n, 1); + d->next = define_list; + define_list = d; + strcpy(d->defname, s1); + if (s2) + strcpy(d->defname + n1, s2); + in_define = 1; + nice_printf(outfile, "#define %s", d->defname); + if (post) + nice_printf(outfile, " %s", post); + } + + void +other_undefs(outfile) + FILE *outfile; +{ + defines *d; + if (d = define_list) { + define_list = 0; + nice_printf(outfile, "\n"); + do + nice_printf(outfile, "#undef %s\n", d->defname); + while(d = d->next); + nice_printf(outfile, "\n"); + } + } diff --git a/usr.bin/f2c/memset.c b/usr.bin/f2c/memset.c new file mode 100644 index 000000000000..98a7ce72a796 --- /dev/null +++ b/usr.bin/f2c/memset.c @@ -0,0 +1,66 @@ +/**************************************************************** +Copyright 1990 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* This is for the benefit of people whose systems don't provide + * memset, memcpy, and memcmp. If yours is such a system, adjust + * the makefile by adding memset.o to the "OBJECTS =" assignment. + * WARNING: the memcpy below is adequate for f2c, but is not a + * general memcpy routine (which must correctly handle overlapping + * fields). + */ + + int +memcmp(s1, s2, n) + register char *s1, *s2; + int n; +{ + register char *se; + + for(se = s1 + n; s1 < se; s1++, s2++) + if (*s1 != *s2) + return *s1 - *s2; + return 0; + } + + char * +memcpy(s1, s2, n) + register char *s1, *s2; + int n; +{ + register char *s0 = s1, *se = s1 + n; + + while(s1 < se) + *s1++ = *s2++; + return s0; + } + +memset(s, c, n) + register char *s; + register int c; + int n; +{ + register char *se = s + n; + + while(s < se) + *s++ = c; + } diff --git a/usr.bin/f2c/misc.c b/usr.bin/f2c/misc.c new file mode 100644 index 000000000000..d8ad3cf30b0e --- /dev/null +++ b/usr.bin/f2c/misc.c @@ -0,0 +1,1054 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" + +int oneof_stg (name, stg, mask) + Namep name; + int stg, mask; +{ + if (stg == STGCOMMON && name) { + if ((mask & M(STGEQUIV))) + return name->vcommequiv; + if ((mask & M(STGCOMMON))) + return !name->vcommequiv; + } + return ONEOF(stg, mask); + } + + +/* op_assign -- given a binary opcode, return the associated assignment + operator */ + +int op_assign (opcode) +int opcode; +{ + int retval = -1; + + switch (opcode) { + case OPPLUS: retval = OPPLUSEQ; break; + case OPMINUS: retval = OPMINUSEQ; break; + case OPSTAR: retval = OPSTAREQ; break; + case OPSLASH: retval = OPSLASHEQ; break; + case OPMOD: retval = OPMODEQ; break; + case OPLSHIFT: retval = OPLSHIFTEQ; break; + case OPRSHIFT: retval = OPRSHIFTEQ; break; + case OPBITAND: retval = OPBITANDEQ; break; + case OPBITXOR: retval = OPBITXOREQ; break; + case OPBITOR: retval = OPBITOREQ; break; + default: + erri ("op_assign: bad opcode '%d'", opcode); + break; + } /* switch */ + + return retval; +} /* op_assign */ + + + char * +Alloc(n) /* error-checking version of malloc */ + /* ckalloc initializes memory to 0; Alloc does not */ + int n; +{ + char errbuf[32]; + register char *rv; + + rv = malloc(n); + if (!rv) { + sprintf(errbuf, "malloc(%d) failure!", n); + Fatal(errbuf); + } + return rv; + } + + +cpn(n, a, b) +register int n; +register char *a, *b; +{ + while(--n >= 0) + *b++ = *a++; +} + + + +eqn(n, a, b) +register int n; +register char *a, *b; +{ + while(--n >= 0) + if(*a++ != *b++) + return(NO); + return(YES); +} + + + + + + + +cmpstr(a, b, la, lb) /* compare two strings */ +register char *a, *b; +ftnint la, lb; +{ + register char *aend, *bend; + aend = a + la; + bend = b + lb; + + + if(la <= lb) + { + while(a < aend) + if(*a != *b) + return( *a - *b ); + else + { + ++a; + ++b; + } + + while(b < bend) + if(*b != ' ') + return(' ' - *b); + else + ++b; + } + + else + { + while(b < bend) + if(*a != *b) + return( *a - *b ); + else + { + ++a; + ++b; + } + while(a < aend) + if(*a != ' ') + return(*a - ' '); + else + ++a; + } + return(0); +} + + +/* hookup -- Same as LISP NCONC, that is a destructive append of two lists */ + +chainp hookup(x,y) +register chainp x, y; +{ + register chainp p; + + if(x == NULL) + return(y); + + for(p = x ; p->nextp ; p = p->nextp) + ; + p->nextp = y; + return(x); +} + + + +struct Listblock *mklist(p) +chainp p; +{ + register struct Listblock *q; + + q = ALLOC(Listblock); + q->tag = TLIST; + q->listp = p; + return(q); +} + + +chainp mkchain(p,q) +register char * p; +register chainp q; +{ + register chainp r; + + if(chains) + { + r = chains; + chains = chains->nextp; + } + else + r = ALLOC(Chain); + + r->datap = p; + r->nextp = q; + return(r); +} + + chainp +revchain(next) + register chainp next; +{ + register chainp p, prev = 0; + + while(p = next) { + next = p->nextp; + p->nextp = prev; + prev = p; + } + return prev; + } + + +/* addunder -- turn a cvarname into an external name */ +/* The cvarname may already end in _ (to avoid C keywords); */ +/* if not, it has room for appending an _. */ + + char * +addunder(s) + register char *s; +{ + register int c, i; + char *s0 = s; + + i = 0; + while(c = *s++) + if (c == '_') + i++; + else + i = 0; + if (!i) { + *s-- = 0; + *s = '_'; + } + return( s0 ); + } + + +/* copyn -- return a new copy of the input Fortran-string */ + +char *copyn(n, s) +register int n; +register char *s; +{ + register char *p, *q; + + p = q = (char *) Alloc(n); + while(--n >= 0) + *q++ = *s++; + return(p); +} + + + +/* copys -- return a new copy of the input C-string */ + +char *copys(s) +char *s; +{ + return( copyn( strlen(s)+1 , s) ); +} + + + +/* convci -- Convert Fortran-string to integer; assumes that input is a + legal number, with no trailing blanks */ + +ftnint convci(n, s) +register int n; +register char *s; +{ + ftnint sum; + sum = 0; + while(n-- > 0) + sum = 10*sum + (*s++ - '0'); + return(sum); +} + +/* convic - Convert Integer constant to string */ + +char *convic(n) +ftnint n; +{ + static char s[20]; + register char *t; + + s[19] = '\0'; + t = s+19; + + do { + *--t = '0' + n%10; + n /= 10; + } while(n > 0); + + return(t); +} + + + +/* mkname -- add a new identifier to the environment, including the closed + hash table. */ + +Namep mkname(s) +register char *s; +{ + struct Hashentry *hp; + register Namep q; + register int c, hash, i; + register char *t; + char *s0; + char errbuf[64]; + + hash = i = 0; + s0 = s; + while(c = *s++) { + hash += c; + if (c == '_') + i = 2; + } + if (!i && in_vector(s0,c_keywords,n_keywords) >= 0) + i = 1; + hash %= maxhash; + +/* Add the name to the closed hash table */ + + hp = hashtab + hash; + + while(q = hp->varp) + if( hash == hp->hashval && !strcmp(s0,q->fvarname) ) + return(q); + else if(++hp >= lasthash) + hp = hashtab; + + if(++nintnames >= maxhash-1) + many("names", 'n', maxhash); /* Fatal error */ + hp->varp = q = ALLOC(Nameblock); + hp->hashval = hash; + q->tag = TNAME; /* TNAME means the tag type is NAME */ + c = s - s0; + if (c > 7 && noextflag) { + sprintf(errbuf, "\"%.35s%s\" over 6 characters long", s0, + c > 36 ? "..." : ""); + errext(errbuf); + } + q->fvarname = strcpy(mem(c,0), s0); + t = q->cvarname = mem(c + i + 1, 0); + s = s0; + /* add __ to the end of any name containing _ and to any C keyword */ + while(*t = *s++) + t++; + if (i) { + do *t++ = '_'; + while(--i > 0); + *t = 0; + } + return(q); +} + + +struct Labelblock *mklabel(l) +ftnint l; +{ + register struct Labelblock *lp; + + if(l <= 0) + return(NULL); + + for(lp = labeltab ; lp < highlabtab ; ++lp) + if(lp->stateno == l) + return(lp); + + if(++highlabtab > labtabend) + many("statement labels", 's', maxstno); + + lp->stateno = l; + lp->labelno = newlabel(); + lp->blklevel = 0; + lp->labused = NO; + lp->fmtlabused = NO; + lp->labdefined = NO; + lp->labinacc = NO; + lp->labtype = LABUNKNOWN; + lp->fmtstring = 0; + return(lp); +} + + +newlabel() +{ + return( ++lastlabno ); +} + + +/* this label appears in a branch context */ + +struct Labelblock *execlab(stateno) +ftnint stateno; +{ + register struct Labelblock *lp; + + if(lp = mklabel(stateno)) + { + if(lp->labinacc) + warn1("illegal branch to inner block, statement label %s", + convic(stateno) ); + else if(lp->labdefined == NO) + lp->blklevel = blklevel; + if(lp->labtype == LABFORMAT) + err("may not branch to a format"); + else + lp->labtype = LABEXEC; + } + else + execerr("illegal label %s", convic(stateno)); + + return(lp); +} + + +/* find or put a name in the external symbol table */ + +Extsym *mkext(f,s) +char *f, *s; +{ + Extsym *p; + + for(p = extsymtab ; p<nextext ; ++p) + if(!strcmp(s,p->cextname)) + return( p ); + + if(nextext >= lastext) + many("external symbols", 'x', maxext); + + nextext->fextname = strcpy(gmem(strlen(f)+1,0), f); + nextext->cextname = f == s + ? nextext->fextname + : strcpy(gmem(strlen(s)+1,0), s); + nextext->extstg = STGUNKNOWN; + nextext->extp = 0; + nextext->allextp = 0; + nextext->extleng = 0; + nextext->maxleng = 0; + nextext->extinit = 0; + nextext->curno = nextext->maxno = 0; + return( nextext++ ); +} + + +Addrp builtin(t, s, dbi) +int t, dbi; +char *s; +{ + register Extsym *p; + register Addrp q; + extern chainp used_builtins; + + p = mkext(s,s); + if(p->extstg == STGUNKNOWN) + p->extstg = STGEXT; + else if(p->extstg != STGEXT) + { + errstr("improper use of builtin %s", s); + return(0); + } + + q = ALLOC(Addrblock); + q->tag = TADDR; + q->vtype = t; + q->vclass = CLPROC; + q->vstg = STGEXT; + q->memno = p - extsymtab; + q->dbl_builtin = dbi; + +/* A NULL pointer here tells you to use memno to check the external + symbol table */ + + q -> uname_tag = UNAM_EXTERN; + +/* Add to the list of used builtins */ + + if (dbi >= 0) + add_extern_to_list (q, &used_builtins); + return(q); +} + + + +add_extern_to_list (addr, list_store) +Addrp addr; +chainp *list_store; +{ + chainp last = CHNULL; + chainp list; + int memno; + + if (list_store == (chainp *) NULL || addr == (Addrp) NULL) + return; + + list = *list_store; + memno = addr -> memno; + + for (;list; last = list, list = list -> nextp) { + Addrp this = (Addrp) (list -> datap); + + if (this -> tag == TADDR && this -> uname_tag == UNAM_EXTERN && + this -> memno == memno) + return; + } /* for */ + + if (*list_store == CHNULL) + *list_store = mkchain((char *)cpexpr((expptr)addr), CHNULL); + else + last->nextp = mkchain((char *)cpexpr((expptr)addr), CHNULL); + +} /* add_extern_to_list */ + + +frchain(p) +register chainp *p; +{ + register chainp q; + + if(p==0 || *p==0) + return; + + for(q = *p; q->nextp ; q = q->nextp) + ; + q->nextp = chains; + chains = *p; + *p = 0; +} + + void +frexchain(p) + register chainp *p; +{ + register chainp q, r; + + if (q = *p) { + for(;;q = r) { + frexpr((expptr)q->datap); + if (!(r = q->nextp)) + break; + } + q->nextp = chains; + chains = *p; + *p = 0; + } + } + + +tagptr cpblock(n,p) +register int n; +register char * p; +{ + register ptr q; + + memcpy((char *)(q = ckalloc(n)), (char *)p, n); + return( (tagptr) q); +} + + + +ftnint lmax(a, b) +ftnint a, b; +{ + return( a>b ? a : b); +} + +ftnint lmin(a, b) +ftnint a, b; +{ + return(a < b ? a : b); +} + + + + +maxtype(t1, t2) +int t1, t2; +{ + int t; + + t = t1 >= t2 ? t1 : t2; + if(t==TYCOMPLEX && (t1==TYDREAL || t2==TYDREAL) ) + t = TYDCOMPLEX; + return(t); +} + + + +/* return log base 2 of n if n a power of 2; otherwise -1 */ +log_2(n) +ftnint n; +{ + int k; + + /* trick based on binary representation */ + + if(n<=0 || (n & (n-1))!=0) + return(-1); + + for(k = 0 ; n >>= 1 ; ++k) + ; + return(k); +} + + + +frrpl() +{ + struct Rplblock *rp; + + while(rpllist) + { + rp = rpllist->rplnextp; + free( (charptr) rpllist); + rpllist = rp; + } +} + + + +/* Call a Fortran function with an arbitrary list of arguments */ + +int callk_kludge; + +expptr callk(type, name, args) +int type; +char *name; +chainp args; +{ + register expptr p; + + p = mkexpr(OPCALL, + (expptr)builtin(callk_kludge ? callk_kludge : type, name, 0), + (expptr)args); + p->exprblock.vtype = type; + return(p); +} + + + +expptr call4(type, name, arg1, arg2, arg3, arg4) +int type; +char *name; +expptr arg1, arg2, arg3, arg4; +{ + struct Listblock *args; + args = mklist( mkchain((char *)arg1, + mkchain((char *)arg2, + mkchain((char *)arg3, + mkchain((char *)arg4, CHNULL)) ) ) ); + return( callk(type, name, (chainp)args) ); +} + + + + +expptr call3(type, name, arg1, arg2, arg3) +int type; +char *name; +expptr arg1, arg2, arg3; +{ + struct Listblock *args; + args = mklist( mkchain((char *)arg1, + mkchain((char *)arg2, + mkchain((char *)arg3, CHNULL) ) ) ); + return( callk(type, name, (chainp)args) ); +} + + + + + +expptr call2(type, name, arg1, arg2) +int type; +char *name; +expptr arg1, arg2; +{ + struct Listblock *args; + + args = mklist( mkchain((char *)arg1, mkchain((char *)arg2, CHNULL) ) ); + return( callk(type,name, (chainp)args) ); +} + + + + +expptr call1(type, name, arg) +int type; +char *name; +expptr arg; +{ + return( callk(type,name, (chainp)mklist(mkchain((char *)arg,CHNULL)) )); +} + + +expptr call0(type, name) +int type; +char *name; +{ + return( callk(type, name, CHNULL) ); +} + + + +struct Impldoblock *mkiodo(dospec, list) +chainp dospec, list; +{ + register struct Impldoblock *q; + + q = ALLOC(Impldoblock); + q->tag = TIMPLDO; + q->impdospec = dospec; + q->datalist = list; + return(q); +} + + + + +/* ckalloc -- Allocate 1 memory unit of size n, checking for out of + memory error */ + +ptr ckalloc(n) +register int n; +{ + register ptr p; + p = (ptr)calloc(1, (unsigned) n); + if (p || !n) + return(p); + fprintf(stderr, "failing to get %d bytes\n",n); + Fatal("out of memory"); + /* NOT REACHED */ return 0; +} + + + +isaddr(p) +register expptr p; +{ + if(p->tag == TADDR) + return(YES); + if(p->tag == TEXPR) + switch(p->exprblock.opcode) + { + case OPCOMMA: + return( isaddr(p->exprblock.rightp) ); + + case OPASSIGN: + case OPASSIGNI: + case OPPLUSEQ: + case OPMINUSEQ: + case OPSLASHEQ: + case OPMODEQ: + case OPLSHIFTEQ: + case OPRSHIFTEQ: + case OPBITANDEQ: + case OPBITXOREQ: + case OPBITOREQ: + return( isaddr(p->exprblock.leftp) ); + } + return(NO); +} + + + + +isstatic(p) +register expptr p; +{ + extern int useauto; + if(p->headblock.vleng && !ISCONST(p->headblock.vleng)) + return(NO); + + switch(p->tag) + { + case TCONST: + return(YES); + + case TADDR: + if(ONEOF(p->addrblock.vstg,MSKSTATIC) && + ISCONST(p->addrblock.memoffset) && !useauto) + return(YES); + + default: + return(NO); + } +} + + + +/* addressable -- return True iff it is a constant value, or can be + referenced by constant values */ + +addressable(p) +register expptr p; +{ + switch(p->tag) + { + case TCONST: + return(YES); + + case TADDR: + return( addressable(p->addrblock.memoffset) ); + + default: + return(NO); + } +} + + +/* isnegative_const -- returns true if the constant is negative. Returns + false for imaginary and nonnumeric constants */ + +int isnegative_const (cp) +struct Constblock *cp; +{ + int retval; + + if (cp == NULL) + return 0; + + switch (cp -> vtype) { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + retval = cp -> Const.ci < 0; + break; + case TYREAL: + case TYDREAL: + retval = cp->vstg ? *cp->Const.cds[0] == '-' + : cp->Const.cd[0] < 0.0; + break; + default: + + retval = 0; + break; + } /* switch */ + + return retval; +} /* isnegative_const */ + +negate_const(cp) + Constp cp; +{ + if (cp == (struct Constblock *) NULL) + return; + + switch (cp -> vtype) { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + cp -> Const.ci = - cp -> Const.ci; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + if (cp->vstg) + switch(*cp->Const.cds[1]) { + case '-': + ++cp->Const.cds[1]; + break; + case '0': + break; + default: + --cp->Const.cds[1]; + } + else + cp->Const.cd[1] = -cp->Const.cd[1]; + /* no break */ + case TYREAL: + case TYDREAL: + if (cp->vstg) + switch(*cp->Const.cds[0]) { + case '-': + ++cp->Const.cds[0]; + break; + case '0': + break; + default: + --cp->Const.cds[0]; + } + else + cp->Const.cd[0] = -cp->Const.cd[0]; + break; + case TYCHAR: + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + erri ("negate_const: can't negate type '%d'", cp -> vtype); + break; + default: + erri ("negate_const: bad type '%d'", + cp -> vtype); + break; + } /* switch */ +} /* negate_const */ + +ffilecopy (infp, outfp) +FILE *infp, *outfp; +{ + while (!feof (infp)) { + register c = getc (infp); + if (!feof (infp)) + putc (c, outfp); + } /* while */ +} /* ffilecopy */ + + +/* in_vector -- verifies whether str is in c_keywords. + If so, the index is returned else -1 is returned. + c_keywords must be in alphabetical order (as defined by strcmp). +*/ + +int in_vector(str, keywds, n) +char *str; char **keywds; register int n; +{ + register char **K = keywds; + register int n1, t; + + do { + n1 = n >> 1; + if (!(t = strcmp(str, K[n1]))) + return K - keywds + n1; + if (t < 0) + n = n1; + else { + n -= ++n1; + K += n1; + } + } + while(n > 0); + + return -1; + } /* in_vector */ + + +int is_negatable (Const) +Constp Const; +{ + int retval = 0; + if (Const != (Constp) NULL) + switch (Const -> vtype) { + case TYINT1: + retval = Const -> Const.ci >= -BIGGEST_CHAR; + break; + case TYSHORT: + retval = Const -> Const.ci >= -BIGGEST_SHORT; + break; + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + retval = Const -> Const.ci >= -BIGGEST_LONG; + break; + case TYREAL: + case TYDREAL: + case TYCOMPLEX: + case TYDCOMPLEX: + retval = 1; + break; + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + case TYCHAR: + case TYSUBR: + default: + retval = 0; + break; + } /* switch */ + + return retval; +} /* is_negatable */ + +backup(fname, bname) + char *fname, *bname; +{ + FILE *b, *f; + static char couldnt[] = "Couldn't open %.80s"; + + if (!(f = fopen(fname, binread))) { + warn1(couldnt, fname); + return; + } + if (!(b = fopen(bname, binwrite))) { + warn1(couldnt, bname); + return; + } + ffilecopy(f, b); + fclose(f); + fclose(b); + } + + +/* struct_eq -- returns YES if structures have the same field names and + types, NO otherwise */ + +int struct_eq (s1, s2) +chainp s1, s2; +{ + struct Dimblock *d1, *d2; + Constp cp1, cp2; + + if (s1 == CHNULL && s2 == CHNULL) + return YES; + for(; s1 && s2; s1 = s1->nextp, s2 = s2->nextp) { + register Namep v1 = (Namep) s1 -> datap; + register Namep v2 = (Namep) s2 -> datap; + + if (v1 == (Namep) NULL || v1 -> tag != TNAME || + v2 == (Namep) NULL || v2 -> tag != TNAME) + return NO; + + if (v1->vtype != v2->vtype || v1->vclass != v2->vclass + || strcmp(v1->fvarname, v2->fvarname)) + return NO; + + /* compare dimensions (needed for comparing COMMON blocks) */ + + if (d1 = v1->vdim) { + if (!(cp1 = (Constp)d1->nelt) || cp1->tag != TCONST) + return NO; + if (!(d2 = v2->vdim)) + if (cp1->Const.ci == 1) + continue; + else + return NO; + if (!(cp2 = (Constp)d2->nelt) || cp2->tag != TCONST + || cp1->Const.ci != cp2->Const.ci) + return NO; + } + else if ((d2 = v2->vdim) && (!(cp2 = (Constp)d2->nelt) + || cp2->tag != TCONST + || cp2->Const.ci != 1)) + return NO; + } /* while s1 != CHNULL && s2 != CHNULL */ + + return s1 == CHNULL && s2 == CHNULL; +} /* struct_eq */ diff --git a/usr.bin/f2c/names.c b/usr.bin/f2c/names.c new file mode 100644 index 000000000000..e826f3e2e653 --- /dev/null +++ b/usr.bin/f2c/names.c @@ -0,0 +1,742 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "output.h" +#include "names.h" +#include "iob.h" + + +/* Names generated by the translator are guaranteed to be unique from the + Fortan names because Fortran does not allow underscores in identifiers, + and all of the system generated names do have underscores. The various + naming conventions are outlined below: + + FORMAT APPLICATION + ---------------------------------------------------------------------- + io_# temporaries generated by IO calls; these will + contain the device number (e.g. 5, 6, 0) + ret_val function return value, required for complex and + character functions. + ret_val_len length of the return value in character functions + + ssss_len length of character argument "ssss" + + c_# member of the literal pool, where # is an + arbitrary label assigned by the system + cs_# short integer constant in the literal pool + t_# expression temporary, # is the depth of arguments + on the stack. + L# label "#", given by user in the Fortran program. + This is unique because Fortran labels are numeric + pad_# label on an init field required for alignment + xxx_init label on a common block union, if a block data + requires a separate declaration +*/ + +/* generate variable references */ + +char *c_type_decl (type, is_extern) +int type, is_extern; +{ + static char buff[100]; + + switch (type) { + case TYREAL: if (!is_extern || !forcedouble) + { strcpy (buff, "real");break; } + case TYDREAL: strcpy (buff, "doublereal"); break; + case TYCOMPLEX: if (is_extern) + strcpy (buff, "/* Complex */ VOID"); + else + strcpy (buff, "complex"); + break; + case TYDCOMPLEX:if (is_extern) + strcpy (buff, "/* Double Complex */ VOID"); + else + strcpy (buff, "doublecomplex"); + break; + case TYADDR: + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: strcpy(buff, typename[type]); + break; + case TYCHAR: if (is_extern) + strcpy (buff, "/* Character */ VOID"); + else + strcpy (buff, "char"); + break; + + case TYUNKNOWN: strcpy (buff, "UNKNOWN"); + +/* If a procedure's type is unknown, assume it's a subroutine */ + + if (!is_extern) + break; + +/* Subroutines must return an INT, because they might return a label + value. Even if one doesn't, the caller will EXPECT it to. */ + + case TYSUBR: strcpy (buff, "/* Subroutine */ int"); + break; + case TYERROR: strcpy (buff, "ERROR"); break; + case TYVOID: strcpy (buff, "void"); break; + case TYCILIST: strcpy (buff, "cilist"); break; + case TYICILIST: strcpy (buff, "icilist"); break; + case TYOLIST: strcpy (buff, "olist"); break; + case TYCLLIST: strcpy (buff, "cllist"); break; + case TYALIST: strcpy (buff, "alist"); break; + case TYINLIST: strcpy (buff, "inlist"); break; + case TYFTNLEN: strcpy (buff, "ftnlen"); break; + default: sprintf (buff, "BAD DECL '%d'", type); + break; + } /* switch */ + + return buff; +} /* c_type_decl */ + + +char *new_func_length() +{ return "ret_val_len"; } + +char *new_arg_length(arg) + Namep arg; +{ + static char buf[64]; + sprintf (buf, "%s_len", arg->fvarname); + + return buf; +} /* new_arg_length */ + + +/* declare_new_addr -- Add a new local variable to the function, given a + pointer to an Addrblock structure (which must have the uname_tag set) + This list of idents will be printed in reverse (i.e., chronological) + order */ + + void +declare_new_addr (addrp) +struct Addrblock *addrp; +{ + extern chainp new_vars; + + new_vars = mkchain((char *)cpexpr((expptr)addrp), new_vars); +} /* declare_new_addr */ + + +wr_nv_ident_help (outfile, addrp) +FILE *outfile; +struct Addrblock *addrp; +{ + int eltcount = 0; + + if (addrp == (struct Addrblock *) NULL) + return; + + if (addrp -> isarray) { + frexpr (addrp -> memoffset); + addrp -> memoffset = ICON(0); + eltcount = addrp -> ntempelt; + addrp -> ntempelt = 0; + addrp -> isarray = 0; + } /* if */ + out_addr (outfile, addrp); + if (eltcount) + nice_printf (outfile, "[%d]", eltcount); +} /* wr_nv_ident_help */ + +int nv_type_help (addrp) +struct Addrblock *addrp; +{ + if (addrp == (struct Addrblock *) NULL) + return -1; + + return addrp -> vtype; +} /* nv_type_help */ + + +/* lit_name -- returns a unique identifier for the given literal. Make + the label useful, when possible. For example: + + 1 -> c_1 (constant 1) + 2 -> c_2 (constant 2) + 1000 -> c_1000 (constant 1000) + 1000000 -> c_b<memno> (big constant number) + 1.2 -> c_1_2 (constant 1.2) + 1.234345 -> c_b<memno> (big constant number) + -1 -> c_n1 (constant -1) + -1.0 -> c_n1_0 (constant -1.0) + .true. -> c_true (constant true) + .false. -> c_false (constant false) + default -> c_b<memno> (default label) +*/ + +char *lit_name (litp) +struct Literal *litp; +{ + static char buf[CONST_IDENT_MAX]; + ftnint val; + + if (litp == (struct Literal *) NULL) + return NULL; + + switch (litp -> littype) { + case TYINT1: + val = litp -> litval.litival; + if (val >= 256 || val < -255) + sprintf (buf, "c_b%d", litp -> litnum); + else if (val < 0) + sprintf (buf, "ci1_n%ld", -val); + else + sprintf(buf, "ci1__%ld", val); + case TYSHORT: + val = litp -> litval.litival; + if (val >= 32768 || val <= -32769) + sprintf (buf, "c_b%d", litp -> litnum); + else if (val < 0) + sprintf (buf, "cs_n%ld", -val); + else + sprintf (buf, "cs__%ld", val); + break; + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + val = litp -> litval.litival; + if (val >= 100000 || val <= -10000) + sprintf (buf, "c_b%d", litp -> litnum); + else if (val < 0) + sprintf (buf, "c_n%ld", -val); + else + sprintf (buf, "c__%ld", val); + break; + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + sprintf (buf, "c_%s", (litp -> litval.litival + ? "true" : "false")); + break; + case TYREAL: + case TYDREAL: + /* Given a limit of 6 or 8 character on external names, */ + /* few f.p. values can be meaningfully encoded in the */ + /* constant name. Just going with the default cb_# */ + /* seems to be the best course for floating-point */ + /* constants. */ + case TYCHAR: + /* Shouldn't be any of these */ + case TYADDR: + case TYCOMPLEX: + case TYDCOMPLEX: + case TYSUBR: + default: + sprintf (buf, "c_b%d", litp -> litnum); + } /* switch */ + return buf; +} /* lit_name */ + + + + char * +comm_union_name(count) + int count; +{ + static char buf[12]; + + sprintf(buf, "%d", count); + return buf; + } + + + + +/* wr_globals -- after every function has been translated, we need to + output the global declarations, such as the static table of constant + values */ + +wr_globals (outfile) +FILE *outfile; +{ + struct Literal *litp, *lastlit; + extern int hsize; + extern char *lit_name(); + char *litname; + int did_one, t; + struct Constblock cb; + ftnint x, y; + + if (nliterals == 0) + return; + + lastlit = litpool + nliterals; + did_one = 0; + for (litp = litpool; litp < lastlit; litp++) { + if (!litp->lituse) + continue; + litname = lit_name(litp); + if (!did_one) { + margin_printf(outfile, "/* Table of constant values */\n\n"); + did_one = 1; + } + cb.vtype = litp->littype; + if (litp->littype == TYCHAR) { + x = litp->litval.litival2[0] + litp->litval.litival2[1]; + if (y = x % hsize) + x += y = hsize - y; + nice_printf(outfile, + "static struct { %s fill; char val[%ld+1];", halign, x); + nice_printf(outfile, " char fill2[%ld];", hsize - 1); + nice_printf(outfile, " } %s_st = { 0,", litname); + cb.vleng = ICON(litp->litval.litival2[0]); + cb.Const.ccp = litp->cds[0]; + cb.Const.ccp1.blanks = litp->litval.litival2[1] + y; + cb.vtype = TYCHAR; + out_const(outfile, &cb); + frexpr(cb.vleng); + nice_printf(outfile, " };\n"); + nice_printf(outfile, "#define %s %s_st.val\n", litname, litname); + continue; + } + nice_printf(outfile, "static %s %s = ", + c_type_decl(litp->littype,0), litname); + + t = litp->littype; + if (ONEOF(t, MSKREAL|MSKCOMPLEX)) { + cb.vstg = 1; + cb.Const.cds[0] = litp->cds[0]; + cb.Const.cds[1] = litp->cds[1]; + } + else { + memcpy((char *)&cb.Const, (char *)&litp->litval, + sizeof(cb.Const)); + cb.vstg = 0; + } + out_const(outfile, &cb); + + nice_printf (outfile, ";\n"); + } /* for */ + if (did_one) + nice_printf (outfile, "\n"); +} /* wr_globals */ + + ftnint +commlen(vl) + register chainp vl; +{ + ftnint size; + int type; + struct Dimblock *t; + Namep v; + + while(vl->nextp) + vl = vl->nextp; + v = (Namep)vl->datap; + type = v->vtype; + if (type == TYCHAR) + size = v->vleng->constblock.Const.ci; + else + size = typesize[type]; + if ((t = v->vdim) && ISCONST(t->nelt)) + size *= t->nelt->constblock.Const.ci; + return size + v->voffset; + } + + static void /* Pad common block if an EQUIVALENCE extended it. */ +pad_common(c) + Extsym *c; +{ + register chainp cvl; + register Namep v; + long L = c->maxleng; + int type; + struct Dimblock *t; + int szshort = typesize[TYSHORT]; + + for(cvl = c->allextp; cvl; cvl = cvl->nextp) + if (commlen((chainp)cvl->datap) >= L) + return; + v = ALLOC(Nameblock); + v->vtype = type = L % szshort ? TYCHAR + : type_choice[L/szshort % 4]; + v->vstg = STGCOMMON; + v->vclass = CLVAR; + v->tag = TNAME; + v->vdim = t = ALLOC(Dimblock); + t->ndim = 1; + t->dims[0].dimsize = ICON(L / typesize[type]); + v->fvarname = v->cvarname = "eqv_pad"; + if (type == TYCHAR) + v->vleng = ICON(1); + c->allextp = mkchain((char *)mkchain((char *)v, CHNULL), c->allextp); + } + + +/* wr_common_decls -- outputs the common declarations in one of three + formats. If all references to a common block look the same (field + names and types agree), only one actual declaration will appear. + Otherwise, the same block will require many structs. If there is no + block data, these structs will be union'ed together (so the linker + knows the size of the largest one). If there IS a block data, only + that version will be associated with the variable, others will only be + defined as types, so the pointer can be cast to it. e.g. + + FORTRAN C +---------------------------------------------------------------------- + common /com1/ a, b, c struct { real a, b, c; } com1_; + + common /com1/ a, b, c union { + common /com1/ i, j, k struct { real a, b, c; } _1; + struct { integer i, j, k; } _2; + } com1_; + + common /com1/ a, b, c struct com1_1_ { real a, b, c; }; + block data struct { integer i, j, k; } com1_ = + common /com1/ i, j, k { 1, 2, 3 }; + data i/1/, j/2/, k/3/ + + + All of these versions will be followed by #defines, since the code in + the function bodies can't know ahead of time which of these options + will be taken */ + +/* Macros for deciding the output type */ + +#define ONE_STRUCT 1 +#define UNION_STRUCT 2 +#define INIT_STRUCT 3 + +wr_common_decls(outfile) + FILE *outfile; +{ + Extsym *ext; + extern int extcomm; + static char *Extern[4] = {"", "Extern ", "extern "}; + char *E, *E0 = Extern[extcomm]; + int did_one = 0; + + for (ext = extsymtab; ext < nextext; ext++) { + if (ext -> extstg == STGCOMMON && ext->allextp) { + chainp comm; + int count = 1; + int which; /* which display to use; + ONE_STRUCT, UNION or INIT */ + + if (!did_one) + nice_printf (outfile, "/* Common Block Declarations */\n\n"); + + pad_common(ext); + +/* Construct the proper, condensed list of structs; eliminate duplicates + from the initial list ext -> allextp */ + + comm = ext->allextp = revchain(ext->allextp); + + if (ext -> extinit) + which = INIT_STRUCT; + else if (comm->nextp) { + which = UNION_STRUCT; + nice_printf (outfile, "%sunion {\n", E0); + next_tab (outfile); + E = ""; + } + else { + which = ONE_STRUCT; + E = E0; + } + + for (; comm; comm = comm -> nextp, count++) { + + if (which == INIT_STRUCT) + nice_printf (outfile, "struct %s%d_ {\n", + ext->cextname, count); + else + nice_printf (outfile, "%sstruct {\n", E); + + next_tab (c_file); + + wr_struct (outfile, (chainp) comm -> datap); + + prev_tab (c_file); + if (which == UNION_STRUCT) + nice_printf (outfile, "} _%d;\n", count); + else if (which == ONE_STRUCT) + nice_printf (outfile, "} %s;\n", ext->cextname); + else + nice_printf (outfile, "};\n"); + } /* for */ + + if (which == UNION_STRUCT) { + prev_tab (c_file); + nice_printf (outfile, "} %s;\n", ext->cextname); + } /* if */ + did_one = 1; + nice_printf (outfile, "\n"); + + for (count = 1, comm = ext -> allextp; comm; + comm = comm -> nextp, count++) { + def_start(outfile, ext->cextname, + comm_union_name(count), ""); + switch (which) { + case ONE_STRUCT: + extern_out (outfile, ext); + break; + case UNION_STRUCT: + nice_printf (outfile, "("); + extern_out (outfile, ext); + nice_printf(outfile, "._%d)", count); + break; + case INIT_STRUCT: + nice_printf (outfile, "(*(struct "); + extern_out (outfile, ext); + nice_printf (outfile, "%d_ *) &", count); + extern_out (outfile, ext); + nice_printf (outfile, ")"); + break; + } /* switch */ + nice_printf (outfile, "\n"); + } /* for count = 1, comm = ext -> allextp */ + nice_printf (outfile, "\n"); + } /* if ext -> extstg == STGCOMMON */ + } /* for ext = extsymtab */ +} /* wr_common_decls */ + + +wr_struct (outfile, var_list) +FILE *outfile; +chainp var_list; +{ + int last_type = -1; + int did_one = 0; + chainp this_var; + + for (this_var = var_list; this_var; this_var = this_var -> nextp) { + Namep var = (Namep) this_var -> datap; + int type; + char *comment = NULL, *wr_ardecls (); + + if (var == (Namep) NULL) + err ("wr_struct: null variable"); + else if (var -> tag != TNAME) + erri ("wr_struct: bad tag on variable '%d'", + var -> tag); + + type = var -> vtype; + + if (last_type == type && did_one) + nice_printf (outfile, ", "); + else { + if (did_one) + nice_printf (outfile, ";\n"); + nice_printf (outfile, "%s ", + c_type_decl (type, var -> vclass == CLPROC)); + } /* else */ + +/* Character type is really a string type. Put out a '*' for parameters + with unknown length and functions returning character */ + + if (var -> vtype == TYCHAR && (!ISICON ((var -> vleng)) + || var -> vclass == CLPROC)) + nice_printf (outfile, "*"); + + var -> vstg = STGAUTO; + out_name (outfile, var); + if (var -> vclass == CLPROC) + nice_printf (outfile, "()"); + else if (var -> vdim) + comment = wr_ardecls(outfile, var->vdim, + var->vtype == TYCHAR && ISICON(var->vleng) + ? var->vleng->constblock.Const.ci : 1L); + else if (var -> vtype == TYCHAR && var -> vclass != CLPROC && + ISICON ((var -> vleng))) + nice_printf (outfile, "[%ld]", + var -> vleng -> constblock.Const.ci); + + if (comment) + nice_printf (outfile, "%s", comment); + did_one = 1; + last_type = type; + } /* for this_var */ + + if (did_one) + nice_printf (outfile, ";\n"); +} /* wr_struct */ + + +char *user_label(stateno) +ftnint stateno; +{ + static char buf[USER_LABEL_MAX + 1]; + static char *Lfmt[2] = { "L_%ld", "L%ld" }; + + if (stateno >= 0) + sprintf(buf, Lfmt[shiftcase], stateno); + else + sprintf(buf, "L_%s", extsymtab[-1-stateno].fextname); + return buf; +} /* user_label */ + + +char *temp_name (starter, num, storage) +char *starter; +int num; +char *storage; +{ + static char buf[IDENT_LEN]; + char *pointer = buf; + char *prefix = "t"; + + if (storage) + pointer = storage; + + if (starter && *starter) + prefix = starter; + + sprintf (pointer, "%s__%d", prefix, num); + return pointer; +} /* temp_name */ + + +char *equiv_name (memno, store) +int memno; +char *store; +{ + static char buf[IDENT_LEN]; + char *pointer = buf; + + if (store) + pointer = store; + + sprintf (pointer, "%s_%d", EQUIV_INIT_NAME, memno); + return pointer; +} /* equiv_name */ + + void +def_commons(of) + FILE *of; +{ + Extsym *ext; + int c, onefile, Union; + char buf[64]; + chainp comm; + extern int ext1comm; + FILE *c_filesave = c_file; + + if (ext1comm == 1) { + onefile = 1; + c_file = of; + fprintf(of, "/*>>>'/dev/null'<<<*/\n\ +#ifdef Define_COMMONs\n\ +/*<<</dev/null>>>*/\n"); + } + else + onefile = 0; + for(ext = extsymtab; ext < nextext; ext++) + if (ext->extstg == STGCOMMON + && !ext->extinit && (comm = ext->allextp)) { + sprintf(buf, "%scom.c", ext->cextname); + if (onefile) + fprintf(of, "/*>>>'%s'<<<*/\n", + buf); + else { + c_file = of = fopen(buf,textwrite); + if (!of) + fatalstr("can't open %s", buf); + } + fprintf(of, "#include \"f2c.h\"\n"); + if (comm->nextp) { + Union = 1; + nice_printf(of, "union {\n"); + next_tab(of); + } + else + Union = 0; + for(c = 1; comm; comm = comm->nextp) { + nice_printf(of, "struct {\n"); + next_tab(of); + wr_struct(of, (chainp)comm->datap); + prev_tab(of); + if (Union) + nice_printf(of, "} _%d;\n", c++); + } + if (Union) + prev_tab(of); + nice_printf(of, "} %s;\n", ext->cextname); + if (onefile) + fprintf(of, "/*<<<%s>>>*/\n", buf); + else + fclose(of); + } + if (onefile) + fprintf(of, "/*>>>'/dev/null'<<<*/\n#endif\n\ +/*<<</dev/null>>>*/\n"); + c_file = c_filesave; + } + +/* C Language keywords. Needed to filter unwanted fortran identifiers like + * "int", etc. Source: Kernighan & Ritchie, eds. 1 and 2; Stroustrup. + * Also includes C++ keywords and types used for I/O in f2c.h . + * These keywords must be in alphabetical order (as defined by strcmp()). + */ + +char *c_keywords[] = { + "Long", "Multitype", "Namelist", "Vardesc", + "abs", "acos", "address", "alist", "asin", "asm", + "atan", "atan2", "auto", "break", + "case", "catch", "char", "cilist", "class", "cllist", + "complex", "const", "continue", "cos", "cosh", + "dabs", "default", "defined", "delete", + "dmax", "dmin", "do", "double", "doublecomplex", "doublereal", + "else", "entry", "enum", "exp", "extern", + "flag", "float", "for", "friend", "ftnint", "ftnlen", "goto", + "icilist", "if", "include", "inline", "inlist", "int", "integer", + "integer1", "log", "logical", "logical1", "long", "longint", + "max", "min", "new", + "olist", "operator", "overload", "private", "protected", "public", + "real", "register", "return", + "short", "shortint", "shortlogical", "signed", "sin", "sinh", + "sizeof", "sqrt", "static", "struct", "switch", + "tan", "tanh", "template", "this", "try", "typedef", + "union", "unsigned", "virtual", "void", "volatile", "while" +}; /* c_keywords */ + +int n_keywords = sizeof(c_keywords)/sizeof(char *); + +char *st_fields[] = { + "addr", "aerr", "aunit", "c", "cerr", "ciend", "cierr", + "cifmt", "cirec", "ciunit", "csta", "cunit", "d", "dims", + "h", "i", "iciend", "icierr", "icifmt", "icirlen", + "icirnum", "iciunit", "inacc", "inacclen", "inblank", + "inblanklen", "indir", "indirlen", "inerr", "inex", + "infile", "infilen", "infmt", "infmtlen", "inform", + "informlen", "inname", "innamed", "innamlen", "innrec", + "innum", "inopen", "inrecl", "inseq", "inseqlen", "inunf", + "inunflen", "inunit", "name", "nvars", "oacc", "oblnk", + "oerr", "ofm", "ofnm", "ofnmlen", "orl", "osta", "ounit", + "r", "type", "vars", "z" + }; +int n_st_fields = sizeof(st_fields)/sizeof(char *); diff --git a/usr.bin/f2c/names.h b/usr.bin/f2c/names.h new file mode 100644 index 000000000000..1ca17d0a0093 --- /dev/null +++ b/usr.bin/f2c/names.h @@ -0,0 +1,22 @@ +#define CONST_IDENT_MAX 30 +#define IO_IDENT_MAX 30 +#define ARGUMENT_MAX 30 +#define USER_LABEL_MAX 30 + +#define EQUIV_INIT_NAME "equiv" + +#define write_nv_ident(fp,a) wr_nv_ident_help ((fp), (struct Addrblock *) (a)) +#define nv_type(x) nv_type_help ((struct Addrblock *) x) + +extern char *c_keywords[]; + +char *new_io_ident (/* char * */); +char *new_func_length (/* char * */); +char *new_arg_length (/* Namep */); +void declare_new_addr (/* struct Addrblock * */); +char *nv_ident_help (/* struct Addrblock * */); +int nv_type_help (/* struct Addrblock */); +char *user_label (/* int */); +char *temp_name (/* int, char */); +char *c_type_decl (/* int, int */); +char *equiv_name (/* int, char * */); diff --git a/usr.bin/f2c/niceprintf.c b/usr.bin/f2c/niceprintf.c new file mode 100644 index 000000000000..3c6cb3a98135 --- /dev/null +++ b/usr.bin/f2c/niceprintf.c @@ -0,0 +1,388 @@ +/**************************************************************** +Copyright 1990, 1991, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "names.h" +#include "output.h" + +#define TOO_LONG_INDENT (2 * tab_size) +#define MAX_INDENT 44 +#define MIN_INDENT 22 +static int last_was_newline = 0; +int indent = 0; +int in_comment = 0; +int in_define = 0; + extern int gflag1; + extern char *file_name; + + static int +write_indent(fp, use_indent, extra_indent, start, end) + FILE *fp; + int use_indent, extra_indent; + char *start, *end; +{ + int ind, tab; + + if (gflag1 && last_was_newline) + fprintf(fp, "#line %ld \"%s\"\n", lineno, infname ? infname : file_name); + if (in_define == 1) { + in_define = 2; + use_indent = 0; + } + if (last_was_newline && use_indent) { + if (*start == '\n') do { + putc('\n', fp); + if (++start > end) + return; + } + while(*start == '\n'); + + ind = indent <= MAX_INDENT + ? indent + : MIN_INDENT + indent % (MAX_INDENT - MIN_INDENT); + + tab = ind + extra_indent; + + while (tab > 7) { + putc ('\t', fp); + tab -= 8; + } /* while */ + + while (tab-- > 0) + putc (' ', fp); + } /* if last_was_newline */ + + while (start <= end) + putc (*start++, fp); +} /* write_indent */ + + +/*VARARGS2*/ +int margin_printf (fp, a, b, c, d, e, f, g) +FILE *fp; +char *a; +long b, c, d, e, f, g; +{ + ind_printf (0, fp, a, b, c, d, e, f, g); +} /* margin_printf */ + +/*VARARGS2*/ +int nice_printf (fp, a, b, c, d, e, f, g) +FILE *fp; +char *a; +long b, c, d, e, f, g; +{ + ind_printf (1, fp, a, b, c, d, e, f, g); +} /* nice_printf */ + + +#define max_line_len c_output_line_length + /* 74Number of characters allowed on an output + line. This assumes newlines are handled + nicely, i.e. a newline after a full text + line on a terminal is ignored */ + +/* output_buf holds the text of the next line to be printed. It gets + flushed when a newline is printed. next_slot points to the next + available location in the output buffer, i.e. where the next call to + nice_printf will have its output stored */ + +static char *output_buf; +static char *next_slot; +static char *string_start; + +static char *word_start = NULL; +static int cursor_pos = 0; +static int In_string = 0; + + void +np_init() +{ + next_slot = output_buf = Alloc(MAX_OUTPUT_SIZE); + memset(output_buf, 0, MAX_OUTPUT_SIZE); + } + + static char * +adjust_pointer_in_string(pointer) + register char *pointer; +{ + register char *s, *s1, *se, *s0; + + /* arrange not to break \002 */ + s1 = string_start ? string_start : output_buf; + for(s = s1; s < pointer; s++) { + s0 = s1; + s1 = s; + if (*s == '\\') { + se = s++ + 4; + if (se > pointer) + break; + if (*s < '0' || *s > '7') + continue; + while(++s < se) + if (*s < '0' || *s > '7') + break; + --s; + } + } + return s0 - 1; + } + +/* ANSI says strcpy's behavior is undefined for overlapping args, + * so we roll our own fwd_strcpy: */ + + static void +fwd_strcpy(t, s) + register char *t, *s; +{ while(*t++ = *s++); } + +/* isident -- true iff character could belong to a unit. C allows + letters, numbers and underscores in identifiers. This also doubles as + a check for numeric constants, since we include the decimal point and + minus sign. The minus has to be here, since the constant "10e-2" + cannot be broken up. The '.' also prevents structure references from + being broken, which is a quite acceptable side effect */ + +#define isident(x) (Tr[x] & 1) +#define isntident(x) (!Tr[x]) + +int ind_printf (use_indent, fp, a, b, c, d, e, f, g) +int use_indent; +FILE *fp; +char *a; +long b, c, d, e, f, g; +{ + extern int max_line_len; + extern FILEP c_file; + extern char tr_tab[]; /* in output.c */ + register char *Tr = tr_tab; + int ch, inc, ind; + static int extra_indent, last_indent, set_cursor = 1; + + cursor_pos += indent - last_indent; + last_indent = indent; + sprintf (next_slot, a, b, c, d, e, f, g); + + if (fp != c_file) { + fprintf (fp,"%s", next_slot); + return 1; + } /* if fp != c_file */ + + do { + char *pointer; + +/* The for loop will parse one output line */ + + if (set_cursor) { + ind = indent <= MAX_INDENT + ? indent + : MIN_INDENT + indent % (MAX_INDENT - MIN_INDENT); + cursor_pos = ind + extra_indent; + set_cursor = 0; + } + if (in_comment) + for (pointer = next_slot; *pointer && *pointer != '\n' && + cursor_pos <= max_line_len; pointer++) + cursor_pos++; + else + for (pointer = next_slot; *pointer && *pointer != '\n' && + cursor_pos <= max_line_len; pointer++) { + + /* Update state variables here */ + + if (In_string) { + switch(*pointer) { + case '\\': + if (++cursor_pos > max_line_len) { + cursor_pos -= 2; + --pointer; + goto overflow; + } + ++pointer; + break; + case '"': + In_string = 0; + word_start = 0; + } + } + else switch (*pointer) { + case '"': + if (cursor_pos + 5 > max_line_len) { + word_start = 0; + --pointer; + goto overflow; + } + In_string = 1; + string_start = word_start = pointer; + break; + case '\'': + if (pointer[1] == '\\') + if ((ch = pointer[2]) >= '0' && ch <= '7') + for(inc = 3; pointer[inc] != '\'' + && ++inc < 5;); + else + inc = 3; + else + inc = 2; + /*debug*/ if (pointer[inc] != '\'') + /*debug*/ fatalstr("Bad character constant %.10s", + pointer); + if ((cursor_pos += inc) > max_line_len) { + cursor_pos -= inc; + word_start = 0; + --pointer; + goto overflow; + } + word_start = pointer; + pointer += inc; + break; + case '\t': + cursor_pos = 8 * ((cursor_pos + 8) / 8) - 1; + break; + default: { + +/* HACK Assumes that all characters in an atomic C token will be written + at the same time. Must check for tokens first, since '-' is considered + part of an identifier; checking isident first would mean breaking up "->" */ + + if (word_start) { + if (isntident(*(unsigned char *)pointer)) + word_start = NULL; + } + else if (isident(*(unsigned char *)pointer)) + word_start = pointer; + break; + } /* default */ + } /* switch */ + cursor_pos++; + } /* for pointer = next_slot */ + overflow: + if (*pointer == '\0') { + +/* The output line is not complete, so break out and don't output + anything. The current line fragment will be stored in the buffer */ + + next_slot = pointer; + break; + } else { + char last_char; + int in_string0 = In_string; + +/* If the line was too long, move pointer back to the character before + the current word. This allows line breaking on word boundaries. Make + sure that 80 character comment lines get broken up somehow. We assume + that any non-string 80 character identifier must be in a comment. +*/ + + if (*pointer == '\n') + in_define = 0; + else if (word_start && word_start > output_buf) + if (In_string) + if (string_start && pointer - string_start < 5) + pointer = string_start - 1; + else { + pointer = adjust_pointer_in_string(pointer); + string_start = 0; + } + else if (word_start == string_start + && pointer - string_start >= 5) { + pointer = adjust_pointer_in_string(next_slot); + In_string = 1; + string_start = 0; + } + else + pointer = word_start - 1; + else if (cursor_pos > max_line_len) { +#ifndef ANSI_Libraries + extern char *strchr(); +#endif + if (In_string) { + pointer = adjust_pointer_in_string(pointer); + if (string_start && pointer > string_start) + string_start = 0; + } + else if (strchr("&*+-/<=>|", *pointer) + && strchr("!%&*+-/<=>^|", pointer[-1])) { + pointer -= 2; + if (strchr("<>", *pointer)) /* <<=, >>= */ + pointer--; + } + else { + if (word_start) + while(isident(*(unsigned char *)pointer)) + pointer++; + pointer--; + } + } + last_char = *pointer; + write_indent(fp, use_indent, extra_indent, output_buf, pointer); + next_slot = output_buf; + if (In_string && !string_start && Ansi == 1 && last_char != '\n') + *next_slot++ = '"'; + fwd_strcpy(next_slot, pointer + 1); + +/* insert a line break */ + + if (last_char == '\n') { + if (In_string) + last_was_newline = 0; + else { + last_was_newline = 1; + extra_indent = 0; + } + } + else { + extra_indent = TOO_LONG_INDENT; + if (In_string && !string_start) { + if (Ansi == 1) { + fprintf(fp, "\"\n"); + use_indent = 1; + last_was_newline = 1; + } + else { + fprintf(fp, "\\\n"); + last_was_newline = 0; + } + In_string = in_string0; + } + else { + if (in_define) + putc('\\', fp); + putc ('\n', fp); + last_was_newline = 1; + } + } /* if *pointer != '\n' */ + + if (In_string && Ansi != 1 && !string_start) + cursor_pos = 0; + else + set_cursor = 1; + + string_start = word_start = NULL; + + } /* else */ + + } while (*next_slot); + + return 0; +} /* ind_printf */ diff --git a/usr.bin/f2c/niceprintf.h b/usr.bin/f2c/niceprintf.h new file mode 100644 index 000000000000..24c65d4db0c4 --- /dev/null +++ b/usr.bin/f2c/niceprintf.h @@ -0,0 +1,16 @@ +/* niceprintf.h -- contains constants and macros from the output filter + for the generated C code. We use macros for increased speed, less + function overhead. */ + +#define MAX_OUTPUT_SIZE 6000 /* Number of chars on one output line PLUS + the length of the longest string + printed using nice_printf */ + + + +#define next_tab(fp) (indent += tab_size) + +#define prev_tab(fp) (indent -= tab_size) + + + diff --git a/usr.bin/f2c/notice b/usr.bin/f2c/notice new file mode 100644 index 000000000000..64af9f12dc4e --- /dev/null +++ b/usr.bin/f2c/notice @@ -0,0 +1,23 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + diff --git a/usr.bin/f2c/output.c b/usr.bin/f2c/output.c new file mode 100644 index 000000000000..6d5bdd4133d4 --- /dev/null +++ b/usr.bin/f2c/output.c @@ -0,0 +1,1495 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "names.h" +#include "output.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +char _assoc_table[] = { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }; + +/* Opcode table -- This array is indexed by the OP_____ macros defined in + defines.h; these macros are expected to be adjacent integers, so that + this table is as small as possible. */ + +table_entry opcode_table[] = { + { 0, 0, NULL }, + /* OPPLUS 1 */ { BINARY_OP, 12, "%l + %r" }, + /* OPMINUS 2 */ { BINARY_OP, 12, "%l - %r" }, + /* OPSTAR 3 */ { BINARY_OP, 13, "%l * %r" }, + /* OPSLASH 4 */ { BINARY_OP, 13, "%l / %r" }, + /* OPPOWER 5 */ { BINARY_OP, 0, "power (%l, %r)" }, + /* OPNEG 6 */ { UNARY_OP, 14, "-%l" }, + /* OPOR 7 */ { BINARY_OP, 4, "%l || %r" }, + /* OPAND 8 */ { BINARY_OP, 5, "%l && %r" }, + /* OPEQV 9 */ { BINARY_OP, 9, "%l == %r" }, + /* OPNEQV 10 */ { BINARY_OP, 9, "%l != %r" }, + /* OPNOT 11 */ { UNARY_OP, 14, "! %l" }, + /* OPCONCAT 12 */ { BINARY_OP, 0, "concat (%l, %r)" }, + /* OPLT 13 */ { BINARY_OP, 10, "%l < %r" }, + /* OPEQ 14 */ { BINARY_OP, 9, "%l == %r" }, + /* OPGT 15 */ { BINARY_OP, 10, "%l > %r" }, + /* OPLE 16 */ { BINARY_OP, 10, "%l <= %r" }, + /* OPNE 17 */ { BINARY_OP, 9, "%l != %r" }, + /* OPGE 18 */ { BINARY_OP, 10, "%l >= %r" }, + /* OPCALL 19 */ { BINARY_OP, 15, SPECIAL_FMT }, + /* OPCCALL 20 */ { BINARY_OP, 15, SPECIAL_FMT }, + +/* Left hand side of an assignment cannot have outermost parens */ + + /* OPASSIGN 21 */ { BINARY_OP, 2, "%l = %r" }, + /* OPPLUSEQ 22 */ { BINARY_OP, 2, "%l += %r" }, + /* OPSTAREQ 23 */ { BINARY_OP, 2, "%l *= %r" }, + /* OPCONV 24 */ { BINARY_OP, 14, "%l" }, + /* OPLSHIFT 25 */ { BINARY_OP, 11, "%l << %r" }, + /* OPMOD 26 */ { BINARY_OP, 13, "%l %% %r" }, + /* OPCOMMA 27 */ { BINARY_OP, 1, "%l, %r" }, + +/* Don't want to nest the colon operator in parens */ + + /* OPQUEST 28 */ { BINARY_OP, 3, "%l ? %r" }, + /* OPCOLON 29 */ { BINARY_OP, 3, "%l : %r" }, + /* OPABS 30 */ { UNARY_OP, 0, "abs(%l)" }, + /* OPMIN 31 */ { BINARY_OP, 0, SPECIAL_FMT }, + /* OPMAX 32 */ { BINARY_OP, 0, SPECIAL_FMT }, + /* OPADDR 33 */ { UNARY_OP, 14, "&%l" }, + + /* OPCOMMA_ARG 34 */ { BINARY_OP, 15, SPECIAL_FMT }, + /* OPBITOR 35 */ { BINARY_OP, 6, "%l | %r" }, + /* OPBITAND 36 */ { BINARY_OP, 8, "%l & %r" }, + /* OPBITXOR 37 */ { BINARY_OP, 7, "%l ^ %r" }, + /* OPBITNOT 38 */ { UNARY_OP, 14, "~ %l" }, + /* OPRSHIFT 39 */ { BINARY_OP, 11, "%l >> %r" }, + +/* This isn't quite right -- it doesn't handle arrays, for instance */ + + /* OPWHATSIN 40 */ { UNARY_OP, 14, "*%l" }, + /* OPMINUSEQ 41 */ { BINARY_OP, 2, "%l -= %r" }, + /* OPSLASHEQ 42 */ { BINARY_OP, 2, "%l /= %r" }, + /* OPMODEQ 43 */ { BINARY_OP, 2, "%l %%= %r" }, + /* OPLSHIFTEQ 44 */ { BINARY_OP, 2, "%l <<= %r" }, + /* OPRSHIFTEQ 45 */ { BINARY_OP, 2, "%l >>= %r" }, + /* OPBITANDEQ 46 */ { BINARY_OP, 2, "%l &= %r" }, + /* OPBITXOREQ 47 */ { BINARY_OP, 2, "%l ^= %r" }, + /* OPBITOREQ 48 */ { BINARY_OP, 2, "%l |= %r" }, + /* OPPREINC 49 */ { UNARY_OP, 14, "++%l" }, + /* OPPREDEC 50 */ { UNARY_OP, 14, "--%l" }, + /* OPDOT 51 */ { BINARY_OP, 15, "%l.%r" }, + /* OPARROW 52 */ { BINARY_OP, 15, "%l -> %r"}, + /* OPNEG1 53 */ { UNARY_OP, 14, "-%l" }, + /* OPDMIN 54 */ { BINARY_OP, 0, "dmin(%l,%r)" }, + /* OPDMAX 55 */ { BINARY_OP, 0, "dmax(%l,%r)" }, + /* OPASSIGNI 56 */ { BINARY_OP, 2, "%l = &%r" }, + /* OPIDENTITY 57 */ { UNARY_OP, 15, "%l" }, + /* OPCHARCAST 58 */ { UNARY_OP, 14, "(char *)&%l" }, + /* OPDABS 59 */ { UNARY_OP, 0, "dabs(%l)" }, + /* OPMIN2 60 */ { BINARY_OP, 0, "min(%l,%r)" }, + /* OPMAX2 61 */ { BINARY_OP, 0, "max(%l,%r)" }, + +/* kludge to imitate (under forcedouble) f77's bizarre treatement of OPNEG... */ + + /* OPNEG KLUDGE */ { UNARY_OP, 14, "-(doublereal)%l" } +}; /* opcode_table */ + +#define OPNEG_KLUDGE (sizeof(opcode_table)/sizeof(table_entry) - 1) + +static char opeqable[sizeof(opcode_table)/sizeof(table_entry)]; + + +static void output_prim (); +static void output_unary (), output_binary (), output_arg_list (); +static void output_list (), output_literal (); + + +void expr_out (fp, e) +FILE *fp; +expptr e; +{ + if (e == (expptr) NULL) + return; + + switch (e -> tag) { + case TNAME: out_name (fp, (struct Nameblock *) e); + return; + + case TCONST: out_const(fp, &e->constblock); + goto end_out; + case TEXPR: + break; + + case TADDR: out_addr (fp, &(e -> addrblock)); + goto end_out; + + case TPRIM: warn ("expr_out: got TPRIM"); + output_prim (fp, &(e -> primblock)); + return; + + case TLIST: output_list (fp, &(e -> listblock)); + end_out: frexpr(e); + return; + + case TIMPLDO: err ("expr_out: got TIMPLDO"); + return; + + case TERROR: + default: + erri ("expr_out: bad tag '%d'", e -> tag); + } /* switch */ + +/* Now we know that the tag is TEXPR */ + +/* Optimize on simple expressions, such as "a = a + b" ==> "a += b" */ + + if (e -> exprblock.opcode == OPASSIGN && e -> exprblock.rightp && + e -> exprblock.rightp -> tag == TEXPR) { + int opcode; + + opcode = e -> exprblock.rightp -> exprblock.opcode; + + if (opeqable[opcode]) { + expptr leftp, rightp; + + if ((leftp = e -> exprblock.leftp) && + (rightp = e -> exprblock.rightp -> exprblock.leftp)) { + + if (same_ident (leftp, rightp)) { + expptr temp = e -> exprblock.rightp; + + e -> exprblock.opcode = op_assign(opcode); + + e -> exprblock.rightp = temp -> exprblock.rightp; + temp->exprblock.rightp = 0; + frexpr(temp); + } /* if same_ident (leftp, rightp) */ + } /* if leftp && rightp */ + } /* if opcode == OPPLUS || */ + } /* if e -> exprblock.opcode == OPASSIGN */ + + +/* Optimize on increment or decrement by 1 */ + + { + int opcode = e -> exprblock.opcode; + expptr leftp = e -> exprblock.leftp; + expptr rightp = e -> exprblock.rightp; + + if (leftp && rightp && (leftp -> headblock.vstg == STGARG || + ISINT (leftp -> headblock.vtype)) && + (opcode == OPPLUSEQ || opcode == OPMINUSEQ) && + ISINT (rightp -> headblock.vtype) && + ISICON (e -> exprblock.rightp) && + (ISONE (e -> exprblock.rightp) || + e -> exprblock.rightp -> constblock.Const.ci == -1)) { + +/* Allow for the '-1' constant value */ + + if (!ISONE (e -> exprblock.rightp)) + opcode = (opcode == OPPLUSEQ) ? OPMINUSEQ : OPPLUSEQ; + +/* replace the existing opcode */ + + if (opcode == OPPLUSEQ) + e -> exprblock.opcode = OPPREINC; + else + e -> exprblock.opcode = OPPREDEC; + +/* Free up storage used by the right hand side */ + + frexpr (e -> exprblock.rightp); + e->exprblock.rightp = 0; + } /* if opcode == OPPLUS */ + } /* block */ + + + if (is_unary_op (e -> exprblock.opcode)) + output_unary (fp, &(e -> exprblock)); + else if (is_binary_op (e -> exprblock.opcode)) + output_binary (fp, &(e -> exprblock)); + else + erri ("expr_out: bad opcode '%d'", (int) e -> exprblock.opcode); + + free((char *)e); + +} /* expr_out */ + + +void out_and_free_statement (outfile, expr) +FILE *outfile; +expptr expr; +{ + if (expr) + expr_out (outfile, expr); + + nice_printf (outfile, ";\n"); +} /* out_and_free_statement */ + + + +int same_ident (left, right) +expptr left, right; +{ + if (!left || !right) + return 0; + + if (left -> tag == TNAME && right -> tag == TNAME && left == right) + return 1; + + if (left -> tag == TADDR && right -> tag == TADDR && + left -> addrblock.uname_tag == right -> addrblock.uname_tag) + switch (left -> addrblock.uname_tag) { + case UNAM_REF: + case UNAM_NAME: + +/* Check for array subscripts */ + + if (left -> addrblock.user.name -> vdim || + right -> addrblock.user.name -> vdim) + if (left -> addrblock.user.name != + right -> addrblock.user.name || + !same_expr (left -> addrblock.memoffset, + right -> addrblock.memoffset)) + return 0; + + return same_ident ((expptr) (left -> addrblock.user.name), + (expptr) right -> addrblock.user.name); + case UNAM_IDENT: + return strcmp(left->addrblock.user.ident, + right->addrblock.user.ident) == 0; + case UNAM_CHARP: + return strcmp(left->addrblock.user.Charp, + right->addrblock.user.Charp) == 0; + default: + return 0; + } /* switch */ + + if (left->tag == TEXPR && left->exprblock.opcode == OPWHATSIN + && right->tag == TEXPR && right->exprblock.opcode == OPWHATSIN) + return same_ident(left->exprblock.leftp, + right->exprblock.leftp); + + return 0; +} /* same_ident */ + + static int +samefpconst(c1, c2, n) + register Constp c1, c2; + register int n; +{ + char *s1, *s2; + if (!c1->vstg && !c2->vstg) + return c1->Const.cd[n] == c2->Const.cd[n]; + s1 = c1->vstg ? c1->Const.cds[n] : dtos(c1->Const.cd[n]); + s2 = c2->vstg ? c2->Const.cds[n] : dtos(c2->Const.cd[n]); + return !strcmp(s1, s2); + } + + static int +sameconst(c1, c2) + register Constp c1, c2; +{ + switch(c1->vtype) { + case TYCOMPLEX: + case TYDCOMPLEX: + if (!samefpconst(c1,c2,1)) + return 0; + case TYREAL: + case TYDREAL: + return samefpconst(c1,c2,0); + case TYCHAR: + return c1->Const.ccp1.blanks == c2->Const.ccp1.blanks + && c1->vleng->constblock.Const.ci + == c2->vleng->constblock.Const.ci + && !memcmp(c1->Const.ccp, c2->Const.ccp, + (int)c1->vleng->constblock.Const.ci); + case TYSHORT: + case TYINT: + case TYLOGICAL: + return c1->Const.ci == c2->Const.ci; + } + err("unexpected type in sameconst"); + return 0; + } + +/* same_expr -- Returns true only if e1 and e2 match. This is + somewhat pessimistic, but can afford to be because it's just used to + optimize on the assignment operators (+=, -=, etc). */ + +int same_expr (e1, e2) +expptr e1, e2; +{ + if (!e1 || !e2) + return !e1 && !e2; + + if (e1 -> tag != e2 -> tag || e1 -> headblock.vtype != e2 -> headblock.vtype) + return 0; + + switch (e1 -> tag) { + case TEXPR: + if (e1 -> exprblock.opcode != e2 -> exprblock.opcode) + return 0; + + return same_expr (e1 -> exprblock.leftp, e2 -> exprblock.leftp) && + same_expr (e1 -> exprblock.rightp, e2 -> exprblock.rightp); + case TNAME: + case TADDR: + return same_ident (e1, e2); + case TCONST: + return sameconst(&e1->constblock, &e2->constblock); + default: + return 0; + } /* switch */ +} /* same_expr */ + + + +void out_name (fp, namep) + FILE *fp; + Namep namep; +{ + extern int usedefsforcommon; + Extsym *comm; + + if (namep == NULL) + return; + +/* DON'T want to use oneof_stg() here; need to find the right common name + */ + + if (namep->vstg == STGCOMMON && !namep->vcommequiv && !usedefsforcommon) { + comm = &extsymtab[namep->vardesc.varno]; + extern_out(fp, comm); + nice_printf(fp, "%d.", comm->curno); + } /* if namep -> vstg == STGCOMMON */ + + if (namep->vprocclass == PTHISPROC && namep->vtype != TYSUBR) + nice_printf(fp, xretslot[namep->vtype]->user.ident); + else + nice_printf (fp, "%s", namep->cvarname); +} /* out_name */ + + +static char *Longfmt = "%ld"; + +#define cpd(n) cp->vstg ? cp->Const.cds[n] : dtos(cp->Const.cd[n]) + +void out_const(fp, cp) + FILE *fp; + register Constp cp; +{ + static char real_buf[50], imag_buf[50]; + unsigned int k; + int type = cp->vtype; + + switch (type) { + case TYINT1: + case TYSHORT: + nice_printf (fp, "%ld", cp->Const.ci); /* don't cast ci! */ + break; + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + nice_printf (fp, Longfmt, cp->Const.ci); /* don't cast ci! */ + break; + case TYREAL: + nice_printf(fp, "%s", flconst(real_buf, cpd(0))); + break; + case TYDREAL: + nice_printf(fp, "%s", cpd(0)); + break; + case TYCOMPLEX: + nice_printf(fp, cm_fmt_string, flconst(real_buf, cpd(0)), + flconst(imag_buf, cpd(1))); + break; + case TYDCOMPLEX: + nice_printf(fp, dcm_fmt_string, cpd(0), cpd(1)); + break; + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + nice_printf (fp, "%s", cp->Const.ci ? "TRUE_" : "FALSE_"); + break; + case TYCHAR: { + char *c = cp->Const.ccp, *ce; + + if (c == NULL) { + nice_printf (fp, "\"\""); + break; + } /* if c == NULL */ + + nice_printf (fp, "\""); + ce = c + cp->vleng->constblock.Const.ci; + while(c < ce) { + k = *(unsigned char *)c++; + nice_printf(fp, str_fmt[k], k); + } + for(k = cp->Const.ccp1.blanks; k > 0; k--) + nice_printf(fp, " "); + nice_printf (fp, "\""); + break; + } /* case TYCHAR */ + default: + erri ("out_const: bad type '%d'", (int) type); + break; + } /* switch */ + +} /* out_const */ +#undef cpd + + static void +out_args(fp, ep) FILE *fp; expptr ep; +{ + chainp arglist; + + if(ep->tag != TLIST) + badtag("out_args", ep->tag); + for(arglist = ep->listblock.listp;;) { + expr_out(fp, (expptr)arglist->datap); + arglist->datap = 0; + if (!(arglist = arglist->nextp)) + break; + nice_printf(fp, ", "); + } + } + + +/* out_addr -- this routine isn't local because it is called by the + system-generated identifier printing routines */ + +void out_addr (fp, addrp) +FILE *fp; +struct Addrblock *addrp; +{ + extern Extsym *extsymtab; + int was_array = 0; + char *s; + + + if (addrp == NULL) + return; + if (doin_setbound + && addrp->vstg == STGARG + && addrp->vtype != TYCHAR + && ISICON(addrp->memoffset) + && !addrp->memoffset->constblock.Const.ci) + nice_printf(fp, "*"); + + switch (addrp -> uname_tag) { + case UNAM_REF: + nice_printf(fp, "%s_%s(", addrp->user.name->cvarname, + addrp->cmplx_sub ? "subscr" : "ref"); + out_args(fp, addrp->memoffset); + nice_printf(fp, ")"); + return; + case UNAM_NAME: + out_name (fp, addrp -> user.name); + break; + case UNAM_IDENT: + if (*(s = addrp->user.ident) == ' ') { + if (multitype) + nice_printf(fp, "%s", + xretslot[addrp->vtype]->user.ident); + else + nice_printf(fp, "%s", s+1); + } + else { + nice_printf(fp, "%s", s); + } + break; + case UNAM_CHARP: + nice_printf(fp, "%s", addrp->user.Charp); + break; + case UNAM_EXTERN: + extern_out (fp, &extsymtab[addrp -> memno]); + break; + case UNAM_CONST: + switch(addrp->vstg) { + case STGCONST: + out_const(fp, (Constp)addrp); + break; + case STGMEMNO: + output_literal (fp, (int)addrp->memno, + (Constp)addrp); + break; + default: + Fatal("unexpected vstg in out_addr"); + } + break; + case UNAM_UNKNOWN: + default: + nice_printf (fp, "Unknown Addrp"); + break; + } /* switch */ + +/* It's okay to just throw in the brackets here because they have a + precedence level of 15, the highest value. */ + + if ((addrp->uname_tag == UNAM_NAME && addrp->user.name->vdim + || addrp->ntempelt > 1 || addrp->isarray) + && addrp->vtype != TYCHAR) { + expptr offset; + + was_array = 1; + + offset = addrp -> memoffset; + addrp->memoffset = 0; + if (ONEOF(addrp->vstg, M(STGCOMMON)|M(STGEQUIV)) + && addrp -> uname_tag == UNAM_NAME + && !addrp->skip_offset) + offset = mkexpr (OPMINUS, offset, mkintcon ( + addrp -> user.name -> voffset)); + + nice_printf (fp, "["); + + offset = mkexpr (OPSLASH, offset, + ICON (typesize[addrp -> vtype] * (addrp -> Field ? 2 : 1))); + expr_out (fp, offset); + nice_printf (fp, "]"); + } + +/* Check for structure field reference */ + + if (addrp -> Field && addrp -> uname_tag != UNAM_CONST && + addrp -> uname_tag != UNAM_UNKNOWN) { + if (oneof_stg((addrp -> uname_tag == UNAM_NAME ? addrp -> user.name : + (Namep) NULL), addrp -> vstg, M(STGARG)|M(STGEQUIV)) + && !was_array && (addrp->vclass != CLPROC || !multitype)) + nice_printf (fp, "->%s", addrp -> Field); + else + nice_printf (fp, ".%s", addrp -> Field); + } /* if */ + +/* Check for character subscripting */ + + if (addrp->vtype == TYCHAR && + (addrp->vclass != CLPROC || addrp->uname_tag == UNAM_NAME + && addrp->user.name->vprocclass == PTHISPROC) && + addrp -> memoffset && + (addrp -> uname_tag != UNAM_NAME || + addrp -> user.name -> vtype == TYCHAR) && + (!ISICON (addrp -> memoffset) || + (addrp -> memoffset -> constblock.Const.ci))) { + + int use_paren = 0; + expptr e = addrp -> memoffset; + + if (!e) + return; + addrp->memoffset = 0; + + if (ONEOF(addrp->vstg, M(STGCOMMON)|M(STGEQUIV)) + && addrp -> uname_tag == UNAM_NAME) { + e = mkexpr (OPMINUS, e, mkintcon (addrp -> user.name -> voffset)); + +/* mkexpr will simplify it to zero if possible */ + if (e->tag == TCONST && e->constblock.Const.ci == 0) + return; + } /* if addrp -> vstg == STGCOMMON */ + +/* In the worst case, parentheses might be needed OUTSIDE the expression, + too. But since I think this subscripting can only appear as a + parameter in a procedure call, I don't think outside parens will ever + be needed. INSIDE parens are handled below */ + + nice_printf (fp, " + "); + if (e -> tag == TEXPR) { + int arg_prec = op_precedence (e -> exprblock.opcode); + int prec = op_precedence (OPPLUS); + use_paren = arg_prec && (arg_prec < prec || (arg_prec == prec && + is_left_assoc (OPPLUS))); + } /* if e -> tag == TEXPR */ + if (use_paren) nice_printf (fp, "("); + expr_out (fp, e); + if (use_paren) nice_printf (fp, ")"); + } /* if */ +} /* out_addr */ + + +static void output_literal (fp, memno, cp) + FILE *fp; + int memno; + Constp cp; +{ + struct Literal *litp, *lastlit; + extern char *lit_name (); + + lastlit = litpool + nliterals; + + for (litp = litpool; litp < lastlit; litp++) { + if (litp -> litnum == memno) + break; + } /* for litp */ + + if (litp >= lastlit) + out_const (fp, cp); + else { + nice_printf (fp, "%s", lit_name (litp)); + litp->lituse++; + } +} /* output_literal */ + + +static void output_prim (fp, primp) +FILE *fp; +struct Primblock *primp; +{ + if (primp == NULL) + return; + + out_name (fp, primp -> namep); + if (primp -> argsp) + output_arg_list (fp, primp -> argsp); + + if (primp -> fcharp != (expptr) NULL || primp -> lcharp != (expptr) NULL) + nice_printf (fp, "Sorry, no substrings yet"); +} + + + +static void output_arg_list (fp, listp) +FILE *fp; +struct Listblock *listp; +{ + chainp arg_list; + + if (listp == (struct Listblock *) NULL || listp -> listp == (chainp) NULL) + return; + + nice_printf (fp, "("); + + for (arg_list = listp -> listp; arg_list; arg_list = arg_list -> nextp) { + expr_out (fp, (expptr) arg_list -> datap); + if (arg_list -> nextp != (chainp) NULL) + +/* Might want to add a hook in here to accomodate the style setting which + wants spaces after commas */ + + nice_printf (fp, ","); + } /* for arg_list */ + + nice_printf (fp, ")"); +} /* output_arg_list */ + + + +static void output_unary (fp, e) +FILE *fp; +struct Exprblock *e; +{ + if (e == NULL) + return; + + switch (e -> opcode) { + case OPNEG: + if (e->vtype == TYREAL && forcedouble) { + e->opcode = OPNEG_KLUDGE; + output_binary(fp,e); + e->opcode = OPNEG; + break; + } + case OPNEG1: + case OPNOT: + case OPABS: + case OPBITNOT: + case OPWHATSIN: + case OPPREINC: + case OPPREDEC: + case OPADDR: + case OPIDENTITY: + case OPCHARCAST: + case OPDABS: + output_binary (fp, e); + break; + case OPCALL: + case OPCCALL: + nice_printf (fp, "Sorry, no OPCALL yet"); + break; + default: + erri ("output_unary: bad opcode", (int) e -> opcode); + break; + } /* switch */ +} /* output_unary */ + + + static char * +findconst(m) + register long m; +{ + register struct Literal *litp, *litpe; + + litp = litpool; + for(litpe = litp + nliterals; litp < litpe; litp++) + if (litp->litnum == m) + return litp->cds[0]; + Fatal("findconst failure!"); + return 0; + } + + static int +opconv_fudge(fp,e) + FILE *fp; + struct Exprblock *e; +{ + /* special handling for ichar and character*1 */ + register expptr lp; + register union Expression *Offset; + register char *cp; + int lt; + char buf[8]; + unsigned int k; + Namep np; + + if (!(lp = e->leftp)) /* possible with erroneous Fortran */ + return 1; + lt = lp->headblock.vtype; + if (lt == TYCHAR) { + switch(lp->tag) { + case TNAME: + nice_printf(fp, "*"); + out_name(fp, (Namep)lp); + return 1; + case TCONST: + tconst: + cp = lp->constblock.Const.ccp; + tconst1: + k = *(unsigned char *)cp; + sprintf(buf, chr_fmt[k], k); + nice_printf(fp, "'%s'", buf); + return 1; + case TADDR: + switch(lp->addrblock.vstg) { + case STGMEMNO: + if (halign && e->vtype != TYCHAR) { + nice_printf(fp, "*(%s *)", + c_type_decl(e->vtype,0)); + expr_out(fp, lp); + return 1; + } + cp = findconst(lp->addrblock.memno); + goto tconst1; + case STGCONST: + goto tconst; + } + lp->addrblock.vtype = tyint; + Offset = lp->addrblock.memoffset; + switch(lp->addrblock.uname_tag) { + case UNAM_REF: + nice_printf(fp, "*"); + return 0; + case UNAM_NAME: + np = lp->addrblock.user.name; + if (ONEOF(np->vstg, + M(STGCOMMON)|M(STGEQUIV))) + Offset = mkexpr(OPMINUS, Offset, + ICON(np->voffset)); + } + lp->addrblock.memoffset = Offset ? + mkexpr(OPSTAR, Offset, + ICON(typesize[tyint])) + : ICON(0); + lp->addrblock.isarray = 1; + /* STGCOMMON or STGEQUIV would cause */ + /* voffset to be added in a second time */ + lp->addrblock.vstg = STGUNKNOWN; + break; + default: + badtag("opconv_fudge", lp->tag); + } + } + if (lt != e->vtype) + nice_printf(fp, "(%s) ", + c_type_decl(e->vtype, 0)); + return 0; + } + + +static void output_binary (fp, e) +FILE *fp; +struct Exprblock *e; +{ + char *format; + extern table_entry opcode_table[]; + int prec; + + if (e == NULL || e -> tag != TEXPR) + return; + +/* Instead of writing a huge switch, I've incorporated the output format + into a table. Things like "%l" and "%r" stand for the left and + right subexpressions. This should allow both prefix and infix + functions to be specified (e.g. "(%l * %r", "z_div (%l, %r"). Of + course, I should REALLY think out the ramifications of writing out + straight text, as opposed to some intermediate format, which could + figure out and optimize on the the number of required blanks (we don't + want "x - (-y)" to become "x --y", for example). Special cases (such as + incomplete implementations) could still be implemented as part of the + switch, they will just have some dummy value instead of the string + pattern. Another difficulty is the fact that the complex functions + will differ from the integer and real ones */ + +/* Handle a special case. We don't want to output "x + - 4", or "y - - 3" +*/ + if ((e -> opcode == OPPLUS || e -> opcode == OPMINUS) && + e -> rightp && e -> rightp -> tag == TCONST && + isnegative_const (&(e -> rightp -> constblock)) && + is_negatable (&(e -> rightp -> constblock))) { + + e -> opcode = (e -> opcode == OPPLUS) ? OPMINUS : OPPLUS; + negate_const (&(e -> rightp -> constblock)); + } /* if e -> opcode == PLUS or MINUS */ + + prec = op_precedence (e -> opcode); + format = op_format (e -> opcode); + + if (format != SPECIAL_FMT) { + while (*format) { + if (*format == '%') { + int arg_prec, use_paren = 0; + expptr lp, rp; + + switch (*(format + 1)) { + case 'l': + lp = e->leftp; + if (lp && lp->tag == TEXPR) { + arg_prec = op_precedence(lp->exprblock.opcode); + + use_paren = arg_prec && + (arg_prec < prec || (arg_prec == prec && + is_right_assoc (prec))); + } /* if e -> leftp */ + if (e->opcode == OPCONV && opconv_fudge(fp,e)) + break; + if (use_paren) + nice_printf (fp, "("); + expr_out(fp, lp); + if (use_paren) + nice_printf (fp, ")"); + break; + case 'r': + rp = e->rightp; + if (rp && rp->tag == TEXPR) { + arg_prec = op_precedence(rp->exprblock.opcode); + + use_paren = arg_prec && + (arg_prec < prec || (arg_prec == prec && + is_left_assoc (prec))); + use_paren = use_paren || + (rp->exprblock.opcode == OPNEG + && prec >= op_precedence(OPMINUS)); + } /* if e -> rightp */ + if (use_paren) + nice_printf (fp, "("); + expr_out(fp, rp); + if (use_paren) + nice_printf (fp, ")"); + break; + case '\0': + case '%': + nice_printf (fp, "%%"); + break; + default: + erri ("output_binary: format err: '%%%c' illegal", + (int) *(format + 1)); + break; + } /* switch */ + format += 2; + } else + nice_printf (fp, "%c", *format++); + } /* while *format */ + } else { + +/* Handle Special cases of formatting */ + + switch (e -> opcode) { + case OPCCALL: + case OPCALL: + out_call (fp, (int) e -> opcode, e -> vtype, + e -> vleng, e -> leftp, e -> rightp); + break; + + case OPCOMMA_ARG: + doin_setbound = 1; + nice_printf(fp, "("); + expr_out(fp, e->leftp); + nice_printf(fp, ", &"); + doin_setbound = 0; + expr_out(fp, e->rightp); + nice_printf(fp, ")"); + break; + + case OPADDR: + default: + nice_printf (fp, "Sorry, can't format OPCODE '%d'", + e -> opcode); + break; + } + + } /* else */ +} /* output_binary */ + + +out_call (outfile, op, ftype, len, name, args) +FILE *outfile; +int op, ftype; +expptr len, name, args; +{ + chainp arglist; /* Pointer to any actual arguments */ + chainp cp; /* Iterator over argument lists */ + Addrp ret_val = (Addrp) NULL; + /* Function return value buffer, if any is + required */ + int byvalue; /* True iff we're calling a C library + routine */ + int done_once; /* Used for writing commas to outfile */ + int narg, t; + register expptr q; + long L; + Argtypes *at; + Atype *A, *Ac; + Namep np; + extern int forcereal; + +/* Don't use addresses if we're calling a C function */ + + byvalue = op == OPCCALL; + + if (args) + arglist = args -> listblock.listp; + else + arglist = CHNULL; + +/* If this is a CHARACTER function, the first argument is the result */ + + if (ftype == TYCHAR) + if (ISICON (len)) { + ret_val = (Addrp) (arglist -> datap); + arglist = arglist -> nextp; + } else { + err ("adjustable character function"); + return; + } /* else */ + +/* If this is a COMPLEX function, the first argument is the result */ + + else if (ISCOMPLEX (ftype)) { + ret_val = (Addrp) (arglist -> datap); + arglist = arglist -> nextp; + } /* if ISCOMPLEX */ + +/* Now we can actually start to write out the function invocation */ + + if (ftype == TYREAL && forcereal) + nice_printf(outfile, "(real)"); + if (name -> tag == TEXPR && name -> exprblock.opcode == OPWHATSIN) { + nice_printf (outfile, "("); + np = (Namep)name->exprblock.leftp; /*expr_out will free name */ + expr_out (outfile, name); + nice_printf (outfile, ")"); + } + else { + np = (Namep)name; + expr_out(outfile, name); + } + + /* prepare to cast procedure parameters -- set A if we know how */ + + A = Ac = 0; + if (np->tag == TNAME && (at = np->arginfo)) { + if (at->nargs > 0) + A = at->atypes; + if (Ansi && (at->defined || at->nargs > 0)) + Ac = at->atypes; + } + + nice_printf(outfile, "("); + + if (ret_val) { + if (ISCOMPLEX (ftype)) + nice_printf (outfile, "&"); + expr_out (outfile, (expptr) ret_val); + if (Ac) + Ac++; + +/* The length of the result of a character function is the second argument */ +/* It should be in place from putcall(), so we won't touch it explicitly */ + + } /* if ret_val */ + done_once = ret_val ? TRUE : FALSE; + +/* Now run through the named arguments */ + + narg = -1; + for (cp = arglist; cp; cp = cp -> nextp, done_once = TRUE) { + + if (done_once) + nice_printf (outfile, ", "); + narg++; + + if (!( q = (expptr)cp->datap) ) + continue; + + if (q->tag == TADDR) { + if (q->addrblock.vtype > TYERROR) { + /* I/O block */ + nice_printf(outfile, "&%s", q->addrblock.user.ident); + continue; + } + if (!byvalue && q->addrblock.isarray + && q->addrblock.vtype != TYCHAR + && q->addrblock.memoffset->tag == TCONST) { + + /* check for 0 offset -- after */ + /* correcting for equivalence. */ + L = q->addrblock.memoffset->constblock.Const.ci; + if (ONEOF(q->addrblock.vstg, M(STGCOMMON)|M(STGEQUIV)) + && q->addrblock.uname_tag == UNAM_NAME) + L -= q->addrblock.user.name->voffset; + if (L) + goto skip_deref; + + if (Ac && narg < at->dnargs + && q->headblock.vtype != (t = Ac[narg].type) + && t > TYADDR && t < TYSUBR) + nice_printf(outfile, "(%s*)", typename[t]); + + /* &x[0] == x */ + /* This also prevents &sizeof(doublereal)[0] */ + + switch(q->addrblock.uname_tag) { + case UNAM_NAME: + out_name(outfile, q->addrblock.user.name); + continue; + case UNAM_IDENT: + nice_printf(outfile, "%s", + q->addrblock.user.ident); + continue; + case UNAM_CHARP: + nice_printf(outfile, "%s", + q->addrblock.user.Charp); + continue; + case UNAM_EXTERN: + extern_out(outfile, + &extsymtab[q->addrblock.memno]); + continue; + } + } + } + +/* Skip over the dereferencing operator generated only for the + intermediate file */ + skip_deref: + if (q -> tag == TEXPR && q -> exprblock.opcode == OPWHATSIN) + q = q -> exprblock.leftp; + + if (q->headblock.vclass == CLPROC) { + if (Castargs && (q->tag != TNAME + || q->nameblock.vprocclass != PTHISPROC) + && (q->tag != TADDR + || q->addrblock.uname_tag != UNAM_NAME + || q->addrblock.user.name->vprocclass + != PTHISPROC)) + { + if (A && (t = A[narg].type) >= 200) + t %= 100; + else { + t = q->headblock.vtype; + if (q->tag == TNAME && q->nameblock.vimpltype) + t = TYUNKNOWN; + } + nice_printf(outfile, "(%s)", usedcasts[t] = casttypes[t]); + } + } + else if (Ac && narg < at->dnargs + && q->headblock.vtype != (t = Ac[narg].type) + && t > TYADDR && t < TYSUBR) + nice_printf(outfile, "(%s*)", typename[t]); + + if ((q -> tag == TADDR || q-> tag == TNAME) && + (byvalue || q -> headblock.vstg != STGREG)) { + if (q -> headblock.vtype != TYCHAR) + if (byvalue) { + + if (q -> tag == TADDR && + q -> addrblock.uname_tag == UNAM_NAME && + ! q -> addrblock.user.name -> vdim && + oneof_stg(q -> addrblock.user.name, q -> addrblock.vstg, + M(STGARG)|M(STGEQUIV)) && + ! ISCOMPLEX(q->addrblock.user.name->vtype)) + nice_printf (outfile, "*"); + else if (q -> tag == TNAME + && oneof_stg(&q->nameblock, q -> nameblock.vstg, + M(STGARG)|M(STGEQUIV)) + && !(q -> nameblock.vdim)) + nice_printf (outfile, "*"); + + } else { + expptr memoffset; + + if (q->tag == TADDR && + !ONEOF (q -> addrblock.vstg, M(STGEXT)|M(STGLENG)) + && ( + ONEOF(q->addrblock.vstg, + M(STGCOMMON)|M(STGEQUIV)|M(STGMEMNO)) + || ((memoffset = q->addrblock.memoffset) + && (!ISICON(memoffset) + || memoffset->constblock.Const.ci))) + || ONEOF(q->addrblock.vstg, + M(STGINIT)|M(STGAUTO)|M(STGBSS)) + && !q->addrblock.isarray) + nice_printf (outfile, "&"); + else if (q -> tag == TNAME + && !oneof_stg(&q->nameblock, q -> nameblock.vstg, + M(STGARG)|M(STGEXT)|M(STGEQUIV))) + nice_printf (outfile, "&"); + } /* else */ + + expr_out (outfile, q); + } /* if q -> tag == TADDR || q -> tag == TNAME */ + +/* Might be a Constant expression, e.g. string length, character constants */ + + else if (q -> tag == TCONST) { + if (tyioint == TYLONG) + Longfmt = "%ldL"; + out_const(outfile, &q->constblock); + Longfmt = "%ld"; + } + +/* Must be some other kind of expression, or register var, or constant. + In particular, this is likely to be a temporary variable assignment + which was generated in p1put_call */ + + else if (!ISCOMPLEX (q -> headblock.vtype) && !ISCHAR (q)){ + int use_paren = q -> tag == TEXPR && + op_precedence (q -> exprblock.opcode) <= + op_precedence (OPCOMMA); + + if (use_paren) nice_printf (outfile, "("); + expr_out (outfile, q); + if (use_paren) nice_printf (outfile, ")"); + } /* if !ISCOMPLEX */ + else + err ("out_call: unknown parameter"); + + } /* for (cp = arglist */ + + if (arglist) + frchain (&arglist); + + nice_printf (outfile, ")"); + +} /* out_call */ + + + char * +flconst(buf, x) + char *buf, *x; +{ + sprintf(buf, fl_fmt_string, x); + return buf; + } + + char * +dtos(x) + double x; +{ + static char buf[64]; + sprintf(buf, db_fmt_string, x); + return buf; + } + +char tr_tab[Table_size]; + +/* out_init -- Initialize the data structures used by the routines in + output.c. These structures include the output format to be used for + Float, Double, Complex, and Double Complex constants. */ + +void out_init () +{ + extern int tab_size; + register char *s; + + s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+-."; + while(*s) + tr_tab[*s++] = 3; + tr_tab['>'] = 1; + + opeqable[OPPLUS] = 1; + opeqable[OPMINUS] = 1; + opeqable[OPSTAR] = 1; + opeqable[OPSLASH] = 1; + opeqable[OPMOD] = 1; + opeqable[OPLSHIFT] = 1; + opeqable[OPBITAND] = 1; + opeqable[OPBITXOR] = 1; + opeqable[OPBITOR ] = 1; + + +/* Set the output format for both types of floating point constants */ + + if (fl_fmt_string == NULL || *fl_fmt_string == '\0') + fl_fmt_string = Ansi == 1 ? "%sf" : "(float)%s"; + + if (db_fmt_string == NULL || *db_fmt_string == '\0') + db_fmt_string = "%.17g"; + +/* Set the output format for both types of complex constants. They will + have string parameters rather than float or double so that the decimal + point may be added to the strings generated by the {db,fl}_fmt_string + formats above */ + + if (cm_fmt_string == NULL || *cm_fmt_string == '\0') { + cm_fmt_string = "{%s,%s}"; + } /* if cm_fmt_string == NULL */ + + if (dcm_fmt_string == NULL || *dcm_fmt_string == '\0') { + dcm_fmt_string = "{%s,%s}"; + } /* if dcm_fmt_string == NULL */ + + tab_size = 4; +} /* out_init */ + + +void extern_out (fp, extsym) +FILE *fp; +Extsym *extsym; +{ + if (extsym == (Extsym *) NULL) + return; + + nice_printf (fp, "%s", extsym->cextname); + +} /* extern_out */ + + + +static void output_list (fp, listp) +FILE *fp; +struct Listblock *listp; +{ + int did_one = 0; + chainp elts; + + nice_printf (fp, "("); + if (listp) + for (elts = listp -> listp; elts; elts = elts -> nextp) { + if (elts -> datap) { + if (did_one) + nice_printf (fp, ", "); + expr_out (fp, (expptr) elts -> datap); + did_one = 1; + } /* if elts -> datap */ + } /* for elts */ + nice_printf (fp, ")"); +} /* output_list */ + + +void out_asgoto (outfile, expr) +FILE *outfile; +expptr expr; +{ + char *user_label(); + chainp value; + Namep namep; + int k; + + if (expr == (expptr) NULL) { + err ("out_asgoto: NULL variable expr"); + return; + } /* if expr */ + + nice_printf (outfile, Ansi ? "switch (" : "switch ((int)"); /*)*/ + expr_out (outfile, expr); + nice_printf (outfile, ") {\n"); + next_tab (outfile); + +/* The initial addrp value will be stored as a namep pointer */ + + switch(expr->tag) { + case TNAME: + /* local variable */ + namep = &expr->nameblock; + break; + case TEXPR: + if (expr->exprblock.opcode == OPWHATSIN + && expr->exprblock.leftp->tag == TNAME) + /* argument */ + namep = &expr->exprblock.leftp->nameblock; + else + goto bad; + break; + case TADDR: + if (expr->addrblock.uname_tag == UNAM_NAME) { + /* initialized local variable */ + namep = expr->addrblock.user.name; + break; + } + default: + bad: + err("out_asgoto: bad expr"); + return; + } + + for(k = 0, value = namep -> varxptr.assigned_values; value; + value = value->nextp, k++) { + nice_printf (outfile, "case %d: goto %s;\n", k, + user_label((long)value->datap)); + } /* for value */ + prev_tab (outfile); + + nice_printf (outfile, "}\n"); +} /* out_asgoto */ + +void out_if (outfile, expr) +FILE *outfile; +expptr expr; +{ + nice_printf (outfile, "if ("); + expr_out (outfile, expr); + nice_printf (outfile, ") {\n"); + next_tab (outfile); +} /* out_if */ + + static void +output_rbrace(outfile, s) + FILE *outfile; + char *s; +{ + extern int last_was_label; + register char *fmt; + + if (last_was_label) { + last_was_label = 0; + fmt = ";%s"; + } + else + fmt = "%s"; + nice_printf(outfile, fmt, s); + } + +void out_else (outfile) +FILE *outfile; +{ + prev_tab (outfile); + output_rbrace(outfile, "} else {\n"); + next_tab (outfile); +} /* out_else */ + +void elif_out (outfile, expr) +FILE *outfile; +expptr expr; +{ + prev_tab (outfile); + output_rbrace(outfile, "} else "); + out_if (outfile, expr); +} /* elif_out */ + +void endif_out (outfile) +FILE *outfile; +{ + prev_tab (outfile); + output_rbrace(outfile, "}\n"); +} /* endif_out */ + +void end_else_out (outfile) +FILE *outfile; +{ + prev_tab (outfile); + output_rbrace(outfile, "}\n"); +} /* end_else_out */ + + + +void compgoto_out (outfile, index, labels) +FILE *outfile; +expptr index, labels; +{ + char *s1, *s2; + + if (index == ENULL) + err ("compgoto_out: null index for computed goto"); + else if (labels && labels -> tag != TLIST) + erri ("compgoto_out: expected label list, got tag '%d'", + labels -> tag); + else { + extern char *user_label (); + chainp elts; + int i = 1; + + s2 = /*(*/ ") {\n"; /*}*/ + if (Ansi) + s1 = "switch ("; /*)*/ + else if (index->tag == TNAME || index->tag == TEXPR + && index->exprblock.opcode == OPWHATSIN) + s1 = "switch ((int)"; /*)*/ + else { + s1 = "switch ((int)("; + s2 = ")) {\n"; /*}*/ + } + nice_printf(outfile, s1); + expr_out (outfile, index); + nice_printf (outfile, s2); + next_tab (outfile); + + for (elts = labels -> listblock.listp; elts; elts = elts -> nextp, i++) { + if (elts -> datap) { + if (ISICON(((expptr) (elts -> datap)))) + nice_printf (outfile, "case %d: goto %s;\n", i, + user_label(((expptr)(elts->datap))->constblock.Const.ci)); + else + err ("compgoto_out: bad label in label list"); + } /* if (elts -> datap) */ + } /* for elts */ + prev_tab (outfile); + nice_printf (outfile, /*{*/ "}\n"); + } /* else */ +} /* compgoto_out */ + + +void out_for (outfile, init, test, inc) +FILE *outfile; +expptr init, test, inc; +{ + nice_printf (outfile, "for ("); + expr_out (outfile, init); + nice_printf (outfile, "; "); + expr_out (outfile, test); + nice_printf (outfile, "; "); + expr_out (outfile, inc); + nice_printf (outfile, ") {\n"); + next_tab (outfile); +} /* out_for */ + + +void out_end_for (outfile) +FILE *outfile; +{ + prev_tab (outfile); + nice_printf (outfile, "}\n"); +} /* out_end_for */ diff --git a/usr.bin/f2c/output.h b/usr.bin/f2c/output.h new file mode 100644 index 000000000000..2bc21da3b12e --- /dev/null +++ b/usr.bin/f2c/output.h @@ -0,0 +1,65 @@ +/* nice_printf -- same arguments as fprintf. + + All output which is to become C code must be directed through this + function. For now, no buffering is done. Later on, every line of + output will be filtered to accomodate the style definitions (e.g. one + statement per line, spaces between function names and argument lists, + etc.) +*/ +#include "niceprintf.h" + +extern int nice_printf (); + + +/* Definitions for the opcode table. The table is indexed by the macros + which are #defined in defines.h */ + +#define UNARY_OP 01 +#define BINARY_OP 02 + +#define SPECIAL_FMT NULL + +#define is_unary_op(x) (opcode_table[x].type == UNARY_OP) +#define is_binary_op(x) (opcode_table[x].type == BINARY_OP) +#define op_precedence(x) (opcode_table[x].prec) +#define op_format(x) (opcode_table[x].format) + +/* _assoc_table -- encodes left-associativity and right-associativity + information; indexed by precedence level. Only 2, 3, 14 are + right-associative. Source: Kernighan & Ritchie, p. 49 */ + +extern char _assoc_table[]; + +#define is_right_assoc(x) (_assoc_table [x]) +#define is_left_assoc(x) (! _assoc_table [x]) + + +typedef struct { + int type; /* UNARY_OP or BINARY_OP */ + int prec; /* Precedence level, useful for adjusting + number of parens to insert. Zero is a + special level, and 2, 3, 14 are + right-associative */ + char *format; +} table_entry; + + +extern char *fl_fmt_string; /* Float constant format string */ +extern char *db_fmt_string; /* Double constant format string */ +extern char *cm_fmt_string; /* Complex constant format string */ +extern char *dcm_fmt_string; /* Double Complex constant format string */ + +extern int indent; /* Number of spaces to indent; this is a + temporary fix */ +extern int tab_size; /* Number of spaces in each tab */ +extern int in_string; + +extern table_entry opcode_table[]; + + +void expr_out (), out_init (), out_addr (), out_const (); +void out_name (), extern_out (), out_asgoto (); +void out_if (), out_else (), elif_out (); +void endif_out (), end_else_out (); +void compgoto_out (), out_for (); +void out_end_for (), out_and_free_statement (); diff --git a/usr.bin/f2c/p1defs.h b/usr.bin/f2c/p1defs.h new file mode 100644 index 000000000000..16bda0edce4b --- /dev/null +++ b/usr.bin/f2c/p1defs.h @@ -0,0 +1,160 @@ +#define P1_UNKNOWN 0 +#define P1_COMMENT 1 /* Fortan comment string */ +#define P1_EOF 2 /* End of file dummy token */ +#define P1_SET_LINE 3 /* Reset the line counter */ +#define P1_FILENAME 4 /* Name of current input file */ +#define P1_NAME_POINTER 5 /* Pointer to hash table entry */ +#define P1_CONST 6 /* Some constant value */ +#define P1_EXPR 7 /* Followed by opcode */ + +/* The next two tokens could be grouped together, since they always come + from an Addr structure */ + +#define P1_IDENT 8 /* Char string identifier in addrp->user + field */ +#define P1_EXTERN 9 /* Pointer to external symbol entry */ + +#define P1_HEAD 10 /* Function header info */ +#define P1_LIST 11 /* A list of data (e.g. arguments) will + follow the tag, type, and count */ +#define P1_LITERAL 12 /* Hold the index into the literal pool */ +#define P1_LABEL 13 /* label value */ +#define P1_ASGOTO 14 /* Store the hash table pointer of + variable used in assigned goto */ +#define P1_GOTO 15 /* Store the statement number */ +#define P1_IF 16 /* store the condition as an expression */ +#define P1_ELSE 17 /* No data */ +#define P1_ELIF 18 /* store the condition as an expression */ +#define P1_ENDIF 19 /* Marks the end of a block IF */ +#define P1_ENDELSE 20 /* Marks the end of a block ELSE */ +#define P1_ADDR 21 /* Addr data; used for arrays, common and + equiv addressing, NOT for names, idents + or externs */ +#define P1_SUBR_RET 22 /* Subroutine return; the return expression + follows */ +#define P1_COMP_GOTO 23 /* Computed goto; has expr, label list */ +#define P1_FOR 24 /* C FOR loop; three expressions follow */ +#define P1_ENDFOR 25 /* End of C FOR loop */ +#define P1_FORTRAN 26 /* original Fortran source */ +#define P1_CHARP 27 /* user.Charp field -- for long names */ +#define P1_WHILE1START 28 /* start of DO WHILE */ +#define P1_WHILE2START 29 /* rest of DO WHILE */ +#define P1_PROCODE 30 /* invoke procode() -- to adjust params */ +#define P1_ELSEIFSTART 31 /* handle extra code for abs, min, max + in else if() */ + +#define P1_FILENAME_MAX 256 /* max filename length to retain (for -g) */ +#define P1_STMTBUFSIZE 1400 + + + +#define COMMENT_BUFFER_SIZE 255 /* max number of chars in each comment */ +#define CONSTANT_STR_MAX 1000 /* max number of chars in string constant */ + +extern void p1put (/* int */); +extern void p1_comment (/* char * */); +extern void p1_label (/* int */); +extern void p1_line_number (/* int */); +extern void p1put_filename(); +extern void p1_expr (/* expptr */); +extern void p1_head (/* int, char * */); +extern void p1_if (/* expptr */); +extern void p1_else (); +extern void p1_elif (/* expptr */); +extern void p1_endif (); +extern void p1else_end (); +extern void p1_subr_ret (/* expptr */); +extern void p1_goto(/* long */); +extern void p1comp_goto (/* expptr, int, struct Labelblock *[] */); +extern void p1_for (/* expptr, expptr, expptr */); +extern void p1for_end (); + + +extern void p1puts (/* int, char * */); + +/* The pass 1 intermediate file has the following format: + + <ascii-integer-rep> [ : [ <sp> [ <data> ]]] \n + + e.g. 1: This is a comment + + This format is destined to change in the future, but for now a readable + form is more desirable than a compact form. + + NOTES ABOUT THE P1 FORMAT + ---------------------------------------------------------------------- + + P1_COMMENT: The comment string (in <data>) may be at most + COMMENT_BUFFER_SIZE bytes long. It must contain no newlines + or null characters. A side effect of the way comments are + read in lex.c is that no '\377' chars may be in a + comment either. + + P1_SET_LINE: <data> holds the line number in the current source file. + + P1_INC_LINE: Increment the source line number; <data> is empty. + + P1_NAME_POINTER: <data> holds the integer representation of a + pointer into a hash table entry. + + P1_CONST: the first field in <data> is a type tag (one of the + TYxxxx macros), the next field holds the constant + value + + P1_EXPR: <data> holds the opcode number of the expression, + followed by the type of the expression (required for + OPCONV). Next is the value of vleng. + The type of operation represented by the + opcode determines how many of the following data items + are part of this expression. + + P1_IDENT: <data> holds the type, then storage, then the + char string identifier in the addrp->user field. + + P1_EXTERN: <data> holds an offset into the external symbol + table entry + + P1_HEAD: the first field in <data> is the procedure class, the + second is the name of the procedure + + P1_LIST: the first field in <data> is the tag, the second the + type of the list, the third the number of elements in + the list + + P1_LITERAL: <data> holds the litnum of a value in the + literal pool. + + P1_LABEL: <data> holds the statement number of the current + line + + P1_ASGOTO: <data> holds the hash table pointer of the variable + + P1_GOTO: <data> holds the statement number to jump to + + P1_IF: <data> is empty, the following expression is the IF + condition. + + P1_ELSE: <data> is empty. + + P1_ELIF: <data> is empty, the following expression is the IF + condition. + + P1_ENDIF: <data> is empty. + + P1_ENDELSE: <data> is empty. + + P1_ADDR: <data> holds a direct copy of the structure. The + next expression is a copy of vleng, and the next a + copy of memoffset. + + P1_SUBR_RET: The next token is an expression for the return value. + + P1_COMP_GOTO: The next token is an integer expression, the + following one a list of labels. + + P1_FOR: The next three expressions are the Init, Test, and + Increment expressions of a C FOR loop. + + P1_ENDFOR: Marks the end of the body of a FOR loop + +*/ diff --git a/usr.bin/f2c/p1output.c b/usr.bin/f2c/p1output.c new file mode 100644 index 000000000000..d4419b5d5f91 --- /dev/null +++ b/usr.bin/f2c/p1output.c @@ -0,0 +1,567 @@ +/**************************************************************** +Copyright 1990, 1991, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "p1defs.h" +#include "output.h" +#include "names.h" + + +static void p1_addr(), p1_big_addr(), p1_binary(), p1_const(), p1_list(), + p1_literal(), p1_name(), p1_unary(), p1putn(); +static void p1putd (/* int, int */); +static void p1putds (/* int, int, char * */); +static void p1putdds (/* int, int, int, char * */); +static void p1putdd (/* int, int, int */); +static void p1putddd (/* int, int, int, int */); + + +/* p1_comment -- save the text of a Fortran comment in the intermediate + file. Make sure that there are no spurious "/ *" or "* /" characters by + mapping them onto "/+" and "+/". str is assumed to hold no newlines and be + null terminated; it may be modified by this function. */ + +void p1_comment (str) +char *str; +{ + register unsigned char *pointer, *ustr; + + if (!str) + return; + +/* Get rid of any open or close comment combinations that may be in the + Fortran input */ + + ustr = (unsigned char *)str; + for(pointer = ustr; *pointer; pointer++) + if (*pointer == '*' && (pointer[1] == '/' + || pointer > ustr && pointer[-1] == '/')) + *pointer = '+'; + /* trim trailing white space */ +#ifdef isascii + while(--pointer >= ustr && (!isascii(*pointer) || isspace(*pointer))); +#else + while(--pointer >= ustr && isspace(*pointer)); +#endif + pointer[1] = 0; + p1puts (P1_COMMENT, str); +} /* p1_comment */ + +/* p1_name -- Writes the address of a hash table entry into the + intermediate file */ + +static void p1_name (namep) +Namep namep; +{ + p1putd (P1_NAME_POINTER, (long) namep); + namep->visused = 1; +} /* p1_name */ + + + +void p1_expr (expr) +expptr expr; +{ +/* An opcode of 0 means a null entry */ + + if (expr == ENULL) { + p1putdd (P1_EXPR, 0, TYUNKNOWN); /* Should this be TYERROR? */ + return; + } /* if (expr == ENULL) */ + + switch (expr -> tag) { + case TNAME: + p1_name ((Namep) expr); + return; + case TCONST: + p1_const(&expr->constblock); + return; + case TEXPR: + /* Fall through the switch */ + break; + case TADDR: + p1_addr (&(expr -> addrblock)); + goto freeup; + case TPRIM: + warn ("p1_expr: got TPRIM"); + return; + case TLIST: + p1_list (&(expr->listblock)); + frchain( &(expr->listblock.listp) ); + return; + case TERROR: + return; + default: + erri ("p1_expr: bad tag '%d'", (int) (expr -> tag)); + return; + } + +/* Now we know that the tag is TEXPR */ + + if (is_unary_op (expr -> exprblock.opcode)) + p1_unary (&(expr -> exprblock)); + else if (is_binary_op (expr -> exprblock.opcode)) + p1_binary (&(expr -> exprblock)); + else + erri ("p1_expr: bad opcode '%d'", (int) expr -> exprblock.opcode); + freeup: + free((char *)expr); + +} /* p1_expr */ + + + +static void p1_const(cp) + register Constp cp; +{ + int type = cp->vtype; + expptr vleng = cp->vleng; + union Constant *c = &cp->Const; + char cdsbuf0[64], cdsbuf1[64]; + char *cds0, *cds1; + + switch (type) { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYLOGICAL: + case TYLOGICAL1: + case TYLOGICAL2: + fprintf(pass1_file, "%d: %d %ld\n", P1_CONST, type, c->ci); + break; + case TYREAL: + case TYDREAL: + fprintf(pass1_file, "%d: %d %s\n", P1_CONST, type, + cp->vstg ? c->cds[0] : cds(dtos(c->cd[0]), cdsbuf0)); + break; + case TYCOMPLEX: + case TYDCOMPLEX: + if (cp->vstg) { + cds0 = c->cds[0]; + cds1 = c->cds[1]; + } + else { + cds0 = cds(dtos(c->cd[0]), cdsbuf0); + cds1 = cds(dtos(c->cd[1]), cdsbuf1); + } + fprintf(pass1_file, "%d: %d %s %s\n", P1_CONST, type, + cds0, cds1); + break; + case TYCHAR: + if (vleng && !ISICON (vleng)) + erri("p1_const: bad vleng '%d'\n", (int) vleng); + else + fprintf(pass1_file, "%d: %d %lx\n", P1_CONST, type, + cpexpr((expptr)cp)); + break; + default: + erri ("p1_const: bad constant type '%d'", type); + break; + } /* switch */ +} /* p1_const */ + + +void p1_asgoto (addrp) +Addrp addrp; +{ + p1put (P1_ASGOTO); + p1_addr (addrp); +} /* p1_asgoto */ + + +void p1_goto (stateno) +ftnint stateno; +{ + p1putd (P1_GOTO, stateno); +} /* p1_goto */ + + +static void p1_addr (addrp) + register struct Addrblock *addrp; +{ + int stg; + + if (addrp == (struct Addrblock *) NULL) + return; + + stg = addrp -> vstg; + + if (ONEOF(stg, M(STGINIT)|M(STGREG)) + || ONEOF(stg, M(STGCOMMON)|M(STGEQUIV)) && + (!ISICON(addrp->memoffset) + || (addrp->uname_tag == UNAM_NAME + ? addrp->memoffset->constblock.Const.ci + != addrp->user.name->voffset + : addrp->memoffset->constblock.Const.ci)) + || ONEOF(stg, M(STGBSS)|M(STGINIT)|M(STGAUTO)|M(STGARG)) && + (!ISICON(addrp->memoffset) + || addrp->memoffset->constblock.Const.ci) + || addrp->Field || addrp->isarray || addrp->vstg == STGLENG) + { + p1_big_addr (addrp); + return; + } + +/* Write out a level of indirection for non-array arguments, which have + addrp -> memoffset set and are handled by p1_big_addr(). + Lengths are passed by value, so don't check STGLENG + 28-Jun-89 (dmg) Added the check for != TYCHAR + */ + + if (oneof_stg ( addrp -> uname_tag == UNAM_NAME ? addrp -> user.name : NULL, + stg, M(STGARG)|M(STGEQUIV)) && addrp->vtype != TYCHAR) { + p1putdd (P1_EXPR, OPWHATSIN, addrp -> vtype); + p1_expr (ENULL); /* Put dummy vleng */ + } /* if stg == STGARG */ + + switch (addrp -> uname_tag) { + case UNAM_NAME: + p1_name (addrp -> user.name); + break; + case UNAM_IDENT: + p1putdds(P1_IDENT, addrp->vtype, addrp->vstg, + addrp->user.ident); + break; + case UNAM_CHARP: + p1putdds(P1_CHARP, addrp->vtype, addrp->vstg, + addrp->user.Charp); + break; + case UNAM_EXTERN: + p1putd (P1_EXTERN, (long) addrp -> memno); + if (addrp->vclass == CLPROC) + extsymtab[addrp->memno].extype = addrp->vtype; + break; + case UNAM_CONST: + if (addrp -> memno != BAD_MEMNO) + p1_literal (addrp -> memno); + else + p1_const((struct Constblock *)addrp); + break; + case UNAM_UNKNOWN: + default: + erri ("p1_addr: unknown uname_tag '%d'", addrp -> uname_tag); + break; + } /* switch */ +} /* p1_addr */ + + +static void p1_list (listp) +struct Listblock *listp; +{ + chainp lis; + int count = 0; + + if (listp == (struct Listblock *) NULL) + return; + +/* Count the number of parameters in the list */ + + for (lis = listp -> listp; lis; lis = lis -> nextp) + count++; + + p1putddd (P1_LIST, listp -> tag, listp -> vtype, count); + + for (lis = listp -> listp; lis; lis = lis -> nextp) + p1_expr ((expptr) lis -> datap); + +} /* p1_list */ + + +void p1_label (lab) +long lab; +{ + if (parstate < INDATA) + earlylabs = mkchain((char *)lab, earlylabs); + else + p1putd (P1_LABEL, lab); + } + + + +static void p1_literal (memno) +long memno; +{ + p1putd (P1_LITERAL, memno); +} /* p1_literal */ + + +void p1_if (expr) +expptr expr; +{ + p1put (P1_IF); + p1_expr (expr); +} /* p1_if */ + + + + +void p1_elif (expr) +expptr expr; +{ + p1put (P1_ELIF); + p1_expr (expr); +} /* p1_elif */ + + + + +void p1_else () +{ + p1put (P1_ELSE); +} /* p1_else */ + + + + +void p1_endif () +{ + p1put (P1_ENDIF); +} /* p1_endif */ + + + + +void p1else_end () +{ + p1put (P1_ENDELSE); +} /* p1else_end */ + + +static void p1_big_addr (addrp) +Addrp addrp; +{ + if (addrp == (Addrp) NULL) + return; + + p1putn (P1_ADDR, (int)sizeof(struct Addrblock), (char *) addrp); + p1_expr (addrp -> vleng); + p1_expr (addrp -> memoffset); + if (addrp->uname_tag == UNAM_NAME) + addrp->user.name->visused = 1; +} /* p1_big_addr */ + + + +static void p1_unary (e) +struct Exprblock *e; +{ + if (e == (struct Exprblock *) NULL) + return; + + p1putdd (P1_EXPR, (int) e -> opcode, e -> vtype); + p1_expr (e -> vleng); + + switch (e -> opcode) { + case OPNEG: + case OPNEG1: + case OPNOT: + case OPABS: + case OPBITNOT: + case OPPREINC: + case OPPREDEC: + case OPADDR: + case OPIDENTITY: + case OPCHARCAST: + case OPDABS: + p1_expr(e -> leftp); + break; + default: + erri ("p1_unary: bad opcode '%d'", (int) e -> opcode); + break; + } /* switch */ + +} /* p1_unary */ + + +static void p1_binary (e) +struct Exprblock *e; +{ + if (e == (struct Exprblock *) NULL) + return; + + p1putdd (P1_EXPR, e -> opcode, e -> vtype); + p1_expr (e -> vleng); + p1_expr (e -> leftp); + p1_expr (e -> rightp); +} /* p1_binary */ + + +void p1_head (class, name) +int class; +char *name; +{ + p1putds (P1_HEAD, class, name ? name : ""); +} /* p1_head */ + + +void p1_subr_ret (retexp) +expptr retexp; +{ + + p1put (P1_SUBR_RET); + p1_expr (cpexpr(retexp)); +} /* p1_subr_ret */ + + + +void p1comp_goto (index, count, labels) +expptr index; +int count; +struct Labelblock *labels[]; +{ + struct Constblock c; + int i; + register struct Labelblock *L; + + p1put (P1_COMP_GOTO); + p1_expr (index); + +/* Write out a P1_LIST directly, to avoid the overhead of allocating a + list before it's needed HACK HACK HACK */ + + p1putddd (P1_LIST, TLIST, TYUNKNOWN, count); + c.vtype = TYLONG; + c.vleng = 0; + + for (i = 0; i < count; i++) { + L = labels[i]; + L->labused = 1; + c.Const.ci = L->stateno; + p1_const(&c); + } /* for i = 0 */ +} /* p1comp_goto */ + + + +void p1_for (init, test, inc) +expptr init, test, inc; +{ + p1put (P1_FOR); + p1_expr (init); + p1_expr (test); + p1_expr (inc); +} /* p1_for */ + + +void p1for_end () +{ + p1put (P1_ENDFOR); +} /* p1for_end */ + + + + +/* ---------------------------------------------------------------------- + The intermediate file actually gets written ONLY by the routines below. + To change the format of the file, you need only change these routines. + ---------------------------------------------------------------------- +*/ + + +/* p1puts -- Put a typed string into the Pass 1 intermediate file. Assumes that + str contains no newlines and is null-terminated. */ + +void p1puts (type, str) +int type; +char *str; +{ + fprintf (pass1_file, "%d: %s\n", type, str); +} /* p1puts */ + + +/* p1putd -- Put a typed integer into the Pass 1 intermediate file. */ + +static void p1putd (type, value) +int type; +long value; +{ + fprintf (pass1_file, "%d: %ld\n", type, value); +} /* p1_putd */ + + +/* p1putdd -- Put a typed pair of integers into the intermediate file. */ + +static void p1putdd (type, v1, v2) +int type, v1, v2; +{ + fprintf (pass1_file, "%d: %d %d\n", type, v1, v2); +} /* p1putdd */ + + +/* p1putddd -- Put a typed triple of integers into the intermediate file. */ + +static void p1putddd (type, v1, v2, v3) +int type, v1, v2, v3; +{ + fprintf (pass1_file, "%d: %d %d %d\n", type, v1, v2, v3); +} /* p1putddd */ + + union dL { + double d; + long L[2]; + }; + +static void p1putn (type, count, str) +int type, count; +char *str; +{ + int i; + + fprintf (pass1_file, "%d: ", type); + + for (i = 0; i < count; i++) + putc (str[i], pass1_file); + + putc ('\n', pass1_file); +} /* p1putn */ + + + +/* p1put -- Put a type marker into the intermediate file. */ + +void p1put(type) +int type; +{ + fprintf (pass1_file, "%d:\n", type); +} /* p1put */ + + + +static void p1putds (type, i, str) +int type; +int i; +char *str; +{ + fprintf (pass1_file, "%d: %d %s\n", type, i, str); +} /* p1putds */ + + +static void p1putdds (token, type, stg, str) +int token, type, stg; +char *str; +{ + fprintf (pass1_file, "%d: %d %d %s\n", token, type, stg, str); +} /* p1putdds */ diff --git a/usr.bin/f2c/parse.h b/usr.bin/f2c/parse.h new file mode 100644 index 000000000000..1eb2c5465200 --- /dev/null +++ b/usr.bin/f2c/parse.h @@ -0,0 +1,39 @@ +#ifndef PARSE_INCLUDE +#define PARSE_INCLUDE + +/* macros for the parse_args routine */ + +#define P_STRING 1 /* Macros for the result_type attribute */ +#define P_CHAR 2 +#define P_SHORT 3 +#define P_INT 4 +#define P_LONG 5 +#define P_FILE 6 +#define P_OLD_FILE 7 +#define P_NEW_FILE 8 +#define P_FLOAT 9 +#define P_DOUBLE 10 + +#define P_CASE_INSENSITIVE 01 /* Macros for the flags attribute */ +#define P_REQUIRED_PREFIX 02 + +#define P_NO_ARGS 0 /* Macros for the arg_count attribute */ +#define P_ONE_ARG 1 +#define P_INFINITE_ARGS 2 + +#define p_entry(pref,swit,flag,count,type,store,size) \ + { (pref), (swit), (flag), (count), (type), (int *) (store), (size) } + +typedef struct { + char *prefix; + char *string; + int flags; + int count; + int result_type; + int *result_ptr; + int table_size; +} arg_info; + +extern int parse_args (); + +#endif diff --git a/usr.bin/f2c/parse_args.c b/usr.bin/f2c/parse_args.c new file mode 100644 index 000000000000..f978383620c5 --- /dev/null +++ b/usr.bin/f2c/parse_args.c @@ -0,0 +1,502 @@ +/**************************************************************** +Copyright 1990 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* parse_args + + This function will parse command line input into appropriate data + structures, output error messages when appropriate and provide some + minimal type conversion. + + Input to the function consists of the standard argc,argv + values, and a table which directs the parser. Each table entry has the + following components: + + prefix -- the (optional) switch character string, e.g. "-" "/" "=" + switch -- the command string, e.g. "o" "data" "file" "F" + flags -- control flags, e.g. CASE_INSENSITIVE, REQUIRED_PREFIX + arg_count -- number of arguments this command requires, e.g. 0 for + booleans, 1 for filenames, INFINITY for input files + result_type -- how to interpret the switch arguments, e.g. STRING, + CHAR, FILE, OLD_FILE, NEW_FILE + result_ptr -- pointer to storage for the result, be it a table or + a string or whatever + table_size -- if the arguments fill a table, the maximum number of + entries; if there are no arguments, the value to + load into the result storage + + Although the table can be used to hold a list of filenames, only + scalar values (e.g. pointers) can be stored in the table. No vector + processing will be done, only pointers to string storage will be moved. + + An example entry, which could be used to parse input filenames, is: + + "-", "o", 0, oo, OLD_FILE, infilenames, INFILE_TABLE_SIZE + +*/ + +#include <stdio.h> +#ifndef NULL +/* ANSI C */ +#include <stddef.h> +#endif +#include "parse.h" +#include <math.h> /* For atof */ +#include <ctype.h> +#include "defs.h" + +#define MAX_INPUT_SIZE 1000 + +#define arg_prefix(x) ((x).prefix) +#define arg_string(x) ((x).string) +#define arg_flags(x) ((x).flags) +#define arg_count(x) ((x).count) +#define arg_result_type(x) ((x).result_type) +#define arg_result_ptr(x) ((x).result_ptr) +#define arg_table_size(x) ((x).table_size) + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +typedef int boolean; + + +char *lower_string (/* char [], char * */); + +static char *this_program = ""; + +#ifndef atol +extern long atol(); +#endif +static int arg_parse (/* char *, arg_info * */); + + +boolean parse_args (argc, argv, table, entries, others, other_count) +int argc; +char *argv[]; +arg_info table[]; +int entries; +char *others[]; +int other_count; +{ + boolean arg_verify (/* argv, table, entries */); + void init_store (/* table, entries */); + + boolean result; + + if (argv) + this_program = argv[0]; + +/* Check the validity of the table and its parameters */ + + result = arg_verify (argv, table, entries); + +/* Initialize the storage values */ + + init_store (table, entries); + + if (result) { + boolean use_prefix = TRUE; + char *argv0; + + argc--; + argv0 = *++argv; + while (argc) { + int index, length; + + index = match_table (*argv, table, entries, use_prefix, &length); + if (index < 0) { + +/* The argument doesn't match anything in the table */ + + if (others) { + + if (*argv > argv0) + *--*argv = '-'; /* complain at invalid flag */ + + if (other_count > 0) { + *others++ = *argv; + other_count--; + } else { + fprintf (stderr, "%s: too many parameters: ", + this_program); + fprintf (stderr, "'%s' ignored\n", *argv); + } /* else */ + } /* if (others) */ + argv0 = *++argv; + argc--; + } else { + +/* A match was found */ + + if (length >= strlen (*argv)) { + argc--; + argv0 = *++argv; + use_prefix = TRUE; + } else { + (*argv) += length; + use_prefix = FALSE; + } /* else */ + +/* Parse any necessary arguments */ + + if (arg_count (table[index]) != P_NO_ARGS) { + +/* Now length will be used to store the number of parsed characters */ + + length = arg_parse(*argv, &table[index]); + if (*argv == NULL) + argc = 0; + else if (length >= strlen (*argv)) { + argc--; + argv0 = *++argv; + use_prefix = TRUE; + } else { + (*argv) += length; + use_prefix = FALSE; + } /* else */ + } /* if (argv_count != P_NO_ARGS) */ + else + *arg_result_ptr(table[index]) = + arg_table_size(table[index]); + } /* else */ + } /* while (argc) */ + } /* if (result) */ + + return result; +} /* parse_args */ + + +boolean arg_verify (argv, table, entries) +char *argv[]; +arg_info table[]; +int entries; +{ + int i; + char *this_program = ""; + + if (argv) + this_program = argv[0]; + + for (i = 0; i < entries; i++) { + arg_info *arg = &table[i]; + +/* Check the argument flags */ + + if (arg_flags (*arg) & ~(P_CASE_INSENSITIVE | P_REQUIRED_PREFIX)) { + fprintf (stderr, "%s [arg_verify]: too many ", this_program); + fprintf (stderr, "flags in entry %d: '%x' (hex)\n", i, + arg_flags (*arg)); + } /* if */ + +/* Check the argument count */ + + { int count = arg_count (*arg); + + if (count != P_NO_ARGS && count != P_ONE_ARG && count != + P_INFINITE_ARGS) { + fprintf (stderr, "%s [arg_verify]: invalid ", this_program); + fprintf (stderr, "argument count in entry %d: '%d'\n", i, + count); + } /* if count != P_NO_ARGS ... */ + +/* Check the result field; want to be able to store results */ + + else + if (arg_result_ptr (*arg) == (int *) NULL) { + fprintf (stderr, "%s [arg_verify]: ", this_program); + fprintf (stderr, "no argument storage given for "); + fprintf (stderr, "entry %d\n", i); + } /* if arg_result_ptr */ + } + +/* Check the argument type */ + + { int type = arg_result_type (*arg); + + if (type < P_STRING || type > P_DOUBLE) + fprintf(stderr, + "%s [arg_verify]: bad arg type in entry %d: '%d'\n", + this_program, i, type); + } + +/* Check table size */ + + { int size = arg_table_size (*arg); + + if (arg_count (*arg) == P_INFINITE_ARGS && size < 1) { + fprintf (stderr, "%s [arg_verify]: bad ", this_program); + fprintf (stderr, "table size in entry %d: '%d'\n", i, + size); + } /* if (arg_count == P_INFINITE_ARGS && size < 1) */ + } + + } /* for i = 0 */ + + return TRUE; +} /* arg_verify */ + + +/* match_table -- returns the index of the best entry matching the input, + -1 if no match. The best match is the one of longest length which + appears lowest in the table. The length of the match will be returned + in length ONLY IF a match was found. */ + +int match_table (norm_input, table, entries, use_prefix, length) +register char *norm_input; +arg_info table[]; +int entries; +boolean use_prefix; +int *length; +{ + extern int match (/* char *, char *, arg_info *, boolean */); + + char low_input[MAX_INPUT_SIZE]; + register int i; + int best_index = -1, best_length = 0; + +/* FUNCTION BODY */ + + (void) lower_string (low_input, norm_input); + + for (i = 0; i < entries; i++) { + int this_length = match (norm_input, low_input, &table[i], use_prefix); + + if (this_length > best_length) { + best_index = i; + best_length = this_length; + } /* if (this_length > best_length) */ + } /* for (i = 0) */ + + if (best_index > -1 && length != (int *) NULL) + *length = best_length; + + return best_index; +} /* match_table */ + + +/* match -- takes an input string and table entry, and returns the length + of the longer match. + + 0 ==> input doesn't match + + For example: + + INPUT PREFIX STRING RESULT +---------------------------------------------------------------------- + "abcd" "-" "d" 0 + "-d" "-" "d" 2 (i.e. "-d") + "dout" "-" "d" 1 (i.e. "d") + "-d" "" "-d" 2 (i.e. "-d") + "dd" "d" "d" 2 <= here's the weird one +*/ + +int match (norm_input, low_input, entry, use_prefix) +char *norm_input, *low_input; +arg_info *entry; +boolean use_prefix; +{ + char *norm_prefix = arg_prefix (*entry); + char *norm_string = arg_string (*entry); + boolean prefix_match = FALSE, string_match = FALSE; + int result = 0; + +/* Buffers for the lowercased versions of the strings being compared. + These are used when the switch is to be case insensitive */ + + static char low_prefix[MAX_INPUT_SIZE]; + static char low_string[MAX_INPUT_SIZE]; + int prefix_length = strlen (norm_prefix); + int string_length = strlen (norm_string); + +/* Pointers for the required strings (lowered or nonlowered) */ + + register char *input, *prefix, *string; + +/* FUNCTION BODY */ + +/* Use the appropriate strings to handle case sensitivity */ + + if (arg_flags (*entry) & P_CASE_INSENSITIVE) { + input = low_input; + prefix = lower_string (low_prefix, norm_prefix); + string = lower_string (low_string, norm_string); + } else { + input = norm_input; + prefix = norm_prefix; + string = norm_string; + } /* else */ + +/* First, check the string formed by concatenating the prefix onto the + switch string, but only when the prefix is not being ignored */ + + if (use_prefix && prefix != NULL && *prefix != '\0') + prefix_match = (strncmp (input, prefix, prefix_length) == 0) && + (strncmp (input + prefix_length, string, string_length) == 0); + +/* Next, check just the switch string, if that's allowed */ + + if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0) + string_match = strncmp (input, string, string_length) == 0; + + if (prefix_match) + result = prefix_length + string_length; + else if (string_match) + result = string_length; + + return result; +} /* match */ + + +char *lower_string (dest, src) +char *dest, *src; +{ + char *result = dest; + register int c; + + if (dest == NULL || src == NULL) + result = NULL; + else + while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c); + + return result; +} /* lower_string */ + + +/* arg_parse -- returns the number of characters parsed for this entry */ + +static int arg_parse (str, entry) +char *str; +arg_info *entry; +{ + int length = 0; + + if (arg_count (*entry) == P_ONE_ARG) { + char **store = (char **) arg_result_ptr (*entry); + + length = put_one_arg (arg_result_type (*entry), str, store, + arg_prefix (*entry), arg_string (*entry)); + + } /* if (arg_count == P_ONE_ARG) */ + else { /* Must be a table of arguments */ + char **store = (char **) arg_result_ptr (*entry); + + if (store) { + while (*store) + store++; + + length = put_one_arg (arg_result_type (*entry), str, store++, + arg_prefix (*entry), arg_string (*entry)); + + *store = (char *) NULL; + } /* if (store) */ + } /* else */ + + return length; +} /* arg_parse */ + + +int put_one_arg (type, str, store, prefix, string) +int type; +char *str; +char **store; +char *prefix, *string; +{ + int length = 0; + long L; + + if (store) { + switch (type) { + case P_STRING: + case P_FILE: + case P_OLD_FILE: + case P_NEW_FILE: + if (str == NULL) + fprintf (stderr, "%s: Missing argument after '%s%s'\n", + this_program, prefix, string); + *store = copys(str); + length = str ? strlen (str) : 0; + break; + case P_CHAR: + *((char *) store) = *str; + length = 1; + break; + case P_SHORT: + L = atol(str); + *(short *)store = (short) L; + if (L != *(short *)store) + fprintf(stderr, + "%s%s parameter '%ld' is not a SHORT INT (truncating to %d)\n", + prefix, string, L, *(short *)store); + length = strlen (str); + break; + case P_INT: + L = atol(str); + *(int *)store = (int)L; + if (L != *(int *)store) + fprintf(stderr, + "%s%s parameter '%ld' is not an INT (truncating to %d)\n", + prefix, string, L, *(int *)store); + length = strlen (str); + break; + case P_LONG: + *(long *)store = atol(str); + length = strlen (str); + break; + case P_FLOAT: + *((float *) store) = (float) atof (str); + length = strlen (str); + break; + case P_DOUBLE: + *((double *) store) = (double) atof (str); + length = strlen (str); + break; + default: + fprintf (stderr, "put_one_arg: bad type '%d'\n", + type); + break; + } /* switch */ + } /* if (store) */ + + return length; +} /* put_one_arg */ + + +void init_store (table, entries) +arg_info *table; +int entries; +{ + int index; + + for (index = 0; index < entries; index++) + if (arg_count (table[index]) == P_INFINITE_ARGS) { + char **place = (char **) arg_result_ptr (table[index]); + + if (place) + *place = (char *) NULL; + } /* if arg_count == P_INFINITE_ARGS */ + +} /* init_store */ + diff --git a/usr.bin/f2c/pccdefs.h b/usr.bin/f2c/pccdefs.h new file mode 100644 index 000000000000..bde81177a7b1 --- /dev/null +++ b/usr.bin/f2c/pccdefs.h @@ -0,0 +1,64 @@ +/* The following numbers are strange, and implementation-dependent */ + +#define P2BAD -1 +#define P2NAME 2 +#define P2ICON 4 /* Integer constant */ +#define P2PLUS 6 +#define P2PLUSEQ 7 +#define P2MINUS 8 +#define P2NEG 10 +#define P2STAR 11 +#define P2STAREQ 12 +#define P2INDIRECT 13 +#define P2BITAND 14 +#define P2BITOR 17 +#define P2BITXOR 19 +#define P2QUEST 21 +#define P2COLON 22 +#define P2ANDAND 23 +#define P2OROR 24 +#define P2GOTO 37 +#define P2LISTOP 56 +#define P2ASSIGN 58 +#define P2COMOP 59 +#define P2SLASH 60 +#define P2MOD 62 +#define P2LSHIFT 64 +#define P2RSHIFT 66 +#define P2CALL 70 +#define P2CALL0 72 + +#define P2NOT 76 +#define P2BITNOT 77 +#define P2EQ 80 +#define P2NE 81 +#define P2LE 82 +#define P2LT 83 +#define P2GE 84 +#define P2GT 85 +#define P2REG 94 +#define P2OREG 95 +#define P2CONV 104 +#define P2FORCE 108 +#define P2CBRANCH 109 + +/* special operators included only for fortran's use */ + +#define P2PASS 200 +#define P2STMT 201 +#define P2SWITCH 202 +#define P2LBRACKET 203 +#define P2RBRACKET 204 +#define P2EOF 205 +#define P2ARIF 206 +#define P2LABEL 207 + +#define P2SHORT 3 +#define P2INT 4 +#define P2LONG 4 + +#define P2CHAR 2 +#define P2REAL 6 +#define P2DREAL 7 +#define P2PTR 020 +#define P2FUNCT 040 diff --git a/usr.bin/f2c/permission b/usr.bin/f2c/permission new file mode 100644 index 000000000000..20d431ed6dc9 --- /dev/null +++ b/usr.bin/f2c/permission @@ -0,0 +1,41 @@ +From ches Tue Mar 6 09:06:22 EST 1990 +It think it probably is. I am told the line is 89% utilized. But the throughpu +t +is shared, so I wouldn't worry about it. +>From ehg Tue Mar 6 08:16 EST 1990 +Received: by coma; Tue Mar 6 08:17:21 1990 +From: pyxis!ehg +Date: Tue, 6 Mar 90 08:16 EST +To: coma!ches + +Thanks. Is it reasonable for people to ask for the 600KB f2c source over +uunet's dedicated line? I'm just trying to find out if there's a problem +before there's a disaster. +>From ches Tue Mar 6 07:16:18 EST 1990 +Inet has no dialers. All its calls go through the internet. The mcsun addresse +s +were uunet.uu.net!mcsun!..., which will travel to uunet via Internet and +then across the ocean on uunet's dedicated line. +/**************************************************************** +Copyright 1990 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + diff --git a/usr.bin/f2c/pread.c b/usr.bin/f2c/pread.c new file mode 100644 index 000000000000..15d8b309c436 --- /dev/null +++ b/usr.bin/f2c/pread.c @@ -0,0 +1,908 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" + + static char Ptok[128], Pct[Table_size]; + static char *Pfname; + static long Plineno; + static int Pbad; + static int *tfirst, *tlast, *tnext, tmax; + +#define P_space 1 +#define P_anum 2 +#define P_delim 3 +#define P_slash 4 + +#define TGULP 100 + + static void +trealloc() +{ + int k = tmax; + tfirst = (int *)realloc((char *)tfirst, + (tmax += TGULP)*sizeof(int)); + if (!tfirst) { + fprintf(stderr, + "Pfile: realloc failure!\n"); + exit(2); + } + tlast = tfirst + tmax; + tnext = tfirst + k; + } + + static void +badchar(c) + int c; +{ + fprintf(stderr, + "unexpected character 0x%.2x = '%c' on line %ld of %s\n", + c, c, Plineno, Pfname); + exit(2); + } + + static void +bad_type() +{ + fprintf(stderr, + "unexpected type \"%s\" on line %ld of %s\n", + Ptok, Plineno, Pfname); + exit(2); + } + + static void +badflag(tname, option) + char *tname, *option; +{ + fprintf(stderr, "%s type from `f2c -%s` on line %ld of %s\n", + tname, option, Plineno, Pfname); + Pbad++; + } + + static void +detected(msg) + char *msg; +{ + fprintf(stderr, + "%sdetected on line %ld of %s\n", msg, Plineno, Pfname); + Pbad++; + } + +#if 0 + static void +checklogical(k) + int k; +{ + static int lastmsg = 0; + static int seen[2] = {0,0}; + + seen[k] = 1; + if (seen[1-k]) { + if (lastmsg < 3) { + lastmsg = 3; + detected( + "Illegal combination of LOGICAL types -- mixing -I4 with -I2 or -i2\n\t"); + } + return; + } + if (k) { + if (tylogical == TYLONG || lastmsg >= 2) + return; + if (!lastmsg) { + lastmsg = 2; + badflag("LOGICAL", "I4"); + } + } + else { + if (tylogical == TYSHORT || lastmsg & 1) + return; + if (!lastmsg) { + lastmsg = 1; + badflag("LOGICAL", "i2` or `f2c -I2"); + } + } + } +#else +#define checklogical(n) /* */ +#endif + + static void +checkreal(k) +{ + static int warned = 0; + static int seen[2] = {0,0}; + + seen[k] = 1; + if (seen[1-k]) { + if (warned < 2) + detected("Illegal mixture of -R and -!R "); + warned = 2; + return; + } + if (k == forcedouble || warned) + return; + warned = 1; + badflag("REAL return", k ? "!R" : "R"); + } + + static void +Pnotboth(e) + Extsym *e; +{ + if (e->curno) + return; + Pbad++; + e->curno = 1; + fprintf(stderr, + "%s cannot be both a procedure and a common block (line %ld of %s)\n", + e->fextname, Plineno, Pfname); + } + + static int +numread(pf, n) + register FILE *pf; + int *n; +{ + register int c, k; + + if ((c = getc(pf)) < '0' || c > '9') + return c; + k = c - '0'; + for(;;) { + if ((c = getc(pf)) == ' ') { + *n = k; + return c; + } + if (c < '0' || c > '9') + break; + k = 10*k + c - '0'; + } + return c; + } + + static void argverify(), Pbadret(); + + static int +readref(pf, e, ftype) + register FILE *pf; + Extsym *e; + int ftype; +{ + register int c, *t; + int i, nargs, type; + Argtypes *at; + Atype *a, *ae; + + if (ftype > TYSUBR) + return 0; + if ((c = numread(pf, &nargs)) != ' ') { + if (c != ':') + return c == EOF; + /* just a typed external */ + if (e->extstg == STGUNKNOWN) { + at = 0; + goto justsym; + } + if (e->extstg == STGEXT) { + if (e->extype != ftype) + Pbadret(ftype, e); + } + else + Pnotboth(e); + return 0; + } + + tnext = tfirst; + for(i = 0; i < nargs; i++) { + if ((c = numread(pf, &type)) != ' ' + || type >= 500 + || type != TYFTNLEN + 100 && type % 100 > TYSUBR) + return c == EOF; + if (tnext >= tlast) + trealloc(); + *tnext++ = type; + } + + if (e->extstg == STGUNKNOWN) { + save_at: + at = (Argtypes *) + gmem(sizeof(Argtypes) + (nargs-1)*sizeof(Atype), 1); + at->dnargs = at->nargs = nargs; + at->changes = 0; + t = tfirst; + a = at->atypes; + for(ae = a + nargs; a < ae; a++) { + a->type = *t++; + a->cp = 0; + } + justsym: + e->extstg = STGEXT; + e->extype = ftype; + e->arginfo = at; + } + else if (e->extstg != STGEXT) { + Pnotboth(e); + } + else if (!e->arginfo) { + if (e->extype != ftype) + Pbadret(ftype, e); + else + goto save_at; + } + else + argverify(ftype, e); + return 0; + } + + static int +comlen(pf) + register FILE *pf; +{ + register int c; + register char *s, *se; + char buf[128], cbuf[128]; + int refread; + long L; + Extsym *e; + + if ((c = getc(pf)) == EOF) + return 1; + if (c == ' ') { + refread = 0; + s = "comlen "; + } + else if (c == ':') { + refread = 1; + s = "ref: "; + } + else { + ret0: + if (c == '*') + ungetc(c,pf); + return 0; + } + while(*s) { + if ((c = getc(pf)) == EOF) + return 1; + if (c != *s++) + goto ret0; + } + s = buf; + se = buf + sizeof(buf) - 1; + for(;;) { + if ((c = getc(pf)) == EOF) + return 1; + if (c == ' ') + break; + if (s >= se || Pct[c] != P_anum) + goto ret0; + *s++ = c; + } + *s-- = 0; + if (s <= buf || *s != '_') + return 0; + strcpy(cbuf,buf); + *s-- = 0; + if (*s == '_') { + *s-- = 0; + if (s <= buf) + return 0; + } + for(L = 0;;) { + if ((c = getc(pf)) == EOF) + return 1; + if (c == ' ') + break; + if (c < '0' && c > '9') + goto ret0; + L = 10*L + c - '0'; + } + if (!L && !refread) + return 0; + e = mkext(buf, cbuf); + if (refread) + return readref(pf, e, (int)L); + if (e->extstg == STGUNKNOWN) { + e->extstg = STGCOMMON; + e->maxleng = L; + } + else if (e->extstg != STGCOMMON) + Pnotboth(e); + else if (e->maxleng != L) { + fprintf(stderr, + "incompatible lengths for common block %s (line %ld of %s)\n", + buf, Plineno, Pfname); + if (e->maxleng < L) + e->maxleng = L; + } + return 0; + } + + static int +Ptoken(pf, canend) + FILE *pf; + int canend; +{ + register int c; + register char *s, *se; + + top: + for(;;) { + c = getc(pf); + if (c == EOF) { + if (canend) + return 0; + goto badeof; + } + if (Pct[c] != P_space) + break; + if (c == '\n') + Plineno++; + } + switch(Pct[c]) { + case P_anum: + if (c == '_') + badchar(c); + s = Ptok; + se = s + sizeof(Ptok) - 1; + do { + if (s < se) + *s++ = c; + if ((c = getc(pf)) == EOF) { + badeof: + fprintf(stderr, + "unexpected end of file in %s\n", + Pfname); + exit(2); + } + } + while(Pct[c] == P_anum); + ungetc(c,pf); + *s = 0; + return P_anum; + + case P_delim: + return c; + + case P_slash: + if ((c = getc(pf)) != '*') { + if (c == EOF) + goto badeof; + badchar('/'); + } + if (canend && comlen(pf)) + goto badeof; + for(;;) { + while((c = getc(pf)) != '*') { + if (c == EOF) + goto badeof; + if (c == '\n') + Plineno++; + } + slashseek: + switch(getc(pf)) { + case '/': + goto top; + case EOF: + goto badeof; + case '*': + goto slashseek; + } + } + default: + badchar(c); + } + /* NOT REACHED */ + return 0; + } + + static int +Pftype() +{ + switch(Ptok[0]) { + case 'C': + if (!strcmp(Ptok+1, "_f")) + return TYCOMPLEX; + break; + case 'E': + if (!strcmp(Ptok+1, "_f")) { + /* TYREAL under forcedouble */ + checkreal(1); + return TYREAL; + } + break; + case 'H': + if (!strcmp(Ptok+1, "_f")) + return TYCHAR; + break; + case 'Z': + if (!strcmp(Ptok+1, "_f")) + return TYDCOMPLEX; + break; + case 'd': + if (!strcmp(Ptok+1, "oublereal")) + return TYDREAL; + break; + case 'i': + if (!strcmp(Ptok+1, "nt")) + return TYSUBR; + if (!strcmp(Ptok+1, "nteger")) + return TYLONG; + if (!strcmp(Ptok+1, "nteger1")) + return TYINT1; + break; + case 'l': + if (!strcmp(Ptok+1, "ogical")) { + checklogical(1); + return TYLOGICAL; + } + if (!strcmp(Ptok+1, "ogical1")) + return TYLOGICAL1; +#ifdef TYQUAD + if (!strcmp(Ptok+1, "ongint")) + return TYQUAD; +#endif + break; + case 'r': + if (!strcmp(Ptok+1, "eal")) { + checkreal(0); + return TYREAL; + } + break; + case 's': + if (!strcmp(Ptok+1, "hortint")) + return TYSHORT; + if (!strcmp(Ptok+1, "hortlogical")) { + checklogical(0); + return TYLOGICAL2; + } + break; + } + bad_type(); + /* NOT REACHED */ + return 0; + } + + static void +wanted(i, what) + int i; + char *what; +{ + if (i != P_anum) { + Ptok[0] = i; + Ptok[1] = 0; + } + fprintf(stderr,"Error: expected %s, not \"%s\" (line %ld of %s)\n", + what, Ptok, Plineno, Pfname); + exit(2); + } + + static int +Ptype(pf) + FILE *pf; +{ + int i, rv; + + i = Ptoken(pf,0); + if (i == ')') + return 0; + if (i != P_anum) + badchar(i); + + rv = 0; + switch(Ptok[0]) { + case 'C': + if (!strcmp(Ptok+1, "_fp")) + rv = TYCOMPLEX+200; + break; + case 'D': + if (!strcmp(Ptok+1, "_fp")) + rv = TYDREAL+200; + break; + case 'E': + case 'R': + if (!strcmp(Ptok+1, "_fp")) + rv = TYREAL+200; + break; + case 'H': + if (!strcmp(Ptok+1, "_fp")) + rv = TYCHAR+200; + break; + case 'I': + if (!strcmp(Ptok+1, "_fp")) + rv = TYLONG+200; + else if (!strcmp(Ptok+1, "1_fp")) + rv = TYINT1+200; +#ifdef TYQUAD + else if (!strcmp(Ptok+1, "8_fp")) + rv = TYQUAD+200; +#endif + break; + case 'J': + if (!strcmp(Ptok+1, "_fp")) + rv = TYSHORT+200; + break; + case 'K': + checklogical(0); + goto Logical; + case 'L': + checklogical(1); + Logical: + if (!strcmp(Ptok+1, "_fp")) + rv = TYLOGICAL+200; + else if (!strcmp(Ptok+1, "1_fp")) + rv = TYLOGICAL1+200; + else if (!strcmp(Ptok+1, "2_fp")) + rv = TYLOGICAL2+200; + break; + case 'S': + if (!strcmp(Ptok+1, "_fp")) + rv = TYSUBR+200; + break; + case 'U': + if (!strcmp(Ptok+1, "_fp")) + rv = TYUNKNOWN+300; + break; + case 'Z': + if (!strcmp(Ptok+1, "_fp")) + rv = TYDCOMPLEX+200; + break; + case 'c': + if (!strcmp(Ptok+1, "har")) + rv = TYCHAR; + else if (!strcmp(Ptok+1, "omplex")) + rv = TYCOMPLEX; + break; + case 'd': + if (!strcmp(Ptok+1, "oublereal")) + rv = TYDREAL; + else if (!strcmp(Ptok+1, "oublecomplex")) + rv = TYDCOMPLEX; + break; + case 'f': + if (!strcmp(Ptok+1, "tnlen")) + rv = TYFTNLEN+100; + break; + case 'i': + if (!strcmp(Ptok+1, "nteger")) + rv = TYLONG; + break; + case 'l': + if (!strcmp(Ptok+1, "ogical")) { + checklogical(1); + rv = TYLOGICAL; + } + else if (!strcmp(Ptok+1, "ogical1")) + rv = TYLOGICAL1; + break; + case 'r': + if (!strcmp(Ptok+1, "eal")) + rv = TYREAL; + break; + case 's': + if (!strcmp(Ptok+1, "hortint")) + rv = TYSHORT; + else if (!strcmp(Ptok+1, "hortlogical")) { + checklogical(0); + rv = TYLOGICAL; + } + break; + case 'v': + if (tnext == tfirst && !strcmp(Ptok+1, "oid")) { + if ((i = Ptoken(pf,0)) != /*(*/ ')') + wanted(i, /*(*/ "\")\""); + return 0; + } + } + if (!rv) + bad_type(); + if (rv < 100 && (i = Ptoken(pf,0)) != '*') + wanted(i, "\"*\""); + if ((i = Ptoken(pf,0)) == P_anum) + i = Ptoken(pf,0); /* skip variable name */ + switch(i) { + case ')': + ungetc(i,pf); + break; + case ',': + break; + default: + wanted(i, "\",\" or \")\""); + } + return rv; + } + + static char * +trimunder() +{ + register char *s; + register int n; + static char buf[128]; + + s = Ptok + strlen(Ptok) - 1; + if (*s != '_') { + fprintf(stderr, + "warning: %s does not end in _ (line %ld of %s)\n", + Ptok, Plineno, Pfname); + return Ptok; + } + if (s[-1] == '_') + s--; + strncpy(buf, Ptok, n = s - Ptok); + buf[n] = 0; + return buf; + } + + static void +Pbadmsg(msg, p) + char *msg; + Extsym *p; +{ + Pbad++; + fprintf(stderr, "%s for %s (line %ld of %s):\n\t", msg, + p->fextname, Plineno, Pfname); + p->arginfo->nargs = -1; + } + + char *Argtype(); + + static void +Pbadret(ftype, p) + int ftype; + Extsym *p; +{ + char buf1[32], buf2[32]; + + Pbadmsg("inconsistent types",p); + fprintf(stderr, "here %s, previously %s\n", + Argtype(ftype+200,buf1), + Argtype(p->extype+200,buf2)); + } + + static void +argverify(ftype, p) + int ftype; + Extsym *p; +{ + Argtypes *at; + register Atype *aty; + int i, j, k; + register int *t, *te; + char buf1[32], buf2[32]; + int type_fixup(); + + at = p->arginfo; + if (at->nargs < 0) + return; + if (p->extype != ftype) { + Pbadret(ftype, p); + return; + } + t = tfirst; + te = tnext; + i = te - t; + if (at->nargs != i) { + j = at->nargs; + Pbadmsg("differing numbers of arguments",p); + fprintf(stderr, "here %d, previously %d\n", + i, j); + return; + } + for(aty = at->atypes; t < te; t++, aty++) { + if (*t == aty->type) + continue; + j = aty->type; + k = *t; + if (k >= 300 || k == j) + continue; + if (j >= 300) { + if (k >= 200) { + if (k == TYUNKNOWN + 200) + continue; + if (j % 100 != k - 200 + && k != TYSUBR + 200 + && j != TYUNKNOWN + 300 + && !type_fixup(at,aty,k)) + goto badtypes; + } + else if (j % 100 % TYSUBR != k % TYSUBR + && !type_fixup(at,aty,k)) + goto badtypes; + } + else if (k < 200 || j < 200) + goto badtypes; + else if (k == TYUNKNOWN+200) + continue; + else if (j != TYUNKNOWN+200) + { + badtypes: + Pbadmsg("differing calling sequences",p); + i = t - tfirst + 1; + fprintf(stderr, + "arg %d: here %s, prevously %s\n", + i, Argtype(k,buf1), Argtype(j,buf2)); + return; + } + /* We've subsequently learned the right type, + as in the call on zoo below... + + subroutine foo(x, zap) + external zap + call goo(zap) + x = zap(3) + call zoo(zap) + end + */ + aty->type = k; + at->changes = 1; + } + } + + static void +newarg(ftype, p) + int ftype; + Extsym *p; +{ + Argtypes *at; + register Atype *aty; + register int *t, *te; + int i, k; + + if (p->extstg == STGCOMMON) { + Pnotboth(p); + return; + } + p->extstg = STGEXT; + p->extype = ftype; + p->exproto = 1; + t = tfirst; + te = tnext; + i = te - t; + k = sizeof(Argtypes) + (i-1)*sizeof(Atype); + at = p->arginfo = (Argtypes *)gmem(k,1); + at->dnargs = at->nargs = i; + at->defined = at->changes = 0; + for(aty = at->atypes; t < te; aty++) { + aty->type = *t++; + aty->cp = 0; + } + } + + static int +Pfile(fname) + char *fname; +{ + char *s; + int ftype, i; + FILE *pf; + Extsym *p; + + for(s = fname; *s; s++); + if (s - fname < 2 + || s[-2] != '.' + || (s[-1] != 'P' && s[-1] != 'p')) + return 0; + + if (!(pf = fopen(fname, textread))) { + fprintf(stderr, "can't open %s\n", fname); + exit(2); + } + Pfname = fname; + Plineno = 1; + if (!Pct[' ']) { + for(s = " \t\n\r\v\f"; *s; s++) + Pct[*s] = P_space; + for(s = "*,();"; *s; s++) + Pct[*s] = P_delim; + for(i = '0'; i <= '9'; i++) + Pct[i] = P_anum; + for(s = "abcdefghijklmnopqrstuvwxyz"; i = *s; s++) + Pct[i] = Pct[i+'A'-'a'] = P_anum; + Pct['_'] = P_anum; + Pct['/'] = P_slash; + } + + for(;;) { + if (!(i = Ptoken(pf,1))) + break; + if (i != P_anum + || !strcmp(Ptok, "extern") && (i = Ptoken(pf,0)) != P_anum) + badchar(i); + ftype = Pftype(); + getname: + if ((i = Ptoken(pf,0)) != P_anum) + badchar(i); + p = mkext(trimunder(), Ptok); + + if ((i = Ptoken(pf,0)) != '(') + badchar(i); + tnext = tfirst; + while(i = Ptype(pf)) { + if (tnext >= tlast) + trealloc(); + *tnext++ = i; + } + if (p->arginfo) { + argverify(ftype, p); + if (p->arginfo->nargs < 0) + newarg(ftype, p); + } + else + newarg(ftype, p); + p->arginfo->defined = 1; + i = Ptoken(pf,0); + switch(i) { + case ';': + break; + case ',': + goto getname; + default: + wanted(i, "\";\" or \",\""); + } + } + fclose(pf); + return 1; + } + + void +read_Pfiles(ffiles) + char **ffiles; +{ + char **f1files, **f1files0, *s; + int k; + register Extsym *e, *ee; + register Argtypes *at; + extern int retcode; + + f1files0 = f1files = ffiles; + while(s = *ffiles++) + if (!Pfile(s)) + *f1files++ = s; + if (Pbad) + retcode = 8; + if (tfirst) { + free((char *)tfirst); + /* following should be unnecessary, as we won't be back here */ + tfirst = tnext = tlast = 0; + tmax = 0; + } + *f1files = 0; + if (f1files == f1files0) + f1files[1] = 0; + + k = 0; + ee = nextext; + for (e = extsymtab; e < ee; e++) + if (e->extstg == STGEXT + && (at = e->arginfo)) { + if (at->nargs < 0 || at->changes) + k++; + at->changes = 2; + } + if (k) { + fprintf(diagfile, + "%d prototype%s updated while reading prototypes.\n", k, + k > 1 ? "s" : ""); + } + fflush(diagfile); + } diff --git a/usr.bin/f2c/proc.c b/usr.bin/f2c/proc.c new file mode 100644 index 000000000000..ca3043e61ebb --- /dev/null +++ b/usr.bin/f2c/proc.c @@ -0,0 +1,1602 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "names.h" +#include "output.h" +#include "p1defs.h" + +#define EXNULL (union Expression *)0 + +LOCAL dobss(), docomleng(), docommon(), doentry(), + epicode(), nextarg(), retval(); + +static char Blank[] = BLANKCOMMON; + + static char *postfix[] = { "g", "h", "i", +#ifdef TYQUAD + "j", +#endif + "r", "d", "c", "z", "g", "h", "i" }; + + chainp new_procs; + int prev_proc, proc_argchanges, proc_protochanges; + + void +changedtype(q) + Namep q; +{ + char buf[200]; + int qtype, type1; + register Extsym *e; + Argtypes *at; + + if (q->vtypewarned) + return; + q->vtypewarned = 1; + qtype = q->vtype; + e = &extsymtab[q->vardesc.varno]; + if (!(at = e->arginfo)) { + if (!e->exused) + return; + } + else if (at->changes & 2 && qtype != TYUNKNOWN && !at->defined) + proc_protochanges++; + type1 = e->extype; + if (type1 == TYUNKNOWN) + return; + if (qtype == TYUNKNOWN) + /* e.g., + subroutine foo + end + external foo + call goo(foo) + end + */ + return; + sprintf(buf, "%.90s: inconsistent declarations:\n\ + here %s%s, previously %s%s.", q->fvarname, ftn_types[qtype], + qtype == TYSUBR ? "" : " function", + ftn_types[type1], type1 == TYSUBR ? "" : " function"); + warn(buf); + } + + void +unamstring(q, s) + register Addrp q; + register char *s; +{ + register int k; + register char *t; + + k = strlen(s); + if (k < IDENT_LEN) { + q->uname_tag = UNAM_IDENT; + t = q->user.ident; + } + else { + q->uname_tag = UNAM_CHARP; + q->user.Charp = t = mem(k+1, 0); + } + strcpy(t, s); + } + + static void +fix_entry_returns() /* for multiple entry points */ +{ + Addrp a; + int i; + struct Entrypoint *e; + Namep np; + + e = entries = (struct Entrypoint *)revchain((chainp)entries); + allargs = revchain(allargs); + if (!multitype) + return; + + /* TYLOGICAL should have been turned into TYLONG or TYSHORT by now */ + + for(i = TYINT1; i <= TYLOGICAL; i++) + if (a = xretslot[i]) + sprintf(a->user.ident, "(*ret_val).%s", + postfix[i-TYINT1]); + + do { + np = e->enamep; + switch(np->vtype) { + case TYINT1: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYREAL: + case TYDREAL: + case TYCOMPLEX: + case TYDCOMPLEX: + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + np->vstg = STGARG; + } + } + while(e = e->entnextp); + } + + static void +putentries(outfile) /* put out wrappers for multiple entries */ + FILE *outfile; +{ + char base[IDENT_LEN]; + struct Entrypoint *e; + Namep *A, *Ae, *Ae1, **Alp, *a, **a1, np; + chainp args, lengths, length_comp(); + void listargs(), list_arg_types(); + int i, k, mt, nL, type; + extern char *dfltarg[], **dfltproc; + + e = entries; + if (!e->enamep) /* only possible with erroneous input */ + return; + nL = (nallargs + nallchargs) * sizeof(Namep *); + A = (Namep *)ckalloc(nL + nallargs*sizeof(Namep **)); + Ae = A + nallargs; + Alp = (Namep **)(Ae1 = Ae + nallchargs); + i = k = 0; + for(a1 = Alp, args = allargs; args; a1++, args = args->nextp) { + np = (Namep)args->datap; + if (np->vtype == TYCHAR && np->vclass != CLPROC) + *a1 = &Ae[i++]; + } + + mt = multitype; + multitype = 0; + sprintf(base, "%s0_", e->enamep->cvarname); + do { + np = e->enamep; + lengths = length_comp(e, 0); + proctype = type = np->vtype; + if (protofile) + protowrite(protofile, type, np->cvarname, e, lengths); + nice_printf(outfile, "\n%s ", c_type_decl(type, 1)); + nice_printf(outfile, "%s", np->cvarname); + if (!Ansi) { + listargs(outfile, e, 0, lengths); + nice_printf(outfile, "\n"); + } + list_arg_types(outfile, e, lengths, 0, "\n"); + nice_printf(outfile, "{\n"); + frchain(&lengths); + next_tab(outfile); + if (mt) + nice_printf(outfile, + "Multitype ret_val;\n%s(%d, &ret_val", + base, k); /*)*/ + else if (ISCOMPLEX(type)) + nice_printf(outfile, "%s(%d,%s", base, k, + xretslot[type]->user.ident); /*)*/ + else if (type == TYCHAR) + nice_printf(outfile, + "%s(%d, ret_val, ret_val_len", base, k); /*)*/ + else + nice_printf(outfile, "return %s(%d", base, k); /*)*/ + k++; + memset((char *)A, 0, nL); + for(args = e->arglist; args; args = args->nextp) { + np = (Namep)args->datap; + A[np->argno] = np; + if (np->vtype == TYCHAR && np->vclass != CLPROC) + *Alp[np->argno] = np; + } + args = allargs; + for(a = A; a < Ae; a++, args = args->nextp) + nice_printf(outfile, ", %s", (np = *a) + ? np->cvarname + : ((Namep)args->datap)->vclass == CLPROC + ? dfltproc[((Namep)args->datap)->vtype] + : dfltarg[((Namep)args->datap)->vtype]); + for(; a < Ae1; a++) + if (np = *a) + nice_printf(outfile, ", %s_len", np->fvarname); + else + nice_printf(outfile, ", (ftnint)0"); + nice_printf(outfile, /*(*/ ");\n"); + if (mt) { + if (type == TYCOMPLEX) + nice_printf(outfile, + "r_v->r = ret_val.c.r; r_v->i = ret_val.c.i;\n"); + else if (type == TYDCOMPLEX) + nice_printf(outfile, + "r_v->r = ret_val.z.r; r_v->i = ret_val.z.i;\n"); + else if (type <= TYLOGICAL) + nice_printf(outfile, "return ret_val.%s;\n", + postfix[type-TYINT1]); + } + nice_printf(outfile, "}\n"); + prev_tab(outfile); + } + while(e = e->entnextp); + free((char *)A); + } + + static void +entry_goto(outfile) + FILEP outfile; +{ + struct Entrypoint *e = entries; + int k = 0; + + nice_printf(outfile, "switch(n__) {\n"); + next_tab(outfile); + while(e = e->entnextp) + nice_printf(outfile, "case %d: goto %s;\n", ++k, + user_label((long)(extsymtab - e->entryname - 1))); + nice_printf(outfile, "}\n\n"); + prev_tab(outfile); + } + +/* start a new procedure */ + +newproc() +{ + if(parstate != OUTSIDE) + { + execerr("missing end statement", CNULL); + endproc(); + } + + parstate = INSIDE; + procclass = CLMAIN; /* default */ +} + + static void +zap_changes() +{ + register chainp cp; + register Argtypes *at; + + /* arrange to get correct count of prototypes that would + change by running f2c again */ + + if (prev_proc && proc_argchanges) + proc_protochanges++; + prev_proc = proc_argchanges = 0; + for(cp = new_procs; cp; cp = cp->nextp) + if (at = ((Namep)cp->datap)->arginfo) + at->changes &= ~1; + frchain(&new_procs); + } + +/* end of procedure. generate variables, epilogs, and prologs */ + +endproc() +{ + struct Labelblock *lp; + Extsym *ext; + + if(parstate < INDATA) + enddcl(); + if(ctlstack >= ctls) + err("DO loop or BLOCK IF not closed"); + for(lp = labeltab ; lp < labtabend ; ++lp) + if(lp->stateno!=0 && lp->labdefined==NO) + errstr("missing statement label %s", + convic(lp->stateno) ); + +/* Save copies of the common variables in extptr -> allextp */ + + for (ext = extsymtab; ext < nextext; ext++) + if (ext -> extstg == STGCOMMON && ext -> extp) { + extern int usedefsforcommon; + +/* Write out the abbreviations for common block reference */ + + copy_data (ext -> extp); + if (usedefsforcommon) { + wr_abbrevs (c_file, 1, ext -> extp); + ext -> used_here = 1; + } + else + ext -> extp = CHNULL; + + } + + if (nentry > 1) + fix_entry_returns(); + epicode(); + donmlist(); + dobss(); + start_formatting (); + if (nentry > 1) + putentries(c_file); + + zap_changes(); + procinit(); /* clean up for next procedure */ +} + + + +/* End of declaration section of procedure. Allocate storage. */ + +enddcl() +{ + register struct Entrypoint *ep; + struct Entrypoint *ep0; + extern void freetemps(); + chainp cp; + extern char *err_proc; + static char comblks[] = "common blocks"; + + err_proc = comblks; + docommon(); + +/* Now the hash table entries for fields of common blocks have STGCOMMON, + vdcldone, voffset, and varno. And the common blocks themselves have + their full sizes in extleng. */ + + err_proc = "equivalences"; + doequiv(); + + err_proc = comblks; + docomleng(); + +/* This implies that entry points in the declarations are buffered in + entries but not written out */ + + err_proc = "entries"; + if (ep = ep0 = (struct Entrypoint *)revchain((chainp)entries)) { + /* entries could be 0 in case of an error */ + do doentry(ep); + while(ep = ep->entnextp); + entries = (struct Entrypoint *)revchain((chainp)ep0); + } + + err_proc = 0; + parstate = INEXEC; + p1put(P1_PROCODE); + freetemps(); + if (earlylabs) { + for(cp = earlylabs = revchain(earlylabs); cp; cp = cp->nextp) + p1_label((long)cp->datap); + frchain(&earlylabs); + } + p1_line_number(lineno); /* for files that start with a MAIN program */ + /* that starts with an executable statement */ +} + +/* ROUTINES CALLED WHEN ENCOUNTERING ENTRY POINTS */ + +/* Main program or Block data */ + +startproc(progname, class) +Extsym * progname; +int class; +{ + register struct Entrypoint *p; + + p = ALLOC(Entrypoint); + if(class == CLMAIN) { + puthead(CNULL, CLMAIN); + if (progname) + strcpy (main_alias, progname->cextname); + } else + puthead(CNULL, CLBLOCK); + if(class == CLMAIN) + newentry( mkname(" MAIN"), 0 )->extinit = 1; + p->entryname = progname; + entries = p; + + procclass = class; + fprintf(diagfile, " %s", (class==CLMAIN ? "MAIN" : "BLOCK DATA") ); + if(progname) { + fprintf(diagfile, " %s", progname->fextname); + procname = progname->cextname; + } + fprintf(diagfile, ":\n"); + fflush(diagfile); +} + +/* subroutine or function statement */ + +Extsym *newentry(v, substmsg) + register Namep v; + int substmsg; +{ + register Extsym *p; + char buf[128], badname[64]; + static int nbad = 0; + static char already[] = "external name already used"; + + p = mkext(v->fvarname, addunder(v->cvarname)); + + if(p->extinit || ! ONEOF(p->extstg, M(STGUNKNOWN)|M(STGEXT)) ) + { + sprintf(badname, "%s_bad%d", v->fvarname, ++nbad); + if (substmsg) { + sprintf(buf,"%s\n\tsubstituting \"%s\"", + already, badname); + dclerr(buf, v); + } + else + dclerr(already, v); + p = mkext(v->fvarname, badname); + } + v->vstg = STGAUTO; + v->vprocclass = PTHISPROC; + v->vclass = CLPROC; + if (p->extstg == STGEXT) + prev_proc = 1; + else + p->extstg = STGEXT; + p->extinit = YES; + v->vardesc.varno = p - extsymtab; + return(p); +} + + +entrypt(class, type, length, entry, args) +int class, type; +ftnint length; +Extsym *entry; +chainp args; +{ + register Namep q; + register struct Entrypoint *p; + + if(class != CLENTRY) + puthead( procname = entry->cextname, class); + else + fprintf(diagfile, " entry "); + fprintf(diagfile, " %s:\n", entry->fextname); + fflush(diagfile); + q = mkname(entry->fextname); + if (type == TYSUBR) + q->vstg = STGEXT; + + type = lengtype(type, length); + if(class == CLPROC) + { + procclass = CLPROC; + proctype = type; + procleng = type == TYCHAR ? length : 0; + } + + p = ALLOC(Entrypoint); + + p->entnextp = entries; + entries = p; + + p->entryname = entry; + p->arglist = revchain(args); + p->enamep = q; + + if(class == CLENTRY) + { + class = CLPROC; + if(proctype == TYSUBR) + type = TYSUBR; + } + + q->vclass = class; + q->vprocclass = 0; + settype(q, type, length); + q->vprocclass = PTHISPROC; + /* hold all initial entry points till end of declarations */ + if(parstate >= INDATA) + doentry(p); +} + +/* generate epilogs */ + +/* epicode -- write out the proper function return mechanism at the end of + the procedure declaration. Handles multiple return value types, as + well as cooercion into the proper value */ + +LOCAL epicode() +{ + extern int lastwasbranch; + + if(procclass==CLPROC) + { + if(proctype==TYSUBR) + { + +/* Return a zero only when the alternate return mechanism has been + specified in the function header */ + + if ((substars || Ansi) && lastwasbranch != YES) + p1_subr_ret (ICON(0)); + } + else if (!multitype && lastwasbranch != YES) + retval(proctype); + } + else if (procclass == CLMAIN && Ansi && lastwasbranch != YES) + p1_subr_ret (ICON(0)); + lastwasbranch = NO; +} + + +/* generate code to return value of type t */ + +LOCAL retval(t) +register int t; +{ + register Addrp p; + + switch(t) + { + case TYCHAR: + case TYCOMPLEX: + case TYDCOMPLEX: + break; + + case TYLOGICAL: + t = tylogical; + case TYINT1: + case TYADDR: + case TYSHORT: + case TYLONG: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYREAL: + case TYDREAL: + case TYLOGICAL1: + case TYLOGICAL2: + p = (Addrp) cpexpr((expptr)retslot); + p->vtype = t; + p1_subr_ret (mkconv (t, fixtype((expptr)p))); + break; + + default: + badtype("retval", t); + } +} + + +/* Do parameter adjustments */ + +procode(outfile) +FILE *outfile; +{ + prolog(outfile, allargs); + + if (nentry > 1) + entry_goto(outfile); + } + +/* Finish bound computations now that all variables are declared. + * This used to be in setbound(), but under -u the following incurred + * an erroneous error message: + * subroutine foo(x,n) + * real x(n) + * integer n + */ + + static void +dim_finish(v) + Namep v; +{ + register struct Dimblock *p; + register expptr q; + register int i, nd; + extern expptr make_int_expr(); + + p = v->vdim; + v->vdimfinish = 0; + nd = p->ndim; + doin_setbound = 1; + for(i = 0; i < nd; i++) + if (q = p->dims[i].dimexpr) { + q = p->dims[i].dimexpr = make_int_expr(putx(fixtype(q))); + if (!ONEOF(q->headblock.vtype, MSKINT|MSKREAL)) + errstr("bad dimension type for %.70s", + v->fvarname); + } + if (q = p->basexpr) + p->basexpr = make_int_expr(putx(fixtype(q))); + doin_setbound = 0; + } + + static void +duparg(q) + Namep q; +{ errstr("duplicate argument %.80s", q->fvarname); } + +/* + manipulate argument lists (allocate argument slot positions) + * keep track of return types and labels + */ + +LOCAL doentry(ep) +struct Entrypoint *ep; +{ + register int type; + register Namep np; + chainp p, p1; + register Namep q; + Addrp mkarg(), rs; + int it, k; + extern char dflttype[26]; + Extsym *entryname = ep->entryname; + + if (++nentry > 1) + p1_label((long)(extsymtab - entryname - 1)); + +/* The main program isn't allowed to have parameters, so any given + parameters are ignored */ + + if(procclass == CLMAIN || procclass == CLBLOCK) + return; + +/* So now we're working with something other than CLMAIN or CLBLOCK. + Determine the type of its return value. */ + + impldcl( np = mkname(entryname->fextname) ); + type = np->vtype; + proc_argchanges = prev_proc && type != entryname->extype; + entryname->extseen = 1; + if(proctype == TYUNKNOWN) + if( (proctype = type) == TYCHAR) + procleng = np->vleng ? np->vleng->constblock.Const.ci + : (ftnint) (-1); + + if(proctype == TYCHAR) + { + if(type != TYCHAR) + err("noncharacter entry of character function"); + +/* Functions returning type char can only have multiple entries if all + entries return the same length */ + + else if( (np->vleng ? np->vleng->constblock.Const.ci : + (ftnint) (-1)) != procleng) + err("mismatched character entry lengths"); + } + else if(type == TYCHAR) + err("character entry of noncharacter function"); + else if(type != proctype) + multitype = YES; + if(rtvlabel[type] == 0) + rtvlabel[type] = newlabel(); + ep->typelabel = rtvlabel[type]; + + if(type == TYCHAR) + { + if(chslot < 0) + { + chslot = nextarg(TYADDR); + chlgslot = nextarg(TYLENG); + } + np->vstg = STGARG; + +/* Put a new argument in the function, one which will hold the result of + a character function. This will have to be named sometime, probably in + mkarg(). */ + + if(procleng < 0) { + np->vleng = (expptr) mkarg(TYLENG, chlgslot); + np->vleng->addrblock.uname_tag = UNAM_IDENT; + strcpy (np -> vleng -> addrblock.user.ident, + new_func_length()); + } + if (!xretslot[TYCHAR]) { + xretslot[TYCHAR] = rs = + autovar(0, type, ISCONST(np->vleng) + ? np->vleng : ICON(0), ""); + strcpy(rs->user.ident, "ret_val"); + } + } + +/* Handle a complex return type -- declare a new parameter (pointer to + a complex value) */ + + else if( ISCOMPLEX(type) ) { + if (!xretslot[type]) + xretslot[type] = + autovar(0, type, EXNULL, " ret_val"); + /* the blank is for use in out_addr */ + np->vstg = STGARG; + if(cxslot < 0) + cxslot = nextarg(TYADDR); + } + else if (type != TYSUBR) { + if (type == TYUNKNOWN) { + dclerr("untyped function", np); + proctype = type = np->vtype = + dflttype[letter(np->fvarname[0])]; + } + if (!xretslot[type]) + xretslot[type] = retslot = + autovar(1, type, EXNULL, " ret_val"); + /* the blank is for use in out_addr */ + np->vstg = STGAUTO; + } + + for(p = ep->arglist ; p ; p = p->nextp) + if(! (( q = (Namep) (p->datap) )->vknownarg) ) { + q->vknownarg = 1; + q->vardesc.varno = nextarg(TYADDR); + allargs = mkchain((char *)q, allargs); + q->argno = nallargs++; + } + else if (nentry == 1) + duparg(q); + else for(p1 = ep->arglist ; p1 != p; p1 = p1->nextp) + if ((Namep)p1->datap == q) + duparg(q); + + k = 0; + for(p = ep->arglist ; p ; p = p->nextp) { + if(! (( q = (Namep) (p->datap) )->vdcldone) ) + { + impldcl(q); + q->vdcldone = YES; + if(q->vtype == TYCHAR) + { + +/* If we don't know the length of a char*(*) (i.e. a string), we must add + in this additional length argument. */ + + ++nallchargs; + if (q->vclass == CLPROC) + nallchargs--; + else if (q->vleng == NULL) { + /* character*(*) */ + q->vleng = (expptr) + mkarg(TYLENG, nextarg(TYLENG) ); + unamstring((Addrp)q->vleng, + new_arg_length(q)); + } + } + } + if (q->vdimfinish) + dim_finish(q); + if (q->vtype == TYCHAR && q->vclass != CLPROC) + k++; + } + + if (entryname->extype != type) + changedtype(np); + + /* save information for checking consistency of arg lists */ + + it = infertypes; + if (entryname->exproto) + infertypes = 1; + save_argtypes(ep->arglist, &entryname->arginfo, &np->arginfo, + 0, np->fvarname, STGEXT, k, np->vtype, 2); + infertypes = it; +} + + + +LOCAL nextarg(type) +int type; +{ return(lastargslot++); } + + LOCAL +dim_check(q) + Namep q; +{ + register struct Dimblock *vdim = q->vdim; + + if(!vdim->nelt || !ISICON(vdim->nelt)) + dclerr("adjustable dimension on non-argument", q); + else if (vdim->nelt->constblock.Const.ci <= 0) + dclerr("nonpositive dimension", q); + } + +LOCAL dobss() +{ + register struct Hashentry *p; + register Namep q; + int qstg, qclass, qtype; + Extsym *e; + + for(p = hashtab ; p<lasthash ; ++p) + if(q = p->varp) + { + qstg = q->vstg; + qtype = q->vtype; + qclass = q->vclass; + + if( (qclass==CLUNKNOWN && qstg!=STGARG) || + (qclass==CLVAR && qstg==STGUNKNOWN) ) { + if (!(q->vis_assigned | q->vimpldovar)) + warn1("local variable %s never used", + q->fvarname); + } + else if(qclass==CLVAR && qstg==STGBSS) + { ; } + +/* Give external procedures the proper storage class */ + + else if(qclass==CLPROC && q->vprocclass==PEXTERNAL + && qstg!=STGARG) { + e = mkext(q->fvarname,addunder(q->cvarname)); + e->extstg = STGEXT; + q->vardesc.varno = e - extsymtab; + if (e->extype != qtype) + changedtype(q); + } + if(qclass==CLVAR) { + if (qstg != STGARG && q->vdim) + dim_check(q); + } /* if qclass == CLVAR */ + } + +} + + + +donmlist() +{ + register struct Hashentry *p; + register Namep q; + + for(p=hashtab; p<lasthash; ++p) + if( (q = p->varp) && q->vclass==CLNAMELIST) + namelist(q); +} + + +/* iarrlen -- Returns the size of the array in bytes, or -1 */ + +ftnint iarrlen(q) +register Namep q; +{ + ftnint leng; + + leng = typesize[q->vtype]; + if(leng <= 0) + return(-1); + if(q->vdim) + if( ISICON(q->vdim->nelt) ) + leng *= q->vdim->nelt->constblock.Const.ci; + else return(-1); + if(q->vleng) + if( ISICON(q->vleng) ) + leng *= q->vleng->constblock.Const.ci; + else return(-1); + return(leng); +} + +namelist(np) +Namep np; +{ + register chainp q; + register Namep v; + int y; + + if (!np->visused) + return; + y = 0; + + for(q = np->varxptr.namelist ; q ; q = q->nextp) + { + vardcl( v = (Namep) (q->datap) ); + if( !ONEOF(v->vstg, MSKSTATIC) ) + dclerr("may not appear in namelist", v); + else { + v->vnamelist = 1; + v->visused = 1; + v->vsave = 1; + y = 1; + } + np->visused = y; + } +} + +/* docommon -- called at the end of procedure declarations, before + equivalences and the procedure body */ + +LOCAL docommon() +{ + register Extsym *extptr; + register chainp q, q1; + struct Dimblock *t; + expptr neltp; + register Namep comvar; + ftnint size; + int i, k, pref, type; + extern int type_pref[]; + + for(extptr = extsymtab ; extptr<nextext ; ++extptr) + if (extptr->extstg == STGCOMMON && (q = extptr->extp)) { + +/* If a common declaration also had a list of variables ... */ + + q = extptr->extp = revchain(q); + pref = 1; + for(k = TYCHAR; q ; q = q->nextp) + { + comvar = (Namep) (q->datap); + + if(comvar->vdcldone == NO) + vardcl(comvar); + type = comvar->vtype; + if (pref < type_pref[type]) + pref = type_pref[k = type]; + if(extptr->extleng % typealign[type] != 0) { + dclerr("common alignment", comvar); + --nerr; /* don't give bad return code for this */ +#if 0 + extptr->extleng = roundup(extptr->extleng, typealign[type]); +#endif + } /* if extptr -> extleng % */ + +/* Set the offset into the common block */ + + comvar->voffset = extptr->extleng; + comvar->vardesc.varno = extptr - extsymtab; + if(type == TYCHAR) + size = comvar->vleng->constblock.Const.ci; + else + size = typesize[type]; + if(t = comvar->vdim) + if( (neltp = t->nelt) && ISCONST(neltp) ) + size *= neltp->constblock.Const.ci; + else + dclerr("adjustable array in common", comvar); + +/* Adjust the length of the common block so far */ + + extptr->extleng += size; + } /* for */ + + extptr->extype = k; + +/* Determine curno and, if new, save this identifier chain */ + + q1 = extptr->extp; + for (q = extptr->allextp, i = 0; q; i++, q = q->nextp) + if (struct_eq((chainp)q->datap, q1)) + break; + if (q) + extptr->curno = extptr->maxno - i; + else { + extptr->curno = ++extptr->maxno; + extptr->allextp = mkchain((char *)extptr->extp, + extptr->allextp); + } + } /* if extptr -> extstg == STGCOMMON */ + +/* Now the hash table entries have STGCOMMON, vdcldone, voffset, and + varno. And the common block itself has its full size in extleng. */ + +} /* docommon */ + + +/* copy_data -- copy the Namep entries so they are available even after + the hash table is empty */ + +copy_data (list) +chainp list; +{ + for (; list; list = list -> nextp) { + Namep namep = ALLOC (Nameblock); + int size, nd, i; + struct Dimblock *dp; + + cpn(sizeof(struct Nameblock), list->datap, (char *)namep); + namep->fvarname = strcpy(gmem(strlen(namep->fvarname)+1,0), + namep->fvarname); + namep->cvarname = strcmp(namep->fvarname, namep->cvarname) + ? strcpy(gmem(strlen(namep->cvarname)+1,0), namep->cvarname) + : namep->fvarname; + if (namep -> vleng) + namep -> vleng = (expptr) cpexpr (namep -> vleng); + if (namep -> vdim) { + nd = namep -> vdim -> ndim; + size = sizeof(int) + (3 + 2 * nd) * sizeof (expptr); + dp = (struct Dimblock *) ckalloc (size); + cpn(size, (char *)namep->vdim, (char *)dp); + namep -> vdim = dp; + dp->nelt = (expptr)cpexpr(dp->nelt); + for (i = 0; i < nd; i++) { + dp -> dims[i].dimsize = (expptr) cpexpr (dp -> dims[i].dimsize); + } /* for */ + } /* if */ + list -> datap = (char *) namep; + } /* for */ +} /* copy_data */ + + + +LOCAL docomleng() +{ + register Extsym *p; + + for(p = extsymtab ; p < nextext ; ++p) + if(p->extstg == STGCOMMON) + { + if(p->maxleng!=0 && p->extleng!=0 && p->maxleng!=p->extleng + && strcmp(Blank, p->cextname) ) + warn1("incompatible lengths for common block %.60s", + p->fextname); + if(p->maxleng < p->extleng) + p->maxleng = p->extleng; + p->extleng = 0; + } +} + + +/* ROUTINES DEALING WITH AUTOMATIC AND TEMPORARY STORAGE */ + +frtemp(p) +Addrp p; +{ + /* put block on chain of temps to be reclaimed */ + holdtemps = mkchain((char *)p, holdtemps); +} + + void +freetemps() +{ + register chainp p, p1; + register Addrp q; + register int t; + + p1 = holdtemps; + while(p = p1) { + q = (Addrp)p->datap; + t = q->vtype; + if (t == TYCHAR && q->varleng != 0) { + /* restore clobbered character string lengths */ + frexpr(q->vleng); + q->vleng = ICON(q->varleng); + } + p1 = p->nextp; + p->nextp = templist[t]; + templist[t] = p; + } + holdtemps = 0; + } + +/* allocate an automatic variable slot for each of nelt variables */ + +Addrp autovar(nelt0, t, lengp, name) +register int nelt0, t; +expptr lengp; +char *name; +{ + ftnint leng; + register Addrp q; + char *temp_name (); + register int nelt = nelt0 > 0 ? nelt0 : 1; + extern char *av_pfix[]; + + if(t == TYCHAR) + if( ISICON(lengp) ) + leng = lengp->constblock.Const.ci; + else { + Fatal("automatic variable of nonconstant length"); + } + else + leng = typesize[t]; + + q = ALLOC(Addrblock); + q->tag = TADDR; + q->vtype = t; + if(t == TYCHAR) + { + q->vleng = ICON(leng); + q->varleng = leng; + } + q->vstg = STGAUTO; + q->ntempelt = nelt; + q->isarray = (nelt > 1); + q->memoffset = ICON(0); + + /* kludge for nls so we can have ret_val rather than ret_val_4 */ + if (*name == ' ') + unamstring(q, name); + else { + q->uname_tag = UNAM_IDENT; + temp_name(av_pfix[t], ++autonum[t], q->user.ident); + } + if (nelt0 > 0) + declare_new_addr (q); + return(q); +} + + +/* Returns a temporary of the appropriate type. Will reuse existing + temporaries when possible */ + +Addrp mktmpn(nelt, type, lengp) +int nelt; +register int type; +expptr lengp; +{ + ftnint leng; + chainp p, oldp; + register Addrp q; + + if(type==TYUNKNOWN || type==TYERROR) + badtype("mktmpn", type); + + if(type==TYCHAR) + if(lengp && ISICON(lengp) ) + leng = lengp->constblock.Const.ci; + else { + err("adjustable length"); + return( (Addrp) errnode() ); + } + else if (type > TYCHAR || type < TYADDR) { + erri("mktmpn: unexpected type %d", type); + exit(1); + } +/* + * if a temporary of appropriate shape is on the templist, + * remove it from the list and return it + */ + for(oldp=CHNULL, p=templist[type]; p ; oldp=p, p=p->nextp) + { + q = (Addrp) (p->datap); + if(q->ntempelt==nelt && + (type!=TYCHAR || q->vleng->constblock.Const.ci==leng) ) + { + if(oldp) + oldp->nextp = p->nextp; + else + templist[type] = p->nextp; + free( (charptr) p); + return(q); + } + } + q = autovar(nelt, type, lengp, ""); + return(q); +} + + + + +/* mktmp -- create new local variable; call it something like name + lengp is taken directly, not copied */ + +Addrp mktmp(type, lengp) +int type; +expptr lengp; +{ + Addrp rv; + /* arrange for temporaries to be recycled */ + /* at the end of this statement... */ + rv = mktmpn(1,type,lengp); + frtemp((Addrp)cpexpr((expptr)rv)); + return rv; +} + +/* mktmp0 omits frtemp() */ +Addrp mktmp0(type, lengp) +int type; +expptr lengp; +{ + Addrp rv; + /* arrange for temporaries to be recycled */ + /* when this Addrp is freed */ + rv = mktmpn(1,type,lengp); + rv->istemp = YES; + return rv; +} + +/* VARIOUS ROUTINES FOR PROCESSING DECLARATIONS */ + +/* comblock -- Declare a new common block. Input parameters name the block; + s will be NULL if the block is unnamed */ + +Extsym *comblock(s) + register char *s; +{ + Extsym *p; + register char *t; + register int c, i; + char cbuf[256], *s0; + +/* Give the unnamed common block a unique name */ + + if(*s == 0) + p = mkext(Blank,Blank); + else { + s0 = s; + t = cbuf; + for(i = 0; c = *t = *s++; t++) + if (c == '_') + i = 1; + if (i) + *t++ = '_'; + t[0] = '_'; + t[1] = 0; + p = mkext(s0,cbuf); + } + if(p->extstg == STGUNKNOWN) + p->extstg = STGCOMMON; + else if(p->extstg != STGCOMMON) + { + errstr("%.68s cannot be a common block name", s); + return(0); + } + + return( p ); +} + + +/* incomm -- add a new variable to a common declaration */ + +incomm(c, v) +Extsym *c; +Namep v; +{ + if (!c) + return; + if(v->vstg != STGUNKNOWN && !v->vimplstg) + dclerr(v->vstg == STGARG + ? "dummy arguments cannot be in common" + : "incompatible common declaration", v); + else + { + v->vstg = STGCOMMON; + c->extp = mkchain((char *)v, c->extp); + } +} + + + + +/* settype -- set the type or storage class of a Namep object. If + v -> vstg == STGUNKNOWN && type < 0, attempt to reset vstg to be + -type. This function will not change any earlier definitions in v, + in will only attempt to fill out more information give the other params */ + +settype(v, type, length) +register Namep v; +register int type; +register ftnint length; +{ + int type1; + + if(type == TYUNKNOWN) + return; + + if(type==TYSUBR && v->vtype!=TYUNKNOWN && v->vstg==STGARG) + { + v->vtype = TYSUBR; + frexpr(v->vleng); + v->vleng = 0; + v->vimpltype = 0; + } + else if(type < 0) /* storage class set */ + { + if(v->vstg == STGUNKNOWN) + v->vstg = - type; + else if(v->vstg != -type) + dclerr("incompatible storage declarations", v); + } + else if(v->vtype == TYUNKNOWN || v->vimpltype && v->vtype != type) + { + if( (v->vtype = lengtype(type, length))==TYCHAR ) + if (length>=0) + v->vleng = ICON(length); + else if (parstate >= INDATA) + v->vleng = ICON(1); /* avoid a memory fault */ + v->vimpltype = 0; + + if (v->vclass == CLPROC) { + if (v->vstg == STGEXT + && (type1 = extsymtab[v->vardesc.varno].extype) + && type1 != v->vtype) + changedtype(v); + else if (v->vprocclass == PTHISPROC + && (parstate >= INDATA + || procclass == CLMAIN) + && !xretslot[type]) { + xretslot[type] = autovar(ONEOF(type, + MSKCOMPLEX|MSKCHAR) ? 0 : 1, type, + v->vleng, " ret_val"); + if (procclass == CLMAIN) + errstr( + "illegal use of %.60s (main program name)", + v->fvarname); + /* not completely right, but enough to */ + /* avoid memory faults; we won't */ + /* emit any C as we have illegal Fortran */ + } + } + } + else if(v->vtype!=type) { + incompat: + dclerr("incompatible type declarations", v); + } + else if (type==TYCHAR) + if (v->vleng && v->vleng->constblock.Const.ci != length) + goto incompat; + else if (parstate >= INDATA) + v->vleng = ICON(1); /* avoid a memory fault */ +} + + + + + +/* lengtype -- returns the proper compiler type, given input of Fortran + type and length specifier */ + +lengtype(type, len) +register int type; +ftnint len; +{ + register int length = (int)len; + switch(type) + { + case TYREAL: + if(length == typesize[TYDREAL]) + return(TYDREAL); + if(length == typesize[TYREAL]) + goto ret; + break; + + case TYCOMPLEX: + if(length == typesize[TYDCOMPLEX]) + return(TYDCOMPLEX); + if(length == typesize[TYCOMPLEX]) + goto ret; + break; + + case TYINT1: + case TYSHORT: + case TYDREAL: + case TYDCOMPLEX: + case TYCHAR: + case TYLOGICAL1: + case TYLOGICAL2: + case TYUNKNOWN: + case TYSUBR: + case TYERROR: +#ifdef TYQUAD + case TYQUAD: +#endif + goto ret; + + case TYLOGICAL: + switch(length) { + case 0: return tylog; + case 1: return TYLOGICAL1; + case 2: return TYLOGICAL2; + case 4: goto ret; + } +#if 0 /*!!??!!*/ + if(length == typesize[TYLOGICAL]) + goto ret; +#endif + break; + + case TYLONG: + if(length == 0) + return(tyint); + if (length == 1) + return TYINT1; + if(length == typesize[TYSHORT]) + return(TYSHORT); +#ifdef TYQUAD + if(length == typesize[TYQUAD] && use_tyquad) + return(TYQUAD); +#endif + if(length == typesize[TYLONG]) + goto ret; + break; + default: + badtype("lengtype", type); + } + + if(len != 0) + err("incompatible type-length combination"); + +ret: + return(type); +} + + + + + +/* setintr -- Set Intrinsic function */ + +setintr(v) +register Namep v; +{ + int k; + + if(v->vstg == STGUNKNOWN) + v->vstg = STGINTR; + else if(v->vstg!=STGINTR) + dclerr("incompatible use of intrinsic function", v); + if(v->vclass==CLUNKNOWN) + v->vclass = CLPROC; + if(v->vprocclass == PUNKNOWN) + v->vprocclass = PINTRINSIC; + else if(v->vprocclass != PINTRINSIC) + dclerr("invalid intrinsic declaration", v); + if(k = intrfunct(v->fvarname)) { + if ((*(struct Intrpacked *)&k).f4) + if (noextflag) + goto unknown; + else + dcomplex_seen++; + v->vardesc.varno = k; + } + else { + unknown: + dclerr("unknown intrinsic function", v); + } +} + + + +/* setext -- Set External declaration -- assume that unknowns will become + procedures */ + +setext(v) +register Namep v; +{ + if(v->vclass == CLUNKNOWN) + v->vclass = CLPROC; + else if(v->vclass != CLPROC) + dclerr("invalid external declaration", v); + + if(v->vprocclass == PUNKNOWN) + v->vprocclass = PEXTERNAL; + else if(v->vprocclass != PEXTERNAL) + dclerr("invalid external declaration", v); +} /* setext */ + + + + +/* create dimensions block for array variable */ + +setbound(v, nd, dims) +register Namep v; +int nd; +struct Dims dims[ ]; +{ + register expptr q, t; + register struct Dimblock *p; + int i; + extern chainp new_vars; + char buf[256]; + + if(v->vclass == CLUNKNOWN) + v->vclass = CLVAR; + else if(v->vclass != CLVAR) + { + dclerr("only variables may be arrays", v); + return; + } + + v->vdim = p = (struct Dimblock *) + ckalloc( sizeof(int) + (3+2*nd)*sizeof(expptr) ); + p->ndim = nd--; + p->nelt = ICON(1); + doin_setbound = 1; + + for(i = 0; i <= nd; ++i) + { + if( (q = dims[i].ub) == NULL) + { + if(i == nd) + { + frexpr(p->nelt); + p->nelt = NULL; + } + else + err("only last bound may be asterisk"); + p->dims[i].dimsize = ICON(1); + p->dims[i].dimexpr = NULL; + } + else + { + + if(dims[i].lb) + { + q = mkexpr(OPMINUS, q, cpexpr(dims[i].lb)); + q = mkexpr(OPPLUS, q, ICON(1) ); + } + if( ISCONST(q) ) + { + p->dims[i].dimsize = q; + p->dims[i].dimexpr = (expptr) PNULL; + } + else { + sprintf(buf, " %s_dim%d", v->fvarname, i+1); + p->dims[i].dimsize = (expptr) + autovar(1, tyint, EXNULL, buf); + p->dims[i].dimexpr = q; + if (i == nd) + v->vlastdim = new_vars; + v->vdimfinish = 1; + } + if(p->nelt) + p->nelt = mkexpr(OPSTAR, p->nelt, + cpexpr(p->dims[i].dimsize) ); + } + } + + q = dims[nd].lb; + if(q == NULL) + q = ICON(1); + + for(i = nd-1 ; i>=0 ; --i) + { + t = dims[i].lb; + if(t == NULL) + t = ICON(1); + if(p->dims[i].dimsize) + q = mkexpr(OPPLUS, t, + mkexpr(OPSTAR, cpexpr(p->dims[i].dimsize), q)); + } + + if( ISCONST(q) ) + { + p->baseoffset = q; + p->basexpr = NULL; + } + else + { + sprintf(buf, " %s_offset", v->fvarname); + p->baseoffset = (expptr) autovar(1, tyint, EXNULL, buf); + p->basexpr = q; + v->vdimfinish = 1; + } + doin_setbound = 0; +} + + + +wr_abbrevs (outfile, function_head, vars) +FILE *outfile; +int function_head; +chainp vars; +{ + for (; vars; vars = vars -> nextp) { + Namep name = (Namep) vars -> datap; + if (!name->visused) + continue; + + if (function_head) + nice_printf (outfile, "#define "); + else + nice_printf (outfile, "#undef "); + out_name (outfile, name); + + if (function_head) { + Extsym *comm = &extsymtab[name -> vardesc.varno]; + + nice_printf (outfile, " ("); + extern_out (outfile, comm); + nice_printf (outfile, "%d.", comm->curno); + nice_printf (outfile, "%s)", name->cvarname); + } /* if function_head */ + nice_printf (outfile, "\n"); + } /* for */ +} /* wr_abbrevs */ diff --git a/usr.bin/f2c/put.c b/usr.bin/f2c/put.c new file mode 100644 index 000000000000..cbe0b4a98d8a --- /dev/null +++ b/usr.bin/f2c/put.c @@ -0,0 +1,399 @@ +/**************************************************************** +Copyright 1990, 1991, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* + * INTERMEDIATE CODE GENERATION PROCEDURES COMMON TO BOTH + * JOHNSON (PORTABLE) AND RITCHIE FAMILIES OF SECOND PASSES +*/ + +#include "defs.h" +#include "names.h" /* For LOCAL_CONST_NAME */ +#include "pccdefs.h" +#include "p1defs.h" + +/* Definitions for putconst() */ + +#define LIT_CHAR 1 +#define LIT_FLOAT 2 +#define LIT_INT 3 + + +/* +char *ops [ ] = + { + "??", "+", "-", "*", "/", "**", "-", + "OR", "AND", "EQV", "NEQV", "NOT", + "CONCAT", + "<", "==", ">", "<=", "!=", ">=", + " of ", " ofC ", " = ", " += ", " *= ", " CONV ", " << ", " % ", + " , ", " ? ", " : " + " abs ", " min ", " max ", " addr ", " indirect ", + " bitor ", " bitand ", " bitxor ", " bitnot ", " >> ", + }; +*/ + +/* Each of these values is defined in pccdefs */ + +int ops2 [ ] = +{ + P2BAD, P2PLUS, P2MINUS, P2STAR, P2SLASH, P2BAD, P2NEG, + P2OROR, P2ANDAND, P2EQ, P2NE, P2NOT, + P2BAD, + P2LT, P2EQ, P2GT, P2LE, P2NE, P2GE, + P2CALL, P2CALL, P2ASSIGN, P2PLUSEQ, P2STAREQ, P2CONV, P2LSHIFT, P2MOD, + P2COMOP, P2QUEST, P2COLON, + 1, P2BAD, P2BAD, P2BAD, P2BAD, + P2BITOR, P2BITAND, P2BITXOR, P2BITNOT, P2RSHIFT, + P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, + P2BAD, P2BAD, P2BAD, P2BAD, + 1,1,1,1,1, /* OPNEG1, OPDMIN, OPDMAX, OPASSIGNI, OPIDENTITY */ + 1,1,1,1 /* OPCHARCAST, OPDABS, OPMIN2, OPMAX2 */ +}; + + +setlog() +{ + typesize[TYLOGICAL] = typesize[tylogical]; + typealign[TYLOGICAL] = typealign[tylogical]; +} + + +putexpr(p) +expptr p; +{ +/* Write the expression to the p1 file */ + + p = (expptr) putx (fixtype (p)); + p1_expr (p); +} + + + + + +expptr putassign(lp, rp) +expptr lp, rp; +{ + return putx(fixexpr((Exprp)mkexpr(OPASSIGN, lp, rp))); +} + + + + +void puteq(lp, rp) +expptr lp, rp; +{ + putexpr(mkexpr(OPASSIGN, lp, rp) ); +} + + + + +/* put code for a *= b */ + +expptr putsteq(a, b) +Addrp a, b; +{ + return putx( fixexpr((Exprp) + mkexpr(OPSTAREQ, cpexpr((expptr)a), cpexpr((expptr)b)))); +} + + + + +Addrp mkfield(res, f, ty) +register Addrp res; +char *f; +int ty; +{ + res -> vtype = ty; + res -> Field = f; + return res; +} /* mkfield */ + + +Addrp realpart(p) +register Addrp p; +{ + register Addrp q; + expptr mkrealcon(); + + if (p->tag == TADDR + && p->uname_tag == UNAM_CONST + && ISCOMPLEX (p->vtype)) + return (Addrp)mkrealcon (p -> vtype + TYREAL - TYCOMPLEX, + p->user.kludge.vstg1 ? p->user.Const.cds[0] + : cds(dtos(p->user.Const.cd[0]),CNULL)); + + q = (Addrp) cpexpr((expptr) p); + if( ISCOMPLEX(p->vtype) ) + q = mkfield (q, "r", p -> vtype + TYREAL - TYCOMPLEX); + + return(q); +} + + + + +expptr imagpart(p) +register Addrp p; +{ + register Addrp q; + expptr mkrealcon(); + + if( ISCOMPLEX(p->vtype) ) + { + if (p->tag == TADDR && p->uname_tag == UNAM_CONST) + return mkrealcon (p -> vtype + TYREAL - TYCOMPLEX, + p->user.kludge.vstg1 ? p->user.Const.cds[1] + : cds(dtos(p->user.Const.cd[1]),CNULL)); + q = (Addrp) cpexpr((expptr) p); + q = mkfield (q, "i", p -> vtype + TYREAL - TYCOMPLEX); + return( (expptr) q ); + } + else + +/* Cast an integer type onto a Double Real type */ + + return( mkrealcon( ISINT(p->vtype) ? TYDREAL : p->vtype , "0")); +} + + + + + +/* ncat -- computes the number of adjacent concatenation operations */ + +ncat(p) +register expptr p; +{ + if(p->tag==TEXPR && p->exprblock.opcode==OPCONCAT) + return( ncat(p->exprblock.leftp) + ncat(p->exprblock.rightp) ); + else return(1); +} + + + + +/* lencat -- returns the length of the concatenated string. Each + substring must have a static (i.e. compile-time) fixed length */ + +ftnint lencat(p) +register expptr p; +{ + if(p->tag==TEXPR && p->exprblock.opcode==OPCONCAT) + return( lencat(p->exprblock.leftp) + lencat(p->exprblock.rightp) ); + else if( p->headblock.vleng!=NULL && ISICON(p->headblock.vleng) ) + return(p->headblock.vleng->constblock.Const.ci); + else if(p->tag==TADDR && p->addrblock.varleng!=0) + return(p->addrblock.varleng); + else + { + err("impossible element in concatenation"); + return(0); + } +} + +/* putconst -- Creates a new Addrp value which maps onto the input + constant value. The Addrp doesn't retain the value of the constant, + instead that value is copied into a table of constants (called + litpool, for pool of literal values). The only way to retrieve the + actual value of the constant is to look at the memno field of the + Addrp result. You know that the associated literal is the one referred + to by q when (q -> memno == litp -> litnum). +*/ + +Addrp putconst(p) +register Constp p; +{ + register Addrp q; + struct Literal *litp, *lastlit; + int k, len, type; + int litflavor; + double cd[2]; + ftnint nblanks; + char *strp; + char cdsbuf0[64], cdsbuf1[64], *ds[2]; + + if (p->tag != TCONST) + badtag("putconst", p->tag); + + q = ALLOC(Addrblock); + q->tag = TADDR; + type = p->vtype; + q->vtype = ( type==TYADDR ? tyint : type ); + q->vleng = (expptr) cpexpr(p->vleng); + q->vstg = STGCONST; + +/* Create the new label for the constant. This is wasteful of labels + because when the constant value already exists in the literal pool, + this label gets thrown away and is never reclaimed. It might be + cleaner to move this down past the first switch() statement below */ + + q->memno = newlabel(); + q->memoffset = ICON(0); + q -> uname_tag = UNAM_CONST; + +/* Copy the constant info into the Addrblock; do this by copying the + largest storage elts */ + + q -> user.Const = p -> Const; + q->user.kludge.vstg1 = p->vstg; /* distinguish string from binary fp */ + + /* check for value in literal pool, and update pool if necessary */ + + k = 1; + switch(type) + { + case TYCHAR: + if (halign) { + strp = p->Const.ccp; + nblanks = p->Const.ccp1.blanks; + len = p->vleng->constblock.Const.ci; + litflavor = LIT_CHAR; + goto loop; + } + else + q->memno = BAD_MEMNO; + break; + case TYCOMPLEX: + case TYDCOMPLEX: + k = 2; + if (p->vstg) + cd[1] = atof(ds[1] = p->Const.cds[1]); + else + ds[1] = cds(dtos(cd[1] = p->Const.cd[1]), cdsbuf1); + case TYREAL: + case TYDREAL: + litflavor = LIT_FLOAT; + if (p->vstg) + cd[0] = atof(ds[0] = p->Const.cds[0]); + else + ds[0] = cds(dtos(cd[0] = p->Const.cd[0]), cdsbuf0); + goto loop; + + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: + type = tylogical; + goto lit_int_flavor; + case TYLONG: + type = tyint; + case TYSHORT: + case TYINT1: +#ifdef TYQUAD + case TYQUAD: +#endif + lit_int_flavor: + litflavor = LIT_INT; + +/* Scan the literal pool for this constant value. If this same constant + has been assigned before, use the same label. Note that this routine + does NOT consider two differently-typed constants with the same bit + pattern to be the same constant */ + + loop: + lastlit = litpool + nliterals; + for(litp = litpool ; litp<lastlit ; ++litp) + +/* Remove this type checking to ensure that all bit patterns are reused */ + + if(type == litp->littype) switch(litflavor) + { + case LIT_CHAR: + if (len == (int)litp->litval.litival2[0] + && nblanks == litp->litval.litival2[1] + && !memcmp(strp, litp->cds[0], len)) { + q->memno = litp->litnum; + frexpr((expptr)p); + q->user.Const.ccp1.ccp0 = litp->cds[0]; + return(q); + } + break; + case LIT_FLOAT: + if(cd[0] == litp->litval.litdval[0] + && !strcmp(ds[0], litp->cds[0]) + && (k == 1 || + cd[1] == litp->litval.litdval[1] + && !strcmp(ds[1], litp->cds[1]))) { +ret: + q->memno = litp->litnum; + frexpr((expptr)p); + return(q); + } + break; + + case LIT_INT: + if(p->Const.ci == litp->litval.litival) + goto ret; + break; + } + +/* If there's room in the literal pool, add this new value to the pool */ + + if(nliterals < maxliterals) + { + ++nliterals; + + /* litp now points to the next free elt */ + + litp->littype = type; + litp->litnum = q->memno; + switch(litflavor) + { + case LIT_CHAR: + litp->litval.litival2[0] = len; + litp->litval.litival2[1] = nblanks; + q->user.Const.ccp = litp->cds[0] = + memcpy(gmem(len,0), strp, len); + break; + + case LIT_FLOAT: + litp->litval.litdval[0] = cd[0]; + litp->cds[0] = copys(ds[0]); + if (k == 2) { + litp->litval.litdval[1] = cd[1]; + litp->cds[1] = copys(ds[1]); + } + break; + + case LIT_INT: + litp->litval.litival = p->Const.ci; + break; + } /* switch (litflavor) */ + } + else + many("literal constants", 'L', maxliterals); + + break; + case TYADDR: + break; + default: + badtype ("putconst", p -> vtype); + break; + } /* switch */ + + if (type != TYCHAR || halign) + frexpr((expptr)p); + return( q ); +} diff --git a/usr.bin/f2c/putpcc.c b/usr.bin/f2c/putpcc.c new file mode 100644 index 000000000000..d96e5e2a3be5 --- /dev/null +++ b/usr.bin/f2c/putpcc.c @@ -0,0 +1,1843 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* INTERMEDIATE CODE GENERATION FOR S. C. JOHNSON C COMPILERS */ +/* NEW VERSION USING BINARY POLISH POSTFIX INTERMEDIATE */ + +#include "defs.h" +#include "pccdefs.h" +#include "output.h" /* for nice_printf */ +#include "names.h" +#include "p1defs.h" + +Addrp realpart(); +LOCAL Addrp intdouble(), putcx1(), putcxeq (), putch1 (); +LOCAL putct1 (); + +expptr putcxop(); +LOCAL expptr putcall (), putmnmx (), putcheq(), putcat (); +LOCAL expptr putaddr(), putchcmp (), putpower(), putop(); +LOCAL expptr putcxcmp (); +expptr imagpart(); +ftnint lencat(); + +#define FOUR 4 +extern int ops2[]; +extern int proc_argchanges, proc_protochanges; +extern int krparens; + +#define P2BUFFMAX 128 + +/* Puthead -- output the header information about subroutines, functions + and entry points */ + +puthead(s, class) +char *s; +int class; +{ + if (headerdone == NO) { + if (class == CLMAIN) + s = "MAIN__"; + p1_head (class, s); + headerdone = YES; + } +} + +putif(p, else_if_p) + register expptr p; + int else_if_p; +{ + register int k; + int n; + long where; + + if (else_if_p) { + p1put(P1_ELSEIFSTART); + where = ftell(pass1_file); + } + if( !ISLOGICAL((k = (p = fixtype(p))->headblock.vtype )) ) + { + if(k != TYERROR) + err("non-logical expression in IF statement"); + } + else { + if (else_if_p) { + if (ei_next >= ei_last) + { + k = ei_last - ei_first; + n = k + 100; + ei_next = mem(n,0); + ei_last = ei_first + n; + if (k) + memcpy(ei_next, ei_first, k); + ei_first = ei_next; + ei_next += k; + ei_last = ei_first + n; + } + p = putx(p); + if (*ei_next++ = ftell(pass1_file) > where) { + p1_if(p); + new_endif(); + } + else + p1_elif(p); + } + else { + p = putx(p); + p1_if(p); + } + } + } + + +putout(p) +expptr p; +{ + p1_expr (p); + +/* Used to make temporaries in holdtemps available here, but they */ +/* may be reused too soon (e.g. when multiple **'s are involved). */ +} + + + +putcmgo(index, nlab, labs) +expptr index; +int nlab; +struct Labelblock *labs[]; +{ + if(! ISINT(index->headblock.vtype) ) + { + execerr("computed goto index must be integer", CNULL); + return; + } + + p1comp_goto (index, nlab, labs); +} + + static expptr +krput(p) + register expptr p; +{ + register expptr e, e1; + register unsigned op; + int t = krparens == 2 ? TYDREAL : p->exprblock.vtype; + + op = p->exprblock.opcode; + e = p->exprblock.leftp; + if (e->tag == TEXPR && e->exprblock.opcode == op) { + e1 = (expptr)mktmp(t, ENULL); + putout(putassign(cpexpr(e1), e)); + p->exprblock.leftp = e1; + } + else + p->exprblock.leftp = putx(e); + + e = p->exprblock.rightp; + if (e->tag == TEXPR && e->exprblock.opcode == op) { + e1 = (expptr)mktmp(t, ENULL); + putout(putassign(cpexpr(e1), e)); + p->exprblock.rightp = e1; + } + else + p->exprblock.rightp = putx(e); + return p; + } + +expptr putx(p) + register expptr p; +{ + int opc; + int k; + + if (p) + switch(p->tag) + { + case TERROR: + break; + + case TCONST: + switch(p->constblock.vtype) + { + case TYLOGICAL1: + case TYLOGICAL2: + case TYLOGICAL: +#ifdef TYQUAD + case TYQUAD: +#endif + case TYLONG: + case TYSHORT: + case TYINT1: + break; + + case TYADDR: + break; + case TYREAL: + case TYDREAL: + +/* Don't write it out to the p2 file, since you'd need to call putconst, + which is just what we need to avoid in the translator */ + + break; + default: + p = putx( (expptr)putconst((Constp)p) ); + break; + } + break; + + case TEXPR: + switch(opc = p->exprblock.opcode) + { + case OPCALL: + case OPCCALL: + if( ISCOMPLEX(p->exprblock.vtype) ) + p = putcxop(p); + else p = putcall(p, (Addrp *)NULL); + break; + + case OPMIN: + case OPMAX: + p = putmnmx(p); + break; + + + case OPASSIGN: + if(ISCOMPLEX(p->exprblock.leftp->headblock.vtype) + || ISCOMPLEX(p->exprblock.rightp->headblock.vtype)) { + (void) putcxeq(p); + p = ENULL; + } else if( ISCHAR(p) ) + p = putcheq(p); + else + goto putopp; + break; + + case OPEQ: + case OPNE: + if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) || + ISCOMPLEX(p->exprblock.rightp->headblock.vtype) ) + { + p = putcxcmp(p); + break; + } + case OPLT: + case OPLE: + case OPGT: + case OPGE: + if(ISCHAR(p->exprblock.leftp)) + { + p = putchcmp(p); + break; + } + goto putopp; + + case OPPOWER: + p = putpower(p); + break; + + case OPSTAR: + /* m * (2**k) -> m<<k */ + if(INT(p->exprblock.leftp->headblock.vtype) && + ISICON(p->exprblock.rightp) && + ( (k = log_2(p->exprblock.rightp->constblock.Const.ci))>0) ) + { + p->exprblock.opcode = OPLSHIFT; + frexpr(p->exprblock.rightp); + p->exprblock.rightp = ICON(k); + goto putopp; + } + if (krparens && ISREAL(p->exprblock.vtype)) + return krput(p); + + case OPMOD: + goto putopp; + case OPPLUS: + if (krparens && ISREAL(p->exprblock.vtype)) + return krput(p); + case OPMINUS: + case OPSLASH: + case OPNEG: + case OPNEG1: + case OPABS: + case OPDABS: + if( ISCOMPLEX(p->exprblock.vtype) ) + p = putcxop(p); + else goto putopp; + break; + + case OPCONV: + if( ISCOMPLEX(p->exprblock.vtype) ) + p = putcxop(p); + else if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) ) + { + p = putx( mkconv(p->exprblock.vtype, + (expptr)realpart(putcx1(p->exprblock.leftp)))); + } + else goto putopp; + break; + + case OPNOT: + case OPOR: + case OPAND: + case OPEQV: + case OPNEQV: + case OPADDR: + case OPPLUSEQ: + case OPSTAREQ: + case OPCOMMA: + case OPQUEST: + case OPCOLON: + case OPBITOR: + case OPBITAND: + case OPBITXOR: + case OPBITNOT: + case OPLSHIFT: + case OPRSHIFT: + case OPASSIGNI: + case OPIDENTITY: + case OPCHARCAST: + case OPMIN2: + case OPMAX2: + case OPDMIN: + case OPDMAX: +putopp: + p = putop(p); + break; + + case OPCONCAT: + /* weird things like ichar(a//a) */ + p = (expptr)putch1(p); + break; + + default: + badop("putx", opc); + p = errnode (); + } + break; + + case TADDR: + p = putaddr(p); + break; + + default: + badtag("putx", p->tag); + p = errnode (); + } + + return p; +} + + + +LOCAL expptr putop(p) +expptr p; +{ + expptr lp, tp; + int pt, lt, lt1; + int comma; + + switch(p->exprblock.opcode) /* check for special cases and rewrite */ + { + case OPCONV: + pt = p->exprblock.vtype; + lp = p->exprblock.leftp; + lt = lp->headblock.vtype; + +/* Simplify nested type casts */ + + while(p->tag==TEXPR && p->exprblock.opcode==OPCONV && + ( (ISREAL(pt)&&ONEOF(lt,MSKREAL|MSKCOMPLEX)) || + (INT(pt)&&(ONEOF(lt,MSKINT|MSKADDR|MSKCHAR|M(TYSUBR)))) )) + { + if(pt==TYDREAL && lt==TYREAL) + { + if(lp->tag==TEXPR + && lp->exprblock.opcode == OPCONV) { + lt1 = lp->exprblock.leftp->headblock.vtype; + if (lt1 == TYDREAL) { + lp->exprblock.leftp = + putx(lp->exprblock.leftp); + return p; + } + if (lt1 == TYDCOMPLEX) { + lp->exprblock.leftp = putx( + (expptr)realpart( + putcx1(lp->exprblock.leftp))); + return p; + } + } + break; + } + else if (ISREAL(pt) && ISCOMPLEX(lt)) { + p->exprblock.leftp = putx(mkconv(pt, + (expptr)realpart( + putcx1(p->exprblock.leftp)))); + break; + } + if(lt==TYCHAR && lp->tag==TEXPR && + lp->exprblock.opcode==OPCALL) + { + +/* May want to make a comma expression here instead. I had one, but took + it out for my convenience, not for the convenience of the end user */ + + putout (putcall (lp, (Addrp *) &(p -> + exprblock.leftp))); + return putop (p); + } + if (lt == TYCHAR) { + p->exprblock.leftp = putx(p->exprblock.leftp); + return p; + } + frexpr(p->exprblock.vleng); + free( (charptr) p ); + p = lp; + if (p->tag != TEXPR) + goto retputx; + pt = lt; + lp = p->exprblock.leftp; + lt = lp->headblock.vtype; + } /* while */ + if(p->tag==TEXPR && p->exprblock.opcode==OPCONV) + break; + retputx: + return putx(p); + + case OPADDR: + comma = NO; + lp = p->exprblock.leftp; + free( (charptr) p ); + if(lp->tag != TADDR) + { + tp = (expptr) + mktmp(lp->headblock.vtype,lp->headblock.vleng); + p = putx( mkexpr(OPASSIGN, cpexpr(tp), lp) ); + lp = tp; + comma = YES; + } + if(comma) + p = mkexpr(OPCOMMA, p, putaddr(lp)); + else + p = (expptr)putaddr(lp); + return p; + + case OPASSIGN: + case OPASSIGNI: + case OPLT: + case OPLE: + case OPGT: + case OPGE: + case OPEQ: + case OPNE: + ; + } + + if( ops2[p->exprblock.opcode] <= 0) + badop("putop", p->exprblock.opcode); + p -> exprblock.leftp = putx (p -> exprblock.leftp); + if (p -> exprblock.rightp) + p -> exprblock.rightp = putx (p -> exprblock.rightp); + return p; +} + +LOCAL expptr putpower(p) +expptr p; +{ + expptr base; + Addrp t1, t2; + ftnint k; + int type; + char buf[80]; /* buffer for text of comment */ + + if(!ISICON(p->exprblock.rightp) || + (k = p->exprblock.rightp->constblock.Const.ci)<2) + Fatal("putpower: bad call"); + base = p->exprblock.leftp; + type = base->headblock.vtype; + t1 = mktmp(type, ENULL); + t2 = NULL; + + free ((charptr) p); + p = putassign (cpexpr((expptr) t1), base); + + sprintf (buf, "Computing %ld%s power", k, + k == 2 ? "nd" : k == 3 ? "rd" : "th"); + p1_comment (buf); + + for( ; (k&1)==0 && k>2 ; k>>=1 ) + { + p = mkexpr (OPCOMMA, p, putsteq(t1, t1)); + } + + if(k == 2) { + +/* Write the power computation out immediately */ + putout (p); + p = putx( mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1))); + } else { + t2 = mktmp(type, ENULL); + p = mkexpr (OPCOMMA, p, putassign(cpexpr((expptr)t2), + cpexpr((expptr)t1))); + + for(k>>=1 ; k>1 ; k>>=1) + { + p = mkexpr (OPCOMMA, p, putsteq(t1, t1)); + if(k & 1) + { + p = mkexpr (OPCOMMA, p, putsteq(t2, t1)); + } + } +/* Write the power computation out immediately */ + putout (p); + p = putx( mkexpr(OPSTAR, cpexpr((expptr)t2), + mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1)))); + } + frexpr((expptr)t1); + if(t2) + frexpr((expptr)t2); + return p; +} + + + + +LOCAL Addrp intdouble(p) +Addrp p; +{ + register Addrp t; + + t = mktmp(TYDREAL, ENULL); + putout (putassign(cpexpr((expptr)t), (expptr)p)); + return(t); +} + + + + + +/* Complex-type variable assignment */ + +LOCAL Addrp putcxeq(p) +register expptr p; +{ + register Addrp lp, rp; + expptr code; + + if(p->tag != TEXPR) + badtag("putcxeq", p->tag); + + lp = putcx1(p->exprblock.leftp); + rp = putcx1(p->exprblock.rightp); + code = putassign ( (expptr)realpart(lp), (expptr)realpart(rp)); + + if( ISCOMPLEX(p->exprblock.vtype) ) + { + code = mkexpr (OPCOMMA, code, putassign + (imagpart(lp), imagpart(rp))); + } + putout (code); + frexpr((expptr)rp); + free ((charptr) p); + return lp; +} + + + +/* putcxop -- used to write out embedded calls to complex functions, and + complex arguments to procedures */ + +expptr putcxop(p) +expptr p; +{ + return (expptr)putaddr((expptr)putcx1(p)); +} + +#define PAIR(x,y) mkexpr (OPCOMMA, (x), (y)) + +LOCAL Addrp putcx1(p) +register expptr p; +{ + expptr q; + Addrp lp, rp; + register Addrp resp; + int opcode; + int ltype, rtype; + long ts, tskludge; + expptr mkrealcon(); + + if(p == NULL) + return(NULL); + + switch(p->tag) + { + case TCONST: + if( ISCOMPLEX(p->constblock.vtype) ) + p = (expptr) putconst((Constp)p); + return( (Addrp) p ); + + case TADDR: + resp = &p->addrblock; + if (addressable(p)) + return (Addrp) p; + ts = tskludge = 0; + if (q = resp->memoffset) { + if (resp->uname_tag == UNAM_REF) { + q = cpexpr((tagptr)resp); + q->addrblock.vtype = tyint; + q->addrblock.cmplx_sub = 1; + p->addrblock.skip_offset = 1; + resp->user.name->vsubscrused = 1; + resp->uname_tag = UNAM_NAME; + tskludge = typesize[resp->vtype] + * (resp->Field ? 2 : 1); + } + else if (resp->isarray + && resp->vtype != TYCHAR) { + if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV)) + && resp->uname_tag == UNAM_NAME) + q = mkexpr(OPMINUS, q, + mkintcon(resp->user.name->voffset)); + ts = typesize[resp->vtype] + * (resp->Field ? 2 : 1); + q = resp->memoffset = mkexpr(OPSLASH, q, + ICON(ts)); + } + } + resp = mktmp(tyint, ENULL); + putout(putassign(cpexpr((expptr)resp), q)); + p->addrblock.memoffset = tskludge + ? mkexpr(OPSTAR, (expptr)resp, ICON(tskludge)) + : (expptr)resp; + if (ts) { + resp = &p->addrblock; + q = mkexpr(OPSTAR, resp->memoffset, ICON(ts)); + if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV)) + && resp->uname_tag == UNAM_NAME) + q = mkexpr(OPPLUS, q, + mkintcon(resp->user.name->voffset)); + resp->memoffset = q; + } + return (Addrp) p; + + case TEXPR: + if( ISCOMPLEX(p->exprblock.vtype) ) + break; + resp = mktmp(TYDREAL, ENULL); + putout (putassign( cpexpr((expptr)resp), p)); + return(resp); + + default: + badtag("putcx1", p->tag); + } + + opcode = p->exprblock.opcode; + if(opcode==OPCALL || opcode==OPCCALL) + { + Addrp t; + p = putcall(p, &t); + putout(p); + return t; + } + else if(opcode == OPASSIGN) + { + return putcxeq (p); + } + +/* BUG (inefficient) Generates too many temporary variables */ + + resp = mktmp(p->exprblock.vtype, ENULL); + if(lp = putcx1(p->exprblock.leftp) ) + ltype = lp->vtype; + if(rp = putcx1(p->exprblock.rightp) ) + rtype = rp->vtype; + + switch(opcode) + { + case OPCOMMA: + frexpr((expptr)resp); + resp = rp; + rp = NULL; + break; + + case OPNEG: + case OPNEG1: + putout (PAIR ( + putassign( (expptr)realpart(resp), + mkexpr(OPNEG, (expptr)realpart(lp), ENULL)), + putassign( imagpart(resp), + mkexpr(OPNEG, imagpart(lp), ENULL)))); + break; + + case OPPLUS: + case OPMINUS: { expptr r; + r = putassign( (expptr)realpart(resp), + mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp) )); + if(rtype < TYCOMPLEX) + q = putassign( imagpart(resp), imagpart(lp) ); + else if(ltype < TYCOMPLEX) + { + if(opcode == OPPLUS) + q = putassign( imagpart(resp), imagpart(rp) ); + else + q = putassign( imagpart(resp), + mkexpr(OPNEG, imagpart(rp), ENULL) ); + } + else + q = putassign( imagpart(resp), + mkexpr(opcode, imagpart(lp), imagpart(rp) )); + r = PAIR (r, q); + putout (r); + break; + } /* case OPPLUS, OPMINUS: */ + case OPSTAR: + if(ltype < TYCOMPLEX) + { + if( ISINT(ltype) ) + lp = intdouble(lp); + putout (PAIR ( + putassign( (expptr)realpart(resp), + mkexpr(OPSTAR, cpexpr((expptr)lp), + (expptr)realpart(rp))), + putassign( imagpart(resp), + mkexpr(OPSTAR, cpexpr((expptr)lp), imagpart(rp))))); + } + else if(rtype < TYCOMPLEX) + { + if( ISINT(rtype) ) + rp = intdouble(rp); + putout (PAIR ( + putassign( (expptr)realpart(resp), + mkexpr(OPSTAR, cpexpr((expptr)rp), + (expptr)realpart(lp))), + putassign( imagpart(resp), + mkexpr(OPSTAR, cpexpr((expptr)rp), imagpart(lp))))); + } + else { + putout (PAIR ( + putassign( (expptr)realpart(resp), mkexpr(OPMINUS, + mkexpr(OPSTAR, (expptr)realpart(lp), + (expptr)realpart(rp)), + mkexpr(OPSTAR, imagpart(lp), imagpart(rp)))), + putassign( imagpart(resp), mkexpr(OPPLUS, + mkexpr(OPSTAR, (expptr)realpart(lp), imagpart(rp)), + mkexpr(OPSTAR, imagpart(lp), + (expptr)realpart(rp)))))); + } + break; + + case OPSLASH: + /* fixexpr has already replaced all divisions + * by a complex by a function call + */ + if( ISINT(rtype) ) + rp = intdouble(rp); + putout (PAIR ( + putassign( (expptr)realpart(resp), + mkexpr(OPSLASH, (expptr)realpart(lp), cpexpr((expptr)rp))), + putassign( imagpart(resp), + mkexpr(OPSLASH, imagpart(lp), cpexpr((expptr)rp))))); + break; + + case OPCONV: + if( ISCOMPLEX(lp->vtype) ) + q = imagpart(lp); + else if(rp != NULL) + q = (expptr) realpart(rp); + else + q = mkrealcon(TYDREAL, "0"); + putout (PAIR ( + putassign( (expptr)realpart(resp), (expptr)realpart(lp)), + putassign( imagpart(resp), q))); + break; + + default: + badop("putcx1", opcode); + } + + frexpr((expptr)lp); + frexpr((expptr)rp); + free( (charptr) p ); + return(resp); +} + + + + +/* Only .EQ. and .NE. may be performed on COMPLEX data, other relations + are not defined */ + +LOCAL expptr putcxcmp(p) +register expptr p; +{ + int opcode; + register Addrp lp, rp; + expptr q; + + if(p->tag != TEXPR) + badtag("putcxcmp", p->tag); + + opcode = p->exprblock.opcode; + lp = putcx1(p->exprblock.leftp); + rp = putcx1(p->exprblock.rightp); + + q = mkexpr( opcode==OPEQ ? OPAND : OPOR , + mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp)), + mkexpr(opcode, imagpart(lp), imagpart(rp)) ); + + free( (charptr) lp); + free( (charptr) rp); + free( (charptr) p ); + if (ISCONST(q)) + return q; + return putx( fixexpr((Exprp)q) ); +} + +/* putch1 -- Forces constants into the literal pool, among other things */ + +LOCAL Addrp putch1(p) +register expptr p; +{ + Addrp t; + expptr e; + + switch(p->tag) + { + case TCONST: + return( putconst((Constp)p) ); + + case TADDR: + return( (Addrp) p ); + + case TEXPR: + switch(p->exprblock.opcode) + { + expptr q; + + case OPCALL: + case OPCCALL: + + p = putcall(p, &t); + putout (p); + break; + + case OPCONCAT: + t = mktmp(TYCHAR, ICON(lencat(p))); + q = (expptr) cpexpr(p->headblock.vleng); + p = putcat( cpexpr((expptr)t), p ); + /* put the correct length on the block */ + frexpr(t->vleng); + t->vleng = q; + putout (p); + break; + + case OPCONV: + if(!ISICON(p->exprblock.vleng) + || p->exprblock.vleng->constblock.Const.ci!=1 + || ! INT(p->exprblock.leftp->headblock.vtype) ) + Fatal("putch1: bad character conversion"); + t = mktmp(TYCHAR, ICON(1)); + e = mkexpr(OPCONV, (expptr)t, ENULL); + e->headblock.vtype = TYCHAR; + p = putop( mkexpr(OPASSIGN, cpexpr(e), p)); + putout (p); + break; + default: + badop("putch1", p->exprblock.opcode); + } + return(t); + + default: + badtag("putch1", p->tag); + } + /* NOT REACHED */ return 0; +} + + +/* putchop -- Write out a character actual parameter; that is, this is + part of a procedure invocation */ + +Addrp putchop(p) +expptr p; +{ + p = putaddr((expptr)putch1(p)); + return (Addrp)p; +} + + + + +LOCAL expptr putcheq(p) +register expptr p; +{ + expptr lp, rp; + int nbad; + + if(p->tag != TEXPR) + badtag("putcheq", p->tag); + + lp = p->exprblock.leftp; + rp = p->exprblock.rightp; + frexpr(p->exprblock.vleng); + free( (charptr) p ); + +/* If s = t // u, don't bother copying the result, write it directly into + this buffer */ + + nbad = badchleng(lp) + badchleng(rp); + if( rp->tag==TEXPR && rp->exprblock.opcode==OPCONCAT ) + p = putcat(lp, rp); + else if( !nbad + && ISONE(lp->headblock.vleng) + && ISONE(rp->headblock.vleng) ) { + lp = mkexpr(OPCONV, lp, ENULL); + rp = mkexpr(OPCONV, rp, ENULL); + lp->headblock.vtype = rp->headblock.vtype = TYCHAR; + p = putop(mkexpr(OPASSIGN, lp, rp)); + } + else + p = putx( call2(TYSUBR, "s_copy", lp, rp) ); + return p; +} + + + + +LOCAL expptr putchcmp(p) +register expptr p; +{ + expptr lp, rp; + + if(p->tag != TEXPR) + badtag("putchcmp", p->tag); + + lp = p->exprblock.leftp; + rp = p->exprblock.rightp; + + if(ISONE(lp->headblock.vleng) && ISONE(rp->headblock.vleng) ) { + lp = mkexpr(OPCONV, lp, ENULL); + rp = mkexpr(OPCONV, rp, ENULL); + lp->headblock.vtype = rp->headblock.vtype = TYCHAR; + } + else { + lp = call2(TYINT,"s_cmp", lp, rp); + rp = ICON(0); + } + p->exprblock.leftp = lp; + p->exprblock.rightp = rp; + p = putop(p); + return p; +} + + + + + +/* putcat -- Writes out a concatenation operation. Two temporary arrays + are allocated, putct1() is called to initialize them, and then a + call to runtime library routine s_cat() is inserted. + + This routine generates code which will perform an (nconc lhs rhs) + at runtime. The runtime funciton does not return a value, the routine + that calls this putcat must remember the name of lhs. +*/ + + +LOCAL expptr putcat(lhs0, rhs) + expptr lhs0; + register expptr rhs; +{ + register Addrp lhs = (Addrp)lhs0; + int n, tyi; + Addrp length_var, string_var; + expptr p; + static char Writing_concatenation[] = "Writing concatenation"; + +/* Create the temporary arrays */ + + n = ncat(rhs); + length_var = mktmpn(n, tyioint, ENULL); + string_var = mktmpn(n, TYADDR, ENULL); + frtemp((Addrp)cpexpr((expptr)length_var)); + frtemp((Addrp)cpexpr((expptr)string_var)); + +/* Initialize the arrays */ + + n = 0; + /* p1_comment scribbles on its argument, so we + * cannot safely pass a string literal here. */ + p1_comment(Writing_concatenation); + putct1(rhs, length_var, string_var, &n); + +/* Create the invocation */ + + tyi = tyint; + tyint = tyioint; /* for -I2 */ + p = putx (call4 (TYSUBR, "s_cat", + (expptr)lhs, + (expptr)string_var, + (expptr)length_var, + (expptr)putconst((Constp)ICON(n)))); + tyint = tyi; + + return p; +} + + + + + +LOCAL putct1(q, length_var, string_var, ip) +register expptr q; +register Addrp length_var, string_var; +int *ip; +{ + int i; + Addrp length_copy, string_copy; + expptr e; + extern int szleng; + + if(q->tag==TEXPR && q->exprblock.opcode==OPCONCAT) + { + putct1(q->exprblock.leftp, length_var, string_var, + ip); + putct1(q->exprblock.rightp, length_var, string_var, + ip); + frexpr (q -> exprblock.vleng); + free ((charptr) q); + } + else + { + i = (*ip)++; + e = cpexpr(q->headblock.vleng); + if (!e) + return; /* error -- character*(*) */ + length_copy = (Addrp) cpexpr((expptr)length_var); + length_copy->memoffset = + mkexpr(OPPLUS,length_copy->memoffset, ICON(i*szleng)); + string_copy = (Addrp) cpexpr((expptr)string_var); + string_copy->memoffset = + mkexpr(OPPLUS, string_copy->memoffset, + ICON(i*typesize[TYADDR])); + putout (PAIR (putassign((expptr)length_copy, e), + putassign((expptr)string_copy, addrof((expptr)putch1(q))))); + } +} + +/* putaddr -- seems to write out function invocation actual parameters */ + +LOCAL expptr putaddr(p0) + expptr p0; +{ + register Addrp p; + chainp cp; + + if (!(p = (Addrp)p0)) + return ENULL; + + if( p->tag==TERROR || (p->memoffset!=NULL && ISERROR(p->memoffset)) ) + { + frexpr((expptr)p); + return ENULL; + } + if (p->isarray && p->memoffset) + if (p->uname_tag == UNAM_REF) { + cp = p->memoffset->listblock.listp; + for(; cp; cp = cp->nextp) + cp->datap = (char *)fixtype((tagptr)cp->datap); + } + else + p->memoffset = putx(p->memoffset); + return (expptr) p; +} + + LOCAL expptr +addrfix(e) /* fudge character string length if it's a TADDR */ + expptr e; +{ + return e->tag == TADDR ? mkexpr(OPIDENTITY, e, ENULL) : e; + } + + LOCAL int +typekludge(ccall, q, at, j) + int ccall; + register expptr q; + Atype *at; + int j; /* alternate type */ +{ + register int i, k; + extern int iocalladdr; + register Namep np; + + /* Return value classes: + * < 100 ==> Fortran arg (pointer to type) + * < 200 ==> C arg + * < 300 ==> procedure arg + * < 400 ==> external, no explicit type + * < 500 ==> arg that may turn out to be + * either a variable or a procedure + */ + + k = q->headblock.vtype; + if (ccall) { + if (k == TYREAL) + k = TYDREAL; /* force double for library routines */ + return k + 100; + } + if (k == TYADDR) + return iocalladdr; + i = q->tag; + if ((i == TEXPR && q->exprblock.opcode != OPCOMMA_ARG) + || (i == TADDR && q->addrblock.charleng) + || i == TCONST) + k = TYFTNLEN + 100; + else if (i == TADDR) + switch(q->addrblock.vclass) { + case CLPROC: + if (q->addrblock.uname_tag != UNAM_NAME) + k += 200; + else if ((np = q->addrblock.user.name)->vprocclass + != PTHISPROC) { + if (k && !np->vimpltype) + k += 200; + else { + if (j > 200 && infertypes && j < 300) { + k = j; + inferdcl(np, j-200); + } + else k = (np->vstg == STGEXT + ? extsymtab[np->vardesc.varno].extype + : 0) + 200; + at->cp = mkchain((char *)np, at->cp); + } + } + else if (k == TYSUBR) + k += 200; + break; + + case CLUNKNOWN: + if (q->addrblock.vstg == STGARG + && q->addrblock.uname_tag == UNAM_NAME) { + k += 400; + at->cp = mkchain((char *)q->addrblock.user.name, + at->cp); + } + } + else if (i == TNAME && q->nameblock.vstg == STGARG) { + np = &q->nameblock; + switch(np->vclass) { + case CLPROC: + if (!np->vimpltype) + k += 200; + else if (j <= 200 || !infertypes || j >= 300) + k += 300; + else { + k = j; + inferdcl(np, j-200); + } + goto add2chain; + + case CLUNKNOWN: + /* argument may be a scalar variable or a function */ + if (np->vimpltype && j && infertypes + && j < 300) { + inferdcl(np, j % 100); + k = j; + } + else + k += 400; + + /* to handle procedure args only so far known to be + * external, save a pointer to the symbol table entry... + */ + add2chain: + at->cp = mkchain((char *)np, at->cp); + } + } + return k; + } + + char * +Argtype(k, buf) + int k; + char *buf; +{ + if (k < 100) { + sprintf(buf, "%s variable", ftn_types[k]); + return buf; + } + if (k < 200) { + k -= 100; + return ftn_types[k]; + } + if (k < 300) { + k -= 200; + if (k == TYSUBR) + return ftn_types[TYSUBR]; + sprintf(buf, "%s function", ftn_types[k]); + return buf; + } + if (k < 400) + return "external argument"; + k -= 400; + sprintf(buf, "%s argument", ftn_types[k]); + return buf; + } + + static void +atype_squawk(at, msg) + Argtypes *at; + char *msg; +{ + register Atype *a, *ae; + warn(msg); + for(a = at->atypes, ae = a + at->nargs; a < ae; a++) + frchain(&a->cp); + at->nargs = -1; + if (at->changes & 2 && !at->defined) + proc_protochanges++; + } + + static char inconsist[] = "inconsistent calling sequences for "; + + void +bad_atypes(at, fname, i, j, k, here, prev) + Argtypes *at; + char *fname, *here, *prev; + int i, j, k; +{ + char buf[208], buf1[32], buf2[32]; + + sprintf(buf, "%s%.90s,\n\targ %d: %s%s%s %s.", + inconsist, fname, i, here, Argtype(k, buf1), + prev, Argtype(j, buf2)); + atype_squawk(at, buf); + } + + int +type_fixup(at,a,k) + Argtypes *at; + Atype *a; + int k; +{ + register struct Entrypoint *ep; + if (!infertypes) + return 0; + for(ep = entries; ep; ep = ep->entnextp) + if (at == ep->entryname->arginfo) { + a->type = k % 100; + return proc_argchanges = 1; + } + return 0; + } + + + void +save_argtypes(arglist, at0, at1, ccall, fname, stg, nchargs, type, zap) + chainp arglist; + Argtypes **at0, **at1; + int ccall, stg, nchargs, type, zap; + char *fname; +{ + Argtypes *at; + chainp cp; + int i, i0, j, k, nargs, nbad, *t, *te; + Atype *atypes; + expptr q; + char buf[208], buf1[32], buf2[32]; + static int initargs[4] = {TYCOMPLEX, TYDCOMPLEX, TYCHAR, TYFTNLEN+100}; + static int *init_ap[TYSUBR+1] = {0,0,0,0,0,0,0, +#ifdef TYQUAD + 0, +#endif + initargs, initargs+1,0,0,0,initargs+2}; + extern int init_ac[TYSUBR+1]; + + i0 = init_ac[type]; + t = init_ap[type]; + te = t + i0; + if (at = *at0) { + *at1 = at; + nargs = at->nargs; + if (nargs < 0 && type && at->changes & 2 && !at->defined) + --proc_protochanges; + if (at->dnargs >= 0 && zap != 2) + type = 0; + if (nargs < 0) { /* inconsistent usage seen */ + if (type) + goto newlist; + return; + } + atypes = at->atypes; + i = nchargs; + for(nbad = 0; t < te; atypes++) { + if (++i > nargs) { + toomany: + i = nchargs + i0; + for(cp = arglist; cp; cp = cp->nextp) + i++; + toofew: + switch(zap) { + case 2: zap = 6; break; + case 1: if (at->defined & 4) + return; + } + sprintf(buf, + "%s%.90s:\n\there %d, previously %d args and string lengths.", + inconsist, fname, i, nargs); + atype_squawk(at, buf); + if (type) + goto newlist; + return; + } + j = atypes->type; + k = *t++; + if (j != k) + goto badtypes; + } + for(cp = arglist; cp; atypes++, cp = cp->nextp) { + if (++i > nargs) + goto toomany; + j = atypes->type; + if (!(q = (expptr)cp->datap)) + continue; + k = typekludge(ccall, q, atypes, j); + if (k >= 300 || k == j) + continue; + if (j >= 300) { + if (k >= 200) { + if (k == TYUNKNOWN + 200) + continue; + if (j % 100 != k - 200 + && k != TYSUBR + 200 + && j != TYUNKNOWN + 300 + && !type_fixup(at,atypes,k)) + goto badtypes; + } + else if (j % 100 % TYSUBR != k % TYSUBR + && !type_fixup(at,atypes,k)) + goto badtypes; + } + else if (k < 200 || j < 200) + if (j) { + if (k == TYUNKNOWN + && q->tag == TNAME + && q->nameblock.vinfproc) { + q->nameblock.vdcldone = 0; + impldcl((Namep)q); + } + goto badtypes; + } + else ; /* fall through to update */ + else if (k == TYUNKNOWN+200) + continue; + else if (j != TYUNKNOWN+200) + { + badtypes: + if (++nbad == 1) + bad_atypes(at, fname, i, j, k, "here ", + ", previously"); + else + fprintf(stderr, + "\targ %d: here %s, previously %s.\n", + i, Argtype(k,buf1), + Argtype(j,buf2)); + continue; + } + /* We've subsequently learned the right type, + as in the call on zoo below... + + subroutine foo(x, zap) + external zap + call goo(zap) + x = zap(3) + call zoo(zap) + end + */ + if (!nbad) { + atypes->type = k; + at->changes |= 1; + } + } + if (i < nargs) + goto toofew; + if (nbad) { + if (type) { + /* we're defining the procedure */ + t = init_ap[type]; + te = t + i0; + proc_argchanges = 1; + goto newlist; + } + return; + } + if (zap == 1 && (at->changes & 5) != 5) + at->changes = 0; + return; + } + newlist: + i = i0 + nchargs; + for(cp = arglist; cp; cp = cp->nextp) + i++; + k = sizeof(Argtypes) + (i-1)*sizeof(Atype); + *at0 = *at1 = at = stg == STGEXT ? (Argtypes *)gmem(k,1) + : (Argtypes *) mem(k,1); + at->dnargs = at->nargs = i; + at->defined = zap & 6; + at->changes = type ? 0 : 4; + atypes = at->atypes; + for(; t < te; atypes++) { + atypes->type = *t++; + atypes->cp = 0; + } + for(cp = arglist; cp; atypes++, cp = cp->nextp) { + atypes->cp = 0; + atypes->type = (q = (expptr)cp->datap) + ? typekludge(ccall, q, atypes, 0) + : 0; + } + for(; --nchargs >= 0; atypes++) { + atypes->type = TYFTNLEN + 100; + atypes->cp = 0; + } + } + + void +saveargtypes(p) /* for writing prototypes */ + register Exprp p; +{ + Addrp a; + Argtypes **at0, **at1; + Namep np; + chainp arglist; + expptr rp; + Extsym *e; + char *fname; + + a = (Addrp)p->leftp; + switch(a->vstg) { + case STGEXT: + switch(a->uname_tag) { + case UNAM_EXTERN: /* e.g., sqrt() */ + e = extsymtab + a->memno; + at0 = at1 = &e->arginfo; + fname = e->fextname; + break; + case UNAM_NAME: + np = a->user.name; + at0 = &extsymtab[np->vardesc.varno].arginfo; + at1 = &np->arginfo; + fname = np->fvarname; + break; + default: + goto bug; + } + break; + case STGARG: + if (a->uname_tag != UNAM_NAME) + goto bug; + np = a->user.name; + at0 = at1 = &np->arginfo; + fname = np->fvarname; + break; + default: + bug: + Fatal("Confusion in saveargtypes"); + } + rp = p->rightp; + arglist = rp && rp->tag == TLIST ? rp->listblock.listp : 0; + save_argtypes(arglist, at0, at1, p->opcode == OPCCALL, + fname, a->vstg, 0, 0, 0); + } + +/* putcall - fix up the argument list, and write out the invocation. p + is expected to be initialized and point to an OPCALL or OPCCALL + expression. The return value is a pointer to a temporary holding the + result of a COMPLEX or CHARACTER operation, or NULL. */ + +LOCAL expptr putcall(p0, temp) + expptr p0; + Addrp *temp; +{ + register Exprp p = (Exprp)p0; + chainp arglist; /* Pointer to actual arguments, if any */ + chainp charsp; /* List of copies of the variables which + hold the lengths of character + parameters (other than procedure + parameters) */ + chainp cp; /* Iterator over argument lists */ + register expptr q; /* Pointer to the current argument */ + Addrp fval; /* Function return value */ + int type; /* type of the call - presumably this was + set elsewhere */ + int byvalue; /* True iff we don't want to massage the + parameter list, since we're calling a C + library routine */ + char *s; + extern struct Listblock *mklist(); + + type = p -> vtype; + charsp = NULL; + byvalue = (p->opcode == OPCCALL); + +/* Verify the actual parameters */ + + if (p == (Exprp) NULL) + err ("putcall: NULL call expression"); + else if (p -> tag != TEXPR) + erri ("putcall: expected TEXPR, got '%d'", p -> tag); + +/* Find the argument list */ + + if(p->rightp && p -> rightp -> tag == TLIST) + arglist = p->rightp->listblock.listp; + else + arglist = NULL; + +/* Count the number of explicit arguments, including lengths of character + variables */ + + for(cp = arglist ; cp ; cp = cp->nextp) + if(!byvalue) { + q = (expptr) cp->datap; + if( ISCONST(q) ) + { + +/* Even constants are passed by reference, so we need to put them in the + literal table */ + + q = (expptr) putconst((Constp)q); + cp->datap = (char *) q; + } + +/* Save the length expression of character variables (NOT character + procedures) for the end of the argument list */ + + if( ISCHAR(q) && + (q->headblock.vclass != CLPROC + || q->headblock.vstg == STGARG + && q->tag == TADDR + && q->addrblock.uname_tag == UNAM_NAME + && q->addrblock.user.name->vprocclass == PTHISPROC)) + { + p0 = cpexpr(q->headblock.vleng); + charsp = mkchain((char *)p0, charsp); + if (q->headblock.vclass == CLUNKNOWN + && q->headblock.vstg == STGARG) + q->addrblock.user.name->vpassed = 1; + else if (q->tag == TADDR + && q->addrblock.uname_tag == UNAM_CONST) + p0->constblock.Const.ci + += q->addrblock.user.Const.ccp1.blanks; + } + } + charsp = revchain(charsp); + +/* If the routine is a CHARACTER function ... */ + + if(type == TYCHAR) + { + if( ISICON(p->vleng) ) + { + +/* Allocate a temporary to hold the return value of the function */ + + fval = mktmp(TYCHAR, p->vleng); + } + else { + err("adjustable character function"); + if (temp) + *temp = 0; + return 0; + } + } + +/* If the routine is a COMPLEX function ... */ + + else if( ISCOMPLEX(type) ) + fval = mktmp(type, ENULL); + else + fval = NULL; + +/* Write the function name, without taking its address */ + + p -> leftp = putx(fixtype(putaddr(p->leftp))); + + if(fval) + { + chainp prepend; + +/* Prepend a copy of the function return value buffer out as the first + argument. */ + + prepend = mkchain((char *)putx(putaddr(cpexpr((expptr)fval))), arglist); + +/* If it's a character function, also prepend the length of the result */ + + if(type==TYCHAR) + { + + prepend->nextp = mkchain((char *)putx(mkconv(TYLENG, + p->vleng)), arglist); + } + if (!(q = p->rightp)) + p->rightp = q = (expptr)mklist(CHNULL); + q->listblock.listp = prepend; + } + +/* Scan through the fortran argument list */ + + for(cp = arglist ; cp ; cp = cp->nextp) + { + q = (expptr) (cp->datap); + if (q == ENULL) + err ("putcall: NULL argument"); + +/* call putaddr only when we've got a parameter for a C routine or a + memory resident parameter */ + + if (q -> tag == TCONST && !byvalue) + q = (expptr) putconst ((Constp)q); + + if(q->tag==TADDR && (byvalue || q->addrblock.vstg!=STGREG) ) { + if (q->addrblock.parenused + && !byvalue && q->headblock.vtype != TYCHAR) + goto make_copy; + cp->datap = (char *)putaddr(q); + } + else if( ISCOMPLEX(q->headblock.vtype) ) + cp -> datap = (char *) putx (fixtype(putcxop(q))); + else if (ISCHAR(q) ) + cp -> datap = (char *) putx (fixtype((expptr)putchop(q))); + else if( ! ISERROR(q) ) + { + if(byvalue + || q->tag == TEXPR && q->exprblock.opcode == OPCHARCAST) + cp -> datap = (char *) putx(q); + else { + expptr t, t1; + +/* If we've got a register parameter, or (maybe?) a constant, save it in a + temporary first */ + make_copy: + t = (expptr) mktmp(q->headblock.vtype, q->headblock.vleng); + +/* Assign to temporary variables before invoking the subroutine or + function */ + + t1 = putassign( cpexpr(t), q ); + if (doin_setbound) + t = mkexpr(OPCOMMA_ARG, t1, t); + else + putout(t1); + cp -> datap = (char *) t; + } /* else */ + } /* if !ISERROR(q) */ + } + +/* Now adjust the lengths of the CHARACTER parameters */ + + for(cp = charsp ; cp ; cp = cp->nextp) + cp->datap = (char *)addrfix(putx( + /* in case MAIN has a character*(*)... */ + (s = cp->datap) ? mkconv(TYLENG,(expptr)s) + : ICON(0))); + +/* ... and add them to the end of the argument list */ + + hookup (arglist, charsp); + +/* Return the name of the temporary used to hold the results, if any was + necessary. */ + + if (temp) *temp = fval; + else frexpr ((expptr)fval); + + saveargtypes(p); + + return (expptr) p; +} + + + +/* putmnmx -- Put min or max. p must point to an EXPR, not just a + CONST */ + +LOCAL expptr putmnmx(p) +register expptr p; +{ + int op, op2, type; + expptr arg, qp, temp; + chainp p0, p1; + Addrp sp, tp; + char comment_buf[80]; + char *what; + + if(p->tag != TEXPR) + badtag("putmnmx", p->tag); + + type = p->exprblock.vtype; + op = p->exprblock.opcode; + op2 = op == OPMIN ? OPMIN2 : OPMAX2; + p0 = p->exprblock.leftp->listblock.listp; + free( (charptr) (p->exprblock.leftp) ); + free( (charptr) p ); + + /* special case for two addressable operands */ + + if (addressable((expptr)p0->datap) + && (p1 = p0->nextp) + && addressable((expptr)p1->datap) + && !p1->nextp) { + if (type == TYREAL && forcedouble) + op2 = op == OPMIN ? OPDMIN : OPDMAX; + p = mkexpr(op2, mkconv(type, cpexpr((expptr)p0->datap)), + mkconv(type, cpexpr((expptr)p1->datap))); + frchain(&p0); + return p; + } + + /* general case */ + + sp = mktmp(type, ENULL); + +/* We only need a second temporary if the arg list has an unaddressable + value */ + + tp = (Addrp) NULL; + qp = ENULL; + for (p1 = p0 -> nextp; p1; p1 = p1 -> nextp) + if (!addressable ((expptr) p1 -> datap)) { + tp = mktmp(type, ENULL); + qp = mkexpr(op2, cpexpr((expptr)sp), cpexpr((expptr)tp)); + qp = fixexpr((Exprp)qp); + break; + } /* if */ + +/* Now output the appropriate number of assignments and comparisons. Min + and max are implemented by the simple O(n) algorithm: + + min (a, b, c, d) ==> + { <type> t1, t2; + + t1 = a; + t2 = b; t1 = (t1 < t2) ? t1 : t2; + t2 = c; t1 = (t1 < t2) ? t1 : t2; + t2 = d; t1 = (t1 < t2) ? t1 : t2; + } +*/ + + if (!doin_setbound) { + switch(op) { + case OPLT: + case OPMIN: + case OPDMIN: + case OPMIN2: + what = "IN"; + break; + default: + what = "AX"; + } + sprintf (comment_buf, "Computing M%s", what); + p1_comment (comment_buf); + } + + p1 = p0->nextp; + temp = (expptr)p0->datap; + if (addressable(temp) && addressable((expptr)p1->datap)) { + p = mkconv(type, cpexpr(temp)); + arg = mkconv(type, cpexpr((expptr)p1->datap)); + temp = mkexpr(op2, p, arg); + if (!ISCONST(temp)) + temp = fixexpr((Exprp)temp); + p1 = p1->nextp; + } + p = putassign (cpexpr((expptr)sp), temp); + + for(; p1 ; p1 = p1->nextp) + { + if (addressable ((expptr) p1 -> datap)) { + arg = mkconv(type, cpexpr((expptr)p1->datap)); + temp = mkexpr(op2, cpexpr((expptr)sp), arg); + temp = fixexpr((Exprp)temp); + } else { + temp = (expptr) cpexpr (qp); + p = mkexpr(OPCOMMA, p, + putassign(cpexpr((expptr)tp), (expptr)p1->datap)); + } /* else */ + + if(p1->nextp) + p = mkexpr(OPCOMMA, p, + putassign(cpexpr((expptr)sp), temp)); + else { + if (type == TYREAL && forcedouble) + temp->exprblock.opcode = + op == OPMIN ? OPDMIN : OPDMAX; + if (doin_setbound) + p = mkexpr(OPCOMMA, p, temp); + else { + putout (p); + p = putx(temp); + } + if (qp) + frexpr (qp); + } /* else */ + } /* for */ + + frchain( &p0 ); + return p; +} + + + void +putwhile(p) + expptr p; +{ + long where; + int k, n; + + if (wh_next >= wh_last) + { + k = wh_last - wh_first; + n = k + 100; + wh_next = mem(n,0); + wh_last = wh_first + n; + if (k) + memcpy(wh_next, wh_first, k); + wh_first = wh_next; + wh_next += k; + wh_last = wh_first + n; + } + p1put(P1_WHILE1START); + where = ftell(pass1_file); + if( !ISLOGICAL((k = (p = fixtype(p))->headblock.vtype))) + { + if(k != TYERROR) + err("non-logical expression in DO WHILE statement"); + } + else { + p = putx(p); + *wh_next++ = ftell(pass1_file) > where; + p1put(P1_WHILE2START); + p1_expr(p); + } + } diff --git a/usr.bin/f2c/readme b/usr.bin/f2c/readme new file mode 100644 index 000000000000..ed88aaa81c75 --- /dev/null +++ b/usr.bin/f2c/readme @@ -0,0 +1,94 @@ +Type "make" to check the validity of the f2c source and compile f2c. + +On a PC, you may need to compile xsum.c with -DMSDOS (i.e., with +MSDOS #defined). If your system does not understand ANSI/ISO C +syntax (i.e., if you have a K&R C compiler), compile xsum.c with +-DKR_headers. (Eventually this will also be required of the f2c +source proper.) + +On non-Unix systems where files have separate binary and text modes, +you may need to "make xsumr.out" rather than "make xsum.out". + +If (in accordance with what follows) you need to modify the makefile +or any of the source files, first issue a "make xsum.out" (or, if +appropriate, "make xsumr.out") to check the validity of the f2c source, +then make your changes, then type "make f2c". + +The file usignal.h is for the benefit of strictly ANSI include files +on a UNIX system -- the ANSI signal.h does not define SIGHUP or SIGQUIT. +You may need to modify usignal.h if you are not running f2c on a UNIX +system. + +Should you get the message "xsum0.out xsum1.out differ", see what lines +are different (`diff xsum0.out xsum1.out`) and ask netlib to send you +the files in question "from f2c/src". For example, if exec.c and +expr.c have incorrect check sums, you would send netlib the message + send exec.c expr.c from f2c/src + +On some systems, the malloc and free in malloc.c let f2c run faster +than do the standard malloc and free. Other systems cannot tolerate +redefinition of malloc and free. If yours is such a system, you may +either modify the makefile appropriately, or simply execute + cc -c -DCRAY malloc.c +before typing "make". Still other systems have a -lmalloc that +provides performance competitive with that from malloc.c; you may +wish to compare the two on your system. + +On some BSD systems, you may need to create a file named "string.h" +whose single line is +#include <strings.h> +you may need to add " -Dstrchr=index" to the "CFLAGS =" assignment +in the makefile, and you may need to add " memset.o" to the "OBJECTS =" +assignment in the makefile -- see the comments in memset.c . + +For non-UNIX systems, you may need to change some things in sysdep.c, +such as the choice of intermediate file names. + +On some systems, you may need to modify parts of sysdep.h (which is +included by defs.h). In particular, for Sun 4.1 systems and perhaps +some others, you need to comment out the typedef of size_t. For some +systems (e.g., IRIX 4.0.1 and AIX) it is better to add +#define ANSI_Libraries +to the beginning of sysdep.h (or to supply -DANSI_Libraries in the +makefile). + +Alas, some systems #define __STDC__ but do not provide a true standard +(ANSI or ISO) C environment, e.g. do not provide stdlib.h . If yours +is such a system, then (a) you should complain loudly to your vendor +about __STDC__ being erroneously defined, and (b) you should insert +#undef __STDC__ +at the beginning of sysdep.h . You may need to make other adjustments. + +For some non-ANSI versions of stdio, you must change the values given +to binread and binwrite in sysdep.c from "rb" and "wb" to "r" and "w". +You may need to make this change if you run f2c and get an error +message of the form + Compiler error ... cannot open intermediate file ... + +On many systems, it is best to combine libF77 and libI77 into a single +library, say libf2c, as suggested in "readme from f2c". If you do this, +then you should adjust the definition of link_msg in sysdep.c +appropriately (e.g., replacing "-lF77 -lI77" by "-lf2c"). + +Some older C compilers object to + typedef void (*foo)(); +or to + typedef void zap; + zap (*foo)(); +If yours is such a compiler, change the definition of VOID in +f2c.h from void to int. + +For convenience with systems that use control-Z to denote end-of-file, +f2c treats control-Z characters (ASCII 26, '\x1a') that appear at the +beginning of a line as an end-of-file indicator. You can disable this +test by compiling lex.c with NO_EOF_CHAR_CHECK #defined, or can +change control-Z to some other character by #defining EOF_CHAR to +be the desired value. + +Please send bug reports to dmg@research.att.com . The old index file +(now called "readme" due to unfortunate changes in netlib conventions: +"send readme from f2c") will report recent changes in the recent-change +log at its end; all changes will be shown in the "changes" file +("send changes from f2c"). To keep current source, you will need to +request xsum0.out and version.c, in addition to the changed source +files. diff --git a/usr.bin/f2c/sysdep.c b/usr.bin/f2c/sysdep.c new file mode 100644 index 000000000000..81bc5af2eb0b --- /dev/null +++ b/usr.bin/f2c/sysdep.c @@ -0,0 +1,442 @@ +/**************************************************************** +Copyright 1990, 1991, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ +#include "defs.h" +#include "usignal.h" + +char binread[] = "rb", textread[] = "r"; +char binwrite[] = "wb", textwrite[] = "w"; +char *c_functions = "c_functions"; +char *coutput = "c_output"; +char *initfname = "raw_data"; +char *initbname = "raw_data.b"; +char *blkdfname = "block_data"; +char *p1_file = "p1_file"; +char *p1_bakfile = "p1_file.BAK"; +char *sortfname = "init_file"; +char *proto_fname = "proto_file"; + +char link_msg[] = "-lf2c -lm"; /* was "-lF77 -lI77 -lm -lc"; */ + +#ifndef TMPDIR +#ifdef MSDOS +#define TMPDIR "" +#else +#define TMPDIR "/tmp" +#endif +#endif + +char *tmpdir = TMPDIR; + + void +Un_link_all(cdelete) +{ + if (!debugflag) { + unlink(c_functions); + unlink(initfname); + unlink(p1_file); + unlink(sortfname); + unlink(blkdfname); + if (cdelete && coutput) + unlink(coutput); + } + } + + void +set_tmp_names() +{ + int k; + if (debugflag == 1) + return; + k = strlen(tmpdir) + 16; + c_functions = (char *)ckalloc(7*k); + initfname = c_functions + k; + initbname = initfname + k; + blkdfname = initbname + k; + p1_file = blkdfname + k; + p1_bakfile = p1_file + k; + sortfname = p1_bakfile + k; + { +#ifdef MSDOS + char buf[64], *s, *t; + if (!*tmpdir || *tmpdir == '.' && !tmpdir[1]) + t = ""; + else { + /* substitute \ for / to avoid confusion with a + * switch indicator in the system("sort ...") + * call in formatdata.c + */ + for(s = tmpdir, t = buf; *s; s++, t++) + if ((*t = *s) == '/') + *t = '\\'; + if (t[-1] != '\\') + *t++ = '\\'; + *t = 0; + t = buf; + } + sprintf(c_functions, "%sf2c_func", t); + sprintf(initfname, "%sf2c_rd", t); + sprintf(blkdfname, "%sf2c_blkd", t); + sprintf(p1_file, "%sf2c_p1f", t); + sprintf(p1_bakfile, "%sf2c_p1fb", t); + sprintf(sortfname, "%sf2c_sort", t); +#else + int pid = getpid(); + sprintf(c_functions, "%s/f2c%d_func", tmpdir, pid); + sprintf(initfname, "%s/f2c%d_rd", tmpdir, pid); + sprintf(blkdfname, "%s/f2c%d_blkd", tmpdir, pid); + sprintf(p1_file, "%s/f2c%d_p1f", tmpdir, pid); + sprintf(p1_bakfile, "%s/f2c%d_p1fb", tmpdir, pid); + sprintf(sortfname, "%s/f2c%d_sort", tmpdir, pid); +#endif + sprintf(initbname, "%s.b", initfname); + } + if (debugflag) + fprintf(diagfile, "%s %s %s %s %s %s\n", c_functions, + initfname, blkdfname, p1_file, p1_bakfile, sortfname); + } + + char * +c_name(s,ft)char *s; +{ + char *b, *s0; + int c; + + b = s0 = s; + while(c = *s++) + if (c == '/') + b = s; + if (--s < s0 + 3 || s[-2] != '.' + || ((c = *--s) != 'f' && c != 'F')) { + infname = s0; + Fatal("file name must end in .f or .F"); + } + *s = ft; + b = copys(b); + *s = c; + return b; + } + + static void +killed(sig) +{ + signal(SIGINT, SIG_IGN); +#ifdef SIGQUIT + signal(SIGQUIT, SIG_IGN); +#endif +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif + signal(SIGTERM, SIG_IGN); + Un_link_all(1); + exit(126); + } + + static void +sig1catch(sig) +{ + if (signal(sig, SIG_IGN) != SIG_IGN) + signal(sig, killed); + } + + static void +flovflo(sig) +{ + Fatal("floating exception during constant evaluation; cannot recover"); + /* vax returns a reserved operand that generates + an illegal operand fault on next instruction, + which if ignored causes an infinite loop. + */ + signal(SIGFPE, flovflo); +} + + void +sigcatch(sig) +{ + sig1catch(SIGINT); +#ifdef SIGQUIT + sig1catch(SIGQUIT); +#endif +#ifdef SIGHUP + sig1catch(SIGHUP); +#endif + sig1catch(SIGTERM); + signal(SIGFPE, flovflo); /* catch overflows */ + } + + +dofork() +{ +#ifdef MSDOS + Fatal("Only one Fortran input file allowed under MS-DOS"); +#else + int pid, status, w; + extern int retcode; + + if (!(pid = fork())) + return 1; + if (pid == -1) + Fatal("bad fork"); + while((w = wait(&status)) != pid) + if (w == -1) + Fatal("bad wait code"); + retcode |= status >> 8; +#endif + return 0; + } + +/* Initialization of tables that change with the character set... */ + +char escapes[Table_size]; + +#ifdef non_ASCII +char *str_fmt[Table_size]; +static char *str0fmt[127] = { /*}*/ +#else +char *str_fmt[Table_size] = { +#endif + "\\000", "\\001", "\\002", "\\003", "\\004", "\\005", "\\006", "\\007", + "\\b", "\\t", "\\n", "\\013", "\\f", "\\r", "\\016", "\\017", + "\\020", "\\021", "\\022", "\\023", "\\024", "\\025", "\\026", "\\027", + "\\030", "\\031", "\\032", "\\033", "\\034", "\\035", "\\036", "\\037", + " ", "!", "\\\"", "#", "$", "%%", "&", "'", + "(", ")", "*", "+", ",", "-", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", ":", ";", "<", "=", ">", "?", + "@", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "[", "\\\\", "]", "^", "_", + "`", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "{", "|", "}", "~" + }; + +#ifdef non_ASCII +char *chr_fmt[Table_size]; +static char *chr0fmt[127] = { /*}*/ +#else +char *chr_fmt[Table_size] = { +#endif + "\\0", "\\1", "\\2", "\\3", "\\4", "\\5", "\\6", "\\7", + "\\b", "\\t", "\\n", "\\13", "\\f", "\\r", "\\16", "\\17", + "\\20", "\\21", "\\22", "\\23", "\\24", "\\25", "\\26", "\\27", + "\\30", "\\31", "\\32", "\\33", "\\34", "\\35", "\\36", "\\37", + " ", "!", "\"", "#", "$", "%%", "&", "\\'", + "(", ")", "*", "+", ",", "-", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", ":", ";", "<", "=", ">", "?", + "@", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "[", "\\\\", "]", "^", "_", + "`", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "{", "|", "}", "~" + }; + + void +fmt_init() +{ + static char *str1fmt[6] = + { "\\b", "\\t", "\\n", "\\f", "\\r", "\\%03o" }; + register int i, j; + register char *s; + + /* str_fmt */ + +#ifdef non_ASCII + i = 0; +#else + i = 127; +#endif + for(; i < Table_size; i++) + str_fmt[i] = "\\%03o"; +#ifdef non_ASCII + for(i = 32; i < 127; i++) { + s = str0fmt[i]; + str_fmt[*(unsigned char *)s] = s; + } + str_fmt['"'] = "\\\""; +#else + if (Ansi == 1) + str_fmt[7] = chr_fmt[7] = "\\a"; +#endif + + /* chr_fmt */ + +#ifdef non_ASCII + for(i = 0; i < 32; i++) + chr_fmt[i] = chr0fmt[i]; +#else + i = 127; +#endif + for(; i < Table_size; i++) + chr_fmt[i] = "\\%o"; +#ifdef non_ASCII + for(i = 32; i < 127; i++) { + s = chr0fmt[i]; + j = *(unsigned char *)s; + if (j == '\\') + j = *(unsigned char *)(s+1); + chr_fmt[j] = s; + } +#endif + + /* escapes (used in lex.c) */ + + for(i = 0; i < Table_size; i++) + escapes[i] = i; + for(s = "btnfr0", i = 0; i < 6; i++) + escapes[*(unsigned char *)s++] = "\b\t\n\f\r"[i]; + /* finish str_fmt and chr_fmt */ + + if (Ansi) + str1fmt[5] = "\\v"; + if ('\v' == 'v') { /* ancient C compiler */ + str1fmt[5] = "v"; +#ifndef non_ASCII + escapes['v'] = 11; +#endif + } + else + escapes['v'] = '\v'; + for(s = "\b\t\n\f\r\v", i = 0; j = *(unsigned char *)s++;) + str_fmt[j] = chr_fmt[j] = str1fmt[i++]; + /* '\v' = 11 for both EBCDIC and ASCII... */ + chr_fmt[11] = Ansi ? "\\v" : "\\13"; + } + + + +/* Unless SYSTEM_SORT is defined, the following gives a simple + * in-core version of dsort(). On Fortran source with huge DATA + * statements, the in-core version may exhaust the available memory, + * in which case you might either recompile this source file with + * SYSTEM_SORT defined (if that's reasonable on your system), or + * replace the dsort below with a more elaborate version that + * does a merging sort with the help of auxiliary files. + */ + +#ifdef SYSTEM_SORT + +dsort(from, to) + char *from, *to; +{ + char buf[200]; + sprintf(buf, "sort <%s >%s", from, to); + return system(buf) >> 8; + } +#else + + static int +compare(a,b) + char *a, *b; +{ return strcmp(*(char **)a, *(char **)b); } + +dsort(from, to) + char *from, *to; +{ + extern char *Alloc(); + + struct Memb { + struct Memb *next; + int n; + char buf[32000]; + }; + typedef struct Memb memb; + memb *mb, *mb1; + register char *x, *x0, *xe; + register int c, n; + FILE *f; + char **z, **z0; + int nn = 0; + + f = opf(from, textread); + mb = (memb *)Alloc(sizeof(memb)); + mb->next = 0; + x0 = x = mb->buf; + xe = x + sizeof(mb->buf); + n = 0; + for(;;) { + c = getc(f); + if (x >= xe && (c != EOF || x != x0)) { + if (!n) + return 126; + nn += n; + mb->n = n; + mb1 = (memb *)Alloc(sizeof(memb)); + mb1->next = mb; + mb = mb1; + memcpy(mb->buf, x0, n = x-x0); + x0 = mb->buf; + x = x0 + n; + xe = x0 + sizeof(mb->buf); + n = 0; + } + if (c == EOF) + break; + if (c == '\n') { + ++n; + *x++ = 0; + x0 = x; + } + else + *x++ = c; + } + clf(&f, from, 1); + f = opf(to, textwrite); + if (x > x0) { /* shouldn't happen */ + *x = 0; + ++n; + } + mb->n = n; + nn += n; + if (!nn) /* shouldn't happen */ + goto done; + z = z0 = (char **)Alloc(nn*sizeof(char *)); + for(mb1 = mb; mb1; mb1 = mb1->next) { + x = mb1->buf; + n = mb1->n; + for(;;) { + *z++ = x; + if (--n <= 0) + break; + while(*x++); + } + } + qsort((char *)z0, nn, sizeof(char *), compare); + for(n = nn, z = z0; n > 0; n--) + fprintf(f, "%s\n", *z++); + free((char *)z0); + done: + clf(&f, to, 1); + do { + mb1 = mb->next; + free((char *)mb); + } + while(mb = mb1); + return 0; + } +#endif diff --git a/usr.bin/f2c/sysdep.h b/usr.bin/f2c/sysdep.h new file mode 100644 index 000000000000..aef7335ecbd7 --- /dev/null +++ b/usr.bin/f2c/sysdep.h @@ -0,0 +1,101 @@ +/**************************************************************** +Copyright 1990, 1991 by AT&T Bell Laboratories, Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +/* This file is included at the start of defs.h; this file + * is an initial attempt to gather in one place some declarations + * that may need to be tweaked on some systems. + */ + +#ifdef __STDC__ +#ifndef ANSI_Libraries +#define ANSI_Libraries +#endif +#ifndef ANSI_Prototypes +#define ANSI_Prototypes +#endif +#endif + +#ifdef __BORLANDC__ +#define MSDOS +extern int ind_printf(), nice_printf(); +#endif + +#ifdef __ZTC__ /* Zortech */ +#define MSDOS +extern int ind_printf(...), nice_printf(...); +#endif + +#ifdef MSDOS +#define ANSI_Libraries +#define ANSI_Prototypes +#define LONG_CAST (long) +#else +#define LONG_CAST +#endif + +#include <stdio.h> + +#ifdef ANSI_Libraries +#include <stddef.h> +#include <stdlib.h> +#else +char *calloc(), *malloc(), *memcpy(), *memset(), *realloc(); +typedef int size_t; +#ifdef ANSI_Prototypes +extern double atof(const char *); +#else +extern double atof(); +#endif +#endif + +#ifdef ANSI_Prototypes +extern char *gmem(int, int); +extern char *mem(int, int); +extern char *Alloc(int); +extern int* ckalloc(int); +#else +extern char *Alloc(), *gmem(), *mem(); +int *ckalloc(); +#endif + +/* On systems like VMS where fopen might otherwise create + * multiple versions of intermediate files, you may wish to + * #define scrub(x) unlink(x) + */ +#ifndef scrub +#define scrub(x) /* do nothing */ +#endif + +/* On systems that severely limit the total size of statically + * allocated arrays, you may need to change the following to + * extern char **chr_fmt, *escapes, **str_fmt; + * and to modify sysdep.c appropriately + */ +extern char *chr_fmt[], escapes[], *str_fmt[]; + +#include <string.h> + +#include "ctype.h" + +#define Table_size 256 +/* Table_size should be 1 << (bits/byte) */ diff --git a/usr.bin/f2c/tokens b/usr.bin/f2c/tokens new file mode 100644 index 000000000000..d97fb52a805d --- /dev/null +++ b/usr.bin/f2c/tokens @@ -0,0 +1,99 @@ +SEOS +SCOMMENT +SLABEL +SUNKNOWN +SHOLLERITH +SICON +SRCON +SDCON +SBITCON +SOCTCON +SHEXCON +STRUE +SFALSE +SNAME +SNAMEEQ +SFIELD +SSCALE +SINCLUDE +SLET +SASSIGN +SAUTOMATIC +SBACKSPACE +SBLOCK +SCALL +SCHARACTER +SCLOSE +SCOMMON +SCOMPLEX +SCONTINUE +SDATA +SDCOMPLEX +SDIMENSION +SDO +SDOUBLE +SELSE +SELSEIF +SEND +SENDFILE +SENDIF +SENTRY +SEQUIV +SEXTERNAL +SFORMAT +SFUNCTION +SGOTO +SASGOTO +SCOMPGOTO +SARITHIF +SLOGIF +SIMPLICIT +SINQUIRE +SINTEGER +SINTRINSIC +SLOGICAL +SNAMELIST +SOPEN +SPARAM +SPAUSE +SPRINT +SPROGRAM +SPUNCH +SREAD +SREAL +SRETURN +SREWIND +SSAVE +SSTATIC +SSTOP +SSUBROUTINE +STHEN +STO +SUNDEFINED +SWRITE +SLPAR +SRPAR +SEQUALS +SCOLON +SCOMMA +SCURRENCY +SPLUS +SMINUS +SSTAR +SSLASH +SPOWER +SCONCAT +SAND +SOR +SNEQV +SEQV +SNOT +SEQ +SLT +SGT +SLE +SGE +SNE +SENDDO +SWHILE +SSLASHD diff --git a/usr.bin/f2c/usignal.h b/usr.bin/f2c/usignal.h new file mode 100644 index 000000000000..ba4ee6ad44c8 --- /dev/null +++ b/usr.bin/f2c/usignal.h @@ -0,0 +1,7 @@ +#include <signal.h> +#ifndef SIGHUP +#define SIGHUP 1 /* hangup */ +#endif +#ifndef SIGQUIT +#define SIGQUIT 3 /* quit */ +#endif diff --git a/usr.bin/f2c/vax.c b/usr.bin/f2c/vax.c new file mode 100644 index 000000000000..e5a6572e2c4c --- /dev/null +++ b/usr.bin/f2c/vax.c @@ -0,0 +1,503 @@ +/**************************************************************** +Copyright 1990, 1992, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "defs.h" +#include "pccdefs.h" +#include "output.h" + +int regnum[] = { + 11, 10, 9, 8, 7, 6 }; + +/* Put out a constant integer */ + +prconi(fp, n) +FILEP fp; +ftnint n; +{ + fprintf(fp, "\t%ld\n", n); +} + + + +/* Put out a constant address */ + +prcona(fp, a) +FILEP fp; +ftnint a; +{ + fprintf(fp, "\tL%ld\n", a); +} + + + +prconr(fp, x, k) + FILEP fp; + int k; + Constp x; +{ + char *x0, *x1; + char cdsbuf0[64], cdsbuf1[64]; + + if (k > 1) { + if (x->vstg) { + x0 = x->Const.cds[0]; + x1 = x->Const.cds[1]; + } + else { + x0 = cds(dtos(x->Const.cd[0]), cdsbuf0); + x1 = cds(dtos(x->Const.cd[1]), cdsbuf1); + } + fprintf(fp, "\t%s %s\n", x0, x1); + } + else + fprintf(fp, "\t%s\n", x->vstg ? x->Const.cds[0] + : cds(dtos(x->Const.cd[0]), cdsbuf0)); +} + + +char *memname(stg, mem) + int stg; + long mem; +{ + static char s[20]; + + switch(stg) + { + case STGCOMMON: + case STGEXT: + sprintf(s, "_%s", extsymtab[mem].cextname); + break; + + case STGBSS: + case STGINIT: + sprintf(s, "v.%ld", mem); + break; + + case STGCONST: + sprintf(s, "L%ld", mem); + break; + + case STGEQUIV: + sprintf(s, "q.%ld", mem+eqvstart); + break; + + default: + badstg("memname", stg); + } + return(s); +} + +/* make_int_expr -- takes an arbitrary expression, and replaces all + occurrences of arguments with indirection */ + +expptr make_int_expr (e) +expptr e; +{ + if (e != ENULL) + switch (e -> tag) { + case TADDR: + if (e -> addrblock.vstg == STGARG + && !e->addrblock.isarray) + e = mkexpr (OPWHATSIN, e, ENULL); + break; + case TEXPR: + e -> exprblock.leftp = make_int_expr (e -> exprblock.leftp); + e -> exprblock.rightp = make_int_expr (e -> exprblock.rightp); + break; + default: + break; + } /* switch */ + + return e; +} /* make_int_expr */ + + + +/* prune_left_conv -- used in prolog() to strip type cast away from + left-hand side of parameter adjustments. This is necessary to avoid + error messages from cktype() */ + +expptr prune_left_conv (e) +expptr e; +{ + struct Exprblock *leftp; + + if (e && e -> tag == TEXPR && e -> exprblock.leftp && + e -> exprblock.leftp -> tag == TEXPR) { + leftp = &(e -> exprblock.leftp -> exprblock); + if (leftp -> opcode == OPCONV) { + e -> exprblock.leftp = leftp -> leftp; + free ((charptr) leftp); + } + } + + return e; +} /* prune_left_conv */ + + + static int wrote_comment; + static FILE *comment_file; + + static void +write_comment() +{ + if (!wrote_comment) { + wrote_comment = 1; + nice_printf (comment_file, "/* Parameter adjustments */\n"); + } + } + + static int * +count_args() +{ + register int *ac; + register chainp cp; + register struct Entrypoint *ep; + register Namep q; + + ac = (int *)ckalloc(nallargs*sizeof(int)); + + for(ep = entries; ep; ep = ep->entnextp) + for(cp = ep->arglist; cp; cp = cp->nextp) + if (q = (Namep)cp->datap) + ac[q->argno]++; + return ac; + } + + static int nu, *refs, *used; + static void awalk(); + + static void +aawalk(P) + struct Primblock *P; +{ + chainp p; + expptr q; + + for(p = P->argsp->listp; p; p = p->nextp) { + q = (expptr)p->datap; + if (q->tag != TCONST) + awalk(q); + } + if (P->namep->vtype == TYCHAR) { + if (q = P->fcharp) + awalk(q); + if (q = P->lcharp) + awalk(q); + } + } + + static void +afwalk(P) + struct Primblock *P; +{ + chainp p; + expptr q; + Namep np; + + for(p = P->argsp->listp; p; p = p->nextp) { + q = (expptr)p->datap; + switch(q->tag) { + case TPRIM: + np = q->primblock.namep; + if (np->vknownarg) + if (!refs[np->argno]++) + used[nu++] = np->argno; + if (q->primblock.argsp == 0) { + if (q->primblock.namep->vclass == CLPROC + && q->primblock.namep->vprocclass + != PTHISPROC + || q->primblock.namep->vdim != NULL) + continue; + } + default: + awalk(q); + /* no break */ + case TCONST: + continue; + } + } + } + + static void +awalk(e) + expptr e; +{ + Namep np; + top: + if (!e) + return; + switch(e->tag) { + default: + badtag("awalk", e); + case TCONST: + case TERROR: + case TLIST: + return; + case TADDR: + if (e->addrblock.uname_tag == UNAM_NAME) { + np = e->addrblock.user.name; + if (np->vknownarg && !refs[np->argno]++) + used[nu++] = np->argno; + } + e = e->addrblock.memoffset; + goto top; + case TPRIM: + np = e->primblock.namep; + if (np->vknownarg && !refs[np->argno]++) + used[nu++] = np->argno; + if (e->primblock.argsp && np->vclass != CLVAR) + afwalk((struct Primblock *)e); + else + aawalk((struct Primblock *)e); + return; + case TEXPR: + awalk(e->exprblock.rightp); + e = e->exprblock.leftp; + goto top; + } + } + + static chainp +argsort(p0) + chainp p0; +{ + Namep *args, q, *stack; + int i, nargs, nout, nst; + chainp *d, *da, p, rv, *rvp; + struct Dimblock *dp; + + if (!p0) + return p0; + for(nargs = 0, p = p0; p; p = p->nextp) + nargs++; + args = (Namep *)ckalloc(i = nargs*(sizeof(Namep) + 2*sizeof(chainp) + + 2*sizeof(int))); + memset((char *)args, 0, i); + stack = args + nargs; + d = (chainp *)(stack + nargs); + refs = (int *)(d + nargs); + used = refs + nargs; + + for(p = p0; p; p = p->nextp) { + q = (Namep) p->datap; + args[q->argno] = q; + } + for(p = p0; p; p = p->nextp) { + q = (Namep) p->datap; + if (!(dp = q->vdim)) + continue; + i = dp->ndim; + while(--i >= 0) + awalk(dp->dims[i].dimexpr); + awalk(dp->basexpr); + while(nu > 0) { + refs[i = used[--nu]] = 0; + d[i] = mkchain((char *)q, d[i]); + } + } + for(i = nst = 0; i < nargs; i++) + for(p = d[i]; p; p = p->nextp) + refs[((Namep)p->datap)->argno]++; + while(--i >= 0) + if (!refs[i]) + stack[nst++] = args[i]; + if (nst == nargs) { + rv = p0; + goto done; + } + nout = 0; + rv = 0; + rvp = &rv; + while(nst > 0) { + nout++; + q = stack[--nst]; + *rvp = p = mkchain((char *)q, CHNULL); + rvp = &p->nextp; + da = d + q->argno; + for(p = *da; p; p = p->nextp) + if (!--refs[(q = (Namep)p->datap)->argno]) + stack[nst++] = q; + frchain(*da); + } + if (nout < nargs) + for(i = 0; i < nargs; i++) + if (refs[i]) { + q = args[i]; + errstr("Can't adjust %.38s correctly\n\ + due to dependencies among arguments.", + q->fvarname); + *rvp = p = mkchain((char *)q, CHNULL); + rvp = &p->nextp; + frchain(d[i]); + } + done: + free((char *)args); + return rv; + } + +prolog(outfile, p) + FILE *outfile; + register chainp p; +{ + int addif, addif0, i, nd, size; + int *ac; + register Namep q; + register struct Dimblock *dp; + chainp p0, p1; + + if(procclass == CLBLOCK) + return; + p0 = p; + p1 = p = argsort(p); + wrote_comment = 0; + comment_file = outfile; + ac = 0; + +/* Compute the base addresses and offsets for the array parameters, and + assign these values to local variables */ + + addif = addif0 = nentry > 1; + for(; p ; p = p->nextp) + { + q = (Namep) p->datap; + if(dp = q->vdim) /* if this param is an array ... */ + { + expptr Q, expr; + + /* See whether to protect the following with an if. */ + /* This only happens when there are multiple entries. */ + + nd = dp->ndim - 1; + if (addif0) { + if (!ac) + ac = count_args(); + if (ac[q->argno] == nentry) + addif = 0; + else if (dp->basexpr + || dp->baseoffset->constblock.Const.ci) + addif = 1; + else for(addif = i = 0; i <= nd; i++) + if (dp->dims[i].dimexpr + && (i < nd || !q->vlastdim)) { + addif = 1; + break; + } + if (addif) { + write_comment(); + nice_printf(outfile, "if (%s) {\n", /*}*/ + q->cvarname); + next_tab(outfile); + } + } + for(i = 0 ; i <= nd; ++i) + +/* Store the variable length of each dimension (which is fixed upon + runtime procedure entry) into a local variable */ + + if ((Q = dp->dims[i].dimexpr) + && (i < nd || !q->vlastdim)) { + expr = (expptr)cpexpr(Q); + write_comment(); + out_and_free_statement (outfile, mkexpr (OPASSIGN, + fixtype(cpexpr(dp->dims[i].dimsize)), expr)); + } /* if dp -> dims[i].dimexpr */ + +/* size will equal the size of a single element, or -1 if the type is + variable length character type */ + + size = typesize[ q->vtype ]; + if(q->vtype == TYCHAR) + if( ISICON(q->vleng) ) + size *= q->vleng->constblock.Const.ci; + else + size = -1; + + /* Fudge the argument pointers for arrays so subscripts + * are 0-based. Not done if array bounds are being checked. + */ + if(dp->basexpr) { + +/* Compute the base offset for this procedure */ + + write_comment(); + out_and_free_statement (outfile, mkexpr (OPASSIGN, + cpexpr(fixtype(dp->baseoffset)), + cpexpr(fixtype(dp->basexpr)))); + } /* if dp -> basexpr */ + + if(! checksubs) { + if(dp->basexpr) { + expptr tp; + +/* If the base of this array has a variable adjustment ... */ + + tp = (expptr) cpexpr (dp -> baseoffset); + if(size < 0 || q -> vtype == TYCHAR) + tp = mkexpr (OPSTAR, tp, cpexpr (q -> vleng)); + + write_comment(); + tp = mkexpr (OPMINUSEQ, + mkconv (TYADDR, (expptr)p->datap), + mkconv(TYINT, fixtype + (fixtype (tp)))); +/* Avoid type clash by removing the type conversion */ + tp = prune_left_conv (tp); + out_and_free_statement (outfile, tp); + } else if(dp->baseoffset->constblock.Const.ci != 0) { + +/* if the base of this array has a nonzero constant adjustment ... */ + + expptr tp; + + write_comment(); + if(size > 0 && q -> vtype != TYCHAR) { + tp = prune_left_conv (mkexpr (OPMINUSEQ, + mkconv (TYADDR, (expptr)p->datap), + mkconv (TYINT, fixtype + (cpexpr (dp->baseoffset))))); + out_and_free_statement (outfile, tp); + } else { + tp = prune_left_conv (mkexpr (OPMINUSEQ, + mkconv (TYADDR, (expptr)p->datap), + mkconv (TYINT, fixtype + (mkexpr (OPSTAR, cpexpr (dp -> baseoffset), + cpexpr (q -> vleng)))))); + out_and_free_statement (outfile, tp); + } /* else */ + } /* if dp -> baseoffset -> const */ + } /* if !checksubs */ + + if (addif) { + nice_printf(outfile, /*{*/ "}\n"); + prev_tab(outfile); + } + } + } + if (wrote_comment) + nice_printf (outfile, "\n/* Function Body */\n"); + if (ac) + free((char *)ac); + if (p0 != p1) + frchain(p1); +} /* prolog */ diff --git a/usr.bin/f2c/version.c b/usr.bin/f2c/version.c new file mode 100644 index 000000000000..e1fabf5f4f4d --- /dev/null +++ b/usr.bin/f2c/version.c @@ -0,0 +1,2 @@ +char F2C_version[] = "19931217"; +char xxxvers[] = "\n@(#) FORTRAN 77 to C Translator, VERSION 19931217\n"; diff --git a/usr.bin/f2c/xsum.c b/usr.bin/f2c/xsum.c new file mode 100644 index 000000000000..817da215fb68 --- /dev/null +++ b/usr.bin/f2c/xsum.c @@ -0,0 +1,233 @@ +/**************************************************************** +Copyright 1990, 1993 by AT&T Bell Laboratories and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +Bellcore or any of their entities not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. + +AT&T and Bellcore disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall AT&T or Bellcore be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether +in an action of contract, negligence or other tortious action, +arising out of or in connection with the use or performance of +this software. +****************************************************************/ + +#include "stdio.h" +#ifndef KR_headers +#include "stdlib.h" +#include "fcntl.h" /* for declaration of open, O_RDONLY */ +#endif +#ifdef MSDOS +#include "io.h" +#endif +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif +#ifndef O_BINARY +#define O_BINARY O_RDONLY +#endif + + char *progname; + static int ignore_cr; + + void +#ifdef KR_headers +usage(rc) +#else +usage(int rc) +#endif +{ + fprintf(stderr, "usage: %s [-r] [file [file...]]\n\ + option -r ignores carriage return characters\n", progname); + exit(rc); + } + +typedef unsigned char Uchar; + + long +#ifdef KR_headers +sum32(sum, x, n) + register long sum; + register Uchar *x; + int n; +#else +sum32(register long sum, register Uchar *x, int n) +#endif +{ + register Uchar *xe; + static long crc_table[256] = { + 0, 151466134, 302932268, 453595578, + -9583591, -160762737, -312236747, -463170141, + -19167182, -136529756, -321525474, -439166584, + 28724267, 145849533, 330837255, 448732561, + -38334364, -189783822, -273059512, -423738914, + 47895677, 199091435, 282375505, 433292743, + 57448534, 174827712, 291699066, 409324012, + -67019697, -184128295, -300991133, -418902539, + -76668728, -227995554, -379567644, -530091662, + 67364049, 218420295, 369985021, 520795499, + 95791354, 213031020, 398182870, 515701056, + -86479645, -203465611, -388624945, -506380967, + 114897068, 266207290, 349655424, 500195606, + -105581387, -256654301, -340093543, -490887921, + -134039394, -251295736, -368256590, -485758684, + 124746887, 241716241, 358686123, 476458301, + -153337456, -2395898, -455991108, -304803798, + 162629001, 11973919, 465560741, 314102835, + 134728098, 16841012, 436840590, 319723544, + -144044613, -26395347, -446403433, -329032703, + 191582708, 40657250, 426062040, 274858062, + -200894995, -50223749, -435620671, -284179369, + -172959290, -55056048, -406931222, -289830788, + 182263263, 64630089, 416513267, 299125861, + 229794136, 78991822, 532414580, 381366498, + -220224191, -69691945, -523123603, -371788549, + -211162774, -93398532, -513308602, -396314416, + 201600371, 84090341, 503991391, 386759881, + -268078788, -117292630, -502591472, -351526778, + 258520357, 107972019, 493278217, 341959839, + 249493774, 131713432, 483432482, 366454964, + -239911657, -122417791, -474129349, -356881235, + -306674912, -457198666, -4791796, -156118374, + 315967289, 466778031, 14362133, 165418627, + 325258002, 442776452, 23947838, 141187752, + -334573813, -452329571, -33509849, -150495567, + 269456196, 419996626, 33682024, 184992510, + -278767779, -429561909, -43239823, -194312473, + -288089226, -405591072, -52790694, -170046772, + 297394031, 415166457, 62373443, 179343061, + 383165416, 533828478, 81314500, 232780370, + -373594127, -524527769, -72022307, -223201717, + -401789990, -519431348, -100447498, -217810336, + 392228803, 510123861, 91131631, 208256633, + -345918580, -496598246, -110112096, -261561802, + 336361365, 487278339, 100800185, 251995695, + 364526526, 482151208, 129260178, 246639108, + -354943065, -472854735, -119955829, -237064675, + 459588272, 308539942, 157983644, 7181066, + -469170519, -317835713, -167286907, -16754925, + -440448382, -323454444, -139383890, -21619912, + 450006683, 332774925, 148697015, 31186721, + -422325548, -271261118, -186797064, -36011154, + 431888077, 280569435, 196114401, 45565815, + 403200742, 286222960, 168180682, 50400092, + -412770561, -295522711, -177471533, -59977915, + -536157576, -384970002, -234585260, -83643454, + 526853729, 375396087, 225003341, 74348507, + 517040714, 399923932, 215944038, 98057200, + -507728301, -390357307, -206385281, -88735767, + 498987548, 347783818, 263426864, 112501670, + -489671163, -338229613, -253864151, -103192641, + -479823314, -362722632, -244835582, -126932076, + 470531639, 353144481, 235265819, 117632909 + }; + + xe = x + n; + while(x < xe) + sum = crc_table[(sum ^ *x++) & 0xff] ^ (sum >> 8 & 0xffffff); + return sum; + } + + int +#ifdef KR_headers +cr_purge(buf, n) + Uchar *buf; + int n; +#else +cr_purge(Uchar *buf, int n) +#endif +{ + register Uchar *b, *b1, *be; + b = buf; + be = b + n; + while(b < be) + if (*b++ == '\r') { + b1 = b - 1; + while(b < be) + if ((*b1 = *b++) != '\r') + b1++; + return b1 - buf; + } + return n; + } + +static Uchar Buf[16*1024]; + + void +#ifdef KR_headers +process(s, x) + char *s; + int x; +#else +process(char *s, int x) +#endif +{ + register int n; + long fsize, sum; + + sum = 0; + fsize = 0; + while((n = read(x, (char *)Buf, sizeof(Buf))) > 0) { + if (ignore_cr) + n = cr_purge(Buf, n); + fsize += n; + sum = sum32(sum, Buf, n); + } + sum &= 0xffffffff; + if (n==0) + printf("%s\t%lx\t%ld\n", s, sum & 0xffffffff, fsize); + else { perror(s); } + close(x); + } + +#ifdef KR_headers +main(argc, argv) + char **argv; +#else +main(int argc, char **argv) +#endif +{ + int x; + char *s; + static int rc; + + progname = *argv; + s = *++argv; + if (s && *s == '-') { + switch(s[1]) { + case '?': + usage(0); + case 'r': + ignore_cr = 1; + case '-': + break; + default: + fprintf(stderr, "invalid option %s\n", s); + usage(1); + } + s = *++argv; + } + if (s) do { + x = open(s, O_RDONLY|O_BINARY); + if (x < 0) { + fprintf(stderr, "%s: can't open %s\n", progname, s); + rc |= 1; + } + else + process(s, x); + } + while(s = *++argv); + else { + process("/dev/stdin", fileno(stdin)); + } + return rc; + } diff --git a/usr.bin/file/file.1 b/usr.bin/file/file.1 index b805b46bef0d..5b34115decee 100644 --- a/usr.bin/file/file.1 +++ b/usr.bin/file/file.1 @@ -147,7 +147,7 @@ option causes symlinks to be followed, as the like-named option in This program is believed to exceed the System V Interface Definition of FILE(CMD), as near as one can determine from the vague language contained therein. -Its behaviour is mostly compatible with the System V program of the same name. +Its behavior is mostly compatible with the System V program of the same name. This version knows more magic, however, so it will produce different (albeit more accurate) output in many cases. .PP @@ -312,7 +312,7 @@ The magic file and keywords should have regular expression support. Their use of ASCII TAB as a field delimiter is ugly and makes it hard to edit the files, but is entrenched. .PP -It might be advisable to allow upper-case letters in keywords +It might be advisible to allow upper-case letters in keywords for e.g., troff commands vs man page macros. Regular expression support would make this easy. .PP diff --git a/usr.bin/file/magdir/freebsd b/usr.bin/file/magdir/freebsd index 9b5e1f9682bf..756b0acf91e3 100644 --- a/usr.bin/file/magdir/freebsd +++ b/usr.bin/file/magdir/freebsd @@ -1,5 +1,12 @@ # the following are for 386BSD/FreeBSD -0 long 0410 pure executable -0 long 0413 demand paged executable ->16 long >0 not stripped +0 long 0410 pure executable +0 long 0413 demand paged executable +0 long&077777777 041400314 FreeBSD/i386 demand paged +>3 byte &0x80 +>>20 long <4096 shared library +>>20 long =4096 dynamically linked executable +>>20 long >4096 dynamically linked executable +>3 byte ^0x80 executable +>16 long >0 not stripped + diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h index 1f02f12bb477..6f5a29dad8e9 100644 --- a/usr.bin/find/extern.h +++ b/usr.bin/find/extern.h @@ -65,6 +65,8 @@ PLAN *c_nouser __P((void)); PLAN *c_path __P((char *)); PLAN *c_perm __P((char *)); PLAN *c_print __P((void)); +PLAN *c_print0 __P((void)); +PLAN *c_printf __P((char *)); PLAN *c_prune __P((void)); PLAN *c_size __P((char *)); PLAN *c_type __P((char *)); diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1 index b6ce6b9d58d2..49d4d7af4bdb 100644 --- a/usr.bin/find/find.1 +++ b/usr.bin/find/find.1 @@ -189,19 +189,6 @@ True if the difference between the file last modification time and the time was started, rounded up to the next full 24\-hour period, is .Ar n 24\-hour periods. -.It Ic \&-ok Ar utility Ns Op argument ... ; -The -.Ic \&-ok -primary is identical to the -.Ic -exec -primary with the exception that -.Nm find -requests user affirmation for the execution of the utility by printing -a message to the terminal and reading a response. -If the response is other than ``y'' the command is not executed and the -value of the -.Ar \&ok -expression is false. .It Ic -name Ar pattern True if the last component of the pathname being examined matches .Ar pattern . @@ -217,6 +204,19 @@ True if the current file has a more recent last modification time than True if the file belongs to an unknown user. .It Ic -nogroup True if the file belongs to an unknown group. +.It Ic \&-ok Ar utility Ns Op argument ... ; +The +.Ic \&-ok +primary is identical to the +.Ic -exec +primary with the exception that +.Nm find +requests user affirmation for the execution of the utility by printing +a message to the terminal and reading a response. +If the response is other than ``y'' the command is not executed and the +value of the +.Ar \&ok +expression is false. .It Ic -path Ar pattern True if the pathname being examined matches .Ar pattern . @@ -258,12 +258,28 @@ Note, the first character of a symbolic mode may not be a dash (``\-''). .It Ic -print This primary always evaluates to true. It prints the pathname of the current file to standard output. -The expression is appended to the user specified expression if neither +The expression is appended to the user specified expression if none of .Ic -exec , -.Ic -ls -or +.Ic -ls , +.Ic -print0 , +.Ic -printf +and .Ic \&-ok -is specified. +are specified. +.It Ic -print0 +This primary prints the pathname of the current file to standard output, +followed by a NUL (ASCII 0) character. No newline is output. It +is intended for use when the files being reported may contain newlines or +other special characters. +.It Ic -printf +This primary takes one argument, which is interpreted as a format string +to be passed to +.Xr printf 3 . +It must contain exactly one +.Dq Li \&%s +format specifier, with or without modifier flags, which receives the +full pathname of the current file. No C-style escape processing is +performed. .It Ic -prune This primary always evaluates to true. It causes @@ -375,14 +391,21 @@ and owned by ``wnj''. .It Li "find / \e( -newer ttt -or -user wnj \e) -print" Print out a list of all the files that are either owned by ``wnj'' or that are newer than ``ttt''. +.It Li "find / -name \e*~ -print0 | perl -n0e unlink" +Find all the +.Xr emacs 1 +backup files and delete them quickly using +.Xr perl 1 . .El .Sh SEE ALSO .Xr chmod 1 , .Xr locate 1 , +.Xr xargs 1 , .Xr stat 2 , .Xr fts 3 , .Xr getpwent 3 , .Xr getgrent 3 , +.Xr printf 3 , .Xr strmode 3 , .Xr symlink 7 .Sh STANDARDS @@ -397,9 +420,11 @@ The and .Fl X options and the -.Ic -inum +.Ic -inum , +.Ic -ls , +.Ic -print0 , and -.Ic -ls +.Ic -printf primaries are extensions to .St -p1003.2 . .Pp diff --git a/usr.bin/find/find.h b/usr.bin/find/find.h index 4c4ffa54e9bd..43ac66a9e818 100644 --- a/usr.bin/find/find.h +++ b/usr.bin/find/find.h @@ -57,6 +57,8 @@ typedef struct _plandata { #define F_MTFLAG 1 /* fstype */ #define F_MTTYPE 2 #define F_ATLEAST 1 /* perm */ +#define F_PRINT0 1 +#define F_PRINTF 2 int flags; /* private flags */ enum ntype type; /* plan node type */ union { diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index df053f9a188d..c56634f3e994 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -673,7 +673,7 @@ f_nogroup(plan, entry) { char *group_from_gid(); - return (group_from_gid(entry->fts_statp->st_gid, 1) ? 1 : 0); + return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); } PLAN * @@ -697,7 +697,7 @@ f_nouser(plan, entry) { char *user_from_uid(); - return (user_from_uid(entry->fts_statp->st_uid, 1) ? 1 : 0); + return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); } PLAN * @@ -790,7 +790,17 @@ f_print(plan, entry) PLAN *plan; FTSENT *entry; { - (void)printf("%s\n", entry->fts_path); + if (plan->flags & F_PRINTF) { + printf(plan->c_data, entry->fts_path); + } else { + fputs(entry->fts_path, stdout); + } + + if (plan->flags & F_PRINT0) { + fputc('\0', stdout); + } else { + fputc('\n', stdout); + } return (1); } @@ -801,6 +811,27 @@ c_print() return (palloc(N_PRINT, f_print)); } + +PLAN * +c_print0() +{ + PLAN *rv = palloc(N_PRINT, f_print); + rv->flags = F_PRINT0; + isoutput = 1; + return rv; +} + +PLAN * +c_printf(arg) + char *arg; +{ + PLAN *rv = palloc(N_PRINT, f_print); + rv->flags = F_PRINTF; + rv->c_data = arg; + isoutput = 1; + return rv; +} + /* * -prune functions -- diff --git a/usr.bin/find/main.c b/usr.bin/find/main.c index 073a5f030def..19f6470761a0 100644 --- a/usr.bin/find/main.c +++ b/usr.bin/find/main.c @@ -113,6 +113,9 @@ main(argc, argv) if ((dotfd = open(".", O_RDONLY, 0)) < 0) err(1, ".:"); + /* make output interperse properly with subprocesses */ + setlinebuf(stdout); + find_execute(find_formplan(argv), start); exit(0); } diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c index 87337ca671d2..db142915d04a 100644 --- a/usr.bin/find/option.c +++ b/usr.bin/find/option.c @@ -77,6 +77,8 @@ static OPTION options[] = { { "-path", N_PATH, c_path, O_ARGV }, { "-perm", N_PERM, c_perm, O_ARGV }, { "-print", N_PRINT, c_print, O_ZERO }, + { "-print0", N_PRINT, c_print0, O_ZERO }, + { "-printf", N_PRINT, c_printf, O_ARGV }, { "-prune", N_PRUNE, c_prune, O_ZERO }, { "-size", N_SIZE, c_size, O_ARGV }, { "-type", N_TYPE, c_type, O_ARGV }, diff --git a/usr.bin/finger/finger.1 b/usr.bin/finger/finger.1 index 05760b5f1f48..aac0e2e14cf1 100644 --- a/usr.bin/finger/finger.1 +++ b/usr.bin/finger/finger.1 @@ -52,7 +52,7 @@ Options are: .It Fl s .Nm Finger displays the user's login name, real name, terminal name and write -status (as a ``*'' after the terminal name if write permission is +status (as a ``*'' before the terminal name if write permission is denied), idle time, login time, office location and office phone number. .Pp diff --git a/usr.bin/fmt/fmt.c b/usr.bin/fmt/fmt.c index 7ddc34e6cb1d..ef7ded6f751b 100644 --- a/usr.bin/fmt/fmt.c +++ b/usr.bin/fmt/fmt.c @@ -151,7 +151,7 @@ fmt(fi) c = getc(fi); continue; } - if ((c < ' ' || c >= 0177) && c != '\t') { + if (c < ' ' && c != '\t' || c == 0177) { c = getc(fi); continue; } @@ -172,7 +172,7 @@ fmt(fi) col = 0; cp = linebuf; cp2 = canonb; - while (c = *cp++) { + while (c = (unsigned char) *cp++) { if (c != '\t') { col++; if (cp2-canonb < BUFSIZ-1) @@ -269,7 +269,7 @@ split(line) * space. */ while (*cp && *cp != ' ') { - if (*cp == '\\' && isspace(cp[1])) + if (*cp == '\\' && isspace((unsigned char)cp[1])) *cp2++ = *cp++; *cp2++ = *cp++; wordl++;/* LIZ@UOM 6/18/85 */ diff --git a/usr.bin/from/from.c b/usr.bin/from/from.c index ea5b849ae69f..4758f810f185 100644 --- a/usr.bin/from/from.c +++ b/usr.bin/from/from.c @@ -46,6 +46,7 @@ static char sccsid[] = "@(#)from.c 5.7 (Berkeley) 3/1/91"; #include <pwd.h> #include <stdio.h> #include <paths.h> +#include <errno.h> main(argc, argv) int argc; @@ -94,7 +95,8 @@ main(argc, argv) file = buf; } if (!freopen(file, "r", stdin)) { - fprintf(stderr, "from: can't read %s.\n", file); + if(errno != ENOENT) + fprintf(stderr, "from: can't read %s.\n", file); exit(1); } for (newline = 1; fgets(buf, sizeof(buf), stdin);) { diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c index 56cc42806f7b..20c8d023e4d8 100644 --- a/usr.bin/ftp/cmds.c +++ b/usr.bin/ftp/cmds.c @@ -181,6 +181,12 @@ setpeer(argc, argv) if (cp) *cp = c; } + /* + * UNIX is a registered trademark of someone. + * Unfortunately, it is also a mandatory part of + * the protocol we talk with the server FTP, so + * we are stuck with it now. + */ if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { if (proxy) unix_proxy = 1; diff --git a/usr.bin/getopt/Makefile b/usr.bin/getopt/Makefile index 0a50de330bf2..c2a538d87973 100644 --- a/usr.bin/getopt/Makefile +++ b/usr.bin/getopt/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/getopt/Makefile,v 1.1 1993/07/26 22:22:35 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/getopt/Makefile,v 1.1 1993/07/26 22:22:35 nate Exp $ # PROG = getopt diff --git a/usr.bin/gprof/arcs.c b/usr.bin/gprof/arcs.c index 42c25ff6bb24..cb843f0e4327 100644 --- a/usr.bin/gprof/arcs.c +++ b/usr.bin/gprof/arcs.c @@ -35,6 +35,7 @@ static char sccsid[] = "@(#)arcs.c 5.6 (Berkeley) 6/1/90"; #endif /* not lint */ +#include <stdlib.h> #include "gprof.h" /* @@ -45,7 +46,6 @@ addarc( parentp , childp , count ) nltype *childp; long count; { - arctype *calloc(); arctype *arcp; # ifdef DEBUG @@ -68,7 +68,7 @@ addarc( parentp , childp , count ) arcp -> arc_count += count; return; } - arcp = calloc( 1 , sizeof *arcp ); + arcp = (arctype *) calloc( 1 , sizeof *arcp ); arcp -> arc_parentp = parentp; arcp -> arc_childp = childp; arcp -> arc_count = count; diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c index 2dd5a2a26424..cb4abcf28b94 100644 --- a/usr.bin/hexdump/odsyntax.c +++ b/usr.bin/hexdump/odsyntax.c @@ -143,7 +143,7 @@ oldsyntax(argc, argvp) argc -= optind; *argvp += optind; - odoffset(argc, argvp); + if(argc > 1) odoffset(argc, argvp); } #define ishexdigit(c) \ diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c index 5f946625ff30..3b4a774d336a 100644 --- a/usr.bin/id/id.c +++ b/usr.bin/id/id.c @@ -268,7 +268,7 @@ user(pw) id = pw->pw_uid; (void)printf("uid=%u(%s)", id, pw->pw_name); (void)printf(" gid=%u", pw->pw_gid); - if (gr = getgrgid(id)) + if (gr = getgrgid(pw->pw_gid)) (void)printf("(%s)", gr->gr_name); for (fmt = " groups=%u(%s)", lastid = -1; gr = getgrent(); lastid = id) { diff --git a/usr.bin/ipcrm/Makefile b/usr.bin/ipcrm/Makefile new file mode 100644 index 000000000000..196c08d611bb --- /dev/null +++ b/usr.bin/ipcrm/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 5.2 (Berkeley) 5/11/90 + +PROG= ipcrm +NOMAN= noman + +.include <bsd.prog.mk> diff --git a/usr.bin/ipcrm/ipcrm.c b/usr.bin/ipcrm/ipcrm.c new file mode 100644 index 000000000000..1becaad61c6d --- /dev/null +++ b/usr.bin/ipcrm/ipcrm.c @@ -0,0 +1,204 @@ +/* + * Implement the SYSV ipcrm command. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/msg.h> +#include <sys/shm.h> +#include <errno.h> + +#define OMIT_SHM /* don't handle shm stuff */ + +int +getnumber(char *num,long *lptr) +{ + char *end; + + if ( num == NULL ) { + fprintf(stderr,"ipcrm: missing numeric parameter\n"); + return(0); + } + + *lptr = strtol(num, &end, 0); + if ( *num == '\0' || *end != '\0' ) { + fprintf(stderr,"ipcrm: can't convert \"%s\" to an integer\n",num); + return(0); + } + if ( *lptr == LONG_MAX || *lptr == LONG_MIN ) { + fprintf(stderr,"ipcrm: absurd numeric parameter \"%s\"\n",num); + return(0); + } + return(1); +} + +main(int argc,char **argv) +{ + char *parm; + char tbuf[10000]; + int errcnt = 0; + long id; + long key; + + while ( (parm = *++argv) != NULL ) { + + if ( strcmp(parm,"-q") == 0 ) { + if ( getnumber(*++argv,&id) ) { + if ( msgctl(id,IPC_RMID,NULL) == 0 ) { + printf("msqid %s deleted\n",*argv); + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV message passing not configured in kernel\n"); + errcnt += 1; + } else { + strcpy(tbuf,"can't remove msqid "); + strcat(tbuf,*argv); + perror(tbuf); + errcnt += 1; + } + } else { + errcnt += 1; + } + } else if ( strcmp(parm,"-m") == 0 ) { + if ( getnumber(*++argv,&id) ) { +#ifndef OMIT_SHM + %%% untested %%% + if ( shmctl(id,IPC_RMID,NULL) == 0 ) { + printf("shmid %s deleted\n",*argv); + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV shared memory not configured in kernel\n"); + errcnt += 1; + } else { + strcpy(tbuf,"can't remove shmid "); + strcat(tbuf,*argv); + perror(tbuf); + errcnt += 1; + } +#else + fprintf(stderr,"ipcrm: sorry, this version of ipcrm doesn't support shared memory\n"); + errcnt += 1; +#endif + } else { + errcnt += 1; + } + } else if ( strcmp(parm,"-s") == 0 ) { + if ( getnumber(*++argv,&id) ) { + union semun junk; + if ( semctl(id,0,IPC_RMID,junk) == 0 ) { + printf("semid %s deleted\n",*argv); + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV semaphores not configured in kernel\n"); + errcnt += 1; + } else { + strcpy(tbuf,"can't remove semid "); + strcat(tbuf,*argv); + perror(tbuf); + errcnt += 1; + } + } else { + errcnt += 1; + } + } else if ( strcmp(parm,"-Q") == 0 ) { + if ( getnumber(*++argv,&key) ) { + if ( key == IPC_PRIVATE ) { + fprintf(stderr,"ipcrm: can't remove private message queues\n"); + } else { + strcpy(tbuf,"can't access msq key "); + strcat(tbuf,*argv); + if ( (id = msgget(key,0000)) >= 0 ) { + strcpy(tbuf,"can't remove msq key "); + strcat(tbuf,*argv); + if ( msgctl(id,IPC_RMID,NULL) == 0 ) { + printf("msq key %s deleted\n",*argv); + } else { + perror(tbuf); + errcnt += 1; + } + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV message passing not configured in kernel\n"); + errcnt += 1; + } else { + perror(tbuf); + errcnt += 1; + } + } + } else { + errcnt += 1; + } + } else if ( strcmp(parm,"-M") == 0 ) { + if ( getnumber(*++argv,&key) ) { +#ifndef OMIT_SHM + %%% untested %%% + if ( key == IPC_PRIVATE ) { + fprintf(stderr,"ipcrm: can't remove private shared memory segments\n"); + } else { + strcpy(tbuf,"can't access shm key "); + strcat(tbuf,*argv); + if ( (id = shmget(key,0,0000)) >= 0 ) { + strcpy(tbuf,"can't remove shm key "); + strcat(tbuf,*argv); + if ( shmctl(id,IPC_RMID,NULL) == 0 ) { + printf("shm key %s deleted\n",*argv); + } else { + perror(tbuf); + errcnt += 1; + } + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV shared memory not configured in kernel\n"); + errcnt += 1; + } else { + perror(tbuf); + errcnt += 1; + } + } +#else + fprintf(stderr,"ipcrm: sorry, this version of ipcrm doesn't support shared memory\n"); + errcnt += 1; +#endif + } else { + errcnt += 1; + } + } else if ( strcmp(parm,"-S") == 0 ) { + if ( getnumber(*++argv,&key) ) { + if ( key == IPC_PRIVATE ) { + fprintf(stderr,"ipcrm: can't remove private semaphores\n"); + } else { + strcpy(tbuf,"can't access sem key "); + strcat(tbuf,*argv); + if ( (id = semget(key,0,0000)) >= 0 ) { + union semun junk; + strcpy(tbuf,"can't remove sem key "); + strcat(tbuf,*argv); + if ( semctl(id,0,IPC_RMID,junk) == 0 ) { + printf("sem key %s deleted\n",*argv); + } else { + perror(tbuf); + errcnt += 1; + } + } else if ( errno == ENOSYS ) { + fprintf(stderr,"ipcrm: SYSV semaphores not configured in kernel\n"); + errcnt += 1; + } else { + perror(tbuf); + errcnt += 1; + } + } + } else { + errcnt += 1; + } + } else { + fprintf(stderr,"ipcrm: illegal parameter \"%s\" - bye!\n",parm); + exit(1); + } + + } + + if ( errcnt == 0 ) { + exit(0); + } else { + exit(1); + } +} diff --git a/usr.bin/ipcs/Makefile b/usr.bin/ipcs/Makefile new file mode 100644 index 000000000000..0854bca423d5 --- /dev/null +++ b/usr.bin/ipcs/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 5.2 (Berkeley) 5/11/90 + +PROG= ipcs +NOMAN= noman + +.include <bsd.prog.mk> diff --git a/usr.bin/ipcs/ipcs.c b/usr.bin/ipcs/ipcs.c new file mode 100644 index 000000000000..b1e844a28c4c --- /dev/null +++ b/usr.bin/ipcs/ipcs.c @@ -0,0 +1,324 @@ +/* + * Simplified implementation of SYSV ipcs. + */ + +#include <nlist.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <paths.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/proc.h> +#define KERNEL +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/msg.h> + +static kmem_fd; + +getsymbol(struct nlist *symbols, char *symname, void *dptr, int len) +{ + int i, rlen; + + for ( i = 0; symbols[i].n_name != NULL; i += 1 ) { + if ( strcmp(symbols[i].n_name,symname) == 0 ) { + break; + } + } + + if ( symbols[i].n_name == NULL ) { + fprintf(stderr,"ipcs(getsymbol): symbol %s not in local symbols list\n", + symname); + exit(1); + } + + if ( symbols[i].n_value == NULL ) { + fprintf(stderr,"ipcs(getsymbol): symbol %s not in %s\n", + symname,_PATH_UNIX); + return(0); + } + + if ( kmem_fd == 0 ) { + kmem_fd = open("/dev/kmem",0); + if ( kmem_fd < 0 ) { + perror("ipcs(getsymbol(open /dev/kmem))"); + exit(1); + } + } + + lseek(kmem_fd,symbols[i].n_value,SEEK_SET); + if ( (rlen = read(kmem_fd,dptr,len)) != len ) { + fprintf(stderr,"ipcs(getsymbol): can't fetch symbol %s from /dev/kmem\n",symname); + exit(1); + } + return(1); +} + +void +getlocation(void *addr, void *dptr, int len) +{ + int i, rlen; + + if ( kmem_fd == 0 ) { + kmem_fd = open("/dev/kmem",0); + if ( kmem_fd < 0 ) { + perror("ipcs(getlocation(open /dev/kmem))"); + exit(1); + } + } + + lseek(kmem_fd,(long)addr,SEEK_SET); + if ( (rlen = read(kmem_fd,dptr,len)) != len ) { + fprintf(stderr,"ipcs(getlocation): can't fetch location %08x from /dev/kmem\n",addr); + exit(1); + } +} + +char * +fmt_perm(ushort mode) +{ + static char buffer[100]; + + buffer[0] = '-'; + buffer[1] = '-'; + buffer[2] = ((mode & 0400) ? 'r' : '-'); + buffer[3] = ((mode & 0200) ? 'w' : '-'); + buffer[4] = ((mode & 0100) ? 'a' : '-'); + buffer[5] = ((mode & 0040) ? 'r' : '-'); + buffer[6] = ((mode & 0020) ? 'w' : '-'); + buffer[7] = ((mode & 0010) ? 'a' : '-'); + buffer[8] = ((mode & 0004) ? 'r' : '-'); + buffer[9] = ((mode & 0002) ? 'w' : '-'); + buffer[10] = ((mode & 0001) ? 'a' : '-'); + buffer[11] = '\0'; + return(&buffer[0]); +} + +void +cvt_time(time_t t,char *buf) +{ + struct tm tms; + static char *months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; + if ( t == 0 ) { + strcpy(buf,"<not set>"); + } else { + tms = *localtime(&t); + if ( t > time(0) - 6 * 30 * 24 * 3600 ) { /* less than about 6 months ago? */ + sprintf(buf,"%s %2d %2d:%2d", + months[tms.tm_mon],tms.tm_mday,tms.tm_hour,tms.tm_min); + } else { + sprintf(buf,"%s %2d %5d", + months[tms.tm_mon],tms.tm_mday,tms.tm_year+1900); + } + } +} + +main() +{ + static struct nlist symbols[] = { + { "_sema" }, + { "_seminfo" }, + { "_semu" }, + { "_msginfo" }, + { "_msqids" }, + { NULL } + }; + int i; + int show_sem_values = 1; + int show_undo_values = 1; + + switch ( nlist(_PATH_UNIX,&symbols[0]) ) { + case 0: break; + case -1: + fprintf(stderr,"ipcs: can't open %s - bye!\n",_PATH_UNIX); + exit(1); + default: + fprintf(stderr,"ipcs: nlist failed\n"); + for ( i = 0; symbols[i].n_name != NULL; i += 1 ) { + if ( symbols[i].n_value == 0 ) { + fprintf(stderr,"\tsymbol %s not found\n",symbols[i].n_name); + } + } + break; + } + + /* + for ( i = 0; symbols[i].n_name != NULL; i += 1 ) { + fprintf(stderr,"\t%s : %08x\n",symbols[i].n_name,symbols[i].n_value); + } + */ + + if ( getsymbol(symbols,"_seminfo",&seminfo,sizeof(seminfo)) ) { + struct semid_ds *xsema; + + printf("seminfo:\n"); + printf("\tsemmap: %6d\t(# of entries in semaphore map)\n",seminfo.semmap); + printf("\tsemmni: %6d\t(# of semaphore identifiers)\n",seminfo.semmni); + printf("\tsemmns: %6d\t(# of semaphores in system)\n",seminfo.semmns); + printf("\tsemmnu: %6d\t(# of undo structures in system)\n",seminfo.semmnu); + printf("\tsemmsl: %6d\t(max # of semaphores per id)\n",seminfo.semmsl); + printf("\tsemopm: %6d\t(max # of operations per semop call)\n",seminfo.semopm); + printf("\tsemume: %6d\t(max # of undo entries per process)\n",seminfo.semume); + printf("\tsemusz: %6d\t(size in bytes of undo structure)\n",seminfo.semusz); + printf("\tsemvmx: %6d\t(semaphore maximum value)\n",seminfo.semvmx); + printf("\tsemaem: %6d\t(adjust on exit max value)\n",seminfo.semaem); + + /* + * Lock out other users of the semaphore facility + */ + + if ( semconfig(SEM_CONFIG_FREEZE) != 0 ) { + perror("semconfig"); + fprintf(stderr,"Can't lock semaphore facility - winging it...\n"); + } + + getsymbol(symbols,"_sema",&sema,sizeof(sema)); + xsema = malloc(sizeof(struct semid_ds) * seminfo.semmni); + getlocation(sema,xsema,sizeof(struct semid_ds) * seminfo.semmni); + + for ( i = 0; i < seminfo.semmni; i += 1 ) { + if ( (xsema[i].sem_perm.mode & SEM_ALLOC) != 0 ) { + char ctime_buf[100], otime_buf[100]; + struct semid_ds *semaptr = &xsema[i]; + cvt_time(semaptr->sem_ctime,ctime_buf); + cvt_time(semaptr->sem_otime,otime_buf); + + printf("\nsema id: %d key: 0x%08x:\n", + IXSEQ_TO_IPCID(i,semaptr->sem_perm), + semaptr->sem_perm.key); + + printf(" cuid: %6d cgid: %6d ctime: %s\n", + semaptr->sem_perm.cuid,semaptr->sem_perm.cgid,ctime_buf); + + printf(" uid: %6d gid: %6d otime: %s\n", + semaptr->sem_perm.uid,semaptr->sem_perm.gid,otime_buf); + + printf(" nsems: %6d perm: %s\n", + semaptr->sem_nsems,fmt_perm(semaptr->sem_perm.mode)); + + if ( show_sem_values ) { + int j, value; + union semun junk; + for ( j = 0; j < semaptr->sem_nsems; j += 1 ) { + if ( (value = semctl( IXSEQ_TO_IPCID(i,semaptr->sem_perm), j, GETVAL, junk )) < 0 ) { + printf("can't get semaphore values\n"); + break; + } + if ( j % 5 == 0 ) { + if ( j == 0 ) { + printf(" values: {"); + } else { + printf("\n"); + printf(" "); + } + } + printf(" %d",value); + if ( j == semaptr->sem_nsems - 1 ) { + printf(" }\n"); + } else { + printf(", "); + } + } + } + + } + + } + + if ( show_undo_values ) { + int j; + int *ksemu, *semu; + int semu_size; + int got_one_undo = 0; + + semu = 0; + semu_size = (int)SEMU(seminfo.semmnu); + semu = (int *)malloc( semu_size ); + getsymbol(symbols,"_semu",&ksemu,sizeof(ksemu)); + getlocation(ksemu,semu,semu_size); + + printf("\nsem undos:\n"); + for ( j = 0; j < seminfo.semmnu; j += 1 ) { + struct sem_undo *suptr; + int k; + + suptr = SEMU(j); + if ( suptr->un_proc != NULL ) { + struct proc proc; + getlocation(suptr->un_proc,&proc,sizeof(proc)); + got_one_undo = 1; + printf(" pid %d: semid semnum adjval\n",proc.p_pid); + for ( k = 0; k < suptr->un_cnt; k += 1 ) { + printf(" %10d %5d %6d\n", + IXSEQ_TO_IPCID(suptr->un_ent[k].un_id,xsema[suptr->un_ent[k].un_id].sem_perm), + suptr->un_ent[k].un_num, + suptr->un_ent[k].un_adjval); + } + } + } + if ( !got_one_undo ) { + printf(" none allocated\n"); + } + } + + (void)semconfig(SEM_CONFIG_THAW); + + } else { + fprintf(stderr,"SVID semaphores facility not configured in the system\n"); + } + + if ( getsymbol(symbols,"_msginfo",&msginfo,sizeof(msginfo)) ) { + struct msqid_ds *xmsqids; + + printf("\nmsginfo:\n"); + printf("\tmsgmax: %6d\t(max characters in a message)\n",msginfo.msgmax); + printf("\tmsgmni: %6d\t(# of message queues)\n",msginfo.msgmni); + printf("\tmsgmnb: %6d\t(max characters in a message queue)\n",msginfo.msgmnb); + printf("\tmsgtql: %6d\t(max # of messages in system)\n",msginfo.msgtql); + printf("\tmsgssz: %6d\t(size of a message segment)\n",msginfo.msgssz); + printf("\tmsgseg: %6d\t(# of message segments in system)\n",msginfo.msgseg); + + getsymbol(symbols,"_msqids",&msqids,sizeof(msqids)); + xmsqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni); + getlocation(msqids,xmsqids,sizeof(struct msqid_ds) * msginfo.msgmni); + + for ( i = 0; i < msginfo.msgmni; i += 1 ) { + if ( xmsqids[i].msg_qbytes != 0 ) { + char stime_buf[100], rtime_buf[100], ctime_buf[100]; + struct msqid_ds *msqptr = &xmsqids[i]; + + cvt_time(msqptr->msg_stime,stime_buf); + cvt_time(msqptr->msg_rtime,rtime_buf); + cvt_time(msqptr->msg_ctime,ctime_buf); + + printf("\nmsgq id: %d key: 0x%08x\n", + IXSEQ_TO_IPCID(i,msqptr->msg_perm), + msqptr->msg_perm.key); + + printf(" cuid: %6d cgid: %6d ctime: %s\n", + msqptr->msg_perm.cuid,msqptr->msg_perm.cgid,ctime_buf); + + printf(" uid: %6d gid: %6d\n", + msqptr->msg_perm.uid,msqptr->msg_perm.gid); + + printf(" lspid: %6d stime: %s\n", + msqptr->msg_lspid,stime_buf); + + printf(" lrpid: %6d qnum: %6d rtime: %s\n", + msqptr->msg_lrpid,msqptr->msg_qnum,rtime_buf); + + printf(" cbytes:%6d qbytes:%6d perm: %s\n", + msqptr->msg_cbytes,msqptr->msg_qbytes,fmt_perm(msqptr->msg_perm.mode)); + + } + } + + } else { + fprintf(stderr,"SVID messages facility not configured in the system\n"); + } + + exit(0); +} diff --git a/usr.bin/join/join.c b/usr.bin/join/join.c index 14bbc4cd1a25..bfe526f67889 100644 --- a/usr.bin/join/join.c +++ b/usr.bin/join/join.c @@ -309,16 +309,16 @@ slurp(F) } if ((bp = fgetline(F->fp, &len)) == NULL) return; - if (lp->linealloc <= len) { + while (lp->linealloc <= len) { lp->linealloc += 100; if ((lp->line = realloc(lp->line, lp->linealloc * sizeof(char))) == NULL) enomem(); } - bcopy(bp, lp->line, len); + bcopy(bp, lp->line, len+1); /* Split the line into fields, allocate space as necessary. */ - token = bp; + token = lp->line; lp->fieldcnt = 0; while ((fieldp = strsep(&token, tabchar)) != NULL) { if (spans && *fieldp == '\0') diff --git a/usr.bin/ktrace/kdump/kdump.c b/usr.bin/ktrace/kdump/kdump.c index a077d97515ea..460f76b1b55a 100644 --- a/usr.bin/ktrace/kdump/kdump.c +++ b/usr.bin/ktrace/kdump/kdump.c @@ -41,16 +41,16 @@ char copyright[] = static char sccsid[] = "@(#)kdump.c 5.3 (Berkeley) 1/17/91"; #endif /* not lint */ -#include <sys/param.h> +#define KERNEL #include <sys/errno.h> +extern int errno; +#undef KERNEL +#include <sys/param.h> #include <sys/time.h> #include <sys/uio.h> #include <sys/ktrace.h> #include <sys/ioctl.h> #include <sys/ptrace.h> -#define KERNEL -#include <sys/errno.h> -#undef KERNEL #include <vis.h> #include <stdio.h> #include <stdlib.h> diff --git a/usr.bin/ktrace/ktrace/ktrace.1 b/usr.bin/ktrace/ktrace/ktrace.1 index 549a3fb3864f..876ed44a7b3a 100644 --- a/usr.bin/ktrace/ktrace/ktrace.1 +++ b/usr.bin/ktrace/ktrace/ktrace.1 @@ -66,7 +66,7 @@ attempting to trace a process. The following command is sufficient to disable tracing on all user owned processes, and, if executed by root, all processes: .Pp -.Dl \&$ trace -C +.Dl \&$ ktrace -C .Pp The trace file is not human readable; use .Xr kdump 1 diff --git a/usr.bin/ktrace/ktrace/ktrace.c b/usr.bin/ktrace/ktrace/ktrace.c index 8f25878a9d9b..5ad07b1c6260 100644 --- a/usr.bin/ktrace/ktrace/ktrace.c +++ b/usr.bin/ktrace/ktrace/ktrace.c @@ -49,8 +49,15 @@ static char sccsid[] = "@(#)ktrace.c 5.2 (Berkeley) 3/5/91"; #include <sys/uio.h> #include <sys/ktrace.h> #include <stdio.h> +#include <signal.h> #include "ktrace.h" +void noktrace() { + (void)fprintf(stderr, "ktrace: ktrace not enabled in kernel,to use ktrace\n"); + (void)fprintf(stderr, "you need to add a line \"options KTRACE\" to your kernel\n"); + exit(1); +} + main(argc, argv) int argc; char **argv; @@ -65,6 +72,9 @@ main(argc, argv) append = ops = pidset = 0; trpoints = ALL_POINTS; tracefile = DEF_TRACEFILE; + /* set up a signal handler for SIGSYS, this indicates that ktrace + is not enabled in the kernel */ + signal(SIGSYS, noktrace); while ((ch = getopt(argc,argv,"aCcdf:g:ip:t:")) != EOF) switch((char)ch) { case 'a': diff --git a/usr.bin/leave/leave.1 b/usr.bin/leave/leave.1 index bfdb670bd0a3..d77dfb011275 100644 --- a/usr.bin/leave/leave.1 +++ b/usr.bin/leave/leave.1 @@ -71,7 +71,7 @@ are minutes. All times are converted to a 12 hour clock, and assumed to be in the next 12 hours. .It Cm \&+ -If the time is preceeded by +If the time is preceded by .Ql Cm \&+ , the alarm will go off in hours and minutes from the current time. diff --git a/usr.bin/lex/ccl.c b/usr.bin/lex/ccl.c index 99762fe230cd..0b9942e1f992 100644 --- a/usr.bin/lex/ccl.c +++ b/usr.bin/lex/ccl.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/ccl.c,v 1.2 1993/06/29 03:27:05 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/ccl.c,v 1.2 1993/06/29 03:27:05 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/dfa.c b/usr.bin/lex/dfa.c index d24f39d55396..c66e02533740 100644 --- a/usr.bin/lex/dfa.c +++ b/usr.bin/lex/dfa.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/dfa.c,v 1.2 1993/06/29 03:27:06 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/dfa.c,v 1.2 1993/06/29 03:27:06 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/ecs.c b/usr.bin/lex/ecs.c index d20baf5fc5f5..711f6fca6187 100644 --- a/usr.bin/lex/ecs.c +++ b/usr.bin/lex/ecs.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/ecs.c,v 1.2 1993/06/29 03:27:07 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/ecs.c,v 1.2 1993/06/29 03:27:07 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/flexdef.h b/usr.bin/lex/flexdef.h index 82e48ecb644a..a7bd7f7be55f 100644 --- a/usr.bin/lex/flexdef.h +++ b/usr.bin/lex/flexdef.h @@ -26,7 +26,7 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -/* @(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/flexdef.h,v 1.3 1993/07/06 18:06:49 nate Exp $ (LBL) */ +/* @(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/flexdef.h,v 1.3 1993/07/06 18:06:49 nate Exp $ (LBL) */ #ifndef FILE #include <stdio.h> diff --git a/usr.bin/lex/gen.c b/usr.bin/lex/gen.c index 92609c60ed2a..bd35e4e7d41e 100644 --- a/usr.bin/lex/gen.c +++ b/usr.bin/lex/gen.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/gen.c,v 1.2 1993/06/29 03:27:10 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/gen.c,v 1.2 1993/06/29 03:27:10 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/initscan.c b/usr.bin/lex/initscan.c index d907ab35bd0c..8b62256af00f 100644 --- a/usr.bin/lex/initscan.c +++ b/usr.bin/lex/initscan.c @@ -1,7 +1,7 @@ /* A lexical scanner generated by flex */ /* scanner skeleton version: - * $Header: /a/cvs/386BSD/src/usr.bin/lex/initscan.c,v 1.2 1993/06/29 03:27:12 nate Exp $ + * $Header: /home/cvs/386BSD/src/usr.bin/lex/initscan.c,v 1.2 1993/06/29 03:27:12 nate Exp $ */ #define FLEX_SCANNER @@ -196,7 +196,7 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/initscan.c,v 1.2 1993/06/29 03:27:12 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/initscan.c,v 1.2 1993/06/29 03:27:12 nate Exp $ (LBL)"; #endif #undef yywrap diff --git a/usr.bin/lex/lex.skel b/usr.bin/lex/lex.skel index 845c8094c230..552ae44971c4 100644 --- a/usr.bin/lex/lex.skel +++ b/usr.bin/lex/lex.skel @@ -1,7 +1,7 @@ /* A lexical scanner generated by flex */ /* scanner skeleton version: - * $Header: /a/cvs/386BSD/src/usr.bin/lex/lex.skel,v 1.2 1993/06/29 03:27:14 nate Exp $ + * $Header: /home/cvs/386BSD/src/usr.bin/lex/lex.skel,v 1.4 1993/12/03 00:05:27 ats Exp $ */ #define FLEX_SCANNER @@ -91,7 +91,7 @@ int read(); * is returned in "result". */ #define YY_INPUT(buf,result,max_size) \ - if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + if ( (result = read(fileno(yyin), (char *)buf, max_size)) < 0 ) \ YY_FATAL_ERROR( "read() in flex scanner failed" ); #define YY_NULL 0 diff --git a/usr.bin/lex/lib/Makefile b/usr.bin/lex/lib/Makefile index 6d5fbc23662a..a1d0fb427853 100644 --- a/usr.bin/lex/lib/Makefile +++ b/usr.bin/lex/lib/Makefile @@ -1,5 +1,8 @@ # @(#)Makefile 5.1 (Berkeley) 6/18/90 +SHLIB_MAJOR=1 +SHLIB_MINOR=0 + LIB= ln SRCS= libmain.c LINKS= ${LIBDIR}/libln.a ${LIBDIR}/libl.a \ diff --git a/usr.bin/lex/lib/libmain.c b/usr.bin/lex/lib/libmain.c index 0848edc64d32..247fcf6af736 100644 --- a/usr.bin/lex/lib/libmain.c +++ b/usr.bin/lex/lib/libmain.c @@ -1,6 +1,6 @@ /* libmain - flex run-time support library "main" function */ -/* $Header: /a/cvs/386BSD/src/usr.bin/lex/lib/libmain.c,v 1.2 1993/06/29 03:27:29 nate Exp $ */ +/* $Header: /home/cvs/386BSD/src/usr.bin/lex/lib/libmain.c,v 1.2 1993/06/29 03:27:29 nate Exp $ */ extern int yylex(); diff --git a/usr.bin/lex/main.c b/usr.bin/lex/main.c index fcd5c32d89a3..bcad18a31303 100644 --- a/usr.bin/lex/main.c +++ b/usr.bin/lex/main.c @@ -34,7 +34,7 @@ char copyright[] = #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/main.c,v 1.2 1993/06/29 03:27:15 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/main.c,v 1.2 1993/06/29 03:27:15 nate Exp $ (LBL)"; #endif diff --git a/usr.bin/lex/misc.c b/usr.bin/lex/misc.c index a113bb4fe19f..ae400504ba3d 100644 --- a/usr.bin/lex/misc.c +++ b/usr.bin/lex/misc.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/misc.c,v 1.2 1993/06/29 03:27:16 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/misc.c,v 1.2 1993/06/29 03:27:16 nate Exp $ (LBL)"; #endif #include <ctype.h> diff --git a/usr.bin/lex/nfa.c b/usr.bin/lex/nfa.c index 3926f8d84399..d43fcb22ffb5 100644 --- a/usr.bin/lex/nfa.c +++ b/usr.bin/lex/nfa.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/nfa.c,v 1.2 1993/06/29 03:27:17 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/nfa.c,v 1.2 1993/06/29 03:27:17 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/parse.y b/usr.bin/lex/parse.y index 5cba911ab336..730573a5b8d2 100644 --- a/usr.bin/lex/parse.y +++ b/usr.bin/lex/parse.y @@ -32,7 +32,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/parse.y,v 1.2 1993/06/29 03:27:18 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/parse.y,v 1.2 1993/06/29 03:27:18 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/scan.l b/usr.bin/lex/scan.l index c0a3ad3832ee..278235f19ee2 100644 --- a/usr.bin/lex/scan.l +++ b/usr.bin/lex/scan.l @@ -30,7 +30,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/scan.l,v 1.2 1993/06/29 03:27:19 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/scan.l,v 1.2 1993/06/29 03:27:19 nate Exp $ (LBL)"; #endif #undef yywrap diff --git a/usr.bin/lex/sym.c b/usr.bin/lex/sym.c index 78841d6e309b..ce47739a379a 100644 --- a/usr.bin/lex/sym.c +++ b/usr.bin/lex/sym.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/sym.c,v 1.2 1993/06/29 03:27:19 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/sym.c,v 1.2 1993/06/29 03:27:19 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/tblcmp.c b/usr.bin/lex/tblcmp.c index 1ee8e4a0d66c..6e4b2d0527de 100644 --- a/usr.bin/lex/tblcmp.c +++ b/usr.bin/lex/tblcmp.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/tblcmp.c,v 1.2 1993/06/29 03:27:20 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/tblcmp.c,v 1.2 1993/06/29 03:27:20 nate Exp $ (LBL)"; #endif #include "flexdef.h" diff --git a/usr.bin/lex/yylex.c b/usr.bin/lex/yylex.c index f01e92ad1afc..aee0492f58e5 100644 --- a/usr.bin/lex/yylex.c +++ b/usr.bin/lex/yylex.c @@ -28,7 +28,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: /a/cvs/386BSD/src/usr.bin/lex/yylex.c,v 1.2 1993/06/29 03:27:21 nate Exp $ (LBL)"; + "@(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/yylex.c,v 1.2 1993/06/29 03:27:21 nate Exp $ (LBL)"; #endif #include <ctype.h> diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index 3aa6883e99ef..ad1d4d60ad57 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -230,12 +230,21 @@ main(argc, argv) * is root or the caller isn't changing their uid, don't * authenticate. */ - if (pwd && (*pwd->pw_passwd == '\0' || - fflag && (uid == 0 || uid == pwd->pw_uid))) - break; + if (pwd) { + if (pwd->pw_uid == 0) + rootlogin = 1; + + if (fflag && (uid == 0 || uid == pwd->pw_uid)) { + /* already authenticated */ + break; + } else if (pwd->pw_passwd[0] == '\0') { + /* pretend password okay */ + rval = 0; + goto ttycheck; + } + } + fflag = 0; - if (pwd && pwd->pw_uid == 0) - rootlogin = 1; (void)setpriority(PRIO_PROCESS, 0, -4); @@ -256,6 +265,7 @@ main(argc, argv) (void)setpriority(PRIO_PROCESS, 0, 0); + ttycheck: /* * If trying to log in as root without Kerberos, * but with insecure terminal, refuse the login attempt. @@ -263,7 +273,7 @@ main(argc, argv) #ifdef KERBEROS if (authok == 0) #endif - if (pwd && rootlogin && !rootterm(tty)) { + if (pwd && !rval && rootlogin && !rootterm(tty)) { (void)fprintf(stderr, "%s login refused on this terminal.\n", pwd->pw_name); diff --git a/usr.bin/look/look.c b/usr.bin/look/look.c index 2db9f7d4d123..cfda7f51ff39 100644 --- a/usr.bin/look/look.c +++ b/usr.bin/look/look.c @@ -86,7 +86,7 @@ void err __P((const char *fmt, ...)); char *linear_search __P((char *, char *, char *)); int look __P((char *, char *, char *)); void print_from __P((char *, char *, char *)); -void usage __P((void)); +static void usage __P((void)); main(argc, argv) int argc; diff --git a/usr.bin/m4/extr.h b/usr.bin/m4/extr.h index bf3ed92cec9a..7d0ba157eee8 100644 --- a/usr.bin/m4/extr.h +++ b/usr.bin/m4/extr.h @@ -38,11 +38,6 @@ extern char csmsg[]; /* error message for chrsave */ #define putback(c) do { if (bp >= endpbb) error(pbmsg); *bp++ = c; } while (0) #define chrsave(c) do { if (ep >= endest) error(csmsg); *ep++ = c; } while (0) -/* getopt() interface */ - -extern char * optarg; -extern int optind; -extern int getopt(); #ifdef __STDC__ #include <stdlib.h> diff --git a/usr.bin/m4/m4.1 b/usr.bin/m4/m4.1 index 8fd1bf09bb7b..53180191e8f5 100644 --- a/usr.bin/m4/m4.1 +++ b/usr.bin/m4/m4.1 @@ -1,5 +1,5 @@ .\" -.\" @(#) m4.1,v 1.1 1993/06/18 21:50:31 glass Exp +.\" @(#) $Id: m4.1,v 1.4 1994/01/16 00:30:31 swallace Exp $ .\" .Dd January 26, 1993 .Dt m4 1 @@ -13,14 +13,16 @@ .Fl D Ns Ar name Ns Op Ar =value .Oc .Op Fl U Ns Ar name +.Op Ar filename +\|.\|.\|. .Sh DESCRIPTION The .Nm m4 utility is a macro processor that can be used as a front end to any language (e.g., C, ratfor, fortran, lex, and yacc). -.Nm m4 -reads from the standard input and writes -the processed text to the standard output. +Each of the argument files is processed in order. +If there are no files, or if a filename is \`-\', the standard input is read. +The processed text is sent to the standard output. .Pp Macro calls have the form name(argument1[, argument2, ...,] argumentN). .Pp diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c index 14ccd9df6b15..d2433877b678 100644 --- a/usr.bin/m4/main.c +++ b/usr.bin/m4/main.c @@ -230,17 +230,7 @@ int strip = 0; /* throw away comments? */ */ #if unix -#include <sys/param.h> -#ifdef BSD -#include <paths.h> -#if __STDC__ -static char DIVNAM[] = _PATH_VARTMP "m40XXXXXX"; -#else -static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; -#endif -#else -static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; -#endif +static char DIVNAM[] = "/tmp/m40XXXXXX"; #else #if vms static char DIVNAM[] = "sys$login:m40XXXXXX"; @@ -873,8 +863,14 @@ int main(argc, argv) } else /* file names in commandline */ for (; optind < argc; optind++) { char *name = argv[optind]; /* next file name */ - infile[0] = fopen(name, "r"); + if(name[1] == 0 && name[0] == '-') + infile[0] = stdin; + else + infile[0] = fopen(name, "r"); if (!infile[0]) cantread(name); + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + ilevel = 0; /* reset input file stack ptr*/ #ifndef NO__FILE dodefine("__FILE__", name); #endif @@ -887,11 +883,14 @@ int main(argc, argv) putback(EOF); /* eof is a must !! */ pbstr(m4wraps); /* user-defined wrapup act */ macro(); /* last will and testament */ - } else { /* default wrap-up: undivert */ - for (n = 1; n < MAXOUT; n++) - if (outfile[n] != NULL) getdiv(n); } + if (active != stdout) + active = stdout; /* reset output just in case */ + + for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ + if (outfile[n] != NULL) getdiv(n); + if (outfile[0] != NULL) { /* remove bitbucket if used */ (void) fclose(outfile[0]); m4temp[UNIQUE] = '0'; diff --git a/usr.bin/m4/serv.c b/usr.bin/m4/serv.c index 6b0a4bb45e08..de6f59d45593 100644 --- a/usr.bin/m4/serv.c +++ b/usr.bin/m4/serv.c @@ -272,8 +272,8 @@ void dochc(argv, argc) ecommt = ECOMMT; } } else { - scommt = SCOMMT; - ecommt = ECOMMT; + scommt = '\0'; /* assuming no nulls in input */ + ecommt = '\0'; } } diff --git a/usr.bin/mail/mail.1 b/usr.bin/mail/mail.1 index d52db01246e8..7b70e4666573 100644 --- a/usr.bin/mail/mail.1 +++ b/usr.bin/mail/mail.1 @@ -78,7 +78,7 @@ special character when sending mail is only active in interactive mode. .It Fl n Inhibits reading -.Pa /usr/share/misc/Mail.rc +.Pa /etc/mail.rc upon startup. .It Fl N Inhibits the initial display of message headers @@ -108,7 +108,7 @@ writes undeleted messages back to this file. .It Fl u Is equivalent to: .Pp -.Dl mail -f /var/spool/mail/user +.Dl mail -f /var/mail/user .El .Ss Sending mail To send a message to one or more people, @@ -795,7 +795,7 @@ Causes messages saved in .Ar mbox to be appended to the end rather than prepended. This should always be set (perhaps in -.Pa /usr/share/misc/Mail.rc ) . +.Pa /etc/mail.rc ) . .It Ar ask Causes .Nm mail @@ -981,7 +981,7 @@ and environment variables. .Sh FILES .Bl -tag -width /usr/share/misc/Mail.help* -compact -.It Pa /var/spool/mail/* +.It Pa /var/mail/* Post office. .It ~/mbox User's old mail. @@ -991,7 +991,7 @@ File giving initial mail commands. Temporary files. .It Pa /usr/share/misc/Mail.help* Help files. -.It Pa /usr/share/misc/Mail.rc +.It Pa /etc/mail.rc System initialization file. .El .Sh SEE ALSO diff --git a/usr.bin/mesg/mesg.c b/usr.bin/mesg/mesg.c index 416f6da2a9c5..308dc6f48aff 100644 --- a/usr.bin/mesg/mesg.c +++ b/usr.bin/mesg/mesg.c @@ -1,4 +1,13 @@ /* + * Copyright (c) UNIX System Laboratories, Inc. All or some portions + * of this file are derived from material licensed to the + * University of California by American Telephone and Telegraph Co. + * or UNIX System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * $Id: mesg.c,v 1.3.2.1 1994/05/04 08:00:52 rgrimes Exp $ + */ +/* * Copyright (c) 1987 Regents of the University of California. * All rights reserved. * @@ -55,6 +64,7 @@ static char sccsid[] = "@(#)mesg.c 4.7 (Berkeley) 3/1/91"; #include <stdio.h> static char *tty; +#define OTHER_WRITE 020 main(argc, argv) int argc; @@ -72,14 +82,13 @@ main(argc, argv) exit(-1); } if (argc < 2) { - if (sbuf.st_mode & 020) { + if (sbuf.st_mode & OTHER_WRITE) { fputs("is y\n", stderr); exit(0); } fputs("is n\n", stderr); exit(1); } -#define OTHER_WRITE 020 switch(*argv[1]) { case 'y': newmode(sbuf.st_mode | OTHER_WRITE); @@ -88,7 +97,7 @@ main(argc, argv) newmode(sbuf.st_mode &~ OTHER_WRITE); exit(1); default: - fputs("usage: mesg [y] [n]\n", stderr); + fputs("usage: mesg [y|n]\n", stderr); exit(-1); } /*NOTREACHED*/ diff --git a/usr.bin/mkdep/mkdep.gcc.sh b/usr.bin/mkdep/mkdep.gcc.sh index dbd401e49b51..8c68817382d7 100644 --- a/usr.bin/mkdep/mkdep.gcc.sh +++ b/usr.bin/mkdep/mkdep.gcc.sh @@ -71,7 +71,7 @@ TMP=/tmp/mkdep$$ trap 'rm -f $TMP ; exit 1' 1 2 3 13 15 -cpp -M $* | sed "$SED" > $TMP +gcc -M $* | sed "$SED" > $TMP if [ $? != 0 ]; then echo 'mkdep: compile failed.' diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile index e61848714e40..78ac71b02b4e 100644 --- a/usr.bin/more/Makefile +++ b/usr.bin/more/Makefile @@ -5,8 +5,8 @@ CFLAGS+=-I${.CURDIR} -DREGEX SRCS= ch.c command.c decode.c filename.c help.c input.c line.c \ linenum.c main.c option.c os.c output.c position.c prim.c \ screen.c signal.c tags.c ttyin.c -LDADD+= -ltermcap -lgnuregex -DPADD+= ${LIBTERM} /usr/lib/libgnuregex.a +LDADD+= -ltermcap +DPADD+= ${LIBTERM} MLINKS= more.1 page.1 LINKS= ${BINDIR}/more ${BINDIR}/page diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c index 8421303e177a..9d8d3f0f6f73 100644 --- a/usr.bin/more/command.c +++ b/usr.bin/more/command.c @@ -42,6 +42,8 @@ static char sccsid[] = "@(#)command.c 5.22 (Berkeley) 6/21/92"; #include <less.h> #include "pathnames.h" +extern off_t position(); + #define NO_MCA 0 #define MCA_DONE 1 #define MCA_MORE 2 @@ -154,7 +156,7 @@ prompt() { extern int linenums, short_file; extern char *current_name, *firstsearch, *next_name; - off_t len, pos, ch_length(), position(), forw_line(); + off_t len, pos, ch_length(), forw_line(); char pbuf[40]; /* diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1 index 20a1e021ce74..aa094cf1fce3 100644 --- a/usr.bin/more/more.1 +++ b/usr.bin/more/more.1 @@ -149,7 +149,7 @@ Interactive commands for .Nm more are based on .Xr vi 1 . -Some commands may be preceeded by a decimal number, called N in the +Some commands may be preceded by a decimal number, called N in the descriptions below. In the following descriptions, ^X means control-X. .Pp @@ -205,7 +205,7 @@ marks the current position with that letter. (Single quote.) Followed by any lowercase letter, returns to the position which was previously marked with that letter. -Followed by another single quote, returns to the postion at +Followed by another single quote, returns to the position at which the last "large" movement command was executed, or the beginning of the file if no such movements have occurred. All marks are lost when a new file is examined. diff --git a/usr.bin/msgs/msgs.1 b/usr.bin/msgs/msgs.1 index 41c512a3fec0..2ef377c40390 100644 --- a/usr.bin/msgs/msgs.1 +++ b/usr.bin/msgs/msgs.1 @@ -39,7 +39,7 @@ .Nd system messages and junk mail program .Sh SYNOPSIS .Nm msgs -.Op Fl fhlpq +.Op Fl fhlpqr .Op Ar number .Op Ar \-number .Nm msgs @@ -94,9 +94,13 @@ A copy of the specified message is placed in a temporary mailbox and .Xr mail 1 is invoked on that mailbox. -Both `m' and `s' accept a numeric argument in place of the `\-'. +.It Cm p +The specified message is piped through $PAGER or, if $PAGER is not defined, +.Xr more 1 . .El .Pp +The commands `m', `p' and `s' all accept a numeric argument in place of the `\-'. +.Pp .Nm Msgs keeps track of the next message you will see by a number in the file .Pa \&.msgsrc @@ -156,6 +160,9 @@ Queries whether there are messages, printing The command ``msgs \-q'' is often used in login scripts. .It Fl h Print the first part of messages only. +.It Fl r +Disables the ability to save messages or enter the mailer. It is +assumed that $PAGER is set to something secure. .It Fl l Option causes only locally originated messages to be reported. .It Ar num @@ -178,7 +185,7 @@ messages back from the one indicated in the .Pa \&.msgsrc file, useful for reviews of recent messages. .It Fl p -Pipe long messages through +Pipe long messages through $PAGER or, if $PAGER is not defined, .Xr more 1 . .El .Pp diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c index c473a89b1a09..8b2ace76786f 100644 --- a/usr.bin/msgs/msgs.c +++ b/usr.bin/msgs/msgs.c @@ -45,7 +45,7 @@ static char sccsid[] = "@(#)msgs.c 5.8 (Berkeley) 2/4/91"; * msgs - a user bulletin board program * * usage: - * msgs [fhlopq] [[-]number] to read messages + * msgs [fhlopqr] [[-]number] to read messages * msgs -s to place messages * msgs -c [-days] to clean up the bulletin board * @@ -145,6 +145,7 @@ bool locomode = NO; bool use_pager = NO; bool clean = NO; bool lastcmd = NO; +bool restricted = NO; jmp_buf tstpbuf; main(argc, argv) @@ -215,13 +216,17 @@ int argc; char *argv[]; qopt = YES; break; + case 'r': /* restricted */ + restricted = YES; + break; + case 's': /* sending TO msgs */ send_msg = YES; break; default: fprintf(stderr, - "usage: msgs [fhlopq] [[-]number]\n"); + "usage: msgs [fhlopqr] [[-]number]\n"); exit(1); } } @@ -596,11 +601,16 @@ prmesg(length) int length; { FILE *outf; + char *env_pager; if (use_pager && length > Lpp) { signal(SIGPIPE, SIG_IGN); signal(SIGQUIT, SIG_IGN); - sprintf(cmdbuf, _PATH_PAGER, Lpp); + if ((env_pager = getenv("PAGER")) == NULL) { + sprintf(cmdbuf, _PATH_PAGER, Lpp); + } else { + strcpy(cmdbuf, env_pager); + } outf = popen(cmdbuf, "w"); if (!outf) outf = stdout; @@ -714,7 +724,7 @@ char *prompt; /* * Handle 'mail' and 'save' here. */ - if ((inch = inbuf[0]) == 's' || inch == 'm') { + if (((inch = inbuf[0]) == 's' || inch == 'm') && !restricted) { if (inbuf[1] == '-') cmsg = prevmsg; else if (isdigit(inbuf[1])) diff --git a/usr.bin/mt/mt.c b/usr.bin/mt/mt.c index 5186feedee2e..1ba3e1d72cd0 100644 --- a/usr.bin/mt/mt.c +++ b/usr.bin/mt/mt.c @@ -97,19 +97,7 @@ main(argc, argv) if ((tape = getenv("TAPE")) == NULL) tape = DEFTAPE; if (argc < 2) { -usage: fprintf(stderr, "Usage:\n"); - fprintf(stderr, "\tmt [ -f device ] command [ count ]\n"); - fprintf(stderr, "Commands:\n"); - fprintf(stderr, "\teof, weof - write tape mmrk\n"); - fprintf(stderr, "\tfsf - seek forward for tape mark\n"); - fprintf(stderr, "\tbsf - seek backward for tape mark\n"); - fprintf(stderr, "\tfsr - seek record forward\n"); - fprintf(stderr, "\tbsr - seek record backward\n"); - fprintf(stderr, "\trewind - rewind the tape\n"); - fprintf(stderr, "\toffline - rewind and unload the tape\n"); - fprintf(stderr, "\tstatus - get tape status\n"); - fprintf(stderr, "\terase - erase the tape\n"); - fprintf(stderr, "\tretension - retension the tape\n"); +usage: fprintf(stderr, "Usage: mt [ -f device ] command [ count ]\n"); exit(1); } cp = argv[1]; diff --git a/usr.bin/netstat/host.c b/usr.bin/netstat/host.c index b459fb06b043..041ec2418e76 100644 --- a/usr.bin/netstat/host.c +++ b/usr.bin/netstat/host.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)host.c 5.12 (Berkeley) 2/2/91"; +/* From: static char sccsid[] = "@(#)host.c 5.12 (Berkeley) 2/2/91"; */ +const char host_c_rcsid[] = + "$Id: host.c,v 1.2 1993/11/17 20:19:17 wollman Exp $"; #endif /* not lint */ #include <sys/param.h> diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c index 6ec3387ed856..8e75e41c7c1e 100644 --- a/usr.bin/netstat/if.c +++ b/usr.bin/netstat/if.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)if.c 5.15 (Berkeley) 3/1/91"; +/* From: static char sccsid[] = "@(#)if.c 5.15 (Berkeley) 3/1/91"; */ +static const char if_c_rcsid[] = + "$Id: if.c,v 1.3 1994/02/21 11:35:23 rgrimes Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -123,7 +125,7 @@ intpr(interval, ifnetaddr) (strcmp(name, interface) != 0 || unit != ifnet.if_unit)) continue; cp = index(name, '\0'); - *cp++ = ifnet.if_unit + '0'; + cp += sprintf(cp, "%d", ifnet.if_unit); if ((ifnet.if_flags&IFF_UP) == 0) *cp++ = '*'; *cp = '\0'; diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 2d8fe285d32e..ed80baea14c5 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -32,7 +32,10 @@ */ #ifndef lint -static char sccsid[] = "@(#)inet.c 5.15 (Berkeley) 6/18/90"; +/* From: static char sccsid[] = "@(#)inet.c 5.15 (Berkeley) 6/18/90"; */ +static const char inet_c_rcsid[] = + "$Id: inet.c,v 1.2 1993/11/17 20:19:20 wollman Exp $"; + #endif /* not lint */ #include <sys/param.h> diff --git a/usr.bin/netstat/iso.c b/usr.bin/netstat/iso.c index 11e2219c95c6..1ba49ffb1347 100644 --- a/usr.bin/netstat/iso.c +++ b/usr.bin/netstat/iso.c @@ -32,13 +32,11 @@ */ #ifndef lint -static char sccsid[] = "@(#)iso.c 5.6 (Berkeley) 4/27/91"; +/* From: static char sccsid[] = "@(#)iso.c 5.6 (Berkeley) 4/27/91"; */ +static const char iso_c_rcsid[] = + "$Id: iso.c,v 1.4 1993/11/17 20:19:21 wollman Exp $"; #endif /* not lint */ -/* - * $Header: /a/cvs/386BSD/src/usr.bin/netstat/iso.c,v 1.2 1993/09/05 23:41:49 rgrimes Exp $ - * $Source: /a/cvs/386BSD/src/usr.bin/netstat/iso.c,v $ - */ /******************************************************************************* Copyright IBM Corporation 1987 @@ -85,7 +83,7 @@ SOFTWARE. #undef satosiso #include <netiso/tp_param.h> #include <netiso/tp_states.h> -#include <netiso/tp_astring.c> +#include "tp_astring.c" #include <netiso/tp_pcb.h> #include <netiso/tp_stat.h> #include <netiso/iso_pcb.h> diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c index 95bc1e5d5758..6809f4c0475c 100644 --- a/usr.bin/netstat/main.c +++ b/usr.bin/netstat/main.c @@ -38,7 +38,9 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 5.23 (Berkeley) 7/1/91"; +/* From: static char sccsid[] = "@(#)main.c 5.23 (Berkeley) 7/1/91"; */ +const char main_c_rcsid[] = + "$Id: main.c,v 1.2 1993/11/17 20:19:22 wollman Exp $"; #endif /* not lint */ #include <sys/param.h> diff --git a/usr.bin/netstat/mbuf.c b/usr.bin/netstat/mbuf.c index 36c10037145f..531699123c4f 100644 --- a/usr.bin/netstat/mbuf.c +++ b/usr.bin/netstat/mbuf.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)mbuf.c 5.10 (Berkeley) 1/30/91"; +/* From: static char sccsid[] = "@(#)mbuf.c 5.10 (Berkeley) 1/30/91"; */ +static const char mbuf_c_rcsid[] = + "$Id: mbuf.c,v 1.2 1993/11/17 20:19:23 wollman Exp $"; #endif /* not lint */ #include <stdio.h> diff --git a/usr.bin/netstat/ns.c b/usr.bin/netstat/ns.c index 8bdc89cfe3c4..2258b9abdbed 100644 --- a/usr.bin/netstat/ns.c +++ b/usr.bin/netstat/ns.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)ns.c 5.13 (Berkeley) 3/1/91"; +/* From: static char sccsid[] = "@(#)ns.c 5.13 (Berkeley) 3/1/91"; */ +static const char ns_c_rcsid[] = + "$Id: ns.c,v 1.2 1993/11/17 20:19:24 wollman Exp $"; #endif /* not lint */ #include <sys/param.h> diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c index 582f52c7e70c..d7d1350a0678 100644 --- a/usr.bin/netstat/route.c +++ b/usr.bin/netstat/route.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)route.c 5.20 (Berkeley) 11/29/90"; +/* From: static char sccsid[] = "@(#)route.c 5.20 (Berkeley) 11/29/90"; */ +static const char route_c_rcsid[] = + "$Id: route.c,v 1.9 1993/12/17 16:52:18 wollman Exp $"; #endif /* not lint */ #include <sys/param.h> @@ -54,6 +56,7 @@ static char sccsid[] = "@(#)route.c 5.20 (Berkeley) 11/29/90"; #include <stdio.h> #include <string.h> +#include <stdlib.h> extern int nflag, aflag, Aflag, af; int do_rtent; @@ -61,7 +64,7 @@ extern char *routename(), *netname(), *plural(); #ifdef NS extern char *ns_print(); #endif -extern char *malloc(); + #define kget(p, d) \ (kvm_read((off_t)(p), (char *)&(d), sizeof (d))) @@ -81,6 +84,8 @@ struct bits { { RTF_XRESOLVE, 'X' }, { RTF_LLINFO, 'L' }, { RTF_REJECT, 'R' }, + { RTF_PROTO2, '2' }, + { RTF_PROTO1, '1' }, { 0 } }; @@ -96,6 +101,18 @@ p_proto(proto) case AF_INET: printf("inet"); break; + case AF_NS: + printf("NS"); + break; + case AF_OSI: + printf("OSI"); + break; + case AF_CCITT: + printf("CCITT"); + break; + case AF_APPLETALK: + printf("AppleTalk"); + break; default: printf("%d", proto); break; @@ -106,6 +123,7 @@ p_proto(proto) /* * Print routing tables. */ +int routepr(hostaddr, netaddr, hashsizeaddr, treeaddr) off_t hostaddr, netaddr, hashsizeaddr, treeaddr; { @@ -117,50 +135,26 @@ routepr(hostaddr, netaddr, hashsizeaddr, treeaddr) int hashsize; int i, doinghost = 1; + if (!treeaddr) { + printf("Could not find routing tables\n"); + return 1; + } printf("Routing tables\n"); if (Aflag) printf("%-8.8s ","Address"); - printf("%-16.16s %-18.18s %-6.6s %6.6s%8.8s %s\n", + printf("%-16.16s %-18.18s %-6.6s %6.6s%8.8s %-5.5s", "Destination", "Gateway", - "Flags", "Refs", "Use", "Interface"); - if (treeaddr) - return treestuff(treeaddr); - if (hostaddr == 0) { - printf("rthost: symbol not in namelist\n"); - return; - } - if (netaddr == 0) { - printf("rtnet: symbol not in namelist\n"); - return; - } - if (hashsizeaddr == 0) { - printf("rthashsize: symbol not in namelist\n"); - return; - } - kget(hashsizeaddr, hashsize); - routehash = (struct mbuf **)malloc( hashsize*sizeof (struct mbuf *) ); - kvm_read(hostaddr, (char *)routehash, hashsize*sizeof (struct mbuf *)); -again: - for (i = 0; i < hashsize; i++) { - if (routehash[i] == 0) - continue; - m = routehash[i]; - while (m) { - kget(m, mb); - if (Aflag) - printf("%8.8x ", m); - p_ortentry((struct ortentry *)(mb.m_dat)); - m = mb.m_next; - } - } - if (doinghost) { - kvm_read(netaddr, (char *)routehash, - hashsize*sizeof (struct mbuf *)); - doinghost = 0; - goto again; + "Flags", "Refs", "Use", "Iface"); + if(!aflag) { + printf("%-6.6s %-6.6s\n", "MTU", "Rtt"); + } else { + printf("\n %8s %8s %8s %8s %8s %8s %8s %8s\n", + "MTU", "Hopcount", "Expire", "recvpipe", + "sendpipe", "ssthresh", "RTT", "RTT var."); } - free((char *)routehash); - return; + + return treestuff(treeaddr); + } static union { @@ -355,15 +349,14 @@ int flags, width; default: { - register u_short *s = ((u_short *)sa->sa_data), *slim; + register u_char *s = ((u_char *)sa->sa_data), *slim; - slim = (u_short *) sa + ((sa->sa_len + sizeof(u_short) - 1) / - sizeof(u_short)); + slim = (u_char *) sa + sa->sa_len; cp = workbuf; cplim = cp + sizeof(workbuf) - 6; cp += sprintf(cp, "(%d)", sa->sa_family); while (s < slim && cp < cplim) - cp += sprintf(cp, " %x", *s++); + cp += sprintf(cp, " %02x%02x", s[0], s[1]), s += 2; cp = workbuf; } } @@ -407,29 +400,90 @@ register struct rtentry *rt; } kget(rt->rt_ifp, ifnet); kvm_read((off_t)ifnet.if_name, name, 16); - printf(" %.15s%d%s", name, ifnet.if_unit, - rt->rt_nodes[0].rn_dupedkey ? " =>\n" : "\n"); -} + printf(" %.2s%d", name, ifnet.if_unit); + if(aflag) { + /* + * MTU + */ + if(rt->rt_rmx.rmx_mtu) + printf("\n %7d%c", rt->rt_rmx.rmx_mtu, + (rt->rt_rmx.rmx_locks & RTV_MTU) ? '*' : ' '); + else + printf("\n %7s ", "-"); -p_ortentry(rt) -register struct ortentry *rt; -{ - char name[16], *flags; - register struct bits *p; - register struct sockaddr_in *sin; - struct ifnet ifnet; + /* + * Hop count + */ + if(rt->rt_rmx.rmx_hopcount) + printf(" %7d%c", rt->rt_rmx.rmx_hopcount, + (rt->rt_rmx.rmx_locks & RTV_HOPCOUNT) ? '*' : ' '); + else + printf(" %7s ", "-"); + + /* + * Expiration time + */ + if(rt->rt_rmx.rmx_expire) + printf(" %7d%c", rt->rt_rmx.rmx_expire, + (rt->rt_rmx.rmx_locks & RTV_EXPIRE) ? '*' : ' '); + else + printf(" %7s ", "-"); - p_sockaddr(&rt->rt_dst, rt->rt_flags, 16); - p_sockaddr(&rt->rt_gateway, 0, 18); - p_flags(rt->rt_flags, "%-6.6s "); - printf("%6d %8d ", rt->rt_refcnt, rt->rt_use); - if (rt->rt_ifp == 0) { - putchar('\n'); - return; + /* + * Receive pipe size (bytes) + */ + if(rt->rt_rmx.rmx_recvpipe) + printf(" %7d%c", rt->rt_rmx.rmx_recvpipe, + (rt->rt_rmx.rmx_locks & RTV_RPIPE) ? '*' : ' '); + else + printf(" %7s ", "-"); + + /* + * Send pipe size (bytes) + */ + if(rt->rt_rmx.rmx_sendpipe) + printf(" %7d%c", rt->rt_rmx.rmx_sendpipe, + (rt->rt_rmx.rmx_locks & RTV_SPIPE) ? '*' : ' '); + else + printf(" %7s ", "-"); + + /* + * Slow-start threshold (bytes) + */ + if(rt->rt_rmx.rmx_ssthresh) + printf(" %7d%c", rt->rt_rmx.rmx_ssthresh, + (rt->rt_rmx.rmx_locks & RTV_SSTHRESH) ? '*' : ' '); + else + printf(" %7s ", "-"); + + /* + * Round-trip time (seconds) + */ + if(rt->rt_rmx.rmx_rtt) + printf(" %7.4f%c", (1.0 * rt->rt_rmx.rmx_rtt) / RTM_RTTUNIT, + (rt->rt_rmx.rmx_locks & RTV_RTT) ? '*' : ' '); + else + printf(" %7s ", "-"); + + /* + * Round-trip time variance (seconds) + */ + if(rt->rt_rmx.rmx_rttvar) + printf(" %7.4f%c", (1.0 * rt->rt_rmx.rmx_rttvar) / RTM_RTTUNIT, + (rt->rt_rmx.rmx_locks & RTV_RTTVAR) ? '*' : ' '); + else + printf(" %7s ", "-"); + } else { /* no -a flag */ + if(rt->rt_rmx.rmx_mtu) + printf(" %6d", rt->rt_rmx.rmx_mtu); + else + printf(" %-6s", "-"); + if(rt->rt_rmx.rmx_rtt) + printf(" %6.3f", (1. * rt->rt_rmx.rmx_rtt) / RTM_RTTUNIT); + else + printf(" %-6s", "-"); } - kget(rt->rt_ifp, ifnet); - kvm_read((off_t)ifnet.if_name, name, 16); - printf(" %.15s%d\n", name, ifnet.if_unit); + printf(rt->rt_nodes[0].rn_dupedkey ? " =>\n" : "\n"); } char * diff --git a/usr.bin/netstat/tp_astring.c b/usr.bin/netstat/tp_astring.c new file mode 100644 index 000000000000..654bab6999d4 --- /dev/null +++ b/usr.bin/netstat/tp_astring.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)tp_astring.c 7.4 (Berkeley) 5/6/91 + * $Id: tp_astring.c,v 1.1 1993/11/16 01:44:52 wollman Exp $ + */ + +#ifndef _NFILE +#include <stdio.h> +#endif _NFILE +char *tp_sstring[] = { +"ST_ERROR(0x0)", +"TP_CLOSED(0x1)", +"TP_CRSENT(0x2)", +"TP_AKWAIT(0x3)", +"TP_OPEN(0x4)", +"TP_CLOSING(0x5)", +"TP_REFWAIT(0x6)", +"TP_LISTENING(0x7)", +"TP_CONFIRMING(0x8)", +}; + +char *tp_estring[] = { +"TM_inact(0x0)", +"TM_retrans(0x1)", +"TM_sendack(0x2)", +"TM_notused(0x3)", +"TM_reference(0x4)", +"TM_data_retrans(0x5)", +"ER_TPDU(0x6)", +"CR_TPDU(0x7)", +"DR_TPDU(0x8)", +"DC_TPDU(0x9)", +"CC_TPDU(0xa)", +"AK_TPDU(0xb)", +"DT_TPDU(0xc)", +"XPD_TPDU(0xd)", +"XAK_TPDU(0xe)", +"T_CONN_req(0xf)", +"T_DISC_req(0x10)", +"T_LISTEN_req(0x11)", +"T_DATA_req(0x12)", +"T_XPD_req(0x13)", +"T_USR_rcvd(0x14)", +"T_USR_Xrcvd(0x15)", +"T_DETACH(0x16)", +"T_NETRESET(0x17)", +"T_ACPT_req(0x18)", +}; diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c index a9225d536da6..3c8857455159 100644 --- a/usr.bin/netstat/unix.c +++ b/usr.bin/netstat/unix.c @@ -32,7 +32,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)unix.c 5.11 (Berkeley) 7/1/91"; +/* from: static char sccsid[] = "@(#)unix.c 5.11 (Berkeley) 7/1/91"; */ +static const char unix_c_rcsid[] = + "$Id: unix.c,v 1.5 1994/02/04 03:17:15 wollman Exp $"; #endif /* not lint */ /* @@ -133,7 +135,7 @@ unixdomainpr(so, soaddr) } else m = (struct mbuf *)0; if (first) { - printf("Active UNIX domain sockets\n"); + printf("Active local domain sockets\n"); printf( "%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n", "Address", "Type", "Recv-Q", "Send-Q", @@ -145,7 +147,7 @@ unixdomainpr(so, soaddr) unp->unp_vnode, unp->unp_conn, unp->unp_refs, unp->unp_nextref); if (m) - printf(" %.*s", m->m_len - sizeof(sa->sun_family), + printf(" %.*s", m->m_len - sizeof(sa->sun_family) - 1, sa->sun_path); putchar('\n'); } diff --git a/usr.bin/nice/Makefile b/usr.bin/nice/Makefile index ca6b03f10d9c..b984f805f1af 100644 --- a/usr.bin/nice/Makefile +++ b/usr.bin/nice/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.5 (Berkeley) 5/11/90 +# from: @(#)Makefile 5.5 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/11/23 00:02:21 jtc Exp $ PROG= nice diff --git a/usr.bin/nice/nice.1 b/usr.bin/nice/nice.1 index 2d95baf61836..8795342cdefb 100644 --- a/usr.bin/nice/nice.1 +++ b/usr.bin/nice/nice.1 @@ -1,7 +1,6 @@ .\" Copyright (c) 1980, 1990 The Regents of the University of California. .\" All rights reserved. .\" -.\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: @@ -30,63 +29,84 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)nice.1 6.7 (Berkeley) 7/24/91 +.\" from: @(#)nice.1 6.7 (Berkeley) 7/24/91 +.\" $Id: nice.1,v 1.2.2.1 1994/05/01 16:08:39 jkh Exp $ .\" .Dd July 24, 1991 .Dt NICE 1 -.Os BSD 4 +.Os .Sh NAME .Nm nice -.Nd execute a Bourne shell command at a low scheduling priority +.Nd execute a utility with an altered scheduling priority .Sh SYNOPSIS .Nm nice -.Op Fl Ns Ar number -.Ar command -.Op Ar arguments +.Op Fl n Ar increment +.Ar utility +.Op Ar argument ... .Sh DESCRIPTION .Nm Nice runs -.Ar command -at a low priority. -(Think of low and slow). -If -.Fl Ns Ar number -is specified, and if it is greater than or equal -to 10 (the default), +.Ar utility +at an altered scheduling priority. +If an +.Ar increment +is given, it is used; otherwise +an increment of 10 is assumed. +The super-user can run utilities with priorities higher than normal by using +a negative +.Ar increment . +The priority can be adjusted over a +range of -20 (the higest) to 20 (the lowest). +.Pp +Available options: +.Bl -tag -width indent +.It Fl n Ar increment +A positive or negative decimal integer used to modify the system scheduling +priority of +.Ar utility. +.El +.Sh DIAGNOSTICS +The .Nm nice -will execute -.Ar command -at that priority. -The upper bound, or lowest priority that +utility shall exit with one of the following values: +.Bl -tag -width indent +.It 1-125 +An error occurred in the .Nm nice -will run a command is 20. -The lower bounds or -higher priorities (integers less than 10) -can only be requested by the super-user. -Negative numbers are expressed as -.Fl - Ns Ar number . +utility. +.It 126 +The +.Ar utility +was found but could not be invoked. +.It 127 +The +.Ar utility +could not be found. +.El .Pp -The returned exit status is the exit value from the -command executed by -.Nm nice . +Otherwise, the exit status of +.Nm nice +shall be that of +.Ar utility . +.Sh COMPATIBILITY +The historic +.Fl Ns Ar increment +option has been deprecated but is still supported in this implementation. .Sh SEE ALSO .Xr csh 1 , .Xr renice 8 +.Sh STANDARDS +The +.Nm nice +utility conforms to +.St -p1003.2-92 . .Sh HISTORY A .Nm nice -command appeared in +utility appeared in .At v6 . .Sh BUGS .Nm Nice -is particular to -.Xr sh 1 . -If you use -.Xr csh 1 , -then commands executed with ``&'' are automatically immune to hangup -signals while in the background. -.Pp -.Nm Nice is built into .Xr csh 1 with a slightly different syntax than described here. The form diff --git a/usr.bin/nice/nice.c b/usr.bin/nice/nice.c index 5e3f09f92dd7..1081e6fac5e6 100644 --- a/usr.bin/nice/nice.c +++ b/usr.bin/nice/nice.c @@ -38,61 +38,78 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)nice.c 5.4 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)nice.c 5.4 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: nice.c,v 1.2 1993/11/23 00:02:23 jtc Exp $"; #endif /* not lint */ #include <sys/time.h> #include <sys/resource.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> #include <ctype.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> #define DEFNICE 10 -/* ARGSUSED */ +static void usage(); + +int main(argc, argv) int argc; char **argv; { - extern int errno; - int niceness; - char *strerror(); + int niceness = DEFNICE; + int c; - niceness = DEFNICE; - if (argv[1][0] == '-') - if (isdigit(argv[1][1])) { - niceness = atoi(argv[1] + 1); - ++argv; - } - else { - (void)fprintf(stderr, "nice: illegal option -- %c\n", - argv[1][1]); + setlocale(LC_ALL, ""); + + /* handle obsolete -number syntax */ + if (argc > 1 && argv[1][0] == '-' && isdigit(argv[1][1])) { + niceness = atoi (argv[1] + 1); + argc--; argv++; + } + + while ((c = getopt (argc, argv, "n:")) != -1) { + switch (c) { + case 'n': + niceness = atoi (optarg); + break; + + case '?': + default: usage(); + break; } + } + argc -= optind; argv += optind; - if (!argv[1]) + if (argc == 0) usage(); errno = 0; niceness += getpriority(PRIO_PROCESS, 0); if (errno) { - (void)fprintf(stderr, "nice: getpriority: %s\n", - strerror(errno)); - exit(1); + err (1, "getpriority"); + /* NOTREACHED */ } if (setpriority(PRIO_PROCESS, 0, niceness)) { - (void)fprintf(stderr, - "nice: setpriority: %s\n", strerror(errno)); - exit(1); + warn ("setpriority"); } - execvp(argv[1], &argv[1]); - (void)fprintf(stderr, - "nice: %s: %s\n", argv[1], strerror(errno)); - exit(1); + + execvp(argv[0], &argv[0]); + err ((errno == ENOENT) ? 127 : 126, "%s", argv[0]); + /* NOTREACHED */ } +static void usage() { (void)fprintf(stderr, - "nice [ -# ] command [ options ] [ operands ]\n"); + "usage: nice [ -n increment ] utility [ argument ...]\n"); + exit(1); } diff --git a/usr.bin/nohup/nohup.1 b/usr.bin/nohup/nohup.1 index 70eb8aac8b89..046894660ac0 100644 --- a/usr.bin/nohup/nohup.1 +++ b/usr.bin/nohup/nohup.1 @@ -85,7 +85,7 @@ was found but could not be invoked. .It 127 The .Ar utility -could not be found or an error occured in +could not be found or an error occurred in .Nm nohup. .El .Pp diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile index 3963a28f99c6..25f20f2c3f4a 100644 --- a/usr.bin/passwd/Makefile +++ b/usr.bin/passwd/Makefile @@ -1,17 +1,22 @@ # @(#)Makefile 5.11 (Berkeley) 2/19/91 PROG= passwd -SRCS= local_passwd.c passwd.c pw_copy.c pw_util.c -CFLAGS+=-I${.CURDIR} +SRCS= local_passwd.c passwd.c pw_copy.c pw_util.c yp_passwd.c getpwent.c +CFLAGS+=-I${.CURDIR} -DYP .PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \ - ${.CURDIR}/../rlogin + ${.CURDIR}/../rlogin ${.CURDIR}/../../lib/libc/gen + +DPADD+= ${LIBRPCSVC} #${LIBKRB} +LDADD+= -lrpcsvc #-lkrb .if exists(/usr/lib/libcrypt.a) #CFLAGS+=-DKERBEROS -DPADD+= ${LIBCRYPT} #${LIBKRB} -LDADD+= -lcrypt #-lkrb +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt .endif +getpwent.o: getpwent.c + ${CC} ${CFLAGS} -UYP -c ${.IMPSRC} BINOWN= root BINMODE=4555 diff --git a/usr.bin/passwd/local_passwd.c b/usr.bin/passwd/local_passwd.c index a196127fb5b8..0a81ab1dce18 100644 --- a/usr.bin/passwd/local_passwd.c +++ b/usr.bin/passwd/local_passwd.c @@ -32,7 +32,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)local_passwd.c 5.5 (Berkeley) 5/6/91"; +/*static char sccsid[] = "from: @(#)local_passwd.c 5.5 (Berkeley) 5/6/91";*/ +static char rcsid[] = "$Id: local_passwd.c,v 1.4 1994/01/11 19:01:13 nate Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -53,14 +54,18 @@ local_passwd(uname) char *getnewpasswd(); if (!(pw = getpwnam(uname))) { +#ifdef YP + extern int use_yp; + if (!use_yp) +#endif (void)fprintf(stderr, "passwd: unknown user %s.\n", uname); - exit(1); + return(1); } uid = getuid(); if (uid && uid != pw->pw_uid) { (void)fprintf(stderr, "passwd: %s\n", strerror(EACCES)); - exit(1); + return(1); } pw_init(); @@ -78,7 +83,7 @@ local_passwd(uname) if (!pw_mkdb()) pw_error((char *)NULL, 0, 1); - exit(0); + return(0); } char * diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c index e528b7a661fd..4ffaeb050e22 100644 --- a/usr.bin/passwd/passwd.c +++ b/usr.bin/passwd/passwd.c @@ -38,14 +38,29 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)passwd.c 5.5 (Berkeley) 7/6/91"; +/*static char sccsid[] = "from: @(#)passwd.c 5.5 (Berkeley) 7/6/91";*/ +static char rcsid[] = "$Id: passwd.c,v 1.2 1994/01/11 19:01:15 nate Exp $"; #endif /* not lint */ #include <stdio.h> #include <unistd.h> +/* + * Note on configuration: + * Generally one would not use both Kerberos and YP + * to maintain passwords. + * + */ #ifdef KERBEROS int use_kerberos = 1; +#else +int use_kerberos = 0; +#endif +#ifdef YP +int force_yp = 0; +int use_yp; +#else +int use_yp = 0; #endif main(argc, argv) @@ -55,16 +70,43 @@ main(argc, argv) extern int optind; register int ch; char *uname; - -#ifdef KERBEROS - while ((ch = getopt(argc, argv, "l")) != EOF) + int status; + +#ifdef YP + use_yp = _yp_check(NULL); +#endif + + while ((ch = getopt(argc, argv, "lky")) != EOF) switch (ch) { case 'l': /* change local password file */ use_kerberos = 0; + use_yp = 0; + break; + case 'k': /* change Kerberos password */ +#ifdef KERBEROS + use_kerberos = 1; + use_yp = 0; + break; +#else + usage(); + exit(1); +#endif + case 'y': /* change YP password */ +#ifdef YP + if (!use_yp) { + fprintf(stderr, "passwd: YP not in use.\n"); + usage(); + exit(1); + } + + /* XXX Maybe just exec yppasswd ?? */ + use_kerberos = 0; + use_yp = 1; + force_yp = 1; break; #else - while ((ch = getopt(argc, argv, "")) != EOF) - switch (ch) { + usage(); + exit(1); #endif default: case '?': @@ -76,7 +118,11 @@ main(argc, argv) argv += optind; uname = getlogin(); - + if (uname == NULL) { + fprintf(stderr, "passwd: who are you ??\n"); + exit(1); + } + switch(argc) { case 0: break; @@ -101,14 +147,23 @@ main(argc, argv) if (use_kerberos) exit(krb_passwd()); #endif +#ifdef YP + if (force_yp || ((status = local_passwd(uname)) && use_yp)) + exit(yp_passwd(uname)); + exit(status); +#endif exit(local_passwd(uname)); } usage() { -#ifdef KERBEROS +#if defined(KERBEROS) && defined(YP) + (void)fprintf(stderr, "usage: passwd [-l] [-k] [-y] user\n"); +#else /* !(KERBEROS && YP) */ +#if defined(KERBEROS) || defined(YP) (void)fprintf(stderr, "usage: passwd [-l] user\n"); #else (void)fprintf(stderr, "usage: passwd user\n"); #endif +#endif /* KERBEROS && YP */ } diff --git a/usr.bin/passwd/yp_passwd.c b/usr.bin/passwd/yp_passwd.c new file mode 100644 index 000000000000..01cac298223a --- /dev/null +++ b/usr.bin/passwd/yp_passwd.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)yp_passwd.c 1.0 2/2/93";*/ +static char rcsid[] = "$Id: yp_passwd.c,v 1.1 1994/01/11 19:01:16 nate Exp $"; +#endif /* not lint */ + +#ifdef YP + +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <time.h> +#include <pwd.h> +#include <errno.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#define passwd yp_passwd_rec +#include <rpcsvc/yppasswd.h> +#undef passwd + +#ifndef _PASSWORD_LEN +#define _PASSWORD_LEN PASS_MAX +#endif + +extern char *progname; +static char *getnewpasswd(); +static struct passwd *ypgetpwnam(); + +static uid_t uid; +char *domain; + +yp_passwd(uname) + char *uname; +{ + char *master; + char *pp; + int r, rpcport, status; + struct yppasswd yppasswd; + struct passwd *pw; + struct timeval tv; + CLIENT *client; + + uid = getuid(); + + /* + * Get local domain + */ + if (r = yp_get_default_domain(&domain)) { + (void)fprintf(stderr, "%s: can't get local YP domain. Reason: %s\n", progname, yperr_string(r)); + exit(1); + } + + /* + * Find the host for the passwd map; it should be running + * the daemon. + */ + if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { + (void)fprintf(stderr, "%s: can't find the master YP server. Reason: %s\n", progname, yperr_string(r)); + exit(1); + } + + /* + * Ask the portmapper for the port of the daemon. + */ + if ((rpcport = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) { + (void)fprintf(stderr, "%s: master YP server not running yppasswd daemon.\n\tCan't change password.\n", progname); + exit(1); + } + + /* + * Be sure the port is priviledged + */ + if (rpcport >= IPPORT_RESERVED) { + (void)fprintf(stderr, "%s: yppasswd daemon running on an invalid port.\n", progname); + exit(1); + } + + /* Get user's login identity */ + if (!(pw = ypgetpwnam(uname))) { + (void)fprintf(stderr, "%s: unknown user %s.\n", progname, uname); + exit(1); + } + + if (uid && uid != pw->pw_uid) { + (void)fprintf(stderr, "%s: you are only allowed to change your own password: %s\n", progname, strerror(EACCES)); + exit(1); + } + + /* prompt for new password */ + yppasswd.newpw.pw_passwd = getnewpasswd(pw, &yppasswd.oldpass); + + /* tell rpc.yppasswdd */ + yppasswd.newpw.pw_name = pw->pw_name; + yppasswd.newpw.pw_uid = pw->pw_uid; + yppasswd.newpw.pw_gid = pw->pw_gid; + yppasswd.newpw.pw_gecos = pw->pw_gecos; + yppasswd.newpw.pw_dir = pw->pw_dir; + yppasswd.newpw.pw_shell = pw->pw_shell; + + client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + if (client==NULL) { + fprintf(stderr, "can't contact yppasswdd on %s: Reason: %s\n", + master, yperr_string(YPERR_YPBIND)); + return(YPERR_YPBIND); + } + client->cl_auth = authunix_create_default(); + tv.tv_sec = 2; + tv.tv_usec = 0; + r = clnt_call(client, YPPASSWDPROC_UPDATE, + xdr_yppasswd, &yppasswd, xdr_int, &status, tv); + if (r) + fprintf(stderr, "%s: rpc to yppasswdd failed.\n"); + else if (status) + printf("Couldn't change YP password.\n"); + else + printf("The YP password has been changed on %s, the master YP passwd server.\n", master); + + exit(0); +} + +static char * +getnewpasswd(pw, old_pass) + register struct passwd *pw; + char **old_pass; +{ + static char buf[_PASSWORD_LEN+1]; + register char *p, *t; + int tries; + char salt[9], *crypt(), *getpass(); + + (void)printf("Changing YP password for %s.\n", pw->pw_name); + + if (uid && old_pass) { + *old_pass = NULL; + + if (pw->pw_passwd && +#ifdef DES + strcmp(crypt(p = getpass("Old password:"), pw->pw_passwd), + pw->pw_passwd)) { +#else + strcmp(p = getpass("Old password:"), pw->pw_passwd)) { +#endif + errno = EACCES; + pw_error(NULL, 1, 1); + } + *old_pass = strdup(p); + } + for (buf[0] = '\0', tries = 0;;) { + p = getpass("New password:"); + if (!*p) { + (void)printf("Password unchanged.\n"); + pw_error(NULL, 0, 0); + } + if (strlen(p) <= 5 && (uid != 0 || ++tries < 2)) { + (void)printf("Please enter a longer password.\n"); + continue; + } + for (t = p; *t && islower(*t); ++t); + if (!*t && (uid != 0 || ++tries < 2)) { + (void)printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n"); + continue; + } + (void)strcpy(buf, p); + if (!strcmp(buf, getpass("Retype new password:"))) + break; + (void)printf("Mismatch; try again, EOF to quit.\n"); + } + /* grab a random printable character that isn't a colon */ + (void)srandom((int)time((time_t *)NULL)); +#ifdef NEWSALT + salt[0] = _PASSWORD_EFMT1; + to64(&salt[1], (long)(29 * 25), 4); + to64(&salt[5], random(), 4); +#else + to64(&salt[0], random(), 2); +#endif +#ifdef DES + return(strdup(crypt(buf, salt))); +#else + return(buf); +#endif +} + +static char * +pwskip(register char *p) +{ + while (*p && *p != ':' && *p != '\n') + ++p; + if (*p) + *p++ = 0; + return (p); +} + +struct passwd * +interpret(struct passwd *pwent, char *line) +{ + register char *p = line; + register int c; + + pwent->pw_passwd = "*"; + pwent->pw_uid = 0; + pwent->pw_gid = 0; + pwent->pw_gecos = ""; + pwent->pw_dir = ""; + pwent->pw_shell = ""; + pwent->pw_change = 0; + pwent->pw_expire = 0; + pwent->pw_class = ""; + + /* line without colon separators is no good, so ignore it */ + if(!strchr(p,':')) + return(NULL); + + pwent->pw_name = p; + p = pwskip(p); + pwent->pw_passwd = p; + p = pwskip(p); + pwent->pw_uid = (uid_t)strtoul(p, NULL, 10); + p = pwskip(p); + pwent->pw_gid = (gid_t)strtoul(p, NULL, 10); + p = pwskip(p); + pwent->pw_gecos = p; + p = pwskip(p); + pwent->pw_dir = p; + p = pwskip(p); + pwent->pw_shell = p; + while (*p && *p != '\n') + p++; + *p = '\0'; + return (pwent); +} + +static struct passwd * +ypgetpwnam(nam) + char *nam; +{ + static struct passwd pwent; + static char line[1024]; + char *val; + int reason, vallen; + + reason = yp_match(domain, "passwd.byname", nam, strlen(nam), + &val, &vallen); + switch(reason) { + case 0: + break; + default: + return (NULL); + break; + } + val[vallen] = '\0'; + strcpy(line, val); + free(val); + + return(interpret(&pwent, line)); +} + +#endif /* YP */ diff --git a/usr.bin/printenv/printenv.1 b/usr.bin/printenv/printenv.1 index c86536944181..2b24b5bab045 100644 --- a/usr.bin/printenv/printenv.1 +++ b/usr.bin/printenv/printenv.1 @@ -31,7 +31,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)printenv.1 6.7 (Berkeley) 7/28/91 +.\" from: @(#)printenv.1 6.7 (Berkeley) 7/28/91 +.\" $Id: printenv.1,v 1.4 1993/11/23 00:34:04 jtc Exp $ .\" .Dd July 28, 1991 .Dt PRINTENV 1 diff --git a/usr.bin/printenv/printenv.c b/usr.bin/printenv/printenv.c index d8d007c68894..6a4ed50fac96 100644 --- a/usr.bin/printenv/printenv.c +++ b/usr.bin/printenv/printenv.c @@ -38,15 +38,20 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)printenv.c 5.4 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)printenv.c 5.4 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: printenv.c,v 1.2 1993/11/23 00:34:05 jtc Exp $"; #endif /* not lint */ +#include <stdio.h> +#include <string.h> + /* * printenv * * Bill Joy, UCB * February, 1979 */ +int main(argc, argv) int argc; char **argv; diff --git a/usr.bin/printf/printf.1 b/usr.bin/printf/printf.1 index 34f0668f52c5..d2c8f979be42 100644 --- a/usr.bin/printf/printf.1 +++ b/usr.bin/printf/printf.1 @@ -32,17 +32,19 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)printf.1 5.11 (Berkeley) 7/24/91 +.\" from: @(#)printf.1 5.11 (Berkeley) 7/24/91 +.\" $Id: printf.1,v 1.2 1993/11/23 00:06:20 jtc Exp $ .\" -.Dd July 24, 1991 +.Dd November 5, 1993 .Dt PRINTF 1 .Os .Sh NAME .Nm printf .Nd formatted output .Sh SYNOPSIS -.Nm printf format -.Op arguments ... +.Nm printf +.Ar format +.Op Ar arguments ... .Sh DESCRIPTION .Nm Printf formats and prints its arguments, after the first, under control @@ -60,6 +62,7 @@ The .Ar arguments after the first are treated as strings if the corresponding format is either +.Cm b , .Cm c or .Cm s ; @@ -69,8 +72,9 @@ otherwise it is evaluated as a C constant, with the following extensions: .It A leading plus or minus sign is allowed. .It -If the leading character is a single or double quote, or not a digit, -plus, or minus sign, the value is the ASCII code of the next character. +If the leading character is a single or double quote, the value is the +.Tn ASCII +code of the next character. .El .Pp The format string is reused as often as necessary to satisfy the @@ -78,11 +82,8 @@ The format string is reused as often as necessary to satisfy the Any extra format specifications are evaluated with zero or the null string. .Pp -Character escape sequences are in backslash notation as defined in the -draft proposed -.Tn ANSI C -Standard -.Tn X3J11 . +Character escape sequences are in backslash notation as defined in +.St -ansiC . The characters and their meanings are as follows: .Bl -tag -width Ds -offset indent @@ -187,7 +188,7 @@ from a string; if the digit string is missing, the precision is treated as zero; .It Format: A character which indicates the type of format to use (one of -.Cm diouxXfwEgGcs ) . +.Cm diouxXfwEgGbcs ) . .El .Pp A field width or precision may be @@ -232,6 +233,10 @@ or in style .Cm e .Pq Cm E whichever gives full precision in minimum space. +.It Cm b +Characters from the string +.Ar argument +are printed with backslash-escape sequences expanded. .It Cm c The first character of .Ar argument @@ -253,20 +258,15 @@ the actual width. .Nm Printf exits 0 on success, 1 on failure. .Sh SEE ALSO +.Xr echo 1 , .Xr printf 3 -.Sh HISTORY +.Sh STANDARDS The .Nm printf -command appeared in -.Bx 4.3 Reno . -It is modeled -after the standard library function, -.Xr printf 3 . +utility conforms to +.St -p1003.2-92 . .Sh BUGS -Since the number is translated from +Since arguments are translated from .Tn ASCII to floating-point, and then back again, floating-point precision may be lost. -.Pp -.Tn ANSI -hexadecimal character constants were deliberately not provided. diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c index a64e4d4c4d2c..0fbcfcf4ab22 100644 --- a/usr.bin/printf/printf.c +++ b/usr.bin/printf/printf.c @@ -32,17 +32,78 @@ */ #ifndef lint +#if !defined(SHELL) && !defined(BUILTIN) char copyright[] = "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ All rights reserved.\n"; +#endif #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: printf.c,v 1.2 1993/11/23 00:06:21 jtc Exp $"; #endif /* not lint */ -#include <sys/types.h> +#include <ctype.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <locale.h> +#include <err.h> + +static int print_escape_str __P((const char *)); +static int print_escape __P((const char *)); + +static int getchr __P((void)); +static double getdouble __P((void)); +static int getint __P((void)); +static long getlong __P((void)); +static char *getstr __P((void)); +static char *mklong __P((char *, int)); +static void usage __P((void)); + +static int rval; +static char **gargv; + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define octtobin(c) ((c) - '0') +#define hextobin(c) ((c) >= 'A' && 'c' <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0') + +#ifdef SHELL +#define main printfcmd +#include "../../bin/sh/bltin/bltin.h" + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <vararg.h> +#endif + +static void +#ifdef __STDC__ +warnx(const char *fmt, ...) +#else +warnx(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + + char buf[64]; + va_list ap; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + error(buf); +} +#endif /* SHELL */ #define PF(f, func) { \ if (fieldwidth) \ @@ -56,196 +117,303 @@ static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) 6/1/90"; (void)printf(f, func); \ } -char **gargv; - +int +#ifdef BUILTIN +progprintf(argc, argv) +#else main(argc, argv) +#endif int argc; char **argv; { - static char *skip1, *skip2; - register char *format, *fmt, *start; - register int end, fieldwidth, precision; - char convch, nextch, *getstr(), *index(), *mklong(); - double getdouble(); - long getlong(); + register char *fmt, *start; + register int fieldwidth, precision; + char convch, nextch; + char *format; + int ch; + +#if !defined(SHELL) && !defined(BUILTIN) + setlocale (LC_ALL, ""); +#endif - if (argc < 2) { - fprintf(stderr, "usage: printf format [arg ...]\n"); - exit(1); + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + case '?': + default: + usage(); + return (1); + } } + argc -= optind; + argv += optind; - /* - * Basic algorithm is to scan the format string for conversion - * specifications -- once one is found, find out if the field - * width or precision is a '*'; if it is, gather up value. Note, - * format strings are reused as necessary to use up the provided - * arguments, arguments of zero/null string are provided to use - * up the format string. - */ - skip1 = "#-+ 0"; - skip2 = "*0123456789"; + if (argc < 1) { + usage(); + return (1); + } - escape(fmt = format = *++argv); /* backslash interpretation */ + format = *argv; gargv = ++argv; - for (;;) { - end = 0; + +#define SKIP1 "#-+ 0" +#define SKIP2 "*0123456789" + do { + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. + * Note, format strings are reused as necessary to use up the + * provided arguments, arguments of zero/null string are + * provided to use up the format string. + */ + /* find next format specification */ -next: for (start = fmt;; ++fmt) { - if (!*fmt) { - /* avoid infinite loop */ - if (end == 1) { - fprintf(stderr, - "printf: missing format character.\n"); - exit(1); + for (fmt = format; *fmt; fmt++) { + switch (*fmt) { + case '%': + start = fmt++; + + if (*fmt == '%') { + putchar ('%'); + break; + } else if (*fmt == 'b') { + char *p = getstr(); + if (print_escape_str(p)) { + return (rval); + } + break; } - end = 1; - if (fmt > start) - (void)printf("%s", start); - if (!*gargv) - exit(0); - fmt = format; - goto next; - } - /* %% prints a % */ - if (*fmt == '%') { - if (*++fmt != '%') + + /* skip to field width */ + for (; index(SKIP1, *fmt); ++fmt); + fieldwidth = *fmt == '*' ? getint() : 0; + + /* skip to possible '.', get following precision */ + for (; index(SKIP2, *fmt); ++fmt); + if (*fmt == '.') + ++fmt; + precision = *fmt == '*' ? getint() : 0; + + for (; index(SKIP2, *fmt); ++fmt); + if (!*fmt) { + warnx ("missing format character"); + rval = 1; + return; + } + + convch = *fmt; + nextch = *(fmt + 1); + *(fmt + 1) = '\0'; + switch(convch) { + case 'c': { + char p = getchr(); + PF(start, p); + break; + } + case 's': { + char *p = getstr(); + PF(start, p); + break; + } + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': { + char *f = mklong(start, convch); + long p = getlong(); + PF(f, p); break; - *fmt++ = '\0'; - (void)printf("%s", start); - goto next; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double p = getdouble(); + PF(start, p); + break; + } + default: + warnx ("%s: invalid directive", start); + rval = 1; + } + *(fmt + 1) = nextch; + break; + + case '\\': + fmt += print_escape(fmt); + break; + + default: + putchar (*fmt); + break; } } + } while (gargv > argv && *gargv); - /* skip to field width */ - for (; index(skip1, *fmt); ++fmt); - fieldwidth = *fmt == '*' ? getint() : 0; + return (rval); +} - /* skip to possible '.', get following precision */ - for (; index(skip2, *fmt); ++fmt); - if (*fmt == '.') - ++fmt; - precision = *fmt == '*' ? getint() : 0; - /* skip to conversion char */ - for (; index(skip2, *fmt); ++fmt); - if (!*fmt) { - fprintf(stderr, "printf: missing format character.\n"); - exit(1); - } +/* + * Print SysV echo(1) style escape string + * Halts processing string and returns 1 if a \c escape is encountered. + */ +static int +print_escape_str(str) + register const char *str; +{ + int value; + int c; - convch = *fmt; - nextch = *++fmt; - *fmt = '\0'; - switch(convch) { - case 'c': { - char p = getchr(); - PF(start, p); - break; - } - case 's': { - char *p = getstr(); - PF(start, p); - break; + while (*str) { + if (*str == '\\') { + str++; + /* + * %b string octal constants are not like those in C. + * They start with a \0, and are followed by 0, 1, 2, + * or 3 octal digits. + */ + if (*str == '0') { + str++; + for (c = 3, value = 0; c-- && isodigit(*str); str++) { + value <<= 3; + value += octtobin(*str); + } + putchar (value); + str--; + } else if (*str == 'c') { + return 1; + } else { + str--; + str += print_escape(str); + } + } else { + putchar (*str); } - case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { - char *f = mklong(start, convch); - long p = getlong(); - PF(f, p); - break; + str++; + } + + return 0; +} + +/* + * Print "standard" escape characters + */ +static int +print_escape(str) + register const char *str; +{ + const char *start = str; + int c; + int value; + + str++; + + switch (*str) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + for (c = 3, value = 0; c-- && isodigit(*str); str++) { + value <<= 3; + value += octtobin(*str); } - case 'e': case 'E': case 'f': case 'g': case 'G': { - double p = getdouble(); - PF(start, p); - break; + putchar(value); + return str - start - 1; + /* NOTREACHED */ + + case 'x': + str++; + for (value = 0; isxdigit(*str); str++) { + value <<= 4; + value += hextobin(*str); } - default: - fprintf(stderr, "printf: illegal format character.\n"); - exit(1); + if (value > UCHAR_MAX) { + warnx ("escape sequence out of range for character"); + rval = 1; } - *fmt = nextch; + putchar (value); + return str - start - 1; + /* NOTREACHED */ + + case '\\': /* backslash */ + putchar('\\'); + break; + + case '\'': /* single quote */ + putchar('\''); + break; + + case '"': /* double quote */ + putchar('"'); + break; + + case 'a': /* alert */ +#ifdef __STDC__ + putchar('\a'); +#else + putchar(007); +#endif + break; + + case 'b': /* backspace */ + putchar('\b'); + break; + + case 'e': /* escape */ +#ifdef __GNUC__ + putchar('\e'); +#else + putchar(033); +#endif + break; + + case 'f': /* form-feed */ + putchar('\f'); + break; + + case 'n': /* newline */ + putchar('\n'); + break; + + case 'r': /* carriage-return */ + putchar('\r'); + break; + + case 't': /* tab */ + putchar('\t'); + break; + + case 'v': /* vertical-tab */ + putchar('\v'); + break; + + default: + putchar(*str); + warnx("unknown escape sequence `\\%c'", *str); + rval = 1; } - /* NOTREACHED */ + + return 1; } -char * +static char * mklong(str, ch) char *str, ch; { - int len; - char *copy, *malloc(); + static char copy[64]; + int len; len = strlen(str) + 2; - if (!(copy = malloc((u_int)len))) { /* never freed; XXX */ - fprintf(stderr, "printf: out of memory.\n"); - exit(1); - } - bcopy(str, copy, len - 3); + memmove(copy, str, len - 3); copy[len - 3] = 'l'; copy[len - 2] = ch; copy[len - 1] = '\0'; - return(copy); -} - -escape(fmt) - register char *fmt; -{ - register char *store; - register int value, c; - - for (store = fmt; c = *fmt; ++fmt, ++store) { - if (c != '\\') { - *store = c; - continue; - } - switch (*++fmt) { - case '\0': /* EOS, user error */ - *store = '\\'; - *++store = '\0'; - return; - case '\\': /* backslash */ - case '\'': /* single quote */ - *store = *fmt; - break; - case 'a': /* bell/alert */ - *store = '\7'; - break; - case 'b': /* backspace */ - *store = '\b'; - break; - case 'f': /* form-feed */ - *store = '\f'; - break; - case 'n': /* newline */ - *store = '\n'; - break; - case 'r': /* carriage-return */ - *store = '\r'; - break; - case 't': /* horizontal tab */ - *store = '\t'; - break; - case 'v': /* vertical tab */ - *store = '\13'; - break; - /* octal constant */ - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - for (c = 3, value = 0; - c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { - value <<= 3; - value += *fmt - '0'; - } - --fmt; - *store = value; - break; - default: - *store = *fmt; - break; - } - } - *store = '\0'; + return (copy); } +static int getchr() { if (!*gargv) @@ -253,7 +421,7 @@ getchr() return((int)**gargv++); } -char * +static char * getstr() { if (!*gargv) @@ -262,46 +430,70 @@ getstr() } static char *number = "+-.0123456789"; +static int getint() { if (!*gargv) return(0); + if (index(number, **gargv)) return(atoi(*gargv++)); - return(asciicode()); + + return 0; } -long +static long getlong() { - long atol(); + long val; + char *ep; if (!*gargv) - return((long)0); - if (index(number, **gargv)) - return(strtol(*gargv++, (char **)NULL, 0)); - return((long)asciicode()); + return(0L); + + if (**gargv == '\"' || **gargv == '\'') { + val = gargv[0][1]; + gargv++; + return val; + } + + val = strtol (*gargv, &ep, 0); + if (*ep) { + warnx ("incompletely converted argument: %s", *gargv); + rval = 1; + } + + gargv++; + return val; } -double +static double getdouble() { - double atof(); + double val; + char *ep; if (!*gargv) - return((double)0); - if (index(number, **gargv)) - return(atof(*gargv++)); - return((double)asciicode()); + return(0.0); + + if (**gargv == '\"' || **gargv == '\'') { + val = gargv[0][1]; + gargv++; + return val; + } + + val = strtod (*gargv, &ep); + if (*ep) { + warnx ("incompletely converted argument: %s", *gargv); + rval = 1; + } + + gargv++; + return val; } -asciicode() +static void +usage() { - register char ch; - - ch = **gargv; - if (ch == '\'' || ch == '"') - ch = (*gargv)[1]; - ++gargv; - return(ch); + (void)fprintf(stderr, "usage: printf format [arg ...]\n"); } diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c index b99d12609f83..e73c1b7afc17 100644 --- a/usr.bin/rdist/docmd.c +++ b/usr.bin/rdist/docmd.c @@ -36,6 +36,7 @@ static char sccsid[] = "@(#)docmd.c 5.8 (Berkeley) 3/1/91"; #endif /* not lint */ #include "defs.h" +#include <stdlib.h> #include <setjmp.h> #include <netdb.h> @@ -506,6 +507,7 @@ notify(file, rhost, to, lmod) * Create a pipe to mailling program. */ (void)sprintf(buf, "%s -oi -t", _PATH_SENDMAIL); + unsetenv("IFS"); pf = popen(buf, "w"); if (pf == NULL) { error("notify: \"%s\" failed\n", _PATH_SENDMAIL); diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c index d3c0e9e7a964..69889b755078 100644 --- a/usr.bin/rdist/expand.c +++ b/usr.bin/rdist/expand.c @@ -58,7 +58,7 @@ char *entp; char **sortbase; char *index(); -int argcmp(); +static int argcmp(); #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1 index 42e200d463f0..11a17b58ede4 100644 --- a/usr.bin/rdist/rdist.1 +++ b/usr.bin/rdist/rdist.1 @@ -139,7 +139,7 @@ link itself. .It Fl i Ignore unresolved links. .Nm Rdist -will normally try to maintain the link structure of files being transfered +will normally try to maintain the link structure of files being transferred and warn the user if all the links cannot be found. .It Fl m Ar host Limit which machines are to be updated. Multiple @@ -287,7 +287,7 @@ unless the destination name is of the format ``login@host". The .Ic notify command is used to mail the list of files updated (and any errors -that may have occured) to the listed names. +that may have occurred) to the listed names. If no `@' appears in the name, the destination host is appended to the name (e.g., name1@host, name2@host, ...). diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c index ca953222102c..4939e7c8c351 100644 --- a/usr.bin/rdist/server.c +++ b/usr.bin/rdist/server.c @@ -637,13 +637,19 @@ query(name) { struct stat stb; - if (catname) + if (catname) { + if (tp - target + 1 + strlen(name) + 1 > sizeof(target)) { + errno = EINVAL; + goto err; + } (void) sprintf(tp, "/%s", name); + } if (lstat(target, &stb) < 0) { if (errno == ENOENT) (void) write(rem, "N\n", 2); else + err: error("%s:%s: %s\n", host, target, strerror(errno)); *tp = '\0'; return; @@ -760,7 +766,7 @@ recvf(cmd, type) errno = ENOTDIR; } else if (errno == ENOENT && (mkdir(target, mode) == 0 || chkparent(target) == 0 && mkdir(target, mode) == 0)) { - if (chog(target, owner, group, mode) == 0) + if (fchog(-1, target, owner, group, mode) == 0) ack(); return; } @@ -819,6 +825,9 @@ recvf(cmd, type) goto fixup; } + if (stat(target, &stb) != 0) + stb.st_atime = 0; + if ((f = creat(new, mode)) < 0) { if (errno != ENOENT || chkparent(new) < 0 || (f = creat(new, mode)) < 0) @@ -852,14 +861,15 @@ recvf(cmd, type) wrerr++; } } - (void) close(f); if (response() < 0) { err(); + (void) close(f); (void) unlink(new); return; } if (wrerr) { error("%s:%s: %s\n", host, new, strerror(errno)); + (void) close(f); (void) unlink(new); return; } @@ -872,6 +882,7 @@ recvf(cmd, type) if ((f2 = fopen(new, "r")) == NULL) { badn: error("%s:%s: %s\n", host, new, strerror(errno)); + (void) close(f); (void) unlink(new); return; } @@ -879,6 +890,7 @@ recvf(cmd, type) if (c == EOF) { (void) fclose(f1); (void) fclose(f2); + (void) close(f); (void) unlink(new); ack(); return; @@ -887,6 +899,7 @@ recvf(cmd, type) (void) fclose(f2); if (opts & VERIFY) { differ: + (void) close(f); (void) unlink(new); buf[0] = '\0'; (void) sprintf(buf + 1, "need to update: %s\n",target); @@ -895,20 +908,13 @@ recvf(cmd, type) } } - /* - * Set last modified time - */ - tvp[0].tv_sec = stb.st_atime; /* old atime from target */ - tvp[0].tv_usec = 0; - tvp[1].tv_sec = mtime; - tvp[1].tv_usec = 0; - if (utimes(new, tvp) < 0) { - note("%s:utimes failed %s: %s\n", host, new, strerror(errno)); - } - if (chog(new, owner, group, mode) < 0) { + if (fchog(f, new, owner, group, mode) < 0) { + (void) close(f); (void) unlink(new); return; } + (void) close(f); + fixup: if (rename(new, target) < 0) { badt: @@ -916,6 +922,20 @@ badt: (void) unlink(new); return; } + + if (type == S_IFREG) { + /* + * Set last modified time + */ + tvp[0].tv_sec = stb.st_atime; /* old atime from target */ + tvp[0].tv_usec = 0; + tvp[1].tv_sec = mtime; + tvp[1].tv_usec = 0; + if (utimes(target, tvp) < 0) { + note("%s:utimes failed %s: %s\n", host, target, strerror(errno)); + } + } + if (opts & COMPARE) { buf[0] = '\0'; (void) sprintf(buf + 1, "updated %s\n", target); @@ -1011,12 +1031,12 @@ chkparent(name) /* * Change owner, group and mode of file. */ -chog(file, owner, group, mode) +fchog(fd, file, owner, group, mode) char *file, *owner, *group; int mode; { register int i; - int uid, gid; + int uid, gid, ret; extern char user[]; extern int userid; @@ -1032,6 +1052,9 @@ chog(file, owner, group, mode) mode &= ~04000; uid = 0; } + else + note("%s:%s: unknown login name", + host, owner); } else uid = pw->pw_uid; } else @@ -1040,16 +1063,20 @@ chog(file, owner, group, mode) gid = atoi(group + 1); goto ok; } - } else if ((mode & 04000) && strcmp(user, owner) != 0) + } else if ((mode & 04000) && strcmp(user, owner) != 0) { + note("%s:%s not owner, clearing setuid", host, user); mode &= ~04000; + } gid = -1; if (gr == NULL || strcmp(group, gr->gr_name) != 0) { if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL)) || ((gr = getgrnam(group)) == NULL)) { if (mode & 02000) { - note("%s:%s: unknown group", host, group); + note("%s:%s: unknown group, clearing setgid", host, group); mode &= ~02000; } + else + note("%s:%s: unknown group", host, group); } else gid = gr->gr_gid; } else @@ -1058,16 +1085,38 @@ chog(file, owner, group, mode) if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++) if (!(strcmp(user, gr->gr_mem[i]))) goto ok; + note("%s:%s not in group %s, clearing setgid", host, user, group); mode &= ~02000; gid = -1; } ok: - if (chown(file, uid, gid) < 0 || - (mode & 07000) && chmod(file, mode) < 0) { - note("%s: chown or chmod failed: file %s: %s", - host, file, strerror(errno)); + if (getuid() != 0) + uid = -1; + if ( (uid != -1 || gid != -1) + && ( fd != -1 && fchown(fd, uid, gid) < 0 + || fd == -1 && chown(file, uid, gid) < 0 + ) + ) { + if (mode & 04000) { + note("%s:chown failed, clearing setuid", host); + mode &= ~04000; + } + if (mode & 02000) { + note("%s:chown failed, clearing setgid", host); + mode &= ~02000; + } } - return(0); + ret = 0; + if (mode & 07000) { + if ( fd != -1 && fchmod(fd, mode) < 0 + || fd == -1 && chmod(file, mode) < 0 + ) { + ret = -1; + note("%s:chmod failed: file %s: %s", + host, file, strerror(errno)); + } + } + return(ret); } /* diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c index fedb615f55ba..5e4a57767288 100644 --- a/usr.bin/rlogin/rlogin.c +++ b/usr.bin/rlogin/rlogin.c @@ -42,7 +42,7 @@ static char sccsid[] = "@(#)rlogin.c 5.33 (Berkeley) 3/1/91"; #endif /* not lint */ /* - * $Source: /a/cvs/386BSD/src/usr.bin/rlogin/rlogin.c,v $ + * $Source: /home/cvs/386BSD/src/usr.bin/rlogin/rlogin.c,v $ * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall * Exp Locker: kfall $ */ diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile index 1312d50327a5..1605846fbea1 100644 --- a/usr.bin/rpcinfo/Makefile +++ b/usr.bin/rpcinfo/Makefile @@ -1,9 +1,7 @@ # from: @(#)Makefile 5.2 (Berkeley) 5/11/90 -# $Id: Makefile,v 1.1 1993/09/13 23:22:40 jtc Exp $ +# $Id: Makefile,v 1.2 1993/11/10 03:46:45 smace Exp $ PROG= rpcinfo -LDADD= -lrpc -DPADD = ${LIBRPC} MAN8 = rpcinfo.8 diff --git a/usr.bin/rsh/rsh.c b/usr.bin/rsh/rsh.c index f2f6df4f1af2..f4a21ca05a2e 100644 --- a/usr.bin/rsh/rsh.c +++ b/usr.bin/rsh/rsh.c @@ -42,8 +42,8 @@ static char sccsid[] = "@(#)rsh.c 5.24 (Berkeley) 7/1/91"; #endif /* not lint */ /* - * $Source: /a/cvs/386BSD/src/usr.bin/rsh/rsh.c,v $ - * $Header: /a/cvs/386BSD/src/usr.bin/rsh/rsh.c,v 1.1.1.1 1993/06/12 14:49:28 rgrimes Exp $ + * $Source: /home/cvs/386BSD/src/usr.bin/rsh/rsh.c,v $ + * $Header: /home/cvs/386BSD/src/usr.bin/rsh/rsh.c,v 1.1.1.1 1993/06/12 14:49:28 rgrimes Exp $ */ #include <sys/types.h> diff --git a/usr.bin/rup/Makefile b/usr.bin/rup/Makefile index 70ff7b491628..1d53109f824f 100644 --- a/usr.bin/rup/Makefile +++ b/usr.bin/rup/Makefile @@ -1,9 +1,9 @@ -# $Id: Makefile,v 1.1 1993/09/16 01:15:34 jtc Exp $ +# $Id: Makefile,v 1.2 1993/11/10 03:47:06 smace Exp $ PROG= rup MAN1= rup.1 -DPADD= ${LIBRPCSVC} ${LIBRPC} -LDADD= -lrpcsvc -lrpc +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc .include <bsd.prog.mk> diff --git a/usr.bin/rusers/Makefile b/usr.bin/rusers/Makefile index fba2fe6b4694..6e2fd6ab2fc6 100644 --- a/usr.bin/rusers/Makefile +++ b/usr.bin/rusers/Makefile @@ -1,9 +1,9 @@ -# $Id: Makefile,v 1.1 1993/09/16 01:15:46 jtc Exp $ +# $Id: Makefile,v 1.2 1993/11/10 03:47:23 smace Exp $ PROG = rusers MAN1 = rusers.1 -DPADD= ${LIBRPCSVC} ${LIBRPC} -LDADD= -lrpcsvc -lrpc +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc .include <bsd.prog.mk> diff --git a/usr.bin/rwall/Makefile b/usr.bin/rwall/Makefile index 95c1130432a6..07bb67880ebd 100644 --- a/usr.bin/rwall/Makefile +++ b/usr.bin/rwall/Makefile @@ -1,9 +1,9 @@ -# $Id: Makefile,v 1.1 1993/09/16 01:11:01 jtc Exp $ +# $Id: Makefile,v 1.2 1993/11/10 03:47:37 smace Exp $ PROG = rwall MAN1 = rwall.1 -DPADD= ${LIBRPCSVC} ${LIBRPC} -LDADD= -lrpcsvc -lrpc +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc .include <bsd.prog.mk> diff --git a/usr.bin/sed/Makefile b/usr.bin/sed/Makefile index 1046ad3ea2e2..52f0fb5f3e5d 100644 --- a/usr.bin/sed/Makefile +++ b/usr.bin/sed/Makefile @@ -3,7 +3,5 @@ PROG= sed SRCS= compile.c main.c misc.c process.c CFLAGS+=-I${.CURDIR} -DHISTORIC_PRACTICE -LDADD+= -lgnuregex -DPADD+= /usr/lib/libgnuregex.a .include <bsd.prog.mk> diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c index d898ca52ca2e..526f21c89f7e 100644 --- a/usr.bin/sed/compile.c +++ b/usr.bin/sed/compile.c @@ -208,6 +208,8 @@ nonsel: /* Now parse the command */ p = NULL; cmd2 = xmalloc(sizeof(struct s_command)); cmd2->code = '}'; + cmd2->a1 = cmd2->a2 = NULL; + cmd2->nonsel = 0; *compile_stream("}", &cmd->u.c, p) = cmd2; cmd->next = cmd2; link = &cmd2->next; diff --git a/usr.bin/sed/sed.1 b/usr.bin/sed/sed.1 index 8fb1d5284486..63bcd329c49d 100644 --- a/usr.bin/sed/sed.1 +++ b/usr.bin/sed/sed.1 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1992 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, Inc. @@ -32,9 +32,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sed.1 5.2 (Berkeley) 8/24/92 +.\" @(#)sed.1 8.2 (Berkeley) 12/30/93 .\" -.Dd "August 24, 1992" +.Dd "December 30, 1993" .Dt SED 1 .Os .Sh NAME @@ -257,7 +257,7 @@ can be preceded by white space and can be followed by white space. The function can be preceded by white space. The terminating .Dq } -must be preceded by a newline an optional white space. +must be preceded by a newline or optional white space. .sp .Bl -tag -width "XXXXXX" -compact .It [2addr] function-list @@ -394,7 +394,7 @@ An ampersand appearing in the replacement is replaced by the string matching the RE. The special meaning of .Dq & -in this context can be suppressed by preceding it by backslash. +in this context can be suppressed by preceding it by a backslash. The string .Dq \e# , where @@ -456,8 +456,9 @@ Within .Em string1 and .Em string2 , -the delimiter itself can be used as a literal character if it is preceded -by a backslash. +a backslash followed by any character other than a newline is that literal +character, and a backslash followed by an ``n'' is replaced by a newline +character. .sp .It [2addr]!function .It [2addr]!function-list diff --git a/usr.bin/showmount/Makefile b/usr.bin/showmount/Makefile index 957f3c5e084e..4ab4952f6039 100644 --- a/usr.bin/showmount/Makefile +++ b/usr.bin/showmount/Makefile @@ -2,7 +2,5 @@ PROG= showmount MAN8= showmount.8 -DPADD= ${LIBRPC} -LDADD= -lrpc .include <bsd.prog.mk> diff --git a/usr.bin/size/size.c b/usr.bin/size/size.c index f9edda6fd059..f91bd5cce839 100644 --- a/usr.bin/size/size.c +++ b/usr.bin/size/size.c @@ -96,6 +96,7 @@ show(count, name) } if (read(fd, &head, sizeof(head)) != sizeof(head) || N_BADMAG(head)) { err("%s: not in a.out format", name); + (void)close(fd); return (1); } (void)close(fd); diff --git a/usr.bin/split/split.1 b/usr.bin/split/split.1 index 0c9c49868e67..e637edd839e9 100644 --- a/usr.bin/split/split.1 +++ b/usr.bin/split/split.1 @@ -52,7 +52,7 @@ smaller files. .Pp Available options: .Bl -tag -width "bb bytesx" -.It Fl Ns Ar lines +.It Fl Ns Ar n Create files of length .Fl Ns Ar n lines long. diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1 index 5f80e085db38..e0de965bd1e4 100644 --- a/usr.bin/su/su.1 +++ b/usr.bin/su/su.1 @@ -41,6 +41,7 @@ .Nm su .Op Fl Kflm .Op Ar login +.Op Ar -c cmd .Sh DESCRIPTION .Nm Su requests the Kerberos password for @@ -119,6 +120,9 @@ and the caller's real uid is non-zero, .Nm su will fail. +.It Fl "c cmd" +Execute command as the nominated user. The user's shell is invoked +to execute the command. It is assumed that the shell supports a -c switch. .El .Pp The diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c index e024d818ec3e..76648e65cb1d 100644 --- a/usr.bin/su/su.c +++ b/usr.bin/su/su.c @@ -76,8 +76,9 @@ main(argc, argv) uid_t ruid, getuid(); int asme, ch, asthem, fastlogin, prio; enum { UNSET, YES, NO } iscsh = UNSET; - char *user, *shell, *username, *cleanenv[2], *nargv[4], **np; + char *user, *shell, *username, *cleanenv[20], *nargv[4], **np; char shellbuf[MAXPATHLEN]; + char avshellbuf[MAXPATHLEN]; char *crypt(), *getpass(), *getenv(), *getlogin(), *ontty(); np = &nargv[3]; @@ -215,9 +216,9 @@ main(argc, argv) if (!asme) { if (asthem) { p = getenv("TERM"); - cleanenv[0] = _PATH_DEFPATH; - cleanenv[1] = NULL; + cleanenv[0] = NULL; environ = cleanenv; + (void)setenv("PATH", _PATH_DEFPATH, 1); (void)setenv("TERM", p, 1); if (chdir(pwd->pw_dir) < 0) { fprintf(stderr, "su: no directory\n"); @@ -237,8 +238,27 @@ main(argc, argv) *np-- = "-m"; } - /* csh strips the first character... */ - *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; + /* Setup argv[0] to show the shell; -shell if login shell */ + if (p = rindex(shell, '/')) { + ++p; + } else { + p = shell; + } + + if (asthem) { + avshellbuf[0] = '-'; + strcpy(avshellbuf+1, p); + } else { + /* csh strips the first character... */ + if (iscsh == YES) { + avshellbuf[0] = '_'; + strcpy(avshellbuf+1, p); + } else { + strcpy(avshellbuf, p); + } + } + + *np = avshellbuf; if (ruid != 0) syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", diff --git a/usr.bin/syscons/syscons.1 b/usr.bin/syscons/syscons.1 index f224f67b0ddc..e8a40b14104a 100644 --- a/usr.bin/syscons/syscons.1 +++ b/usr.bin/syscons/syscons.1 @@ -37,7 +37,12 @@ Sets the screensaver timeout to .I N seconds, or turns it .I off -. +.TP +.BI "\-V\ " NAME|list +Sets the screensaver appearance to +.I NAME . +Use \-V list to list the available +.I NAMEs . .TP .BI "\-m\ " 80x25|80x50 Set screen size to 80x25 or 80x50 chars. @@ -87,6 +92,8 @@ to send .SH BUGS Sure to be some. .SH "SEE ALSO" +.BR keyboard (4) , +.BR screen (4) , .BR /sys/i386/conf/SYSCONS .SH AUTHORS Christoph M. Robitschko diff --git a/usr.bin/syscons/syscons.c b/usr.bin/syscons/syscons.c index 0c2a79e1c685..9637d7b6add3 100644 --- a/usr.bin/syscons/syscons.c +++ b/usr.bin/syscons/syscons.c @@ -47,6 +47,7 @@ char *prgname; __BEGIN_DECLS int is_syscons __P((int)); void screensaver __P((char *)); +void screensavertype __P((char *)); void linemode __P((char *)); void keyrate __P((char *)); void keymap __P((char *)); @@ -75,11 +76,14 @@ int opt; prgname = argv[0]; if (!is_syscons(0)) exit(1); - while((opt = getopt(argc, argv, "s:m:r:k:f:t:F:S:vd")) != -1) + while((opt = getopt(argc, argv, "s:S:m:r:k:f:t:F:V:vd")) != -1) switch(opt) { case 's': screensaver(optarg); break; + case 'V': + screensavertype(optarg); + break; case 'm': linemode(optarg); break; @@ -150,6 +154,7 @@ usage(void) const char usagestr[] = {"\ Usage: syscons -v (be verbose)\n\ -s {TIME|off} (set screensaver timeout to TIME seconds)\n\ + -V {NAME|list} (set screensaver type or list available types\n\ -m {80x25|80x50} (set screen to 25 or 50 lines)\n\ -r DELAY.REPEAT (set keyboard delay & repeat rate)\n\ -r fast (set keyboard delay & repeat to fast)\n\ @@ -159,7 +164,7 @@ Usage: syscons -v (be verbose)\n\ -f SIZE FILE (load font file of size 8, 14 or 16)\n\ -t SCRNUM (switch to specified VT)\n\ -F NUM STRING (set function key NUM to send STRING)\n\ - -S SCRNMAP (load screen map file)\n\ + -S SCRNMAP (load screen map file)\n\ "}; fprintf(stderr, usagestr); } @@ -234,6 +239,46 @@ char *s1; void +screensavertype(opt) +char *opt; +{ +ssaver_t shaver; +int i, e; + + + if (!strcmp(opt, "list")) { + i = 0; + printf("available screen saver types:\n"); + do { + shaver.num = i; + e = ioctl(0, CONS_GSAVER, &shaver); + i ++; + if (e == 0) + printf("\t%d\t%s\n", shaver.num, shaver.name); + } while (e == 0); + if (e == -1 && errno != EIO) + perror("getting screensaver info"); + } else { + i = 0; + do { + shaver.num = i; + e = ioctl(0, CONS_GSAVER, &shaver); + i ++; + if (e == 0 && !strcmp(opt, shaver.name)) { + if (ioctl(0, CONS_SSAVER, &shaver) == -1) + perror("setting screensaver type"); + return; + } + } while (e == 0); + if (e == -1 && errno != EIO) + perror("getting screensaver info"); + else + fprintf(stderr, "%s: No such screensaver\n", opt); + } +} + + +void linemode(opt) char *opt; { diff --git a/usr.bin/talk/display.c b/usr.bin/talk/display.c index 8f82ca3799f8..24195371608c 100644 --- a/usr.bin/talk/display.c +++ b/usr.bin/talk/display.c @@ -64,20 +64,24 @@ max(a,b) */ display(win, text, size) register xwin_t *win; - register char *text; + register unsigned char *text; int size; { register int i; char cch; for (i = 0; i < size; i++) { - if (*text == '\n') { - xscroll(win, 0); + if (*text == '\n' || *text == '\r') { + waddch(win->x_win, '\n'); + getyx(win->x_win, win->x_line, win->x_col); text++; continue; } /* erase character */ - if (*text == win->cerase) { + if ( *text == win->cerase + || *text == 010 /* backspace */ + || *text == 0177 /* del */ + ) { wmove(win->x_win, win->x_line, max(--win->x_col, 0)); getyx(win->x_win, win->x_line, win->x_col); waddch(win->x_win, ' '); @@ -91,7 +95,9 @@ display(win, text, size) * the beginning of a word or the beginning of * the line. */ - if (*text == win->werase) { + if ( *text == win->werase + || *text == 027 /* ^W */ + ) { int endcol, xcol, i, c; endcol = win->x_col; @@ -116,7 +122,9 @@ display(win, text, size) continue; } /* line kill */ - if (*text == win->kill) { + if ( *text == win->kill + || *text == 025 /* ^U */ + ) { wmove(win->x_win, win->x_line, 0); wclrtoeol(win->x_win); getyx(win->x_win, win->x_line, win->x_col); @@ -129,15 +137,14 @@ display(win, text, size) text++; continue; } - if (win->x_col == COLS-1) { - /* check for wraparound */ - xscroll(win, 0); + if (*text == '\7') { + _putchar(*text); + text++; + continue; } if (*text < ' ' && *text != '\t') { waddch(win->x_win, '^'); getyx(win->x_win, win->x_line, win->x_col); - if (win->x_col == COLS-1) /* check for wraparound */ - xscroll(win, 0); cch = (*text & 63) + 64; waddch(win->x_win, cch); } else @@ -163,27 +170,3 @@ readwin(win, line, col) wmove(win, oldline, oldcol); return (c); } - -/* - * Scroll a window, blanking out the line following the current line - * so that the current position is obvious - */ -xscroll(win, flag) - register xwin_t *win; - int flag; -{ - - if (flag == -1) { - wmove(win->x_win, 0, 0); - win->x_line = 0; - win->x_col = 0; - return; - } - win->x_line = (win->x_line + 1) % win->x_nlines; - win->x_col = 0; - wmove(win->x_win, win->x_line, win->x_col); - wclrtoeol(win->x_win); - wmove(win->x_win, (win->x_line + 1) % win->x_nlines, win->x_col); - wclrtoeol(win->x_win); - wmove(win->x_win, win->x_line, win->x_col); -} diff --git a/usr.bin/talk/init_disp.c b/usr.bin/talk/init_disp.c index 761deacdb6f5..9eef4cc04e72 100644 --- a/usr.bin/talk/init_disp.c +++ b/usr.bin/talk/init_disp.c @@ -52,7 +52,10 @@ init_display() void sig_sent(); struct sigvec sigv; - initscr(); + if (!initscr()) { + fprintf(stderr, "Couldn't initialize terminal. Is TERM set?\n"); + exit(1); + } (void) sigvec(SIGTSTP, (struct sigvec *)0, &sigv); sigv.sv_mask |= sigmask(SIGALRM); (void) sigvec(SIGTSTP, &sigv, (struct sigvec *)0); @@ -67,14 +70,14 @@ init_display() my_win.x_nlines = LINES / 2; my_win.x_ncols = COLS; my_win.x_win = newwin(my_win.x_nlines, my_win.x_ncols, 0, 0); - scrollok(my_win.x_win, FALSE); + scrollok(my_win.x_win, TRUE); wclear(my_win.x_win); his_win.x_nlines = LINES / 2 - 1; his_win.x_ncols = COLS; his_win.x_win = newwin(his_win.x_nlines, his_win.x_ncols, my_win.x_nlines+1, 0); - scrollok(his_win.x_win, FALSE); + scrollok(his_win.x_win, TRUE); wclear(his_win.x_win); line_win = newwin(1, COLS, my_win.x_nlines, 0); diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile index 96bf01563d2c..80f52d9c97c6 100644 --- a/usr.bin/telnet/Makefile +++ b/usr.bin/telnet/Makefile @@ -4,7 +4,7 @@ PROG= telnet -CFLAGS+=-DTERMCAP -DKLUDGELINEMODE -DUSE_TERMIO +CFLAGS+=-DTERMCAP -DUSE_TERMIO CFLAGS+=-I${.CURDIR}/../../lib .if exists(/usr/lib/libcrypt.a) diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index 863064f04ab7..c5e175bfec95 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -46,6 +46,7 @@ static char sccsid[] = "@(#)commands.c 5.5 (Berkeley) 3/22/91"; #endif /* defined(unix) */ #include <sys/socket.h> #include <netinet/in.h> +#include <arpa/inet.h> #ifdef CRAY #include <fcntl.h> #endif /* CRAY */ @@ -75,6 +76,10 @@ static char sccsid[] = "@(#)commands.c 5.5 (Berkeley) 3/22/91"; #endif /* CRAY */ #include <netinet/ip.h> +extern void send_do(); +extern void send_dont(); +extern void send_will(); +extern void send_wont(); #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 @@ -246,7 +251,7 @@ struct sendlist { }; -extern int +static int send_esc P((void)), send_help P((void)), send_docmd P((char *)), @@ -381,7 +386,6 @@ send_esc() send_docmd(name) char *name; { - void send_do(); return(send_tncmd(send_do, "do", name)); } @@ -389,21 +393,18 @@ send_docmd(name) send_dontcmd(name) char *name; { - void send_dont(); return(send_tncmd(send_dont, "dont", name)); } static int send_willcmd(name) char *name; { - void send_will(); return(send_tncmd(send_will, "will", name)); } static int send_wontcmd(name) char *name; { - void send_wont(); return(send_tncmd(send_wont, "wont", name)); } @@ -602,7 +603,7 @@ togxbinary(val) } -extern int togglehelp P((void)); +static int togglehelp P((void)); #if defined(AUTHENTICATE) extern int auth_togdebug P((int)); #endif @@ -1464,7 +1465,7 @@ struct slclist { int arg; }; -extern void slc_help(); +static void slc_help(); struct slclist SlcList[] = { { "export", "Use local special character definitions", @@ -1547,7 +1548,8 @@ extern void env_export P((unsigned char *)), env_unexport P((unsigned char *)), env_send P((unsigned char *)), - env_list P((void)), + env_list P((void)); +static void env_help P((void)); struct envlist EnvList[] = { @@ -2388,7 +2390,7 @@ static char envhelp[] = "change environment variables ('environ ?' for more)", modestring[] = "try to enter line or character mode ('mode ?' for more)"; -extern int help(); +static int help(); static Command cmdtab[] = { { "close", closehelp, bye, 1 }, diff --git a/usr.bin/telnet/sys_bsd.c b/usr.bin/telnet/sys_bsd.c index 52b28554d0c3..85d9540354b5 100644 --- a/usr.bin/telnet/sys_bsd.c +++ b/usr.bin/telnet/sys_bsd.c @@ -62,6 +62,10 @@ static char sccsid[] = "@(#)sys_bsd.c 5.2 (Berkeley) 3/1/91"; #else #define SIG_FUNC_RET int #endif +#ifdef SIGINFO +extern SIG_FUNC_RET ayt_status(); +#endif +extern void intp(), sendbrk(); int tout, /* Output file descriptor */ @@ -175,7 +179,7 @@ extern int kludgelinemode; TerminalSpecialChars(c) int c; { - void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk(); + void xmitAO(), xmitEL(), xmitEC(); if (c == termIntChar) { intp(); @@ -575,13 +579,15 @@ TerminalNewMode(f) if (f & MODE_INBIN) lmode |= LPASS8; - else + else { lmode &= ~LPASS8; + lmode |= olmode & LPASS8; + } #else if (f & MODE_INBIN) tmp_tc.c_iflag &= ~ISTRIP; else - tmp_tc.c_iflag |= ISTRIP; + tmp_tc.c_iflag |= old_tc.c_iflag & ISTRIP; if (f & MODE_OUTBIN) { tmp_tc.c_cflag &= ~(CSIZE|PARENB); tmp_tc.c_cflag |= CS8; @@ -649,7 +655,6 @@ TerminalNewMode(f) #endif } else { #ifdef SIGINFO - SIG_FUNC_RET ayt_status(); (void) signal(SIGINFO, ayt_status); #endif SIGINFO @@ -696,6 +701,14 @@ TerminalNewMode(f) # define B38400 B19200 #endif +#ifndef B57600 +# define B57600 B38400 +#endif + +#ifndef B115200 +# define B115200 B57600 +#endif + /* * This code assumes that the values B0, B50, B75... * are in ascending order. They do not have to be @@ -710,7 +723,8 @@ struct termspeeds { { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, - { 38400, B38400 }, { -1, B38400 } + { 38400, B38400 }, { 57600, B57600 },{ 115200,B115200}, + { -1, B115200 } }; void diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1 index 6dfcf2490bfc..84c55c529e66 100644 --- a/usr.bin/telnet/telnet.1 +++ b/usr.bin/telnet/telnet.1 @@ -328,12 +328,12 @@ When connecting to a non-standard port, .Nm telnet omits any automatic initiation of .Tn TELNET -options. When the port number is preceeded by a minus sign, -the inital option negotiation is done. +options. When the port number is preceded by a minus sign, +the initial option negotiation is done. After establishing a connection, the file .Pa \&.telnetrc in the -users home directory is opened. Lines begining with a # are +users home directory is opened. Lines beginning with a # are comment lines. Blank lines are ignored. Lines that begin without whitespace are the start of a machine entry. The first thing on the line is the name of the machine that is @@ -1018,9 +1018,9 @@ is enabled the output from the .Ic netdata command will be formated in a more user readable format. Spaces are put between each character in the output, and the -begining of any +beginning of any .Tn TELNET -escape sequence is preceeded by a '*' to aid in locating them. +escape sequence is preceded by a '*' to aid in locating them. .It Ic \&? Displays the legal .Ic toggle diff --git a/usr.bin/telnet/telnet.c b/usr.bin/telnet/telnet.c index 46a980c37f06..386295261d4e 100644 --- a/usr.bin/telnet/telnet.c +++ b/usr.bin/telnet/telnet.c @@ -57,8 +57,6 @@ static char sccsid[] = "@(#)telnet.c 5.53 (Berkeley) 3/22/91"; #include "general.h" -#define strip(x) ((x)&0x7f) - static unsigned char subbuffer[SUBBUFSIZE], *subpointer, *subend; /* buffer for sub-options */ #define SB_CLEAR() subpointer = subbuffer; @@ -1918,7 +1916,7 @@ telsnd() break; } } - c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; + c = *tbp++, sc = c, tcc--; count++; if (rlogin != _POSIX_VDISABLE) { if (bol) { bol = 0; @@ -1959,7 +1957,7 @@ telsnd() /* * Double escape is a pass through of a single escape character. */ - if (tcc && strip(*tbp) == escape) { + if (tcc && *tbp == escape) { tbp++; tcc--; count++; @@ -1976,7 +1974,7 @@ telsnd() bol = 0; #ifdef KLUDGELINEMODE if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) { - if (tcc > 0 && strip(*tbp) == echoc) { + if (tcc > 0 && *tbp == echoc) { tcc--; tbp++; count++; } else { dontlecho = !dontlecho; diff --git a/usr.bin/telnet/tn3270.c b/usr.bin/telnet/tn3270.c index 67aa569bdd24..a157ce44494a 100644 --- a/usr.bin/telnet/tn3270.c +++ b/usr.bin/telnet/tn3270.c @@ -286,6 +286,7 @@ StringToTerminal(s) } +#if 0 #if ((!defined(NOT43)) || defined(PUTCHAR)) /* _putchar - output a single character to the terminal. This name is so that * curses(3x) can call us to send out data. @@ -308,6 +309,7 @@ _putchar(c) } } #endif /* ((!defined(NOT43)) || defined(PUTCHAR)) */ +#endif /* 0 */ void SetIn3270() diff --git a/usr.bin/telnet/utilities.c b/usr.bin/telnet/utilities.c index 0a6a8828d4cd..7fb7594cd405 100644 --- a/usr.bin/telnet/utilities.c +++ b/usr.bin/telnet/utilities.c @@ -294,6 +294,7 @@ printsub(direction, pointer, length) int length; /* length of suboption data */ { register int i; + int tmpint; char buf[512]; extern int want_status_response; @@ -589,7 +590,8 @@ printsub(direction, pointer, length) case LM_SLC: fprintf(NetTrace, "SLC"); for (i = 2; i < length - 2; i += 3) { - if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + tmpint = pointer[i+SLC_FUNC]; + if (SLC_NAME_OK(tmpint)) fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC])); else fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]); @@ -678,7 +680,8 @@ printsub(direction, pointer, length) case WONT: cp = "WONT"; goto common2; common2: i++; - if (TELOPT_OK((int)pointer[i])) + tmpint = (int) pointer[i]; + if (TELOPT_OK(tmpint)) fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i])); else fprintf(NetTrace, " %s %d", cp, pointer[i]); @@ -800,7 +803,8 @@ printsub(direction, pointer, length) break; default: - if (TELOPT_OK(pointer[0])) + tmpint = pointer[0]; + if (TELOPT_OK(tmpint)) fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0])); else fprintf(NetTrace, "%d (unknown)", pointer[i]); diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c index 48ee1879fd23..bd3fb6c8c504 100644 --- a/usr.bin/tftp/main.c +++ b/usr.bin/tftp/main.c @@ -60,6 +60,7 @@ static char sccsid[] = "@(#)main.c 5.10 (Berkeley) 3/1/91"; #include <netdb.h> #define TIMEOUT 5 /* secs between rexmt's */ +#define LBUFLEN 200 /* size of input buffer */ struct sockaddr_in s_in; int f; @@ -68,7 +69,7 @@ int trace; int verbose; int connected; char mode[32]; -char line[200]; +char line[LBUFLEN]; int margc; char *margv[20]; char *prompt = "tftp"; @@ -169,12 +170,12 @@ setpeer(argc, argv) if (argc < 2) { strcpy(line, "Connect "); printf("(to) "); - gets(&line[strlen(line)]); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); makeargv(); argc = margc; argv = margv; } - if (argc > 3) { + if ((argc < 2) || (argc > 3)) { printf("usage: %s host-name [port]\n", argv[0]); return; } @@ -284,7 +285,7 @@ put(argc, argv) if (argc < 2) { strcpy(line, "send "); printf("(file) "); - gets(&line[strlen(line)]); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); makeargv(); argc = margc; argv = margv; @@ -375,7 +376,7 @@ get(argc, argv) if (argc < 2) { strcpy(line, "get "); printf("(files) "); - gets(&line[strlen(line)]); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); makeargv(); argc = margc; argv = margv; @@ -455,7 +456,7 @@ setrexmt(argc, argv) if (argc < 2) { strcpy(line, "Rexmt-timeout "); printf("(value) "); - gets(&line[strlen(line)]); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); makeargv(); argc = margc; argv = margv; @@ -481,7 +482,7 @@ settimeout(argc, argv) if (argc < 2) { strcpy(line, "Maximum-timeout "); printf("(value) "); - gets(&line[strlen(line)]); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); makeargv(); argc = margc; argv = margv; @@ -547,14 +548,14 @@ command(top) putchar('\n'); for (;;) { printf("%s> ", prompt); - if (gets(line) == 0) { + if (fgets(line, LBUFLEN, stdin) == 0) { if (feof(stdin)) { quit(); } else { continue; } } - if (line[0] == 0) + if ((line[0] == 0) || (line[0] == '\n')) continue; makeargv(); c = getcmd(margv[0]); diff --git a/usr.bin/time/time.1 b/usr.bin/time/time.1 index 65004258c4f5..9ad867ff0d34 100644 --- a/usr.bin/time/time.1 +++ b/usr.bin/time/time.1 @@ -78,7 +78,7 @@ has its own and syntactically different builtin version of .Nm time. The command described here is available as -.Pa /bin/time +.Pa /usr/bin/time to .Xr csh users. diff --git a/usr.bin/tip/acu.c b/usr.bin/tip/acu.c index da958b9b75b2..b8f55ec9cbe6 100644 --- a/usr.bin/tip/acu.c +++ b/usr.bin/tip/acu.c @@ -116,6 +116,8 @@ connect() return ("can't open phone number file"); } while (fgets(string, sizeof(string), fd) != NOSTR) { + if(*string == '#') + continue; for (cp = string; !any(*cp, " \t\n"); cp++) ; if (*cp == '\n') { diff --git a/usr.bin/tip/cmdtab.c b/usr.bin/tip/cmdtab.c index 9efa7ba7762d..df0362a2540d 100644 --- a/usr.bin/tip/cmdtab.c +++ b/usr.bin/tip/cmdtab.c @@ -45,8 +45,8 @@ esctable_t etable[] = { { '!', NORM, "shell", shell }, { '<', NORM, "receive file from remote host", getfl }, { '>', NORM, "send file to remote host", sendfile }, - { 't', NORM, "take file from remote UNIX", cu_take }, - { 'p', NORM, "put file to remote UNIX", cu_put }, + { 't', NORM, "take file from remote UNIX-like system", cu_take }, + { 'p', NORM, "put file to remote UNIX-like system", cu_put }, { '|', NORM, "pipe remote file", pipefile }, { '$', NORM, "pipe local command to remote host", pipeout }, #ifdef CONNECT diff --git a/usr.bin/tn3270/Makefile b/usr.bin/tn3270/Makefile index 387261e3d92a..a5a7fd0d9899 100644 --- a/usr.bin/tn3270/Makefile +++ b/usr.bin/tn3270/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/Makefile,v 1.1 1993/06/29 11:39:52 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/Makefile,v 1.1 1993/06/29 11:39:52 nate Exp $ .if !make(install) SUBDIR += tools diff --git a/usr.bin/tn3270/Makefile.inc b/usr.bin/tn3270/Makefile.inc index fc91c1829b16..2a95a9a3c61d 100644 --- a/usr.bin/tn3270/Makefile.inc +++ b/usr.bin/tn3270/Makefile.inc @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/Makefile.inc,v 1.1 1993/06/29 11:39:53 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/Makefile.inc,v 1.1 1993/06/29 11:39:53 nate Exp $ CFLAGS += -DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO -DTN3270 KBD = unix.kbd diff --git a/usr.bin/tn3270/mset/Makefile b/usr.bin/tn3270/mset/Makefile index 7e5b3224d4b9..4947310623ea 100644 --- a/usr.bin/tn3270/mset/Makefile +++ b/usr.bin/tn3270/mset/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/mset/Makefile,v 1.3 1993/07/07 22:27:07 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/mset/Makefile,v 1.3 1993/07/07 22:27:07 nate Exp $ .include <../../Makefile.inc> diff --git a/usr.bin/tn3270/mset/map3270.5 b/usr.bin/tn3270/mset/map3270.5 index 595742199dc6..38ff8133d078 100644 --- a/usr.bin/tn3270/mset/map3270.5 +++ b/usr.bin/tn3270/mset/map3270.5 @@ -38,7 +38,7 @@ map3270 \- database for mapping ascii keystrokes into IBM 3270 keys .SH SYNOPSIS .B map3270 .SH DESCRIPTION -When emulating IBM-syle 3270 terminals under \s-1UNIX\s0 (see \fItn3270\fR(1)), +When emulating IBM-style 3270 terminals under \s-1FreeBSD\s0 (see \fItn3270\fR(1)), a mapping must be performed between sequences of keys hit on a user's (ascii) keyboard, and the keys that are available on a 3270. For example, a 3270 has a key labeled @@ -113,7 +113,7 @@ Note: the ctrl-caret sequence (to generate a hexadecimal 1E) is represented as `^^' (not `^\e^'). .PP -In addition to the caret, a letter may be preceeded by a backslash (`\e'). +In addition to the caret, a letter may be preceded by a backslash (`\e'). Since this has little effect for most characters, its use is usually not recommended. For the case of a single quote (`\(aa'), the backslash diff --git a/usr.bin/tn3270/telextrn.h b/usr.bin/tn3270/telextrn.h index 55bc0dbfd9d8..0b01967b3d9f 100644 --- a/usr.bin/tn3270/telextrn.h +++ b/usr.bin/tn3270/telextrn.h @@ -60,7 +60,6 @@ extern void outputPurge(), EmptyTerminal(), StringToTerminal(), - _putchar(), ExitPerror(), setcommandmode(); diff --git a/usr.bin/tn3270/tn3270/tn3270.1 b/usr.bin/tn3270/tn3270/tn3270.1 index 51873d2f2b9a..610a4a4810e8 100644 --- a/usr.bin/tn3270/tn3270/tn3270.1 +++ b/usr.bin/tn3270/tn3270/tn3270.1 @@ -278,7 +278,7 @@ receives Output and input pipes are created for communication between the two processes. The pipes are closed when a 3270 clear command is received from the remote -hosts, signalling the end of transparent mode output. +hosts, signaling the end of transparent mode output. Transparent mode is necessary for sending .Tn ASCII control characters over the diff --git a/usr.bin/tn3270/tools/Makefile b/usr.bin/tn3270/tools/Makefile index 7230900feb5c..8d86375f2b45 100644 --- a/usr.bin/tn3270/tools/Makefile +++ b/usr.bin/tn3270/tools/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/Makefile,v 1.1 1993/06/29 11:40:07 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/Makefile,v 1.1 1993/06/29 11:40:07 nate Exp $ SUBDIR = mkhits mkastosc mkastods mkdstoas mkdctype diff --git a/usr.bin/tn3270/tools/mkastods/Makefile b/usr.bin/tn3270/tools/mkastods/Makefile index 7b373d7debad..93216f194f53 100644 --- a/usr.bin/tn3270/tools/mkastods/Makefile +++ b/usr.bin/tn3270/tools/mkastods/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/mkastods/Makefile,v 1.1 1993/06/29 11:40:10 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/mkastods/Makefile,v 1.1 1993/06/29 11:40:10 nate Exp $ CFLAGS += -I${.CURDIR}/.. -I. diff --git a/usr.bin/tn3270/tools/mkastosc/Makefile b/usr.bin/tn3270/tools/mkastosc/Makefile index 5f9f2f0e4e39..a0d48dc76a66 100644 --- a/usr.bin/tn3270/tools/mkastosc/Makefile +++ b/usr.bin/tn3270/tools/mkastosc/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/mkastosc/Makefile,v 1.1 1993/06/29 11:40:12 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/mkastosc/Makefile,v 1.1 1993/06/29 11:40:12 nate Exp $ CFLAGS += -I${.CURDIR}/../mkhits -I${.CURDIR}/.. -I. diff --git a/usr.bin/tn3270/tools/mkdctype/Makefile b/usr.bin/tn3270/tools/mkdctype/Makefile index e7f651d104b8..1e16bfcdcc8c 100644 --- a/usr.bin/tn3270/tools/mkdctype/Makefile +++ b/usr.bin/tn3270/tools/mkdctype/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/mkdctype/Makefile,v 1.1 1993/06/29 11:40:15 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/mkdctype/Makefile,v 1.1 1993/06/29 11:40:15 nate Exp $ CFLAGS += -I${.CURDIR}/.. -I. diff --git a/usr.bin/tn3270/tools/mkdstoas/Makefile b/usr.bin/tn3270/tools/mkdstoas/Makefile index 3b09e9f8cadf..df0d6dacdecb 100644 --- a/usr.bin/tn3270/tools/mkdstoas/Makefile +++ b/usr.bin/tn3270/tools/mkdstoas/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/mkdstoas/Makefile,v 1.1 1993/06/29 11:40:19 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/mkdstoas/Makefile,v 1.1 1993/06/29 11:40:19 nate Exp $ CFLAGS += -I${.CURDIR}/.. -I. diff --git a/usr.bin/tn3270/tools/mkhits/Makefile b/usr.bin/tn3270/tools/mkhits/Makefile index 67845adf5c39..091d03ab2f80 100644 --- a/usr.bin/tn3270/tools/mkhits/Makefile +++ b/usr.bin/tn3270/tools/mkhits/Makefile @@ -1,4 +1,4 @@ -# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/tools/mkhits/Makefile,v 1.1 1993/06/29 11:40:21 nate Exp $ +# $Header: /home/cvs/386BSD/src/usr.bin/tn3270/tools/mkhits/Makefile,v 1.1 1993/06/29 11:40:21 nate Exp $ CFLAGS += -I${.CURDIR}/.. -I. diff --git a/usr.bin/tr/str.c b/usr.bin/tr/str.c index daa719510f48..e81df3356455 100644 --- a/usr.bin/tr/str.c +++ b/usr.bin/tr/str.c @@ -145,7 +145,7 @@ bracket(s) int isalnum __P((int)), isalpha __P((int)), - isblank __P((int)), +/* isblank __P((int)), until 4.4 */ isspace __P((int)), iscntrl __P((int)), isdigit __P((int)), diff --git a/usr.bin/tset/map.c b/usr.bin/tset/map.c index d71fefbaa0d3..2f9af337b00a 100644 --- a/usr.bin/tset/map.c +++ b/usr.bin/tset/map.c @@ -242,6 +242,12 @@ SPEEDS speeds[] = { "38400", B38400, "exta", B19200, "extb", B38400, +#ifdef B57600 + "57600", B57600, +#endif +#ifdef B115200 + "115200", B115200, +#endif NULL }; diff --git a/usr.bin/tsort/tsort.c b/usr.bin/tsort/tsort.c index a484d9b6fab8..c4e6af71f550 100644 --- a/usr.bin/tsort/tsort.c +++ b/usr.bin/tsort/tsort.c @@ -112,7 +112,7 @@ main(argc, argv) if (argc < 2) fp = stdin; - else if (argc == 2) { + else if (argc > 2) { (void)fprintf(stderr, "usage: tsort [ inputfile ]\n"); exit(1); } else if (!(fp = fopen(argv[1], "r"))) { @@ -271,7 +271,7 @@ add_node(name) void tsort() { - register NODE *n, *next; + register NODE *n, *m, *next; register int cnt; while (graph) { @@ -310,6 +310,8 @@ tsort() } for (n = graph; n; n = n->n_next) if (!(n->n_flags & NF_ACYCLIC)) { + for (m=graph; m; m=m->n_next) + m->n_flags &= ~NF_MARK; if (cnt = find_cycle(n, n, 0, 0)) { register int i; @@ -350,7 +352,7 @@ remove_node(n) n->n_next->n_prevp = n->n_prevp; } -/* look for the longest cycle from node from to node to. */ +/* look for a path from node from to node to. */ find_cycle(from, to, longest_len, depth) NODE *from, *to; int depth, longest_len; @@ -377,11 +379,12 @@ find_cycle(from, to, longest_len, depth) } } else { len = find_cycle(*np, to, longest_len, depth + 1); - if (len > longest_len) + if (len > longest_len) { longest_len = len; + break; + } } } - from->n_flags &= ~NF_MARK; return(longest_len); } diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 index dc7f83f8b1cf..0efc1b332a7d 100644 --- a/usr.bin/uname/uname.1 +++ b/usr.bin/uname/uname.1 @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" from: @(#)du.1 6.13 (Berkeley) 6/20/91 -.\" $Id: uname.1,v 1.1 1993/10/08 00:40:49 jkh Exp $ +.\" $Id: uname.1,v 1.3 1994/01/24 20:45:40 davidg Exp $ .\" .Dd March 29, 1992 .Dt UNAME 1 diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c index 0fe26e52d15e..8c83fd50550b 100644 --- a/usr.bin/uname/uname.c +++ b/usr.bin/uname/uname.c @@ -10,7 +10,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: uname.c,v 1.1 1993/10/08 00:40:50 jkh Exp $"; +static char rcsid[] = "$Id: uname.c,v 1.3 1994/01/24 20:45:41 davidg Exp $"; #endif /* not lint */ #include <stdio.h> diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile new file mode 100644 index 000000000000..fbb1588cebd8 --- /dev/null +++ b/usr.bin/vi/Makefile @@ -0,0 +1,99 @@ +# @(#)Makefile 8.26 (Berkeley) 1/12/94 + +PROG= vi +MAN1= vi.1 +BINDIR?= /usr/bin + +#CFLAGS=-g -DDEBUG +#CFLAGS+=-pg +CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/include -I${.CURDIR}/nex -I${.CURDIR}/nvi +#STRIP= +.PATH: ${.CURDIR}/nex ${.CURDIR}/sex ${.CURDIR}/nvi ${.CURDIR}/svi \ + ${.CURDIR}/xaw +CLEANFILES+=ex + +# General sources. +SRCS= ascii.c cut.c delete.c exf.c line.c log.c main.c mark.c \ + options.c options_f.c screen.c search.c seq.c recover.c \ + term.c timer.c trace.c util.c + +# Ex source. +SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c \ + ex_bang.c ex_cd.c ex_delete.c ex_digraph.c ex_display.c \ + ex_edit.c ex_equal.c ex_exit.c ex_file.c ex_global.c ex_init.c \ + ex_join.c ex_map.c ex_mark.c ex_mkexrc.c ex_move.c ex_open.c \ + ex_preserve.c ex_print.c ex_put.c ex_read.c ex_screen.c \ + ex_script.c ex_set.c ex_shell.c ex_shift.c ex_source.c ex_stop.c \ + ex_subst.c ex_tag.c ex_undo.c ex_usage.c ex_util.c ex_version.c \ + ex_visual.c ex_write.c ex_yank.c ex_z.c excmd.c filter.c + +# Ex screen source. +SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \ + sex_util.c + +# Vi source. +SRCS+= getc.c v_again.c v_at.c v_ch.c v_delete.c v_ex.c v_exit.c \ + v_exmode.c v_filter.c v_increment.c v_init.c v_join.c v_left.c \ + v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \ + v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \ + v_sentence.c v_shift.c v_status.c v_stop.c v_switch.c v_tag.c \ + v_text.c v_ulcase.c v_undo.c v_util.c v_word.c v_xchar.c v_yank.c \ + v_z.c vcmd.c vi.c + +# Vi curses screen source. +SRCS+= svi_confirm.c svi_ex.c svi_get.c svi_line.c svi_refresh.c \ + svi_relative.c svi_screen.c svi_smap.c svi_split.c svi_util.c + +# Athena widget set screen source. +SRCS+= xaw_screen.c + +#LDADD+=-pg +DPADD+= ${LIBCURSES} ${LIBTERM} ${LIBUTIL} +LDADD+= -lcurses -ltermlib -lutil +SPECHDR=excmd.h options.h +CLEANFILES+=${SPECHDR} +DPSRCS+=${SPECHDR} +LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view + +all: vi vi.1 + +warn:: ${SRCS} + -(cd ${.CURDIR} && \ + gcc -Wall -O -DDEBUG -Iobj -Invi -Inex -I. ${.ALLSRC} \ + -lcurses -ltermlib 2>&1 | \ + sed -e "/warning: .*sccsid.*defined but not used/d" \ + -e "/warning: suggest parentheses around/d" \ + -e "/In function /d" \ + -e "/At top level:/d" \ + -e "/warning: .*inline call to/d" \ + -e "/warning: comparison is always 1 due /d") > \ + ${.CURDIR}/WARN.OUT + +options.h: options.h.stub options.c # Makefile + rm -f options.h + cp ${.CURDIR}/options.h.stub options.h + chmod 664 options.h + (echo '/^\/\* O_[0-9A-Z_]*/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}'; \ + echo 'END {'; \ + echo 'printf("#define O_OPTIONCOUNT %d\n", cnt)'; \ + echo '}') > /tmp/__vi.options.h + awk -f /tmp/__vi.options.h ${.CURDIR}/options.c >> options.h + rm -f /tmp/__vi.options.h + +excmd.h: excmd.h.stub excmd.c # Makefile + rm -f excmd.h + cp ${.CURDIR}/nex/excmd.h.stub excmd.h + chmod 664 excmd.h + (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}') > /tmp/__vi.excmd.h + awk -f /tmp/__vi.excmd.h ${.CURDIR}/nex/excmd.c >> excmd.h + rm -f /tmp/__vi.excmd.h + +.include <bsd.prog.mk> + +.depend: ${SPECHDR} diff --git a/usr.bin/vi/README b/usr.bin/vi/README new file mode 100644 index 000000000000..a33bfc6f04c3 --- /dev/null +++ b/usr.bin/vi/README @@ -0,0 +1,193 @@ +# @(#)README 8.40 (Berkeley) 1/12/94 + +This is the README for version 1.01 of nvi, a freely redistributable +replacement for the vi and ex text editors. It can be retrieved via +anonymous ftp from ftp.uu.net, or from ftp.cs.berkeley.edu. In the +latter, it is in the directory ucb/4bsd, and is named nvi.tar.Z. + +If you have any questions or problems with nvi, please send them to +me by electronic mail at one of the following addresses: + + uunet!bostic + bostic@cs.berkeley.edu + +Keith Bostic + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +o Redistribution: + +Nvi is copyrighted by the The Regents of the University of California, +but may be freely redistributed (or used to line your birdcage) under +the following conditions: + +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +o Credit where it's due: + + This software was originally derived from software contributed + to the University of California, Berkeley by Steve Kirkendall, + the author of the vi clone elvis. Without his work, this work + would have been far more difficult. + + POSIX 1003.2 style regular expression support is courtesy of + Henry Spencer, for which I am *very* grateful. + + The curses library was originally done by Ken Arnold. Scrolling + and general reworking for 4.4BSD was done by Elan Amir. + +o From the original vi acknowledgements, by William Joy and Mark Horton: + + Bruce Englar encouraged the early development of this display + editor. Peter Kessler helped bring sanity to version 2's + command layout. Bill Joy wrote versions 1 and 2.0 through 2.7, + and created the framework that users see in the present editor. + Mark Horton added macros and other features and made the editor + work on a large number of terminals and Unix systems. + +o And... + + The financial support of UUNET Communications Services is gratefully + acknowledged. + +=-=-=-=-=-=-=-=-=-=-= +o Comments: + +This software is beta software, although it's pretty stable. Almost +of the necessary functionality for ex/vi is in it, the only missing +pieces are fairly obscure. + +Code fixes are very much appreciated, of course, but if you can't +provide them, please send me as much information as you can as to how +to reproduce the bug, and I'll try to fix it here. In particular, the +screen routines are nasty stuff, and you probably don't want to mess +with them. Stack traces of core dumps are sometimes helpful, but an +example file with a set of keystrokes that causes the problem is far +better. + +Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left in +during initial development to make things easier. Wide character support +will be integrated at the same time it is made fully 8-bit clean. + +=-=-=-=-=-=-=-=-=-=-= +o New features: + +There aren't a lot of new features in nex/nvi, but there are a few things +you might like: + + o 8-bit clean data, practically infinite lines/files. + ^Vx[0-9A-Fa-f]* in input mode will insert any + legal character value. + o Split screens: + :sp[lit] [file ...] splits the screen. + ^W switches between screens. + :resize count grows/shrinks the current screen. + o Background/foreground screens + :bg backgrounds the current screen. + :di[splay] s[creens] lists the hidden screens. + :fg [file] foregrounds the specified (or next) screen. + o Shell screens: + :sc[ript] [file ...] runs a shell in the screen. + Carriage return sends a line to the shell. + o Buffer, screens, tags display: + :di[splay] b[uffers] displays the current cut buffers. + :di[splay] s[creens] displays the hidden screen names. + :di[splay] t[ags] displays the current tags stack. + o Tag stacks: + ^T returns to previous tag location. + :tagpop [number | file] returns to previous tag location, + or, optionally tag #N, or the tag in a specific file. + :tagtop returns to first tag location. + o Infinite undo: + A '.' command immediately after a 'u' command continues + either forward or backward depending on whether the 'u' + command was an undo or a redo. + o Usage information: + :exu[sage] [cmd] for ex commands. + :viu[sage] [key] for vi commands. + :help + o Extended RE expressions: + :set extended turns on extended RE's, so you can + do "/in|or" and search for the next occurrence of + more than one expression. + o Word search: + ^A searches for the word referenced by the cursor. + o Number increment: + # increments the number referenced by the cursor. + o Previous file: + :prev[ious][!] edits the previous file from the + argument list. + +=-=-=-=-=-=-=-=-=-=-= +o Porting information: + +The directory PORT has directories per machine/OS combination, with +V7-style Makefiles which build nvi. See the file PORT/README for +more detailed information. + +=-=-=-=-=-=-=-=-=-=-= +o Directories: + +The main directory, nvi, contains source files for pieces of code that +are shared by all the editors, like searching and logging code or code +translating line numbers into requests to the dbopen(3) database code. +It also has the code for adding, deleting, and changing "records" in +the underlying database. + +nvi/docs: + The nvi/docs directory has technical information about data + structures and some of the trickier parts of vi like quoting, + key mapping, input queues, and executing buffers, and a + description of how nvi does edit session recovery. + +nvi/ex: + The nvi/ex directory is the ex source code. Because vi has the + colon command, lots of this code is used by vi. Generally, if + functionality is shared by both ex and vi, it's in nvi/ex, if + it's vi only, it's in nvi/vi. Files are generally named by the + command(s) they support, but occasionally with a name that + describes their functionality. + +nvi/sex: + The nvi/sex directory is the screen support for the ex editor. + +nvi/svi: + The nvi/svi directory is the screen support for a curses based + vi editor. + +nvi/vi: + The nvi/vi directory is the vi source code. + +nvi/xaw: + Place reserved for an X11 (Athena Widget) screen. diff --git a/usr.bin/vi/args.h b/usr.bin/vi/args.h new file mode 100644 index 000000000000..4d437442d866 --- /dev/null +++ b/usr.bin/vi/args.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)args.h 8.3 (Berkeley) 12/19/93 + */ + +/* + * Structure for building "argc/argv" vector of arguments. + * + * !!! + * All arguments are nul terminated as well as having an associated length. + * The argument vector is NOT necessarily NULL terminated. The proper way + * to check the number of arguments is to use the argc value in the EXCMDARG + * structure or to walk the array until an ARGS structure with a length of 0 + * is found. + */ +typedef struct _args { + CHAR_T *bp; /* Argument. */ + size_t blen; /* Buffer length. */ + size_t len; /* Argument length. */ + +#define A_ALLOCATED 0x01 /* If allocated space. */ + u_char flags; +} ARGS; diff --git a/usr.bin/vi/ascii.c b/usr.bin/vi/ascii.c new file mode 100644 index 000000000000..7a7102574165 --- /dev/null +++ b/usr.bin/vi/ascii.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ascii.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" + +CHNAME const asciiname[UCHAR_MAX + 1] = { + {"^@", 2}, {"^A", 2}, {"^B", 2}, {"^C", 2}, + {"^D", 2}, {"^E", 2}, {"^F", 2}, {"^G", 2}, + {"^H", 2}, {"^I", 2}, {"^J", 2}, {"^K", 2}, + {"^L", 2}, {"^M", 2}, {"^N", 2}, {"^O", 2}, + {"^P", 2}, {"^Q", 2}, {"^R", 2}, {"^S", 2}, + {"^T", 2}, {"^U", 2}, {"^V", 2}, {"^W", 2}, + {"^X", 2}, {"^Y", 2}, {"^Z", 2}, {"^[", 2}, + {"^\\", 2}, {"^]", 2}, {"^^", 2}, {"^_", 2}, + {" ", 1}, {"!", 1}, {"\"", 1}, {"#", 1}, + {"$", 1}, {"%", 1}, {"&", 1}, {"'", 1}, + {"(", 1}, {")", 1}, {"*", 1}, {"+", 1}, + {",", 1}, {"-", 1}, {".", 1}, {"/", 1}, + {"0", 1}, {"1", 1}, {"2", 1}, {"3", 1}, + {"4", 1}, {"5", 1}, {"6", 1}, {"7", 1}, + {"8", 1}, {"9", 1}, {":", 1}, {";", 1}, + {"<", 1}, {"=", 1}, {">", 1}, {"?", 1}, + {"@", 1}, {"A", 1}, {"B", 1}, {"C", 1}, + {"D", 1}, {"E", 1}, {"F", 1}, {"G", 1}, + {"H", 1}, {"I", 1}, {"J", 1}, {"K", 1}, + {"L", 1}, {"M", 1}, {"N", 1}, {"O", 1}, + {"P", 1}, {"Q", 1}, {"R", 1}, {"S", 1}, + {"T", 1}, {"U", 1}, {"V", 1}, {"W", 1}, + {"X", 1}, {"Y", 1}, {"Z", 1}, {"[", 1}, + {"\\", 1}, {"]", 1}, {"^", 1}, {"_", 1}, + {"`", 1}, {"a", 1}, {"b", 1}, {"c", 1}, + {"d", 1}, {"e", 1}, {"f", 1}, {"g", 1}, + {"h", 1}, {"i", 1}, {"j", 1}, {"k", 1}, + {"l", 1}, {"m", 1}, {"n", 1}, {"o", 1}, + {"p", 1}, {"q", 1}, {"r", 1}, {"s", 1}, + {"t", 1}, {"u", 1}, {"v", 1}, {"w", 1}, + {"x", 1}, {"y", 1}, {"z", 1}, {"{", 1}, + {"|", 1}, {"}", 1}, {"~", 1}, {"^?", 2}, + {"0x80", 4}, {"0x81", 4}, {"0x82", 4}, {"0x83", 4}, + {"0x84", 4}, {"0x85", 4}, {"0x86", 4}, {"0x87", 4}, + {"0x88", 4}, {"0x89", 4}, {"0x8a", 4}, {"0x8b", 4}, + {"0x8c", 4}, {"0x8d", 4}, {"0x8e", 4}, {"0x8f", 4}, + {"0x90", 4}, {"0x91", 4}, {"0x92", 4}, {"0x93", 4}, + {"0x94", 4}, {"0x95", 4}, {"0x96", 4}, {"0x97", 4}, + {"0x98", 4}, {"0x99", 4}, {"0x9a", 4}, {"0x9b", 4}, + {"0x9c", 4}, {"0x9d", 4}, {"0x9e", 4}, {"0x9f", 4}, + {"\xa0", 1}, {"\xa1", 1}, {"\xa2", 1}, {"\xa3", 1}, + {"\xa4", 1}, {"\xa5", 1}, {"\xa6", 1}, {"\xa7", 1}, + {"\xa8", 1}, {"\xa9", 1}, {"\xaa", 1}, {"\xab", 1}, + {"\xac", 1}, {"\xad", 1}, {"\xae", 1}, {"\xaf", 1}, + {"\xb0", 1}, {"\xb1", 1}, {"\xb2", 1}, {"\xb3", 1}, + {"\xb4", 1}, {"\xb5", 1}, {"\xb6", 1}, {"\xb7", 1}, + {"\xb8", 1}, {"\xb9", 1}, {"\xba", 1}, {"\xbb", 1}, + {"\xbc", 1}, {"\xbd", 1}, {"\xbe", 1}, {"\xbf", 1}, + {"\xc0", 1}, {"\xc1", 1}, {"\xc2", 1}, {"\xc3", 1}, + {"\xc4", 1}, {"\xc5", 1}, {"\xc6", 1}, {"\xc7", 1}, + {"\xc8", 1}, {"\xc9", 1}, {"\xca", 1}, {"\xcb", 1}, + {"\xcc", 1}, {"\xcd", 1}, {"\xce", 1}, {"\xcf", 1}, + {"\xd0", 1}, {"\xd1", 1}, {"\xd2", 1}, {"\xd3", 1}, + {"\xd4", 1}, {"\xd5", 1}, {"\xd6", 1}, {"\xd7", 1}, + {"\xd8", 1}, {"\xd9", 1}, {"\xda", 1}, {"\xdb", 1}, + {"\xdc", 1}, {"\xdd", 1}, {"\xde", 1}, {"\xdf", 1}, + {"\xe0", 1}, {"\xe1", 1}, {"\xe2", 1}, {"\xe3", 1}, + {"\xe4", 1}, {"\xe5", 1}, {"\xe6", 1}, {"\xe7", 1}, + {"\xe8", 1}, {"\xe9", 1}, {"\xea", 1}, {"\xeb", 1}, + {"\xec", 1}, {"\xed", 1}, {"\xee", 1}, {"\xef", 1}, + {"\xf0", 1}, {"\xf1", 1}, {"\xf2", 1}, {"\xf3", 1}, + {"\xf4", 1}, {"\xf5", 1}, {"\xf6", 1}, {"\xf7", 1}, + {"\xf8", 1}, {"\xf9", 1}, {"\xfa", 1}, {"\xfb", 1}, + {"\xfc", 1}, {"\xfd", 1}, {"\xfe", 1}, {"\xff", 1}, +}; + +char * +charname(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + return (sp->gp->cname[ch & UCHAR_MAX].name); +} diff --git a/usr.bin/vi/cut.c b/usr.bin/vi/cut.c new file mode 100644 index 000000000000..5a9b292ce175 --- /dev/null +++ b/usr.bin/vi/cut.c @@ -0,0 +1,526 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cut.c 8.20 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +static int cb_line __P((SCR *, EXF *, recno_t, size_t, size_t, TEXT **)); +static int cb_rotate __P((SCR *)); + +/* + * cut -- + * Put a range of lines/columns into a buffer. + * + * There are two buffer areas, both found in the global structure. The first + * is the linked list of all the buffers the user has named, the second is the + * default buffer storage. There is a pointer, too, which is the current + * default buffer, i.e. it may point to the default buffer or a named buffer + * depending on into what buffer the last text was cut. In both delete and + * yank operations, text is cut into either the buffer named by the user, or + * the default buffer. If it's a delete of information on more than a single + * line, the contents of the numbered buffers are rotated up one, the contents + * of the buffer named '9' are discarded, and the text is also cut into the + * buffer named '1'. + * + * In all cases, upper-case buffer names are the same as lower-case names, + * with the exception that they cause the buffer to be appended to instead + * of replaced. + * + * !!! + * The contents of the default buffer would disappear after most operations in + * historic vi. It's unclear that this is useful, so we don't bother. + * + * When users explicitly cut text into the numeric buffers, historic vi became + * genuinely strange. I've never been able to figure out what was supposed to + * happen. It behaved differently if you deleted text than if you yanked text, + * and, in the latter case, the text was appended to the buffer instead of + * replacing the contents. Hopefully it's not worth getting right. + */ +int +cut(sp, ep, cbp, namep, fm, tm, flags) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + int flags; + MARK *fm, *tm; +{ + CHAR_T name; + TEXT *tp; + recno_t lno; + size_t len; + int append, namedbuffer, setdefcb; + +#if defined(DEBUG) && 0 + TRACE(sp, "cut: from {%lu, %d}, to {%lu, %d}%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, + LF_ISSET(CUT_LINEMODE) ? " LINE MODE" : ""); +#endif + if (cbp == NULL) { + if (LF_ISSET(CUT_DELETE) && + (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) { + (void)cb_rotate(sp); + name = '1'; + goto defcb; + } + if (namep == NULL) { + cbp = sp->gp->dcb_store; + append = namedbuffer = 0; + setdefcb = 1; + } else { + name = *namep; +defcb: CBNAME(sp, cbp, name); + append = isupper(name); + namedbuffer = setdefcb = 1; + } + } else + append = namedbuffer = setdefcb = 0; + + /* + * If this is a new buffer, create it and add it into the list. + * Otherwise, if it's not an append, free its current contents. + */ + if (cbp == NULL) { + CALLOC(sp, cbp, CB *, 1, sizeof(CB)); + cbp->name = name; + CIRCLEQ_INIT(&cbp->textq); + if (namedbuffer) { + LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q); + } else + sp->gp->dcb_store = cbp; + } else if (!append) { + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + } + + /* In line mode, it's pretty easy, just cut the lines. */ + if (LF_ISSET(CUT_LINEMODE)) { + for (lno = fm->lno; lno <= tm->lno; ++lno) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + cbp->flags |= CB_LMODE; + } else { + /* Get the first line. */ + len = fm->lno < tm->lno ? 0 : tm->cno - fm->cno; + if (cb_line(sp, ep, fm->lno, fm->cno, len, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + + /* Get the intermediate lines. */ + for (lno = fm->lno; ++lno < tm->lno;) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + + /* Get the last line. */ + if (tm->lno > fm->lno && tm->cno > 0) { + if (cb_line(sp, ep, lno, 0, tm->cno, &tp)) { +mem: if (append) + msgq(sp, M_ERR, + "Contents of %s buffer lost.", + charname(sp, name)); + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + return (1); + } + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + } + if (setdefcb) + sp->gp->dcbp = cbp; /* Repoint default buffer. */ + return (0); +} + +/* + * cb_rotate -- + * Rotate the numbered buffers up one. + */ +static int +cb_rotate(sp) + SCR *sp; +{ + CB *cbp, *del_cbp; + + del_cbp = NULL; + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) + switch(cbp->name) { + case '1': + cbp->name = '2'; + break; + case '2': + cbp->name = '3'; + break; + case '3': + cbp->name = '4'; + break; + case '4': + cbp->name = '5'; + break; + case '5': + cbp->name = '6'; + break; + case '6': + cbp->name = '7'; + break; + case '7': + cbp->name = '8'; + break; + case '8': + cbp->name = '9'; + break; + case '9': + del_cbp = cbp; + break; + } + if (del_cbp != NULL) { + LIST_REMOVE(del_cbp, q); + text_lfree(&del_cbp->textq); + FREE(del_cbp, sizeof(CB)); + } + return (0); +} + +/* + * cb_line -- + * Cut a portion of a single line. + */ +static int +cb_line(sp, ep, lno, fcno, clen, newp) + SCR *sp; + EXF *ep; + recno_t lno; + size_t fcno, clen; + TEXT **newp; +{ + TEXT *tp; + size_t len; + char *p; + + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + if ((*newp = tp = text_init(sp, NULL, 0, len)) == NULL) + return (1); + + /* + * A length of zero means to cut from the MARK to the end + * of the line. + */ + if (len != 0) { + if (clen == 0) + clen = len - fcno; + memmove(tp->lb, p + fcno, clen); + tp->len = clen; + } + return (0); +} + +/* + * text_init -- + * Allocate a new TEXT structure. + */ +TEXT * +text_init(sp, p, len, total_len) + SCR *sp; + const char *p; + size_t len, total_len; +{ + TEXT *tp; + + MALLOC(sp, tp, TEXT *, sizeof(TEXT)); + if (tp == NULL) + return (NULL); + /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */ + if (tp->lb_len = total_len) { + MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len); + if (tp->lb == NULL) { + free(tp); + return (NULL); + } + if (p != NULL && len != 0) + memmove(tp->lb, p, len); + } else + tp->lb = NULL; + tp->len = len; + tp->ai = tp->insert = tp->offset = tp->owrite = 0; + tp->wd = NULL; + tp->wd_len = 0; + return (tp); +} + +/* + * text_lfree -- + * Free a chain of text structures. + */ +void +text_lfree(headp) + TEXTH *headp; +{ + TEXT *tp; + + while ((tp = headp->cqh_first) != (void *)headp) { + CIRCLEQ_REMOVE(headp, tp, q); + text_free(tp); + } +} + +/* + * text_free -- + * Free a text structure. + */ +void +text_free(tp) + TEXT *tp; +{ + if (tp->lb != NULL) + FREE(tp->lb, tp->lb_len); + if (tp->wd != NULL) + FREE(tp->wd, tp->wd_len); + FREE(tp, sizeof(TEXT)); +} + +/* + * put -- + * Put text buffer contents into the file. + * + * !!! + * Historically, pasting into a file with no lines in vi would preserve + * the single blank line. This is almost certainly a result of the fact + * that historic vi couldn't deal with a file that had no lines in it. + * This implementation treats that as a bug, and does not retain the + * blank line. + */ +int +put(sp, ep, cbp, namep, cp, rp, append) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + MARK *cp, *rp; + int append; +{ + CHAR_T name; + TEXT *ltp, *tp; + recno_t lno; + size_t blen, clen, len; + char *bp, *p, *t; + + if (cbp == NULL) + if (namep == NULL) { + cbp = sp->gp->dcbp; + if (cbp == NULL) { + msgq(sp, M_ERR, "The default buffer is empty."); + return (1); + } + } else { + name = *namep; + CBNAME(sp, cbp, name); + if (cbp == NULL) { + msgq(sp, M_ERR, + "Buffer %s is empty.", charname(sp, name)); + return (1); + } + } + tp = cbp->textq.cqh_first; + + /* + * It's possible to do a put into an empty file, meaning that the + * cut buffer simply becomes the file. It's a special case so + * that we can ignore it in general. + * + * Historical practice is that the cursor ends up on the first + * non-blank character of the first line inserted. + */ + if (cp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) { + for (; tp != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->lno = 1; + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + } + + /* If a line mode buffer, append each new line into the file. */ + if (F_ISSET(cbp, CB_LMODE)) { + lno = append ? cp->lno : cp->lno - 1; + rp->lno = lno + 1; + for (; tp != (void *)&cbp->textq; ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + + /* + * If buffer was cut in character mode, replace the current line with + * one built from the portion of the first line to the left of the + * split plus the first line in the CB. Append each intermediate line + * in the CB. Append a line built from the portion of the first line + * to the right of the split plus the last line in the CB. + * + * Get the first line. + */ + lno = cp->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tp->len + len + 1); + t = bp; + + /* Original line, left of the split. */ + if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) { + memmove(bp, p, clen); + p += clen; + t += clen; + } + + /* First line from the CB. */ + memmove(t, tp->lb, tp->len); + t += tp->len; + + /* Calculate length left in original line. */ + clen = len ? len - cp->cno - (append ? 1 : 0) : 0; + + /* + * If no more lines in the CB, append the rest of the original + * line and quit. Otherwise, build the last line before doing + * the intermediate lines, because the line changes will lose + * the cached line. + */ + if (tp->q.cqe_next == (void *)&cbp->textq) { + /* + * Historical practice is that if a non-line mode put + * is inside a single line, the cursor ends up on the + * last character inserted. + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + if (clen > 0) { + memmove(t, p, clen); + t += clen; + } + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + } else { + /* + * Have to build both the first and last lines of the + * put before doing any sets or we'll lose the cached + * line. Build both the first and last lines in the + * same buffer, so we don't have to have another buffer + * floating around. + * + * Last part of original line; check for space, reset + * the pointer into the buffer. + */ + ltp = cbp->textq.cqh_last; + len = t - bp; + ADD_SPACE_RET(sp, bp, blen, ltp->len + clen); + t = bp + len; + + /* Add in last part of the CB. */ + memmove(t, ltp->lb, ltp->len); + if (clen) + memmove(t + ltp->len, p, clen); + clen += ltp->len; + + /* + * Now: bp points to the first character of the first + * line, t points to the last character of the last + * line, t - bp is the length of the first line, and + * clen is the length of the last. Just figured you'd + * want to know. + * + * Output the line replacing the original line. + */ + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + + /* + * Historical practice is that if a non-line mode put + * covers multiple lines, the cursor ends up on the + * first character inserted. (Of course.) + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + /* Output any intermediate lines in the CB. */ + for (tp = tp->q.cqe_next; + tp->q.cqe_next != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + goto mem; + + if (file_aline(sp, ep, 1, lno, t, clen)) { +mem: FREE_SPACE(sp, bp, blen); + return (1); + } + } + FREE_SPACE(sp, bp, blen); + + /* Reporting... */ +ret: sp->rptlines[L_PUT] += lno - cp->lno; + + return (0); +} diff --git a/usr.bin/vi/cut.h b/usr.bin/vi/cut.h new file mode 100644 index 000000000000..8ade88f05633 --- /dev/null +++ b/usr.bin/vi/cut.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cut.h 8.11 (Berkeley) 1/11/94 + */ + +typedef struct _texth TEXTH; /* TEXT list head structure. */ +CIRCLEQ_HEAD(_texth, _text); + +/* Cut buffers. */ +struct _cb { + LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */ + TEXTH textq; /* Linked list of TEXT structures. */ + CHAR_T name; /* Cut buffer name. */ + size_t len; /* Total length of cut text. */ + +#define CB_LMODE 0x01 /* Cut was in line mode. */ + u_char flags; +}; + +/* Lines/blocks of text. */ +struct _text { /* Text: a linked list of lines. */ + CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */ + char *lb; /* Line buffer. */ + size_t lb_len; /* Line buffer length. */ + size_t len; /* Line length. */ + + /* These fields are used by the vi text input routine. */ + recno_t lno; /* 1-N: line number. */ + size_t ai; /* 0-N: autoindent bytes. */ + size_t insert; /* 0-N: bytes to insert (push). */ + size_t offset; /* 0-N: initial, unerasable bytes. */ + size_t owrite; /* 0-N: bytes to overwrite. */ + + /* These fields are used by the ex text input routine. */ + u_char *wd; /* Width buffer. */ + size_t wd_len; /* Width buffer length. */ +}; + +/* + * Get named buffer 'name'. + * Translate upper-case buffer names to lower-case buffer names. + */ +#define CBNAME(sp, cbp, name) { \ + CHAR_T __name; \ + __name = isupper(name) ? tolower(name) : (name); \ + for (cbp = sp->gp->cutq.lh_first; \ + cbp != NULL; cbp = cbp->q.le_next) \ + if (cbp->name == __name) \ + break; \ +} + +#define CUT_DELETE 0x01 /* Delete (rotate numeric buffers). */ +#define CUT_LINEMODE 0x02 /* Cut in line mode. */ +int cut __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); +int delete __P((SCR *, EXF *, MARK *, MARK *, int)); +int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); + +void text_free __P((TEXT *)); +TEXT *text_init __P((SCR *, const char *, size_t, size_t)); +void text_lfree __P((TEXTH *)); diff --git a/usr.bin/vi/delete.c b/usr.bin/vi/delete.c new file mode 100644 index 000000000000..e8c0a5d80e1f --- /dev/null +++ b/usr.bin/vi/delete.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)delete.c 8.7 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +/* + * delete -- + * Delete a range of text. + */ +int +delete(sp, ep, fm, tm, lmode) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + int lmode; +{ + recno_t lno; + size_t blen, len, tlen; + char *bp, *p; + int eof; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete: from %lu/%d to %lu/%d%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, lmode ? " (LINE MODE)" : ""); +#endif + bp = NULL; + + /* Case 1 -- delete in line mode. */ + if (lmode) { + for (lno = tm->lno; lno >= fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + goto vdone; + } + + /* + * Case 2 -- delete to EOF. This is a special case because it's + * easier to pick it off than try and find it in the other cases. + */ + if (file_lline(sp, ep, &lno)) + return (1); + if (tm->lno >= lno) { + if (tm->lno == lno) { + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + eof = tm->cno >= len ? 1 : 0; + } else + eof = 1; + if (eof) { + for (lno = tm->lno; lno > fm->lno; --lno) { + if (file_dline(sp, ep, lno)) + return (1); + ++sp->rptlines[L_DELETED]; + } + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, fm->cno); + memmove(bp, p, fm->cno); + if (file_sline(sp, ep, fm->lno, bp, fm->cno)) + return (1); + goto done; + } + } + + /* Case 3 -- delete within a single line. */ + if (tm->lno == fm->lno) { + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, fm->cno); + memmove(bp + fm->cno, p + tm->cno, len - tm->cno); + if (file_sline(sp, ep, fm->lno, bp, len - (tm->cno - fm->cno))) + goto err; + goto done; + } + + /* + * Case 4 -- delete over multiple lines. + * + * Figure out how big a buffer we need. + */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + tlen = len; + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + return (1); + } + + /* + * XXX + * We can overflow memory here, if (len + tlen) > SIZE_T_MAX. The + * only portable way I've found to test is to depend on the overflow + * being less than the value. + */ + tlen += len; + if (len > tlen) { + msgq(sp, M_ERR, "Error: line length overflow"); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tlen); + + /* Copy the start partial line into place. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + goto err; + } + memmove(bp, p, fm->cno); + tlen = fm->cno; + + /* Copy the end partial line into place. */ + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + goto err; + } + memmove(bp + tlen, p + tm->cno, len - tm->cno); + tlen += len - tm->cno; + + /* Set the current line. */ + if (file_sline(sp, ep, fm->lno, bp, tlen)) + goto err; + + /* Delete the last and intermediate lines. */ + for (lno = tm->lno; lno > fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + + /* Reporting. */ +vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1; + +done: if (bp != NULL) + FREE_SPACE(sp, bp, blen); + + return (0); + + /* Free memory. */ +err: FREE_SPACE(sp, bp, blen); + + return (1); +} diff --git a/usr.bin/vi/exf.c b/usr.bin/vi/exf.c new file mode 100644 index 000000000000..0f9ac4ba0e4b --- /dev/null +++ b/usr.bin/vi/exf.c @@ -0,0 +1,690 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exf.c 8.65 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +/* + * We include <sys/file.h>, because the flock(2) #defines were + * found there on historical systems. We also include <fcntl.h> + * because the open(2) #defines are found there on newer systems. + */ +#include <sys/file.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +/* + * file_add -- + * Insert a file name into the FREF list, if it doesn't already + * appear in it. + * + * !!! + * The "if it doesn't already appear" changes vi's semantics slightly. If + * you do a "vi foo bar", and then execute "next bar baz", the edit of bar + * will reflect the line/column of the previous edit session. Historic nvi + * did not do this. The change is a logical extension of the change where + * vi now remembers the last location in any file that it has ever edited, + * not just the previously edited file. + */ +FREF * +file_add(sp, frp_append, name, ignore) + SCR *sp; + FREF *frp_append; + CHAR_T *name; + int ignore; +{ + FREF *frp; + char *p; + + /* + * Return it if it already exists. Note that we test against the + * user's current name, whatever that happens to be, including if + * it's a temporary file. If the user is trying to set an argument + * list, the ignore argument will be on -- if we're ignoring the + * file turn off the ignore bit, so it's back in the argument list. + */ + if (name != NULL) + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) { + if (!ignore) + F_CLR(frp, FR_IGNORE); + return (frp); + } + + /* Allocate and initialize the FREF structure. */ + CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); + if (frp == NULL) + return (NULL); + + /* + * If no file name specified, or if the file name is a request + * for something temporary, file_init() will allocate the file + * name. Temporary files are always ignored. + */ +#define TEMPORARY_FILE_STRING "/tmp" + if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && + (frp->name = strdup(name)) == NULL) { + FREE(frp, sizeof(FREF)); + msgq(sp, M_SYSERR, NULL); + return (NULL); + } + + /* Only the initial argument list is "remembered". */ + if (ignore) + F_SET(frp, FR_IGNORE); + + /* Append into the chain of file names. */ + if (frp_append != NULL) { + CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q); + } else + CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q); + + return (frp); +} + +/* + * file_first -- + * Return the first file name for editing, if any. + */ +FREF * +file_first(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the first file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_next -- + * Return the next file name, if any. + */ +FREF * +file_next(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_prev -- + * Return the previous file name, if any. + */ +FREF * +file_prev(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_unedited -- + * Return if there are files that aren't ignored and are unedited. + */ +FREF * +file_unedited(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the next file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_EDITED | FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_init -- + * Start editing a file, based on the FREF structure. If successsful, + * let go of any previous file. Don't release the previous file until + * absolutely sure we have the new one. + */ +int +file_init(sp, frp, rcv_name, force) + SCR *sp; + FREF *frp; + char *rcv_name; + int force; +{ + EXF *ep; + RECNOINFO oinfo; + struct stat sb; + size_t psize; + int fd; + char *p, *oname, tname[MAXPATHLEN]; + + /* + * Required ep initialization: + * Flush the line caches. + * Default recover mail file fd to -1. + * Set initial EXF flag bits. + */ + CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); + ep->c_lno = ep->c_nlines = OOBLNO; + ep->rcv_fd = -1; + LIST_INIT(&ep->marks); + F_SET(ep, F_FIRSTMODIFY); + + /* + * If no name or backing file, create a backing temporary file, saving + * the temp file name so can later unlink it. Repoint the name to the + * temporary name (we display it to the user until they rename it). + * There are some games we play with the FR_FREE_TNAME and FR_NONAME + * flags (see ex/ex_file.c) to make sure that the temporary memory gets + * free'd up. + */ + if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) { + (void)snprintf(tname, sizeof(tname), + "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY)); + if ((fd = mkstemp(tname)) == -1) { + msgq(sp, M_SYSERR, "Temporary file"); + goto err; + } + (void)close(fd); + if ((frp->tname = strdup(tname)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(tname); + goto err; + } + oname = frp->tname; + psize = 4 * 1024; + F_SET(frp, FR_NEWFILE); + } else { + /* Try to keep it at 10 pages or less per file. */ + if (sb.st_size < 40 * 1024) + psize = 4 * 1024; + else if (sb.st_size < 320 * 1024) + psize = 32 * 1024; + else + psize = 64 * 1024; + + frp->mtime = sb.st_mtime; + + if (!S_ISREG(sb.st_mode)) + msgq(sp, M_ERR, + "Warning: %s is not a regular file.", oname); + } + + /* Set up recovery. */ + memset(&oinfo, 0, sizeof(RECNOINFO)); + oinfo.bval = '\n'; /* Always set. */ + oinfo.psize = psize; + oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; + if (rcv_name == NULL) { + if (rcv_tmp(sp, ep, FILENAME(frp))) + msgq(sp, M_ERR, + "Modifications not recoverable if the system crashes."); + else + oinfo.bfname = ep->rcv_path; + } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err; + } else { + oinfo.bfname = ep->rcv_path; + F_SET(ep, F_MODIFIED | F_RCV_ON); + } + + /* Open a db structure. */ + if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, + O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) { + msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name); + goto err; + } + + /* Init file marks. */ + if (mark_init(sp, ep)) + goto err; + + /* Start logging. */ + if (log_init(sp, ep)) + goto err; + + /* + * The -R flag, or doing a "set readonly" during a session causes + * all files edited during the session (using an edit command, or + * even using tags) to be marked read-only. Changing the file name + * (see ex/ex_file.c), clears this flag. + * + * Otherwise, try and figure out if a file is readonly. This is a + * dangerous thing to do. The kernel is the only arbiter of whether + * or not a file is writeable, and the best that a user program can + * do is guess. Obvious loopholes are files that are on a file system + * mounted readonly (access catches this one on a few systems), or + * alternate protection mechanisms, ACL's for example, that we can't + * portably check. Lots of fun, and only here because users whined. + * + * !!! + * Historic vi displayed the readonly message if none of the file + * write bits were set, or if an an access(2) call on the path + * failed. This seems reasonable. If the file is mode 444, root + * users may want to know that the owner of the file did not expect + * it to be written. + * + * Historic vi set the readonly bit if no write bits were set for + * a file, even if the access call would have succeeded. This makes + * the superuser force the write even when vi expects that it will + * succeed. I'm less supportive of this semantic, but it's historic + * practice and the conservative approach to vi'ing files as root. + * + * It would be nice if there was some way to update this when the user + * does a "^Z; chmod ...". The problem is that we'd first have to + * distinguish between readonly bits set because of file permissions + * and those set for other reasons. That's not too hard, but deciding + * when to reevaluate the permissions is trickier. An alternative + * might be to turn off the readonly bit if the user forces a write + * and it succeeds. + * + * XXX + * Access(2) doesn't consider the effective uid/gid values. This + * probably isn't a problem for vi when it's running standalone. + */ + if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) && + (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || + access(FILENAME(frp), W_OK))) + F_SET(frp, FR_RDONLY); + else + F_CLR(frp, FR_RDONLY); + + /* + * Close the previous file; if that fails, close the new one + * and run for the border. + */ + if (sp->ep != NULL && file_end(sp, sp->ep, force)) { + (void)file_end(sp, ep, 1); + goto err; + } + + /* + * 4.4BSD supports locking in the open call, other systems don't. + * Since the user can't interrupt us between the open and here, + * it's a don't care. + * + * !!! + * We need to distinguish a lock not being available for the file + * from the file system not supporting locking. Assume that EAGAIN + * or EWOULDBLOCK is the former. There isn't a portable way to do + * this. + * + * XXX + * The locking is flock(2) style, not fcntl(2). The latter is known + * to fail badly on some systems, and its only advantage is that it + * occasionally works over NFS. + */ + if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB)) + if (errno == EAGAIN || errno == EWOULDBLOCK) { + msgq(sp, M_INFO, + "%s already locked, session is read-only", oname); + F_SET(frp, FR_RDONLY); + } else + msgq(sp, M_VINFO, "%s cannot be locked", oname); + + /* + * Set the previous file pointer and the alternate file name to be + * the file we're about to discard. + * + * !!! + * If the current file was a temporary file, the call to file_end() + * unlinked it and free'd the name. So, there is no previous file, + * and there is no alternate file name. This matches historical + * practice, although in historical vi it could only happen as the + * result of the initial command, i.e. if vi was execute without a + * file name. + */ + if (sp->frp != NULL) { + p = FILENAME(sp->frp); + if (p == NULL) + sp->p_frp = NULL; + else + sp->p_frp = sp->frp; + set_alt_name(sp, p); + } + + /* The new file has now been officially edited. */ + F_SET(frp, FR_EDITED); + + /* Switch... */ + ++ep->refcnt; + sp->ep = ep; + sp->frp = frp; + return (0); + +err: if (frp->tname != NULL) { + (void)unlink(frp->tname); + free(frp->tname); + frp->tname = NULL; + } + if (ep->rcv_path != NULL) { + free(ep->rcv_path); + ep->rcv_path = NULL; + } + FREE(ep, sizeof(EXF)); + return (1); +} + +/* + * file_end -- + * Stop editing a file. + */ +int +file_end(sp, ep, force) + SCR *sp; + EXF *ep; + int force; +{ + FREF *frp; + + /* + * + * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT! + * + * Save the cursor location. + * + * XXX + * It would be cleaner to do this somewhere else, but by the time + * ex or vi knows that we're changing files it's already happened. + */ + frp = sp->frp; + frp->lno = sp->lno; + frp->cno = sp->cno; + F_SET(frp, FR_CURSORSET); + + /* If multiply referenced, just decrement the count and return. */ + if (--ep->refcnt != 0) + return (0); + + /* Close the db structure. */ + if (ep->db->close != NULL && ep->db->close(ep->db) && !force) { + msgq(sp, M_ERR, + "%s: close: %s", FILENAME(frp), strerror(errno)); + ++ep->refcnt; + return (1); + } + + /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */ + + /* Stop logging. */ + (void)log_end(sp, ep); + + /* Free up any marks. */ + mark_end(sp, ep); + + /* + * Delete the recovery files, close the open descriptor, + * free recovery memory. + */ + if (!F_ISSET(ep, F_RCV_NORM)) { + if (ep->rcv_path != NULL && unlink(ep->rcv_path)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_path, strerror(errno)); + if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_mpath, strerror(errno)); + } + if (ep->rcv_fd != -1) + (void)close(ep->rcv_fd); + if (ep->rcv_path != NULL) + free(ep->rcv_path); + if (ep->rcv_mpath != NULL) + free(ep->rcv_mpath); + + /* + * Unlink any temporary file, file name. We also turn on the + * ignore bit at this point, because it was a "created" file, + * not an argument file. + */ + if (frp->tname != NULL) { + if (unlink(frp->tname)) + msgq(sp, M_ERR, + "%s: remove: %s", frp->tname, strerror(errno)); + free(frp->tname); + frp->tname = NULL; + + if (frp->name == NULL && frp->cname == NULL) + F_SET(frp, FR_IGNORE); + } + /* Free the EXF structure. */ + FREE(ep, sizeof(EXF)); + return (0); +} + +/* + * file_write -- + * Write the file to disk. Historic vi had fairly convoluted + * semantics for whether or not writes would happen. That's + * why all the flags. + */ +int +file_write(sp, ep, fm, tm, name, flags) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + char *name; + int flags; +{ + struct stat sb; + FILE *fp; + FREF *frp; + MARK from, to; + u_long nlno, nch; + int fd, oflags, rval; + char *msg; + + /* + * Don't permit writing to temporary files. The problem is that + * if it's a temp file, and the user does ":wq", we write and quit, + * unlinking the temporary file. Not what the user had in mind + * at all. This test cannot be forced. + */ + frp = sp->frp; + if (name == NULL && frp->cname == NULL && frp->name == NULL) { + msgq(sp, M_ERR, "No filename to which to write."); + return (1); + } + + /* Can't write files marked read-only, unless forced. */ + if (!LF_ISSET(FS_FORCE) && + name == NULL && F_ISSET(frp, FR_RDONLY)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Read-only file, not written; use ! to override."); + else + msgq(sp, M_ERR, + "Read-only file, not written."); + return (1); + } + + /* If not forced, not appending, and "writeany" not set ... */ + if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { + /* Don't overwrite anything but the original file. */ + if (name != NULL) { + if (!stat(name, &sb)) + goto exists; + } else if (frp->cname != NULL && + !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) { + name = frp->cname; +exists: if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "%s exists, not written; use ! to override.", name); + else + msgq(sp, M_ERR, + "%s exists, not written.", name); + return (1); + } + + /* + * Don't write part of any existing file. Only test for the + * original file, the previous test catches anything else. + */ + if (!LF_ISSET(FS_ALL) && name == NULL && + frp->cname == NULL && !stat(frp->name, &sb)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Use ! to write a partial file."); + else + msgq(sp, M_ERR, "Partial file, not written."); + return (1); + } + } + + /* + * Figure out if the file already exists -- if it doesn't, we display + * the "new file" message. The stat might not be necessary, but we + * just repeat it because it's easier than hacking the previous tests. + * The information is only used for the user message and modification + * time test, so we can ignore the obvious race condition. + * + * If the user is overwriting a file other than the original file, and + * O_WRITEANY was what got us here (neither force nor append was set), + * display the "existing file" messsage. Since the FR_CHANGEWRITE flag + * is set on a successful write, the message only appears once when the + * user changes a file name. This is historic practice. + * + * One final test. If we're not forcing or appending, and we have a + * saved modification time, stop the user if it's been written since + * we last edited or wrote it, and make them force it. + */ + if (stat(name == NULL ? FILENAME(frp) : name, &sb)) + msg = ": new file"; + else { + msg = ""; + if (!LF_ISSET(FS_FORCE | FS_APPEND)) { + if (frp->mtime && sb.st_mtime > frp->mtime) { + msgq(sp, M_ERR, + "%s: file modified more recently than this copy%s.", + name == NULL ? frp->name : name, + LF_ISSET(FS_POSSIBLE) ? + "; use ! to override" : ""); + return (1); + } + if (name != NULL || + !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL) + msg = ": existing file"; + } + } + + /* We no longer care where the name came from. */ + if (name == NULL) + name = FILENAME(frp); + + /* Set flags to either append or truncate. */ + oflags = O_CREAT | O_WRONLY; + if (LF_ISSET(FS_APPEND)) + oflags |= O_APPEND; + else + oflags |= O_TRUNC; + + /* Open the file. */ + if ((fd = open(name, oflags, DEFFILEMODE)) < 0) { + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Use stdio for buffering. */ + if ((fp = fdopen(fd, "w")) == NULL) { + (void)close(fd); + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Build fake addresses, if necessary. */ + if (fm == NULL) { + from.lno = 1; + from.cno = 0; + fm = &from; + if (file_lline(sp, ep, &to.lno)) + return (1); + to.cno = 0; + tm = &to; + } + + /* Write the file. */ + rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch); + + /* + * Save the new last modification time -- even if the write fails + * we re-init the time if we wrote anything. That way the user can + * clean up the disk and rewrite without having to force it. + */ + if (nlno || nch) + frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime; + + /* If the write failed, complain loudly. */ + if (rval) { + if (!LF_ISSET(FS_APPEND)) + msgq(sp, M_ERR, "%s: WARNING: file truncated!", name); + return (1); + } + + /* + * Once we've actually written the file, it doesn't matter that the + * file name was changed -- if it was, we've already whacked it. + */ + F_SET(frp, FR_CHANGEWRITE); + + /* If wrote the entire file, clear the modified bit. */ + if (LF_ISSET(FS_ALL)) + F_CLR(ep, F_MODIFIED); + + msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.", + name, msg, nlno, nlno == 1 ? "" : "s", nch); + + return (0); +} diff --git a/usr.bin/vi/exf.h b/usr.bin/vi/exf.h new file mode 100644 index 000000000000..5049f3ea2418 --- /dev/null +++ b/usr.bin/vi/exf.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exf.h 8.20 (Berkeley) 12/28/93 + */ + /* Undo direction. */ +/* + * exf -- + * The file structure. + */ +struct _exf { + int refcnt; /* Reference count. */ + + /* Underlying database state. */ + DB *db; /* File db structure. */ + char *c_lp; /* Cached line. */ + size_t c_len; /* Cached line length. */ + recno_t c_lno; /* Cached line number. */ + recno_t c_nlines; /* Cached lines in the file. */ + + DB *log; /* Log db structure. */ + char *l_lp; /* Log buffer. */ + size_t l_len; /* Log buffer length. */ + recno_t l_high; /* Log last + 1 record number. */ + recno_t l_cur; /* Log current record number. */ + MARK l_cursor; /* Log cursor position. */ + enum direction lundo; /* Last undo direction. */ + + LIST_HEAD(_markh, _mark) marks; /* Linked list of file MARK's. */ + + /* + * Paths for the recovery mail file and the vi recovery file and + * a file descriptor for the former. We keep a file descriptor + * to the recovery file open and locked, while the file is in use. + * This allows the recovery option to distinguish between files + * that are live, and those that should be recovered. + * + * F_RCV_ON is set as long as we believe that the file is recoverable. + * This doesn't mean that any initialization has been done, however. + * If F_RCV_NORM is not set and rcv_path and rcv_mpath are not NULL, + * they are unlinked on file exit. If not NULL they are free'd on file + * exit. On file exit, if rcv_fd is not -1, it is closed. + */ +#define RCV_PERIOD 120 /* Sync every two minutes. */ + char *rcv_path; /* Recover file name. */ + char *rcv_mpath; /* Recover mail file name. */ + int rcv_fd; /* Locked mail file descriptor. */ + +#define F_FIRSTMODIFY 0x001 /* File not yet modified. */ +#define F_MODIFIED 0x002 /* File is currently dirty. */ +#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */ +#define F_NOLOG 0x008 /* Logging turned off. */ +#define F_RCV_ALRM 0x010 /* File needs to be synced. */ +#define F_RCV_NORM 0x020 /* Don't delete recovery files. */ +#define F_RCV_ON 0x040 /* Recovery is possible. */ +#define F_UNDO 0x080 /* No change since last undo. */ + u_int flags; +}; + +/* Flags to file_write(). */ +#define FS_ALL 0x01 /* Write the entire file. */ +#define FS_APPEND 0x02 /* Append to the file. */ +#define FS_FORCE 0x04 /* Force is set. */ +#define FS_POSSIBLE 0x08 /* Force could be set. */ + +#define GETLINE_ERR(sp, lno) { \ + msgq((sp), M_ERR, \ + "Error: %s/%d: unable to retrieve line %u.", \ + tail(__FILE__), __LINE__, (lno)); \ +} + +/* FREF routines. */ +FREF *file_add __P((SCR *, FREF *, CHAR_T *, int)); +FREF *file_first __P((SCR *)); +FREF *file_next __P((SCR *, FREF *)); +FREF *file_prev __P((SCR *, FREF *)); +FREF *file_unedited __P((SCR *)); + +/* EXF routines. */ +int file_end __P((SCR *, EXF *, int)); +int file_init __P((SCR *, FREF *, char *, int)); +int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int)); + +/* Recovery routines. */ +void rcv_hup __P((void)); +int rcv_init __P((SCR *, EXF *)); +int rcv_list __P((SCR *)); +int rcv_read __P((SCR *, char *)); +int rcv_sync __P((SCR *, EXF *)); +void rcv_term __P((void)); +int rcv_tmp __P((SCR *, EXF *, char *)); + +/* DB interface routines */ +int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t)); +int file_dline __P((SCR *, EXF *, recno_t)); +char *file_gline __P((SCR *, EXF *, recno_t, size_t *)); +int file_iline __P((SCR *, EXF *, recno_t, char *, size_t)); +int file_lline __P((SCR *, EXF *, recno_t *)); +char *file_rline __P((SCR *, EXF *, recno_t, size_t *)); +int file_sline __P((SCR *, EXF *, recno_t, char *, size_t)); diff --git a/usr.bin/vi/gs.h b/usr.bin/vi/gs.h new file mode 100644 index 000000000000..86af7299b3c7 --- /dev/null +++ b/usr.bin/vi/gs.h @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)gs.h 8.26 (Berkeley) 1/9/94 + */ + +struct _gs { + CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */ + CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */ + + mode_t origmode; /* Original terminal mode. */ + struct termios + original_termios; /* Original terminal values. */ + struct termios + s5_curses_botch; /* System V curses workaround. */ + + MSGH msgq; /* User message list. */ + + char *tmp_bp; /* Temporary buffer. */ + size_t tmp_blen; /* Size of temporary buffer. */ + +#ifdef DEBUG + FILE *tracefp; /* Trace file pointer. */ +#endif + +/* INFORMATION SHARED BY ALL SCREENS. */ + IBUF *tty; /* Key input buffer. */ + + CB *dcbp; /* Default cut buffer pointer. */ + CB *dcb_store; /* Default cut buffer storage. */ + LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */ + +#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */ + LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */ + bitstr_t bit_decl(seqb, MAX_BIT_SEQ); + +#define term_key_val(sp, ch) \ + ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \ + (ch) > sp->gp->max_special ? 0 : __term_key_val(sp, ch)) +#define MAX_FAST_KEY 255 /* Max + 1 fast check character.*/ + CHAR_T max_special; /* Max special character. */ + u_char *special_key; /* Fast lookup table. */ + CHNAME const *cname; /* Display names of ASCII characters. */ + +#define G_ABBREV 0x00001 /* If have abbreviations. */ +#define G_BELLSCHED 0x00002 /* Bell scheduled. */ +#define G_CURSES_INIT 0x00004 /* Curses: initialized. */ +#define G_CURSES_S5CB 0x00008 /* Curses: s5_curses_botch set. */ +#define G_ISFROMTTY 0x00010 /* Reading from a tty. */ +#define G_RECOVER_SET 0x00020 /* Recover system initialized. */ +#define G_SETMODE 0x00040 /* Tty mode changed. */ +#define G_SIGALRM 0x00080 /* SIGALRM arrived. */ +#define G_SIGHUP 0x00100 /* SIGHUP arrived. */ +#define G_SIGTERM 0x00200 /* SIGTERM arrived. */ +#define G_SIGWINCH 0x00400 /* SIGWINCH arrived. */ +#define G_SLEEPING 0x00800 /* Asleep (die on signal). */ +#define G_SNAPSHOT 0x01000 /* Always snapshot files. */ +#define G_TMP_INUSE 0x02000 /* Temporary buffer in use. */ + u_int flags; +}; + +extern GS *__global_list; /* List of screens. */ diff --git a/usr.bin/vi/include/bitstring.h b/usr.bin/vi/include/bitstring.h new file mode 100644 index 000000000000..88437e7fb9f7 --- /dev/null +++ b/usr.bin/vi/include/bitstring.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Vixie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 + */ + +#ifndef _BITSTRING_H_ +#define _BITSTRING_H_ + +typedef unsigned char bitstr_t; + +/* internal macros */ + /* byte of the bitstring bit is in */ +#define _bit_byte(bit) \ + ((bit) >> 3) + + /* mask for the bit within its byte */ +#define _bit_mask(bit) \ + (1 << ((bit)&0x7)) + +/* external macros */ + /* bytes in a bitstring of nbits bits */ +#define bitstr_size(nbits) \ + ((((nbits) - 1) >> 3) + 1) + + /* allocate a bitstring */ +#define bit_alloc(nbits) \ + (bitstr_t *)calloc(1, \ + (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t)) + + /* allocate a bitstring on the stack */ +#define bit_decl(name, nbits) \ + (name)[bitstr_size(nbits)] + + /* is bit N of bitstring name set? */ +#define bit_test(name, bit) \ + ((name)[_bit_byte(bit)] & _bit_mask(bit)) + + /* set bit N of bitstring name */ +#define bit_set(name, bit) \ + (name)[_bit_byte(bit)] |= _bit_mask(bit) + + /* clear bit N of bitstring name */ +#define bit_clear(name, bit) \ + (name)[_bit_byte(bit)] &= ~_bit_mask(bit) + + /* clear bits start ... stop in bitstring */ +#define bit_nclear(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \ + (0xff << ((_stop&0x7) + 1))); \ + } else { \ + _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0; \ + _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \ + } \ +} + + /* set bits start ... stop in bitstring */ +#define bit_nset(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] |= ((0xff << (_start&0x7)) & \ + (0xff >> (7 - (_stop&0x7)))); \ + } else { \ + _name[_startbyte] |= 0xff << ((_start)&0x7); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0xff; \ + _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \ + } \ +} + + /* find first bit clear in name */ +#define bit_ffc(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte] != 0xff) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + + /* find first bit set in name */ +#define bit_ffs(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte]) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + +#endif /* !_BITSTRING_H_ */ diff --git a/usr.bin/vi/include/cdefs.h b/usr.bin/vi/include/cdefs.h new file mode 100644 index 000000000000..c4157bcd1a8d --- /dev/null +++ b/usr.bin/vi/include/cdefs.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.2 (Berkeley) 10/4/93 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#if !defined(__GNUC__) && !defined(__cplusplus) +#define inline +#endif + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifdef __GNUC__ +#define const __const /* GCC: ANSI C with -traditional */ +#define inline __inline +#define signed __signed +#define volatile __volatile + +#else /* !__GNUC__ */ +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC has extensions for declaring functions as `pure' (always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and `dead' (nonreturning). These mainly affect + * optimization and warnings. Unfortunately, GCC complains if these are + * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to + * define them only if compiling without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/usr.bin/vi/include/compat.h b/usr.bin/vi/include/compat.h new file mode 100644 index 000000000000..10406993014b --- /dev/null +++ b/usr.bin/vi/include/compat.h @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)compat.h 8.10 (Berkeley) 1/11/94 + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#include <sys/types.h> +#include <machine/limits.h> +#include <termios.h> +#include <errno.h> + +/* + * If your system doesn't typedef u_long, u_short, or u_char, change + * the 0 to a 1. + */ +#if 0 +typedef unsigned char u_char; /* 4.[34]BSD names. */ +typedef unsigned int u_int; +typedef unsigned long u_long; +typedef unsigned short u_short; +#endif + +/* If your system doesn't typedef size_t, change the 0 to a 1. */ +#if 0 +typedef unsigned int size_t; /* 4.[34]BSD names. */ +#endif + +/* + * If your system doesn't have the POSIX type for a signal mask, + * change the 0 to a 1. + */ +#if 0 /* POSIX 1003.1 signal mask type. */ +typedef unsigned int sigset_t; +#endif + +/* + * If your system's vsprintf returns a char *, not an int, + * change the 0 to a 1. + */ +#if 0 +#define VSPRINTF_CHARSTAR +#endif + +/* + * If you don't have POSIX 1003.1 signals, the signal code surrounding the + * temporary file creation is intended to block all of the possible signals + * long enough to create the file and unlink it. All of this stuff is + * intended to use old-style BSD calls to fake POSIX 1003.1 calls. + */ +#ifdef NO_POSIX_SIGNALS +#define sigemptyset(set) (*(set) = 0) +#define sigfillset(set) (*(set) = ~(sigset_t)0, 0) +#define sigaddset(set,signo) (*(set) |= sigmask(signo), 0) +#define sigdelset(set,signo) (*(set) &= ~sigmask(signo), 0) +#define sigismember(set,signo) ((*(set) & sigmask(signo)) != 0) + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +static int __sigtemp; /* For the use of sigprocmask */ + +/* Repeated test of oset != NULL is to avoid "*0". */ +#define sigprocmask(how, set, oset) \ + ((__sigtemp = \ + (((how) == SIG_BLOCK) ? \ + sigblock(0) | *(set) : \ + (((how) == SIG_UNBLOCK) ? \ + sigblock(0) & ~(*(set)) : \ + ((how) == SIG_SETMASK ? \ + *(set) : sigblock(0))))), \ + ((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) : \ + sigsetmask(__sigtemp)), 0) +#endif + +/* + * If realloc(3) of a NULL pointer on your system isn't the same as + * a malloc(3) call, change the 0 to a 1, and add realloc.o to the + * MISC line in your Makefile. + */ +#if 0 +#define realloc __fix_realloc +#endif + +/* + * If your system doesn't have an include file with the appropriate + * byte order set, make sure you specify the correct one. + */ +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#define BYTE_ORDER LITTLE_ENDIAN /* Set for your system. */ +#endif + +#if defined(SYSV) || defined(SYSTEM5) +#define index(a, b) strchr(a, b) +#define rindex(a, b) strrchr(a, b) +#define bzero(a, b) memset(a, 0, b) +#define bcmp(a, b, n) memcmp(a, b, n) +#define bcopy(a, b, n) memmove(b, a, n) +#endif + +#if defined(BSD) || defined(BSD4_3) +#define strchr(a, b) index(a, b) +#define strrchr(a, b) rindex(a, b) +#define memcmp(a, b, n) bcmp(a, b, n) +#define memmove(a, b, n) bcopy(b, a, n) +#endif + +/* + * 32-bit machine. The db routines are theoretically independent of + * the size of u_shorts and u_longs, but I don't know that anyone has + * ever actually tried it. At a minimum, change the following #define's + * if you are trying to compile on a different type of system. + */ +#ifndef USHRT_MAX +#define USHRT_MAX 0xFFFF +#define ULONG_MAX 0xFFFFFFFF +#endif + +#ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */ +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 RE limit. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +/* + * If you can't provide lock values in the open(2) call. Note, this + * allows races to happen. + */ +#ifndef O_EXLOCK /* 4.4BSD extension. */ +#define O_EXLOCK 0 +#endif + +#ifndef O_SHLOCK /* 4.4BSD extension. */ +#define O_SHLOCK 0 +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ +#endif + +#ifndef WCOREDUMP /* 4.4BSD extension */ +#define WCOREDUMP(a) 0 +#endif + +#ifndef STDERR_FILENO +#define STDIN_FILENO 0 /* ANSI C #defines */ +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifndef SEEK_END +#define SEEK_SET 0 /* POSIX 1003.1 seek values */ +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */ +#define _POSIX_VDISABLE 0 /* Some systems used 0. */ +#endif + +#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) +#define S_ISSOCK(m) ((m & 0170000) == 0140000) +#endif + +#ifndef TCSASOFT /* 4.4BSD extension. */ +#define TCSASOFT 0 +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 values. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +#ifndef NULL /* ANSI C #defines NULL everywhere. */ +#define NULL 0 +#endif + +#ifndef MAX /* Usually found in <sys/param.h>. */ +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN /* Usually found in <sys/param.h>. */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Default file permissions. */ +#ifndef DEFFILEMODE /* 4.4BSD extension. */ +#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) +#endif + +#ifndef S_ISDIR /* POSIX 1003.1 file type tests. */ +#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */ +#endif + +/* The type of a va_list. */ +#ifndef _BSD_VA_LIST_ /* 4.4BSD #define. */ +#define _BSD_VA_LIST_ char * +#endif + +#endif /* !_COMPAT_H_ */ diff --git a/usr.bin/vi/include/err.h b/usr.bin/vi/include/err.h new file mode 100644 index 000000000000..b6d7e5f94b87 --- /dev/null +++ b/usr.bin/vi/include/err.h @@ -0,0 +1,16 @@ +#include <sys/cdefs.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void err __P((int, const char *, ...)); +void verr __P((int, const char *, va_list)); +void errx __P((int, const char *, ...)); +void verrx __P((int, const char *, va_list)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, va_list)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, va_list)); diff --git a/usr.bin/vi/include/file.h b/usr.bin/vi/include/file.h new file mode 100644 index 000000000000..680797fa6a52 --- /dev/null +++ b/usr.bin/vi/include/file.h @@ -0,0 +1,15 @@ +/* + * If we're doing flock(2) emulation, we need to get the LOCK_* #defines. + * This stub <sys/file.h> includes the real one, and, if they're not in + * it, we #define them here. + */ + +#include </usr/include/sys/file.h> + +#ifndef LOCK_SH +/* lock operations for flock(2) */ +#define LOCK_SH 0x01 /* shared file lock */ +#define LOCK_EX 0x02 /* exclusive file lock */ +#define LOCK_NB 0x04 /* don't block when locking */ +#define LOCK_UN 0x08 /* unlock file */ +#endif diff --git a/usr.bin/vi/include/glob.h b/usr.bin/vi/include/glob.h new file mode 100644 index 000000000000..b679f6aef62a --- /dev/null +++ b/usr.bin/vi/include/glob.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _GLOB_H_ +#define _GLOB_H_ + +#include <sys/cdefs.h> + +#include "compat.h" + +struct stat; +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_matchc; /* Count of paths matching pattern. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ + /* Copy of errfunc parameter to glob. */ + int (*gl_errfunc) __P((const char *, int)); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir) __P((void *)); + struct dirent *(*gl_readdir) __P((void *)); + void *(*gl_opendir) __P((const char *)); + int (*gl_lstat) __P((const char *, struct stat *)); + int (*gl_stat) __P((const char *, struct stat *)); +} glob_t; + +#define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */ +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_MARK 0x0008 /* Append / to matching directories. */ +#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +#define GLOB_NOSORT 0x0020 /* Don't sort. */ + +#ifndef _POSIX_SOURCE +#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ +#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +#endif + +#define GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define GLOB_ABEND (-2) /* Unignored error. */ + +__BEGIN_DECLS +int glob __P((const char *, int, int (*)(const char *, int), glob_t *)); +void globfree __P((glob_t *)); +__END_DECLS + +#endif /* !_GLOB_H_ */ diff --git a/usr.bin/vi/include/mpool.h b/usr.bin/vi/include/mpool.h new file mode 100644 index 000000000000..910e0782aa9d --- /dev/null +++ b/usr.bin/vi/include/mpool.h @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mpool.h 8.1 (Berkeley) 6/2/93 + */ + +/* + * The memory pool scheme is a simple one. Each in memory page is referenced + * by a bucket which is threaded in three ways. All active pages are threaded + * on a hash chain (hashed by the page number) and an lru chain. Inactive + * pages are threaded on a free chain. Each reference to a memory pool is + * handed an MPOOL which is the opaque cookie passed to all of the memory + * routines. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the lists. */ +typedef struct BKT { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ + void *page; /* page */ + pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ + unsigned long flags; /* flags */ +} BKT; + +/* The BKTHDR structures are the heads of the lists. */ +typedef struct BKTHDR { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ +} BKTHDR; + +typedef struct MPOOL { + BKTHDR free; /* The free list. */ + BKTHDR lru; /* The LRU list. */ + BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */ + pgno_t curcache; /* Current number of cached pages. */ + pgno_t maxcache; /* Max number of cached pages. */ + pgno_t npages; /* Number of pages in the file. */ + u_long pagesize; /* File page size. */ + int fd; /* File descriptor. */ + /* Page in conversion routine. */ + void (*pgin) __P((void *, pgno_t, void *)); + /* Page out conversion routine. */ + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; /* Cookie for page in/out routines. */ +#ifdef STATISTICS + unsigned long cachehit; + unsigned long cachemiss; + unsigned long pagealloc; + unsigned long pageflush; + unsigned long pageget; + unsigned long pagenew; + unsigned long pageput; + unsigned long pageread; + unsigned long pagewrite; +#endif +} MPOOL; + +#ifdef __MPOOLINTERFACE_PRIVATE +/* Macros to insert/delete into/from hash chain. */ +#define rmhash(bp) { \ + (bp)->hprev->hnext = (bp)->hnext; \ + (bp)->hnext->hprev = (bp)->hprev; \ +} +#define inshash(bp, pg) { \ + hp = &mp->hashtable[HASHKEY(pg)]; \ + (bp)->hnext = hp->hnext; \ + (bp)->hprev = (struct BKT *)hp; \ + hp->hnext->hprev = (bp); \ + hp->hnext = (bp); \ +} + +/* Macros to insert/delete into/from lru and free chains. */ +#define rmchain(bp) { \ + (bp)->cprev->cnext = (bp)->cnext; \ + (bp)->cnext->cprev = (bp)->cprev; \ +} +#define inschain(bp, dp) { \ + (bp)->cnext = (dp)->cnext; \ + (bp)->cprev = (struct BKT *)(dp); \ + (dp)->cnext->cprev = (bp); \ + (dp)->cnext = (bp); \ +} +#endif + +__BEGIN_DECLS +MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), + void (*)(void *, pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, pgno_t *)); +void *mpool_get __P((MPOOL *, pgno_t, u_int)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS diff --git a/usr.bin/vi/include/ndbm.h b/usr.bin/vi/include/ndbm.h new file mode 100644 index 000000000000..a545bca1326e --- /dev/null +++ b/usr.bin/vi/include/ndbm.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NDBM_H_ +#define _NDBM_H_ + +#include <db.h> + +/* Map dbm interface onto db(3). */ +#define DBM_RDONLY O_RDONLY + +/* Flags to dbm_store(). */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * The db(3) support for ndbm(3) always appends this suffix to the + * file name to avoid overwriting the user's original database. + */ +#define DBM_SUFFIX ".db" + +typedef struct { + char *dptr; + int dsize; +} datum; + +typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE + +__BEGIN_DECLS +void dbm_close __P((DBM *)); +int dbm_delete __P((DBM *, datum)); +datum dbm_fetch __P((DBM *, datum)); +datum dbm_firstkey __P((DBM *)); +long dbm_forder __P((DBM *, datum)); +datum dbm_nextkey __P((DBM *)); +DBM *dbm_open __P((const char *, int, int)); +int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); +__END_DECLS + +#endif /* !_NDBM_H_ */ diff --git a/usr.bin/vi/include/queue.h b/usr.bin/vi/include/queue.h new file mode 100644 index 000000000000..40d32ccb6e29 --- /dev/null +++ b/usr.bin/vi/include/queue.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +/* + * This file defines three types of data structures: lists, tail queues, + * and circular queues. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element or at the head of the list. A list may only be + * traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) { \ + (head)->lh_first = NULL; \ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} + +#define LIST_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} + +#define LIST_REMOVE(elm, field) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} + +#define TAILQ_REMOVE(head, elm, field) { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} + +#define CIRCLEQ_REMOVE(head, elm, field) { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} +#endif /* !_QUEUE_H_ */ diff --git a/usr.bin/vi/interrupt.h b/usr.bin/vi/interrupt.h new file mode 100644 index 000000000000..baa75fef2ca9 --- /dev/null +++ b/usr.bin/vi/interrupt.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)interrupt.h 8.1 (Berkeley) 1/9/94 + */ + +/* + * Macros to declare the variables and then turn on and off interrupts. + */ +#define DECLARE_INTERRUPTS \ + struct sigaction __act, __oact; \ + struct termios __nterm, __term; \ + u_int __istate; \ + int __isig, __termreset + +/* + * Search, global, and substitute interruptions. + * + * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the + * search, so we install a handler. VQUIT is ignored by main() because + * nvi never wants to catch it. A handler for VSUSP should have been + * installed by the screen code. + */ +#define SET_UP_INTERRUPTS(handler) { \ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { \ + __act.sa_handler = handler; \ + sigemptyset(&__act.sa_mask); \ + __act.sa_flags = 0; \ + if (__isig = !sigaction(SIGINT, &__act, &__oact)) { \ + __termreset = 0; \ + __istate = F_ISSET(sp, S_INTERRUPTIBLE); \ + F_CLR(sp, S_INTERRUPTED); \ + F_SET(sp, S_INTERRUPTIBLE); \ + if (tcgetattr(STDIN_FILENO, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcgetattr"); \ + goto interrupt_err; \ + } \ + __nterm = __term; \ + __nterm.c_lflag |= ISIG; \ + if (tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__nterm)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + goto interrupt_err; \ + } \ + __termreset = 1; \ + } \ + } \ +} + +#define TEAR_DOWN_INTERRUPTS { \ + if (F_ISSET(sp->gp, G_ISFROMTTY) && __isig) { \ + if (sigaction(SIGINT, &__oact, NULL)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "signal"); \ + } \ + if (__termreset && tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + } \ + F_CLR(sp, S_INTERRUPTED); \ + if (!__istate) \ + F_CLR(sp, S_INTERRUPTIBLE); \ + } \ +} diff --git a/usr.bin/vi/line.c b/usr.bin/vi/line.c new file mode 100644 index 000000000000..a7473491cf17 --- /dev/null +++ b/usr.bin/vi/line.c @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)line.c 8.20 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int)); + +/* + * file_gline -- + * Look in the text buffers for a line; if it's not there + * call file_rline to retrieve it from the database. + */ +char * +file_gline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + TEXT *tp; + recno_t l1, l2; + + /* + * The underlying recno stuff handles zero by returning NULL, but + * have to have an oob condition for the look-aside into the input + * buffer anyway. + */ + if (lno == 0) + return (NULL); + + /* + * Look-aside into the TEXT buffers and see if the line we want + * is there. + */ + if (F_ISSET(sp, S_INPUT)) { + l1 = ((TEXT *)sp->tiq.cqh_first)->lno; + l2 = ((TEXT *)sp->tiq.cqh_last)->lno; + if (l1 <= lno && l2 >= lno) { + for (tp = sp->tiq.cqh_first; + tp->lno != lno; tp = tp->q.cqe_next); + if (lenp) + *lenp = tp->len; + return (tp->lb); + } + /* + * Adjust the line number for the number of lines used + * by the text input buffers. + */ + if (lno > l2) + lno -= l2 - l1; + } + return (file_rline(sp, ep, lno, lenp)); +} + +/* + * file_rline -- + * Look in the cache for a line; if it's not there retrieve + * it from the file. + */ +char * +file_rline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + DBT data, key; + + /* Check the cache. */ + if (lno == ep->c_lno) { + if (lenp) + *lenp = ep->c_len; + return (ep->c_lp); + } + ep->c_lno = OOBLNO; + + /* Get the line from the underlying database. */ + key.data = &lno; + key.size = sizeof(lno); + switch (ep->db->get(ep->db, &key, &data, 0)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + /* FALLTHROUGH */ + case 1: + return (NULL); + /* NOTREACHED */ + } + if (lenp) + *lenp = data.size; + + /* Fill the cache. */ + ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + return (data.data); +} + +/* + * file_dline -- + * Delete a line from the file. + */ +int +file_dline(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + DBT key; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete line %lu\n", lno); +#endif + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_DELETE, lno); + global_insdel(sp, ep, LINE_DELETE, lno); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_DELETE); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + if (ep->db->del(ep->db, &key, 0) == 1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to delete line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno <= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + --ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_DELETE, 1)); +} + +/* + * file_aline -- + * Append a line into the file. + */ +int +file_aline(sp, ep, update, lno, p, len) + SCR *sp; + EXF *ep; + int update; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* + * Very nasty special case. The historic vi code displays a single + * space (or a '$' if the list option is set) for the first line in + * an "empty" file. If we "insert" a line, that line gets scrolled + * down, not repainted, so it's incorrect when we refresh the the + * screen. This is really hard to find and fix in the vi code -- the + * text input functions detect it explicitly and don't insert a new + * line. The hack here is to repaint the screen if we're appending + * to an empty file. + */ + if (lno == 0) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to append to line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno < ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno + 1, LOG_LINE_APPEND); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno + 1); + global_insdel(sp, ep, LINE_INSERT, lno + 1); + + /* + * Update screen. + * + * XXX + * Nasty hack. If multiple lines are input by the user, they aren't + * committed until an <ESC> is entered. The problem is the screen was + * updated/scrolled as each line was entered. So, when this routine + * is called to copy the new lines from the cut buffer into the file, + * it has to know not to update the screen again. + */ + return (scr_update(sp, ep, lno, LINE_APPEND, update)); +} + +/* + * file_iline -- + * Insert a line into the file. + */ +int +file_iline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, + "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + + /* Very nasty special case. See comment in file_aline(). */ + if (lno == 1) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to insert at line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno >= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_INSERT); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno); + global_insdel(sp, ep, LINE_INSERT, lno); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_INSERT, 1)); +} + +/* + * file_sline -- + * Store a line in the file. + */ +int +file_sline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + +#if defined(DEBUG) && 0 + TRACE(sp, + "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* Log before change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_B); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, 0) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to store line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, before logging or screen update. */ + if (lno == ep->c_lno) + ep->c_lno = OOBLNO; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log after change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_F); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_RESET, 1)); +} + +/* + * file_lline -- + * Return the number of lines in the file. + */ +int +file_lline(sp, ep, lnop) + SCR *sp; + EXF *ep; + recno_t *lnop; +{ + DBT data, key; + recno_t lno; + + /* Check the cache. */ + if (ep->c_nlines != OOBLNO) { + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ? + ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines); + return (0); + } + + key.data = &lno; + key.size = sizeof(lno); + + switch (ep->db->seq(ep->db, &key, &data, R_LAST)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get last line: %s.", + tail(__FILE__), __LINE__, strerror(errno)); + *lnop = 0; + return (1); + case 1: + lno = 0; + break; + default: + memmove(&lno, key.data, sizeof(lno)); + break; + } + + /* Fill the cache. */ + ep->c_nlines = ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > lno ? + ((TEXT *)sp->tiq.cqh_last)->lno : lno); + return (0); +} + +/* + * scr_update -- + * Update all of the screens that are backed by the file that + * just changed. + */ +static inline int +scr_update(sp, ep, lno, op, current) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; + int current; +{ + SCR *tsp; + + if (ep->refcnt != 1) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (sp != tsp && tsp->ep == ep) + (void)sp->s_change(tsp, ep, lno, op); + return (current && sp->s_change(sp, ep, lno, op)); +} diff --git a/usr.bin/vi/log.c b/usr.bin/vi/log.c new file mode 100644 index 000000000000..fdf959f4d8a9 --- /dev/null +++ b/usr.bin/vi/log.c @@ -0,0 +1,663 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)log.c 8.9 (Berkeley) 12/28/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +/* + * The log consists of records, each containing a type byte and a variable + * length byte string, as follows: + * + * LOG_CURSOR_INIT MARK + * LOG_CURSOR_END MARK + * LOG_LINE_APPEND recno_t char * + * LOG_LINE_DELETE recno_t char * + * LOG_LINE_INSERT recno_t char * + * LOG_LINE_RESET_F recno_t char * + * LOG_LINE_RESET_B recno_t char * + * LOG_MARK MARK + * + * We do before image physical logging. This means that the editor layer + * MAY NOT modify records in place, even if simply deleting or overwriting + * characters. Since the smallest unit of logging is a line, we're using + * up lots of space. This may eventually have to be reduced, probably by + * doing logical logging, which is a much cooler database phrase. + * + * The implementation of the historic vi 'u' command, using roll-forward and + * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, + * followed by a number of other records, followed by a LOG_CURSOR_END record. + * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B + * record, and is the line before the change. The second is LOG_LINE_RESET_F, + * and is the line after the change. Roll-back is done by backing up to the + * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a + * similar fashion. + * + * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END + * record for a line different from the current one. It should be noted that + * this means that a subsequent 'u' command will make a change based on the + * new position of the log's cursor. This is okay, and, in fact, historic vi + * behaved that way. + */ + +static int log_cursor1 __P((SCR *, EXF *, int)); +#if defined(DEBUG) && 0 +static void log_trace __P((SCR *, char *, recno_t, u_char *)); +#endif + +/* Try and restart the log on failure, i.e. if we run out of memory. */ +#define LOG_ERR { \ + msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.", \ + tail(__FILE__), __LINE__, strerror(errno)); \ + (void)ep->log->close(ep->log); \ + if (!log_init(sp, ep)) \ + msgq(sp, M_ERR, "Log restarted."); \ + return (1); \ +} + +/* + * log_init -- + * Initialize the logging subsystem. + */ +int +log_init(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Initialize the buffer. The logging subsystem has its own + * buffers because the global ones are almost by definition + * going to be in use when the log runs. + */ + ep->l_lp = NULL; + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + + ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR, + S_IRUSR | S_IWUSR, DB_RECNO, NULL); + if (ep->log == NULL) { + msgq(sp, M_ERR, "log db: %s", strerror(errno)); + F_SET(ep, F_NOLOG); + return (1); + } + + return (0); +} + +/* + * log_end -- + * Close the logging subsystem. + */ +int +log_end(sp, ep) + SCR *sp; + EXF *ep; +{ + if (ep->log != NULL) { + (void)(ep->log->close)(ep->log); + ep->log = NULL; + } + if (ep->l_lp != NULL) { + free(ep->l_lp); + ep->l_lp = NULL; + } + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + return (0); +} + +/* + * log_cursor -- + * Log the current cursor position, starting an event. + */ +int +log_cursor(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * If any changes were made since the last cursor init, + * put out the ending cursor record. + */ + if (ep->l_cursor.lno == OOBLNO) { + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (log_cursor1(sp, ep, LOG_CURSOR_END)); + } + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (0); +} + +/* + * log_cursor1 -- + * Actually push a cursor record out. + */ +static int +log_cursor1(sp, ep, type) + SCR *sp; + EXF *ep; + int type; +{ + DBT data, key; + + BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = type; + memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, + type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", + sp->lno, sp->cno); +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_line -- + * Log a line change. + */ +int +log_line(sp, ep, lno, action) + SCR *sp; + EXF *ep; + recno_t lno; + u_int action; +{ + DBT data, key; + size_t len; + char *lp; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* + * XXX + * + * Kluge for vi. Clear the EXF undo flag so that the + * next 'u' command does a roll-back, regardless. + */ + F_CLR(ep, F_UNDO); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + /* + * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a + * special case, avoid the caches. Also, if it fails and it's + * line 1, it just means that the user started with an empty file, + * so fake an empty length line. + */ + if (action == LOG_LINE_RESET_B) { + if ((lp = file_rline(sp, ep, lno, &len)) == NULL) { + if (lno != 1) { + GETLINE_ERR(sp, lno); + return (1); + } + len = 0; + lp = ""; + } + } else + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + BINC_RET(sp, + ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t)); + ep->l_lp[0] = action; + memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); + memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = len + sizeof(u_char) + sizeof(recno_t); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + switch (action) { + case LOG_LINE_APPEND: + TRACE(sp, "%u: log_line: append: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_DELETE: + TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_INSERT: + TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_F: + TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_B: + TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + } +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_mark -- + * Log a mark position. For the log to work, we assume that there + * aren't any operations that just put out a log record -- this + * would mean that undo operations would only reset marks, and not + * cause any other change. + */ +int +log_mark(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + DBT data, key; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + BINC_RET(sp, ep->l_lp, + ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = LOG_MARK; + memmove(ep->l_lp + sizeof(u_char), mp, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + return (0); +} + +/* + * Log_backward -- + * Roll the log backward one operation. + */ +int +log_backward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) { + msgq(sp, M_BERR, "No changes to undo."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_backward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + if (didop) { + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_setline -- + * Reset the line to its original appearance. + * + * XXX + * There's a bug in this code due to our not logging cursor movements + * unless a change was made. If you do a change, move off the line, + * then move back on and do a 'U', the line will be restored to the way + * it was before the original change. + */ +int +log_setline(sp, ep) + SCR *sp; + EXF *ep; +{ + DBT key, data; + MARK m; + recno_t lno; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) + return (1); + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + + for (;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_setline", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno || ep->l_cur == 1) { + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno) { + ++ep->l_cur; + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + case LOG_LINE_DELETE: + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (lno == sp->lno && + file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_forward -- + * Roll the log forward one operation. + */ +int +log_forward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, roll-forward not possible."); + return (1); + } + + if (ep->l_cur == ep->l_high) { + msgq(sp, M_BERR, "No changes to re-do."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + ++ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_forward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_END: + if (didop) { + ++ep->l_cur; + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_INIT: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_RESET_B: + break; + case LOG_LINE_RESET_F: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +#if defined(DEBUG) && 0 +static void +log_trace(sp, msg, rno, p) + SCR *sp; + char *msg; + recno_t rno; + u_char *p; +{ + MARK m; + recno_t lno; + + switch (*p) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_LINE_APPEND: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); + break; + case LOG_LINE_INSERT: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); + break; + case LOG_LINE_DELETE: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_F: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); + break; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: MARK: %u/%u\n", rno, msg, m.lno, m.cno); + break; + default: + abort(); + } +} +#endif diff --git a/usr.bin/vi/log.h b/usr.bin/vi/log.h new file mode 100644 index 000000000000..840cf8532c0c --- /dev/null +++ b/usr.bin/vi/log.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)log.h 8.3 (Berkeley) 12/28/93 + */ + +#define LOG_NOTYPE 0 +#define LOG_CURSOR_INIT 1 +#define LOG_CURSOR_END 2 +#define LOG_LINE_APPEND 3 +#define LOG_LINE_DELETE 4 +#define LOG_LINE_INSERT 5 +#define LOG_LINE_RESET_F 6 +#define LOG_LINE_RESET_B 7 +#define LOG_MARK 8 + +int log_backward __P((SCR *, EXF *, MARK *)); +int log_cursor __P((SCR *, EXF *)); +int log_end __P((SCR *, EXF *)); +int log_forward __P((SCR *, EXF *, MARK *)); +int log_init __P((SCR *, EXF *)); +int log_line __P((SCR *, EXF *, recno_t, u_int)); +int log_mark __P((SCR *, EXF *, MARK *)); +int log_setline __P((SCR *, EXF *)); diff --git a/usr.bin/vi/main.c b/usr.bin/vi/main.c new file mode 100644 index 000000000000..3413a475ddf8 --- /dev/null +++ b/usr.bin/vi/main.c @@ -0,0 +1,743 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.65 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" +#include "tag.h" + +static int exrc_isok __P((SCR *, char *, int)); +static void gs_end __P((GS *)); +static GS *gs_init __P((void)); +static void h_hup __P((int)); +static void h_term __P((int)); +static void h_winch __P((int)); +static void obsolete __P((char *[])); +static void usage __P((int)); + +GS *__global_list; /* GLOBAL: List of screens. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + static int reenter; /* STATIC: Re-entrancy check. */ + struct sigaction act; + GS *gp; + FREF *frp; + SCR *sp; + u_int flags, saved_vi_mode; + int ch, eval, flagchk, readonly, silent, snapshot; + char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg; + char path[MAXPATHLEN]; + + /* Stop if indirecting through a NULL pointer. */ + if (reenter++) + abort(); + + /* Set screen type and mode based on the program name. */ + readonly = 0; + if ((myname = strrchr(*argv, '/')) == NULL) + myname = *argv; + else + ++myname; + if (!strcmp(myname, "ex") || !strcmp(myname, "nex")) + LF_INIT(S_EX); + else { + /* View is readonly. */ + if (!strcmp(myname, "view")) + readonly = 1; + LF_INIT(S_VI_CURSES); + } + saved_vi_mode = S_VI_CURSES; + + /* Convert old-style arguments into new-style ones. */ + obsolete(argv); + + /* Parse the arguments. */ + flagchk = '\0'; + excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL; + silent = 0; + snapshot = 1; + while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF) + switch (ch) { + case 'c': /* Run the command. */ + excmdarg = optarg; + break; + case 'e': /* Ex mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_EX); + break; + case 'F': /* No snapshot. */ + snapshot = 0; + break; + case 'l': + if (flagchk != '\0' && flagchk != 'l') + errx(1, + "only one of -%c and -l may be specified.", + flagchk); + flagchk = 'l'; + break; + case 'R': /* Readonly. */ + readonly = 1; + break; + case 'r': /* Recover. */ + if (flagchk == 'r') + errx(1, + "only one recovery file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -r may be specified.", + flagchk); + flagchk = 'r'; + rec_f = optarg; + break; + case 's': + if (!LF_ISSET(S_EX)) + errx(1, "-s only applicable to ex."); + silent = 1; + break; + case 'T': /* Trace. */ + trace_f = optarg; + break; + case 't': /* Tag. */ + if (flagchk == 't') + errx(1, + "only one tag file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -t may be specified.", + flagchk); + flagchk = 't'; + tag_f = optarg; + break; + case 'v': /* Vi mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_VI_CURSES); + break; + case 'w': + wsizearg = optarg; + break; + case 'x': + if (!strcmp(optarg, "aw")) { + LF_CLR(S_SCREENS); + LF_SET(S_VI_XAW); + saved_vi_mode = S_VI_XAW; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(LF_ISSET(S_EX)); + } + argc -= optind; + argv += optind; + + /* Build and initialize the GS structure. */ + __global_list = gp = gs_init(); + + if (snapshot) + F_SET(gp, G_SNAPSHOT); + + /* + * Build and initialize the first/current screen. This is a bit + * tricky. If an error is returned, we may or may not have a + * screen structure. If we have a screen structure, put it on a + * display queue so that the error messages get displayed. + */ + if (screen_init(NULL, &sp, flags)) { + if (sp != NULL) + CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); + goto err; + } + sp->saved_vi_mode = saved_vi_mode; + CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); + + if (trace_f != NULL) { +#ifdef DEBUG + if ((gp->tracefp = fopen(optarg, "w")) == NULL) + err(1, "%s", optarg); + (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg); +#else + msgq(sp, M_ERR, "-T support not compiled into this version."); +#endif + } + + if (set_window_size(sp, 0, 0)) /* Set the window size. */ + goto err; + if (opts_init(sp)) /* Options initialization. */ + goto err; + if (readonly) /* Global read-only bit. */ + O_SET(sp, O_READONLY); + if (silent) { /* Ex batch mode. */ + O_CLR(sp, O_AUTOPRINT); + O_CLR(sp, O_PROMPT); + O_CLR(sp, O_VERBOSE); + O_CLR(sp, O_WARN); + F_SET(sp, S_EXSILENT); + } + if (wsizearg != NULL) { + ARGS *av[2], a, b; + if (strtol(optarg, &p, 10) < 0 || *p) + errx(1, "illegal window size -- %s", optarg); + (void)snprintf(path, sizeof(path), "window=%s", optarg); + a.bp = (CHAR_T *)path; + a.len = strlen(path); + b.bp = NULL; + b.len = 0; + av[0] = &a; + av[1] = &b; + if (opts_set(sp, av)) + msgq(sp, M_ERR, + "Unable to set command line window option"); + } + + /* Keymaps, special keys, must follow option initializations. */ + if (term_init(sp)) + goto err; + +#ifdef DIGRAPHS + if (digraph_init(sp)) /* Digraph initialization. */ + goto err; +#endif + + /* + * Source the system, environment, ~user and local .exrc values. + * Vi historically didn't check ~user/.exrc if the environment + * variable EXINIT was set. This is all done before the file is + * read in because things in the .exrc information can set, for + * example, the recovery directory. + * + * !!! + * While nvi can handle any of the options settings of historic vi, + * the converse is not true. Since users are going to have to have + * files and environmental variables that work with both, we use nvi + * versions if they exist, otherwise the historic ones. + */ + if (!silent) { + if (exrc_isok(sp, _PATH_SYSEXRC, 1)) + (void)ex_cfile(sp, NULL, _PATH_SYSEXRC); + + /* Source the {N,}EXINIT environment variable. */ + if ((p = getenv("NEXINIT")) != NULL || + (p = getenv("EXINIT")) != NULL) + if ((p = strdup(p)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err; + } else { + (void)ex_icmd(sp, NULL, p, strlen(p)); + free(p); + } + else if ((p = getenv("HOME")) != NULL && *p) { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_NEXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + else { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_EXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + } + } + /* + * !!! + * According to O'Reilly ("Learning the VI Editor", Fifth Ed., + * May 1992, page 106), System V release 3.2 and later, has an + * option "[no]exrc", causing vi to not "read .exrc files in + * the current directory unless you first set the exrc option + * in your home directory's .exrc file". Yeah, right. Did + * someone actually believe that users would change their home + * .exrc file based on whether or not they wanted to source the + * current local .exrc? Or that users would want ALL the local + * .exrc files on some systems, and none of them on others? + * I think not. + * + * Apply the same tests to local .exrc files that are applied + * to any other .exrc file. + */ + if (exrc_isok(sp, _PATH_EXRC, 0)) + (void)ex_cfile(sp, NULL, _PATH_EXRC); + } + + /* List recovery files if -l specified. */ + if (flagchk == 'l') + exit(rcv_list(sp)); + + /* Use a tag file or recovery file if specified. */ + if (tag_f != NULL && ex_tagfirst(sp, tag_f)) + goto err; + else if (rec_f != NULL && rcv_read(sp, rec_f)) + goto err; + + /* Append any remaining arguments as file names. */ + if (*argv != NULL) + for (; *argv != NULL; ++argv) + if (file_add(sp, NULL, *argv, 0) == NULL) + goto err; + + /* + * If no recovery or tag file, get an EXF structure. + * If no argv file, use a temporary file. + */ + if (tag_f == NULL && rec_f == NULL) { + if ((frp = file_first(sp)) == NULL && + (frp = file_add(sp, NULL, NULL, 1)) == NULL) + goto err; + if (file_init(sp, frp, NULL, 0)) + goto err; + } + + /* Set up the argument pointer. */ + sp->a_frp = sp->frp; + + /* + * Initialize the signals. Use sigaction(2), not signal(3), because + * we don't want to always restart system calls on 4BSD systems. It + * would be nice in some cases to restart system calls, but SA_RESTART + * is a 4BSD extension so we can't use it. + * + * SIGWINCH, SIGHUP, SIGTERM: + * Catch and set a global bit. + */ + act.sa_handler = h_hup; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGHUP, &act, NULL); + act.sa_handler = h_term; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGTERM, &act, NULL); + act.sa_handler = h_winch; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGWINCH, &act, NULL); + + /* + * SIGQUIT: + * Always ignore. + */ + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGQUIT, &act, NULL); + + /* + * If there's an initial command, push it on the command stack. + * Historically, it was always an ex command, not vi in vi mode + * or ex in ex mode. So, make it look like an ex command to vi. + */ + if (excmdarg != NULL) + if (IN_EX_MODE(sp)) { + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err; + } else if (IN_VI_MODE(sp)) { + if (term_push(sp, "\n", 1, 0, 0)) + goto err; + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err; + if (term_push(sp, ":", 1, 0, 0)) + goto err; + } + + /* Vi reads from the terminal. */ + if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) { + msgq(sp, M_ERR, "Vi's standard input must be a terminal."); + goto err; + } + + for (;;) { + if (sp->s_edit(sp, sp->ep)) + goto err; + + /* + * Edit the next screen on the display queue, or, move + * a screen from the hidden queue to the display queue. + */ + if ((sp = __global_list->dq.cqh_first) == + (void *)&__global_list->dq) + if ((sp = __global_list->hq.cqh_first) != + (void *)&__global_list->hq) { + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + } else + break; + + /* + * The screen type may have changed -- reinitialize the + * functions in case it has. + */ + switch (F_ISSET(sp, S_SCREENS)) { + case S_EX: + if (sex_screen_init(sp)) + goto err; + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + goto err; + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + goto err; + break; + default: + abort(); + } + } + + eval = 0; + if (0) +err: eval = 1; + + /* + * NOTE: sp may be GONE when the screen returns, so only + * the gp can be trusted. + */ + gs_end(gp); + + /* + * XXX + * Make absolutely sure that the modes are restored correctly. + * + * This should no longer be needed, and it's here to handle what I + * believe are SunOS/Solaris curses problems. The problem is that + * for some unknown reason, when endwin() is called in the svi + * routines, it isn't resetting the terminal correctly. I have not + * been able to figure it out, so this resets the terminal to the + * right modes regardless. The problem is that, in most tty driver + * implementations, you can only reset the terminal modes once + * (changing from !ICANON to ICANON) without losing the re-parsing + * effect on the pending input. This means that this "fix" will make + * other systems mess up characters typed after the quit command to + * vi but before vi actually exits. + */ + if (F_ISSET(gp, G_ISFROMTTY)) + (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios); + exit(eval); +} + +/* + * gs_init -- + * Build and initialize the GS structure. + */ +static GS * +gs_init() +{ + GS *gp; + int fd; + + CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS)); + if (gp == NULL) + err(1, NULL); + + CIRCLEQ_INIT(&gp->dq); + CIRCLEQ_INIT(&gp->hq); + LIST_INIT(&gp->msgq); + + /* Structures shared by screens so stored in the GS structure. */ + CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF)); + if (gp->tty == NULL) + err(1, NULL); + + LIST_INIT(&gp->cutq); + LIST_INIT(&gp->seqq); + + /* Set a flag if we're reading from the tty. */ + if (isatty(STDIN_FILENO)) + F_SET(gp, G_ISFROMTTY); + + /* + * XXX + * Set a flag and don't do terminal sets/resets if the input isn't + * from a tty. Under all circumstances put reasonable things into + * the original_termios field, as some routines (seq.c:seq_save() + * and term.c:term_init()) want values for special characters. + */ + if (F_ISSET(gp, G_ISFROMTTY)) { + if (tcgetattr(STDIN_FILENO, &gp->original_termios)) + err(1, "tcgetattr"); + } else { + if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1) + err(1, "%s", _PATH_TTY); + if (tcgetattr(fd, &gp->original_termios)) + err(1, "tcgetattr"); + (void)close(fd); + } + + return (gp); +} + + +/* + * gs_end -- + * End the GS structure. + */ +static void +gs_end(gp) + GS *gp; +{ + MSG *mp; + SCR *sp; + char *tty; + + /* Reset anything that needs resetting. */ + if (gp->flags & G_SETMODE) /* O_MESG */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) + warn("ttyname"); + else if (chmod(tty, gp->origmode) < 0) + warn("%s", tty); + + /* Ring the bell if scheduled. */ + if (F_ISSET(gp, G_BELLSCHED)) + (void)fprintf(stderr, "\07"); /* \a */ + + /* If there are any remaining screens, flush their messages. */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + /* Flush messages on the global queue. */ + for (mp = gp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + + if (gp->special_key != NULL) + FREE(gp->special_key, MAX_FAST_KEY); + + /* + * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN + * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED. + */ +} + +/* + * h_hup -- + * Handle SIGHUP. + */ +static void +h_hup(signo) + int signo; +{ + F_SET(__global_list, G_SIGHUP); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_hup(); +} + +/* + * h_term -- + * Handle SIGTERM. + */ +static void +h_term(signo) + int signo; +{ + F_SET(__global_list, G_SIGTERM); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_term(); +} + +/* + * h_winch -- + * Handle SIGWINCH. + */ +static void +h_winch(signo) + int signo; +{ + F_SET(__global_list, G_SIGWINCH); +} + +/* + * exrc_isok -- + * Check a .exrc for source-ability. + */ +static int +exrc_isok(sp, path, rootok) + SCR *sp; + char *path; + int rootok; +{ + struct stat sb; + uid_t uid; + char *emsg, buf[MAXPATHLEN]; + + /* Check for the file's existence. */ + if (stat(path, &sb)) + return (0); + + /* + * !!! + * Historically, vi did not read the .exrc files if they were owned + * by someone other than the user, unless the undocumented option + * sourceany was set. We don't support the sourceany option. We + * check that the user (or root, for system files) owns the file and + * require that it not be writeable by anyone other than the owner. + */ + + /* Owned by the user or root. */ + uid = getuid(); + if (rootok) { + if (sb.st_uid != uid && sb.st_uid != 0) { + emsg = "not owned by you or root"; + goto err; + } + } else + if (sb.st_uid != uid) { + emsg = "not owned by you"; + goto err; + } + + /* Not writeable by anyone but the owner. */ + if (sb.st_mode & (S_IWGRP | S_IWOTH)) { + emsg = "writeable by a user other than the owner"; +err: if (strchr(path, '/') == NULL && + getcwd(buf, sizeof(buf)) != NULL) + msgq(sp, M_ERR, + "%s/%s: not sourced: %s.", buf, path, emsg); + else + msgq(sp, M_ERR, + "%s: not sourced: %s.", path, emsg); + return (0); + } + return (1); +} + +static void +obsolete(argv) + char *argv[]; +{ + size_t len; + char *p, *myname; + + /* + * Translate old style arguments into something getopt will like. + * Make sure it's not text space memory, because ex changes the + * strings. + * Change "+" into "-c$". + * Change "+<anything else>" into "-c<anything else>". + * Change "-" into "-s" + * Change "-r" into "-l" + */ + for (myname = argv[0]; *++argv;) + if (argv[0][0] == '+') { + if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 4); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-c$"); + } else { + p = argv[0]; + len = strlen(argv[0]); + MALLOC_NOMSG(NULL, argv[0], char *, len + 2); + if (argv[0] == NULL) + err(1, NULL); + argv[0][0] = '-'; + argv[0][1] = 'c'; + (void)strcpy(argv[0] + 2, p + 1); + } + } else if (argv[0][0] == '-') { + if (argv[0][1] == 'r') { + if (argv[0][2] == '\0' && argv[1] == NULL) + argv[0][1] = 'l'; + } else if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 3); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-s"); + } + } +} + +static void +usage(is_ex) + int is_ex; +{ +#define EX_USAGE \ + "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" +#define VI_USAGE \ + "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" + + (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE); + exit(1); +} diff --git a/usr.bin/vi/mark.c b/usr.bin/vi/mark.c new file mode 100644 index 000000000000..c510a22002fc --- /dev/null +++ b/usr.bin/vi/mark.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mark.c 8.12 (Berkeley) 12/27/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T)); + +/* + * Marks are maintained in a key sorted doubly linked list. We can't + * use arrays because we have no idea how big an index key could be. + * The underlying assumption is that users don't have more than, say, + * 10 marks at any one time, so this will be is fast enough. + * + * Marks are fixed, and modifications to the line don't update the mark's + * position in the line. This can be hard. If you add text to the line, + * place a mark in that text, undo the addition and use ` to move to the + * mark, the location will have disappeared. It's tempting to try to adjust + * the mark with the changes in the line, but this is hard to do, especially + * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi + * would move to the first non-blank on the line when the mark location was + * past the end of the line. This can be complicated by deleting to a mark + * that has disappeared using the ` command. Historic vi vi treated this as + * a line-mode motion and deleted the line. This implementation complains to + * the user. + * + * In historic vi, marks returned if the operation was undone, unless the + * mark had been subsequently reset. Tricky. This is hard to start with, + * but in the presence of repeated undo it gets nasty. When a line is + * deleted, we delete (and log) any marks on that line. An undo will create + * the mark. Any mark creations are noted as to whether the user created + * it or if it was created by an undo. The former cannot be reset by another + * undo, but the latter may. + * + * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of + * the absolute mark locations sets both, so that "m'" and "m`" work like + * they, ah, for lack of a better word, "should". + */ + +/* + * mark_init -- + * Set up the marks. + */ +int +mark_init(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + /* + * Make sure the marks have been set up. If they + * haven't, do so, and create the absolute mark. + */ + MALLOC_RET(sp, mp, MARK *, sizeof(MARK)); + mp->lno = 1; + mp->cno = 0; + mp->name = ABSMARK1; + mp->flags = 0; + LIST_INSERT_HEAD(&ep->marks, mp, q); + return (0); +} + +/* + * mark_end -- + * Free up the marks. + */ +int +mark_end(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + while ((mp = ep->marks.lh_first) != NULL) { + LIST_REMOVE(mp, q); + FREE(mp, sizeof(MARK)); + } + return (0); +} + +/* + * mark_get -- + * Get the location referenced by a mark. + */ +MARK * +mark_get(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp; + size_t len; + char *p; + + if (key == ABSMARK2) + key = ABSMARK1; + + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key)); + return (NULL); + } + if (F_ISSET(mp, MARK_DELETED)) { + msgq(sp, M_BERR, + "Mark %s: the line was deleted.", charname(sp, key)); + return (NULL); + } + if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL || + mp->cno > len || mp->cno == len && len != 0) { + msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.", + charname(sp, key)); + return (NULL); + } + return (mp); +} + +/* + * mark_set -- + * Set the location referenced by a mark. + */ +int +mark_set(sp, ep, key, value, userset) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; + MARK *value; + int userset; +{ + MARK *mp, *mt; + + if (key == ABSMARK2) + key = ABSMARK1; + + /* + * The rules are simple. If the user is setting a mark (if it's a + * new mark this is always true), it always happens. If not, it's + * an undo, and we set it if it's not already set or if it was set + * by a previous undo. + */ + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + MALLOC_RET(sp, mt, MARK *, sizeof(MARK)); + if (mp == NULL) { + LIST_INSERT_HEAD(&ep->marks, mt, q); + } else + LIST_INSERT_AFTER(mp, mt, q); + mp = mt; + } else if (!userset && + !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET)) + return (0); + + mp->lno = value->lno; + mp->cno = value->cno; + mp->name = key; + mp->flags = userset ? MARK_USERSET : 0; + return (0); +} + +/* + * mark_find -- + * Find the requested mark, or, the slot immediately before + * where it would go. + */ +static MARK * +mark_find(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp, *lastmp; + + /* + * Return the requested mark or the slot immediately before + * where it should go. + */ + for (lastmp = NULL, mp = ep->marks.lh_first; + mp != NULL; lastmp = mp, mp = mp->q.le_next) + if (mp->name >= key) + return (mp->name == key ? mp : lastmp); + return (lastmp); +} + +/* + * mark_insdel -- + * Update the marks based on an insertion or deletion. + */ +void +mark_insdel(sp, ep, op, lno) + SCR *sp; + EXF *ep; + enum operation op; + recno_t lno; +{ + MARK *mp; + + switch (op) { + case LINE_APPEND: + return; + case LINE_DELETE: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + if (mp->lno == lno) { + F_SET(mp, MARK_DELETED); + (void)log_mark(sp, ep, mp); + } else + --mp->lno; + return; + case LINE_INSERT: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + ++mp->lno; + return; + case LINE_RESET: + return; + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/mark.h b/usr.bin/vi/mark.h new file mode 100644 index 000000000000..9c28151314b9 --- /dev/null +++ b/usr.bin/vi/mark.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mark.h 8.5 (Berkeley) 12/27/93 + */ + +/* + * The MARK structure defines a position in the file. Because of the different + * interfaces used by the db(3) package, curses, and users, the line number is + * 1 based, while the column number is 0 based. Additionally, it is known that + * the out-of-band line number is less than any legal line number. The line + * number is of type recno_t, as that's the underlying type of the database. + * The column number is of type size_t, guaranteeing that we can malloc a line. + */ +struct _mark { + LIST_ENTRY(_mark) q; /* Linked list of marks. */ +#define OOBLNO 0 /* Out-of-band line number. */ + recno_t lno; /* Line number. */ + size_t cno; /* Column number. */ + CHAR_T name; /* Mark name. */ + +#define MARK_DELETED 0x01 /* Mark was deleted. */ +#define MARK_USERSET 0x02 /* User set this mark. */ + u_char flags; +}; + +#define ABSMARK1 '\'' /* Absolute mark name. */ +#define ABSMARK2 '`' /* Absolute mark name. */ + +/* Mark routines. */ +int mark_end __P((SCR *, EXF *)); +MARK *mark_get __P((SCR *, EXF *, ARG_CHAR_T)); +int mark_init __P((SCR *, EXF *)); +void mark_insdel __P((SCR *, EXF *, enum operation, recno_t)); +int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int)); diff --git a/usr.bin/vi/mem.h b/usr.bin/vi/mem.h new file mode 100644 index 000000000000..0f8fac4b8f39 --- /dev/null +++ b/usr.bin/vi/mem.h @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mem.h 8.2 (Berkeley) 12/19/93 + */ + +/* Increase the size of a malloc'd buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define BINC_GOTO(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + goto binc_err; \ +} +#define BINC_RET(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + return (1); \ +} + +/* + * Get some temporary space, preferably from the global temporary buffer, + * from a malloc'd buffer otherwise. Two versions, one that returns, one + * that jumps to an error label. + */ +#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_GOTO(sp, bp, blen, nlen); \ + } else { \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} +#define GET_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_RET(sp, bp, blen, nlen); \ + } else { \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} + +/* + * Add space to a GET_SPACE returned buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_GOTO(sp, bp, blen, nlen); \ +} +#define ADD_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_RET(sp, bp, blen, nlen); \ +} + +/* Free memory, optionally making pointers unusable. */ +#ifdef DEBUG +#define FREE(p, sz) { \ + memset(p, 0xff, sz); \ + free(p); \ +} +#else +#define FREE(p, sz) free(p); +#endif + +/* Free a GET_SPACE returned buffer. */ +#define FREE_SPACE(sp, bp, blen) { \ + if (bp == sp->gp->tmp_bp) \ + F_CLR(sp->gp, G_TMP_INUSE); \ + else \ + FREE(bp, blen); \ +} + +/* + * Malloc a buffer, casting the return pointer. Various versions. + * + * !!! + * The cast should be unnecessary, malloc(3) and friends return void *'s, + * which is all we need. However, some systems that nvi needs to run on + * don't do it right yet, resulting in the compiler printing out roughly + * a million warnings. After awhile, it seemed easier to put the casts + * in instead of explaining it all the time. + */ +#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \ + p = (cast)calloc(nmemb, size); \ +} +#define CALLOC(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define CALLOC_RET(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define MALLOC_NOMSG(sp, p, cast, size) { \ + p = (cast)malloc(size); \ +} +#define MALLOC(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define MALLOC_RET(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define REALLOC(sp, p, cast, size) { \ + if ((p = (cast)realloc(p, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} + +int binc __P((SCR *, void *, size_t *, size_t)); diff --git a/usr.bin/vi/msg.h b/usr.bin/vi/msg.h new file mode 100644 index 000000000000..6b20bb4bb63b --- /dev/null +++ b/usr.bin/vi/msg.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)msg.h 8.8 (Berkeley) 11/18/93 + */ + +/* + * M_BERR -- Error: ring a bell if O_VERBOSE not set, else + * display in inverse video. + * M_ERR -- Error: display in inverse video. + * M_INFO -- Info: display in normal video. + * M_SYSERR -- M_ERR, but use standard error message. + * M_VINFO -- Info: display only if O_VERBOSE set. + * + * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the + * error messages shorter. In this version, O_TERSE has no effect + * and O_VERBOSE results in informational displays about common + * errors. + */ +enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO }; + +typedef struct _msgh MSGH; /* MESG list head structure. */ +LIST_HEAD(_msgh, _msg); + +struct _msg { + LIST_ENTRY(_msg) q; /* Linked list of messages. */ + char *mbuf; /* Message buffer. */ + size_t blen; /* Message buffer length. */ + size_t len; /* Message length. */ + +#define M_EMPTY 0x01 /* No message. */ +#define M_INV_VIDEO 0x02 /* Inverse video. */ + u_int flags; /* Flags. */ +}; + +/* Messages. */ +void msg_app __P((GS *, SCR *, int, char *, size_t)); +int msg_rpt __P((SCR *, int)); +void msgq __P((SCR *, enum msgtype, const char *, ...)); diff --git a/usr.bin/vi/nex/ex.c b/usr.bin/vi/nex/ex.c new file mode 100644 index 000000000000..3f5beac4a734 --- /dev/null +++ b/usr.bin/vi/nex/ex.c @@ -0,0 +1,1523 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex.c 8.90 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +static inline EXCMDLIST const * + ex_comm_search __P((char *, size_t)); +static int ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *)); +static int ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *)); + +#define DEFCOM ".+1" + +/* + * ex -- + * Read an ex command and execute it. + */ +int +ex(sp, ep) + SCR *sp; + EXF *ep; +{ + TEXT *tp; + u_int saved_mode; + int eval; + char defcom[sizeof(DEFCOM)]; + + if (ex_init(sp, ep)) + return (1); + + if (sp->s_refresh(sp, ep)) + return (ex_end(sp)); + + /* If reading from a file, messages should have line info. */ + if (!F_ISSET(sp->gp, G_ISFROMTTY)) { + sp->if_lno = 1; + sp->if_name = strdup("input"); + } + for (eval = 0;; ++sp->if_lno) { + /* Get the next command. */ + switch (sp->s_get(sp, ep, &sp->tiq, ':', TXT_CR | TXT_PROMPT)) { + case INP_OK: + break; + case INP_EOF: + F_SET(sp, S_EXIT_FORCE); + goto ret; + case INP_ERR: + continue; + } + + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + tp = sp->tiq.cqh_first; + if (tp->len == 0) { + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + (void)fputc('\r', stdout); + (void)fflush(stdout); + } + memmove(defcom, DEFCOM, sizeof(DEFCOM)); + (void)ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1); + } else { + if (F_ISSET(sp->gp, G_ISFROMTTY)) + (void)fputc('\n', stdout); + (void)ex_icmd(sp, ep, tp->lb, tp->len); + } + (void)msg_rpt(sp, 0); + + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) + break; + + if (sp->s_refresh(sp, ep)) { + eval = 1; + break; + } + } +ret: if (sp->if_name != NULL) { + FREE(sp->if_name, strlen(sp->if_name) + 1); + sp->if_name = NULL; + } + return (ex_end(sp) || eval); +} + +/* + * ex_cfile -- + * Execute ex commands from a file. + */ +int +ex_cfile(sp, ep, filename) + SCR *sp; + EXF *ep; + char *filename; +{ + struct stat sb; + int fd, len, rval; + char *bp; + + bp = NULL; + if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb)) + goto err; + + /* + * XXX + * We'd like to test if the file is too big to malloc. Since we don't + * know what size or type off_t's or size_t's are, what the largest + * unsigned integral type is, or what random insanity the local C + * compiler will perpetrate, doing the comparison in a portable way + * is flatly impossible. Hope that malloc fails if the file is too + * large. + */ + MALLOC(sp, bp, char *, (size_t)sb.st_size + 1); + if (bp == NULL) + goto err; + + len = read(fd, bp, (int)sb.st_size); + if (len == -1 || len != sb.st_size) { + if (len != sb.st_size) + errno = EIO; +err: rval = 1; + msgq(sp, M_SYSERR, filename); + } else { + bp[sb.st_size] = '\0'; /* XXX */ + + /* Run the command. Messages include file/line information. */ + sp->if_lno = 1; + sp->if_name = strdup(filename); + rval = ex_icmd(sp, ep, bp, len); + FREE(sp->if_name, strlen(sp->if_name) + 1); + sp->if_name = NULL; + } + + /* + * !!! + * THE UNDERLYING EXF MAY HAVE CHANGED. + */ + if (bp != NULL) + FREE(bp, sb.st_size); + if (fd >= 0) + (void)close(fd); + return (rval); +} + +/* + * ex_icmd -- + * Call ex_cmd() after turning off interruptible bits. + */ +int +ex_icmd(sp, ep, cmd, len) + SCR *sp; + EXF *ep; + char *cmd; + size_t len; +{ + /* + * Ex goes through here for each vi :colon command and for each ex + * command, however, globally executed commands don't go through + * here, instead, they call ex_cmd directly. So, reset all of the + * interruptible flags now. + */ + F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE); + + return (ex_cmd(sp, ep, cmd, len)); +} + +/* Special command structure for :s as a repeat substitution command. */ +static EXCMDLIST const cmd_subagain = + {"s", ex_subagain, E_ADDR2|E_NORC, + "s", + "[line [,line]] s [cgr] [count] [#lp]", + "repeat the last subsitution"}; + +/* + * ex_cmd -- + * Parse and execute a string containing ex commands. + */ +int +ex_cmd(sp, ep, cmd, cmdlen) + SCR *sp; + EXF *ep; + char *cmd; + size_t cmdlen; +{ + CHAR_T vlit; + EX_PRIVATE *exp; + EXCMDARG exc; + EXCMDLIST const *cp; + MARK cur; + recno_t lno, num; + size_t arg1_len, len, save_cmdlen; + long flagoff; + u_int saved_mode; + int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp; + char *arg1, *save_cmd, *p, *t; + + /* Init. */ + nl = 0; +loop: if (nl) { + nl = 0; + ++sp->if_lno; + } + arg1 = NULL; + save_cmdlen = 0; + + /* + * It's possible that we've been interrupted during a + * command. + */ + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + + /* Skip whitespace, separators, newlines. */ + for (; cmdlen > 0; ++cmd, --cmdlen) + if ((ch = *cmd) == '\n') + ++sp->if_lno; + else if (!isblank(ch)) + break; + if (cmdlen == 0) + return (0); + + /* Command lines that start with a double-quote are comments. */ + if (ch == '"') { + while (--cmdlen > 0 && *++cmd != '\n'); + if (*cmd == '\n') { + ++cmd; + --cmdlen; + ++sp->if_lno; + } + goto loop; + } + + /* + * !!! + * Permit extra colons at the start of the line. Historically, + * ex/vi allowed a single extra one. It's simpler not to count. + * The stripping is done here because, historically, any command + * could have preceding colons, e.g. ":g/pattern/:p" worked. + */ + if (ch == ':') + while (--cmdlen > 0 && *++cmd == ':'); + + /* Skip whitespace. */ + for (; cmdlen > 0; ++cmd, --cmdlen) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* The last point at which an empty line means do nothing. */ + if (cmdlen == 0) + return (0); + + /* Initialize the structure passed to underlying functions. */ + memset(&exc, 0, sizeof(EXCMDARG)); + exp = EXP(sp); + if (argv_init(sp, ep, &exc)) + goto err; + + /* Parse command addresses. */ + if (ep_range(sp, ep, &exc, &cmd, &cmdlen)) + goto err; + + /* Skip whitespace. */ + for (; cmdlen > 0; ++cmd, --cmdlen) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* + * If no command, ex does the last specified of p, l, or #, and vi + * moves to the line. Otherwise, determine the length of the command + * name by looking for the first non-alphabetic character. (There + * are a few non-alphabetic characters in command names, but they're + * all single character commands.) This isn't a great test, because + * it means that, for the command ":e +cut.c file", we'll report that + * the command "cut" wasn't known. However, it makes ":e+35 file" work + * correctly. + */ +#define SINGLE_CHAR_COMMANDS "!#&<=>@~" + if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') { + if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) { + p = cmd; + ++cmd; + --cmdlen; + namelen = 1; + } else { + for (p = cmd; cmdlen > 0; --cmdlen, ++cmd) + if (!isalpha(*cmd)) + break; + if ((namelen = cmd - p) == 0) { + msgq(sp, M_ERR, "Unknown command name."); + goto err; + } + } + + /* + * Search the table for the command. + * + * !!! + * Historic vi permitted the mark to immediately follow the + * 'k' in the 'k' command. Make it work. + * + * !!! + * Historic vi permitted pretty much anything to follow the + * substitute command, e.g. "s/e/E/|s|sgc3p" was fine. Make + * it work. + * + * Use of msgq below is safe, command names are all alphabetics. + */ + if ((cp = ex_comm_search(p, namelen)) == NULL) + if (p[0] == 'k' && p[1] && !p[2]) { + cmd -= namelen - 1; + cmdlen += namelen - 1; + cp = &cmds[C_K]; + } else if (p[0] == 's') { + cmd -= namelen - 1; + cmdlen += namelen - 1; + cp = &cmd_subagain; + } else { + msgq(sp, M_ERR, + "The %.*s command is unknown.", namelen, p); + goto err; + } + + /* Some commands are either not implemented or turned off. */ + if (F_ISSET(cp, E_NOPERM)) { + msgq(sp, M_ERR, + "The %s command is not currently supported.", + cp->name); + goto err; + } + + /* Some commands aren't okay in globals. */ + if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) { + msgq(sp, M_ERR, + "The %s command can't be used as part of a global command.", + cp->name); + goto err; + } + + /* + * Multiple < and > characters; another "feature". Note, + * The string passed to the underlying function may not be + * nul terminated in this case. + */ + if ((cp == &cmds[C_SHIFTL] && *p == '<') || + (cp == &cmds[C_SHIFTR] && *p == '>')) { + for (ch = *p; cmdlen > 0; --cmdlen, ++cmd) + if (*cmd != ch) + break; + if (argv_exp0(sp, ep, &exc, p, cmd - p)) + goto err; + } + + /* + * The visual command has a different syntax when called + * from ex than when called from a vi colon command. FMH. + */ + if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp)) + cp = &cmds[C_VISUAL_VI]; + + uselastcmd = 0; + } else { + cp = exp->lastcmd; + uselastcmd = 1; + } + + /* Initialize local flags to the command flags. */ + LF_INIT(cp->flags); + + /* + * File state must be checked throughout this code, because it is + * called when reading the .exrc file and similar things. There's + * this little chicken and egg problem -- if we read the file first, + * we won't know how to display it. If we read/set the exrc stuff + * first, we can't allow any command that requires file state. + * Historic vi generally took the easy way out and dropped core. + */ + if (LF_ISSET(E_NORC) && ep == NULL) { + msgq(sp, M_ERR, + "The %s command requires that a file already have been read in.", + cp->name); + goto err; + } + + /* + * There are three normal termination cases for an ex command. They + * are the end of the string (cmdlen), or unescaped (by literal next + * characters) newline or '|' characters. As we're past any addresses, + * we can now determine how long the command is, so we don't have to + * look for all the possible terminations. There are three exciting + * special cases: + * + * 1: The bang, global, vglobal and the filter versions of the read and + * write commands are delimited by newlines (they can contain shell + * pipes). + * 2: The ex, edit and visual in vi mode commands take ex commands as + * their first arguments. + * 3: The substitute command takes an RE as its first argument, and + * wants it to be specially delimited. + * + * Historically, '|' characters in the first argument of the ex, edit, + * and substitute commands did not delimit the command. And, in the + * filter cases for read and write, and the bang, global and vglobal + * commands, they did not delimit the command at all. + * + * For example, the following commands were legal: + * + * :edit +25|s/abc/ABC/ file.c + * :substitute s/|/PIPE/ + * :read !spell % | columnate + * :global/pattern/p|l + * + * It's not quite as simple as it sounds, however. The command: + * + * :substitute s/a/b/|s/c/d|set + * + * was also legal, i.e. the historic ex parser (using the word loosely, + * since "parser" implies some regularity) delimited the RE's based on + * its delimiter and not anything so irretrievably vulgar as a command + * syntax. + * + * One thing that makes this easier is that we can ignore most of the + * command termination conditions for the commands that want to take + * the command up to the next newline. None of them are legal in .exrc + * files, so if we're here, we only dealing with a single line, and we + * can just eat it. + * + * Anyhow, the following code makes this all work. First, for the + * special cases we move past their special argument. Then, we do + * normal command processing on whatever is left. Barf-O-Rama. + */ + arg1_len = 0; + save_cmd = cmd; + (void)term_key_ch(sp, K_VLNEXT, &vlit); + if (cp == &cmds[C_EDIT] || + cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) { + /* + * Move to the next non-whitespace character. As '+' must + * be the character after the command name, if there isn't + * one, we're done. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + /* + * QUOTING NOTE: + * + * The historic implementation ignored all escape characters + * so there was no way to put a space or newline into the +cmd + * field. We do a simplistic job of fixing it by moving to the + * first whitespace character that isn't escaped by a literal + * next character. The literal next characters are stripped + * as they're no longer useful. + */ + if (cmdlen > 0 && ch == '+') { + ++cmd; + --cmdlen; + for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + ch = *++cmd; + } else if (isblank(ch)) + break; + *p++ = ch; + } + arg1_len = cmd - arg1; + + /* Reset, so the first argument isn't reparsed. */ + save_cmd = cmd; + } + } else if (cp == &cmds[C_BANG] || + cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) { + cmd += cmdlen; + cmdlen = 0; + } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) { + /* + * Move to the next character. If it's a '!', it's a filter + * command and we want to eat it all, otherwise, we're done. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen > 0 && ch == '!') { + cmd += cmdlen; + cmdlen = 0; + } + } else if (cp == &cmds[C_SUBSTITUTE]) { + /* + * Move to the next non-whitespace character, we'll use it as + * the delimiter. If the character isn't an alphanumeric or + * a '|', it's the delimiter, so parse it. Otherwise, we're + * into something like ":s g", so use the special substitute + * command. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) + if (!isblank(cmd[0])) + break; + + if (isalnum(cmd[0]) || cmd[0] == '|') + cp = &cmd_subagain; + else if (cmdlen > 0) { + /* + * QUOTING NOTE: + * + * Backslashes quote delimiter characters for RE's. + * The backslashes are NOT removed since they'll be + * used by the RE code. Move to the third delimiter + * that's not escaped (or the end of the command). + */ + delim = *cmd; + ++cmd; + --cmdlen; + for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd) + if (cmd[0] == '\\' && cmdlen > 1) { + ++cmd; + --cmdlen; + } else if (cmd[0] == delim) + --cnt; + } + } + /* + * Use normal quoting and termination rules to find the end + * of this command. + * + * QUOTING NOTE: + * + * Historically, vi permitted ^V's to escape <newline>'s in the .exrc + * file. It was almost certainly a bug, but that's what bug-for-bug + * compatibility means, Grasshopper. Also, ^V's escape the command + * delimiters. Literal next quote characters in front of the newlines, + * '|' characters or literal next characters are stripped as as they're + * no longer useful. + */ + for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = cmd[0]) == vlit && cmdlen > 1) { + ch = cmd[1]; + if (ch == '\n' || ch == '|') { + if (ch == '\n') + ++sp->if_lno; + --cmdlen; + ++cmd; + ++cnt; + } else + ch = vlit; + } else if (ch == '\n' || ch == '|') { + if (ch == '\n') + nl = 1; + --cmdlen; + break; + } + *p++ = ch; + } + + /* + * Save off the next command information, go back to the + * original start of the command. + */ + p = cmd + 1; + cmd = save_cmd; + save_cmd = p; + save_cmdlen = cmdlen; + cmdlen = ((save_cmd - cmd) - 1) - cnt; + + /* + * !!! + * The "set tags" command historically used a backslash, not the + * user's literal next character, to escape whitespace. Handle + * it here instead of complicating the argv_exp3() code. Note, + * this isn't a particularly complex trap, and if backslashes were + * legal in set commands, this would have to be much more complicated. + */ + if (cp == &cmds[C_SET]) + for (p = cmd, len = cmdlen; len > 0; --len, ++p) + if (*p == '\\') + *p = vlit; + + /* + * Set the default addresses. It's an error to specify an address for + * a command that doesn't take them. If two addresses are specified + * for a command that only takes one, lose the first one. Two special + * cases here, some commands take 0 or 2 addresses. For most of them + * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one + * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines. + * + * Also, if the file is empty, some commands want to use an address of + * 0, i.e. the entire file is 0 to 0, and the default first address is + * 0. Otherwise, an entire file is 1 to N and the default line is 1. + * Note, we also add the E_ZERO flag to the command flags, for the case + * where the 0 address is only valid if it's a default address. + * + * Also, set a flag if we set the default addresses. Some commands + * (ex: z) care if the user specified an address of if we just used + * the current cursor. + */ + switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) { + case E_ADDR1: /* One address: */ + switch (exc.addrcnt) { + case 0: /* Default cursor/empty file. */ + exc.addrcnt = 1; + F_SET(&exc, E_ADDRDEF); + if (LF_ISSET(E_ZERODEF)) { + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + exc.addr1.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = sp->lno; + } else + exc.addr1.lno = sp->lno; + exc.addr1.cno = sp->cno; + break; + case 1: + break; + case 2: /* Lose the first address. */ + exc.addrcnt = 1; + exc.addr1 = exc.addr2; + } + break; + case E_ADDR2_NONE: /* Zero/two addresses: */ + if (exc.addrcnt == 0) /* Default to nothing. */ + break; + goto two; + case E_ADDR2_ALL: /* Zero/two addresses: */ + if (exc.addrcnt == 0) { /* Default entire/empty file. */ + exc.addrcnt = 2; + F_SET(&exc, E_ADDRDEF); + if (file_lline(sp, ep, &exc.addr2.lno)) + goto err; + if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) { + exc.addr1.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = 1; + exc.addr1.cno = exc.addr2.cno = 0; + F_SET(&exc, E_ADDR2_ALL); + break; + } + /* FALLTHROUGH */ + case E_ADDR2: /* Two addresses: */ +two: switch (exc.addrcnt) { + case 0: /* Default cursor/empty file. */ + exc.addrcnt = 2; + F_SET(&exc, E_ADDRDEF); + if (LF_ISSET(E_ZERODEF) && sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + exc.addr1.lno = exc.addr2.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = exc.addr2.lno = sp->lno; + } else + exc.addr1.lno = exc.addr2.lno = sp->lno; + exc.addr1.cno = exc.addr2.cno = sp->cno; + break; + case 1: /* Default to first address. */ + exc.addrcnt = 2; + exc.addr2 = exc.addr1; + break; + case 2: + break; + } + break; + default: + if (exc.addrcnt) /* Error. */ + goto usage; + } + + flagoff = 0; + for (p = cp->syntax; *p != '\0'; ++p) { + /* + * The write command is sensitive to leading whitespace, e.g. + * "write !" is different from "write!". If not the write + * command, skip leading whitespace. + */ + if (cp != &cmds[C_WRITE]) + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* + * Quit when reach the end of the command, unless it's a + * command that does its own parsing, in which case we want + * to build a reasonable argv for it. This code guarantees + * that there will be an argv when the function gets called, + * so the correct test is for a length of 0, not for the + * argc > 0. + */ + if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's') + break; + + switch (*p) { + case '!': /* ! */ + if (*cmd == '!') { + ++cmd; + --cmdlen; + F_SET(&exc, E_FORCE); + } + break; + case '1': /* +, -, #, l, p */ + /* + * !!! + * Historically, some flags were ignored depending + * on where they occurred in the command line. For + * example, in the command, ":3+++p--#", historic vi + * acted on the '#' flag, but ignored the '-' flags. + * It's unambiguous what the flags mean, so we just + * handle them regardless of the stupidity of their + * location. + */ + for (; cmdlen; --cmdlen, ++cmd) + switch (*cmd) { + case '+': + ++flagoff; + break; + case '-': + --flagoff; + break; + case '#': + F_SET(&exc, E_F_HASH); + break; + case 'l': + F_SET(&exc, E_F_LIST); + break; + case 'p': + F_SET(&exc, E_F_PRINT); + break; + default: + goto end1; + } +end1: break; + case '2': /* -, ., +, ^ */ + case '3': /* -, ., +, ^, = */ + for (; cmdlen; --cmdlen, ++cmd) + switch (*cmd) { + case '-': + F_SET(&exc, E_F_DASH); + break; + case '.': + F_SET(&exc, E_F_DOT); + break; + case '+': + F_SET(&exc, E_F_PLUS); + break; + case '^': + F_SET(&exc, E_F_CARAT); + break; + case '=': + if (*p == '3') { + F_SET(&exc, E_F_EQUAL); + break; + } + /* FALLTHROUGH */ + default: + goto end2; + } +end2: break; + case 'b': /* buffer */ + /* + * Digits can't be buffer names in ex commands, or the + * command "d2" would be a delete into buffer '2', and + * not a two-line deletion. + */ + if (!isdigit(cmd[0])) { + exc.buffer = *cmd; + ++cmd; + --cmdlen; + F_SET(&exc, E_BUFFER); + } + break; + case 'c': /* count [01+a] */ + ++p; + if (!isdigit(*cmd) && + (*p != '+' || (*cmd != '+' && *cmd != '-'))) + break; +/* 8-bit XXX */ if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') { + msgq(sp, M_ERR, "Count may not be zero."); + goto err; + } + cmdlen -= (t - cmd); + cmd = t; + /* + * Count as address offsets occur in commands taking + * two addresses. Historic vi practice was to use + * the count as an offset from the *second* address. + * + * Set a count flag; some underlying commands (see + * join) do different things with counts than with + * line addresses. + */ + if (*p == 'a') { + exc.addr1 = exc.addr2; + exc.addr2.lno = exc.addr1.lno + lno - 1; + } else + exc.count = lno; + F_SET(&exc, E_COUNT); + break; + case 'f': /* file */ + if (argv_exp2(sp, ep, + &exc, cmd, cmdlen, cp == &cmds[C_BANG])) + goto err; + goto countchk; + case 'l': /* line */ + if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp)) + goto err; + /* Line specifications are always required. */ + if (!tmp) { + msgq(sp, M_ERR, + "%s: bad line specification", cmd); + goto err; + } + exc.lineno = cur.lno; + break; + case 'S': /* string, file exp. */ + if (argv_exp1(sp, ep, + &exc, cmd, cmdlen, cp == &cmds[C_BANG])) + goto err; + goto addr2; + case 's': /* string */ + if (argv_exp0(sp, ep, &exc, cmd, cmdlen)) + goto err; + goto addr2; + case 'W': /* word string */ + /* + * QUOTING NOTE: + * + * Literal next characters escape the following + * character. Quoting characters are stripped + * here since they are no longer useful. + * + * First there was the word. + */ + for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + *p++ = *++cmd; + } else if (isblank(ch)) { + ++cmd; + --cmdlen; + break; + } else + *p++ = ch; + } + if (argv_exp0(sp, ep, &exc, t, p - t)) + goto err; + + /* Delete intervening whitespace. */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen == 0) + goto usage; + + /* Followed by the string. */ + for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p) + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + *p = *++cmd; + } else + *p = ch; + if (argv_exp0(sp, ep, &exc, t, p - t)) + goto err; + goto addr2; + case 'w': /* word */ + if (argv_exp3(sp, ep, &exc, cmd, cmdlen)) + goto err; +countchk: if (*++p != 'N') { /* N */ + /* + * If a number is specified, must either be + * 0 or that number, if optional, and that + * number, if required. + */ + num = *p - '0'; + if ((*++p != 'o' || exp->argsoff != 0) && + exp->argsoff != num) + goto usage; + } + goto addr2; + default: + msgq(sp, M_ERR, + "Internal syntax table error (%s: %c).", + cp->name, *p); + } + } + + /* Skip trailing whitespace. */ + for (; cmdlen; --cmdlen) { + ch = *cmd++; + if (!isblank(ch)) + break; + } + + /* + * There shouldn't be anything left, and no more required + * fields, i.e neither 'l' or 'r' in the syntax string. + */ + if (cmdlen || strpbrk(p, "lr")) { +usage: msgq(sp, M_ERR, "Usage: %s.", cp->usage); + goto err; + } + + /* Verify that the addresses are legal. */ +addr2: switch (exc.addrcnt) { + case 2: + if (file_lline(sp, ep, &lno)) + goto err; + /* + * Historic ex/vi permitted commands with counts to go past + * EOF. So, for example, if the file only had 5 lines, the + * ex command "1,6>" would fail, but the command ">300" + * would succeed. Since we don't want to have to make all + * of the underlying commands handle random line numbers, + * fix it here. + */ + if (exc.addr2.lno > lno) + if (F_ISSET(&exc, E_COUNT)) + exc.addr2.lno = lno; + else { + if (lno == 0) + msgq(sp, M_ERR, "The file is empty."); + else + msgq(sp, M_ERR, + "Only %lu line%s in the file", + lno, lno > 1 ? "s" : ""); + goto err; + } + /* FALLTHROUGH */ + case 1: + num = exc.addr1.lno; + /* + * If it's a "default vi command", zero is okay. Historic + * vi allowed this, note, it's also the hack that allows + * "vi + nonexistent_file" to work. + */ + if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) && + !LF_ISSET(E_ZERO)) { + msgq(sp, M_ERR, + "The %s command doesn't permit an address of 0.", + cp->name); + goto err; + } + if (file_lline(sp, ep, &lno)) + goto err; + if (num > lno) { + if (lno == 0) + msgq(sp, M_ERR, "The file is empty."); + else + msgq(sp, M_ERR, "Only %lu line%s in the file", + lno, lno > 1 ? "s" : ""); + goto err; + } + break; + } + + /* If doing a default command, vi just moves to the line. */ + if (IN_VI_MODE(sp) && uselastcmd) { + switch (exc.addrcnt) { + case 2: + sp->lno = exc.addr2.lno ? exc.addr2.lno : 1; + sp->cno = exc.addr2.cno; + break; + case 1: + sp->lno = exc.addr1.lno ? exc.addr1.lno : 1; + sp->cno = exc.addr1.cno; + break; + } + cmd = save_cmd; + cmdlen = save_cmdlen; + goto loop; + } + + /* Reset "last" command. */ + if (LF_ISSET(E_SETLAST)) + exp->lastcmd = cp; + + /* Final setup for the command. */ + exc.cmd = cp; + +#if defined(DEBUG) && 0 + TRACE(sp, "ex_cmd: %s", exc.cmd->name); + if (exc.addrcnt > 0) { + TRACE(sp, "\taddr1 %d", exc.addr1.lno); + if (exc.addrcnt > 1) + TRACE(sp, " addr2: %d", exc.addr2.lno); + TRACE(sp, "\n"); + } + if (exc.lineno) + TRACE(sp, "\tlineno %d", exc.lineno); + if (exc.flags) + TRACE(sp, "\tflags %0x", exc.flags); + if (F_ISSET(&exc, E_BUFFER)) + TRACE(sp, "\tbuffer %c", exc.buffer); + TRACE(sp, "\n"); + if (exc.argc) { + for (cnt = 0; cnt < exc.argc; ++cnt) + TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]); + TRACE(sp, "\n"); + } +#endif + /* Clear autoprint flag. */ + F_CLR(exp, EX_AUTOPRINT); + + /* Increment the command count if not called from vi. */ + if (!IN_VI_MODE(sp)) + ++sp->ccnt; + + /* + * If file state and not doing a global command, log the start of + * an action. + */ + if (ep != NULL && !F_ISSET(sp, S_GLOBAL)) + (void)log_cursor(sp, ep); + + /* Save the current mode. */ + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + + /* Do the command. */ + if ((cp->fn)(sp, ep, &exc)) + goto err; + +#ifdef DEBUG + /* Make sure no function left the temporary space locked. */ + if (F_ISSET(sp->gp, G_TMP_INUSE)) { + F_CLR(sp->gp, G_TMP_INUSE); + msgq(sp, M_ERR, "Error: ex: temporary buffer not released."); + goto err; + } +#endif + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) { + /* + * Only here if the mode of the underlying file changed, e.g. + * the user switched files or is exiting. There are two things + * that we might have to save. First, any "+cmd" field set up + * for an ex/edit command will have to be saved for later, also, + * any not yet executed part of the current ex command. + * + * :edit +25 file.c|s/abc/ABC/|1 + * + * for example. + * + * The historic vi just hung, of course; we handle by + * pushing the keys onto the tty queue. If we're + * switching modes to vi, since the commands are intended + * as ex commands, add the extra characters to make it + * work. + * + * For the fun of it, if you want to see if a vi clone got + * the ex argument parsing right, try: + * + * echo 'foo|bar' > file1; echo 'foo/bar' > file2; + * vi + * :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq + */ + if (arg1_len == NULL && save_cmdlen == 0) + return (0); + if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0)) + goto err; + if (save_cmdlen != 0) + if (term_push(sp, save_cmd, save_cmdlen, 0, 0)) + goto err; + if (arg1 != NULL) { + if (IN_VI_MODE(sp) && save_cmdlen != 0 && + term_push(sp, "|", 1, 0, 0)) + goto err; + if (term_push(sp, arg1, arg1_len, 0, 0)) + goto err; + } + if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0)) + goto err; + return (0); + } + + if (IN_EX_MODE(sp) && ep != NULL) { + /* + * The print commands have already handled the `print' flags. + * If so, clear them. Don't return, autoprint may still have + * stuff to print out. + */ + if (LF_ISSET(E_F_PRCLEAR)) + F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT); + + /* + * If the command was successful, and there was an explicit + * flag to display the new cursor line, or we're in ex mode, + * autoprint is set, and a change was made, display the line. + */ + if (flagoff) { + if (flagoff < 0) { + if (sp->lno < -flagoff) { + msgq(sp, M_ERR, + "Flag offset before line 1."); + goto err; + } + } else { + if (file_lline(sp, ep, &lno)) + goto err; + if (sp->lno + flagoff > lno) { + msgq(sp, M_ERR, + "Flag offset past end-of-file."); + goto err; + } + } + sp->lno += flagoff; + } + + if (O_ISSET(sp, O_AUTOPRINT) && + (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT))) + LF_INIT(E_F_PRINT); + else + LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT)); + + memset(&exc, 0, sizeof(EXCMDARG)); + exc.addrcnt = 2; + exc.addr1.lno = exc.addr2.lno = sp->lno; + exc.addr1.cno = exc.addr2.cno = sp->cno; + switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) { + case E_F_HASH: + exc.cmd = &cmds[C_HASH]; + ex_number(sp, ep, &exc); + break; + case E_F_LIST: + exc.cmd = &cmds[C_LIST]; + ex_list(sp, ep, &exc); + break; + case E_F_PRINT: + exc.cmd = &cmds[C_PRINT]; + ex_pr(sp, ep, &exc); + break; + } + } + + cmd = save_cmd; + cmdlen = save_cmdlen; + goto loop; + /* NOTREACHED */ + + /* + * On error, we discard any keys we have left, as well as any keys + * that were mapped. The test of save_cmdlen isn't necessarily + * correct. If we fail early enough we don't know if the entire + * string was a single command or not. Try and guess, it's useful + * to know if part of the command was discarded. + */ +err: if (save_cmdlen == 0) + for (; cmdlen; --cmdlen) + if ((ch = *cmd++) == vlit && cmdlen > 1) { + --cmdlen; + ++cmd; + } else if (ch == '\n' || ch == '|') { + if (cmdlen > 1) + save_cmdlen = 1; + break; + } + if (save_cmdlen != 0) + msgq(sp, M_ERR, + "Ex command failed: remaining command input discarded."); + term_map_flush(sp, "Ex command failed"); + return (1); +} + +/* + * ep_range -- + * Get a line range for ex commands. + */ +static int +ep_range(sp, ep, excp, cmdp, cmdlenp) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char **cmdp; + size_t *cmdlenp; +{ + MARK cur, savecursor; + size_t cmdlen; + int savecursor_set, tmp; + char *cmd; + + /* Percent character is all lines in the file. */ + cmd = *cmdp; + cmdlen = *cmdlenp; + if (*cmd == '%') { + excp->addr1.lno = 1; + if (file_lline(sp, ep, &excp->addr2.lno)) + return (1); + + /* If an empty file, then the first line is 0, not 1. */ + if (excp->addr2.lno == 0) + excp->addr1.lno = 0; + excp->addr1.cno = excp->addr2.cno = 0; + excp->addrcnt = 2; + + ++*cmdp; + --*cmdlenp; + return (0); + } + + /* Parse comma or semi-colon delimited line specs. */ + for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;) + switch (*cmd) { + case ';': /* Semi-colon delimiter. */ + /* + * Comma delimiters delimit; semi-colon delimiters + * change the current address for the 2nd address + * to be the first address. Trailing or multiple + * delimiters are discarded. + */ + if (excp->addrcnt == 0) + goto done; + if (!savecursor_set) { + savecursor.lno = sp->lno; + savecursor.cno = sp->cno; + sp->lno = excp->addr1.lno; + sp->cno = excp->addr1.cno; + savecursor_set = 1; + } + ++cmd; + --cmdlen; + break; + case ',': /* Comma delimiter. */ + /* If no addresses yet, defaults to ".". */ + if (excp->addrcnt == 0) { + excp->addr1.lno = sp->lno; + excp->addr1.cno = sp->cno; + excp->addrcnt = 1; + } + /* FALLTHROUGH */ + case ' ': /* Whitespace. */ + case '\t': /* Whitespace. */ + ++cmd; + --cmdlen; + break; + default: + if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp)) + return (1); + if (!tmp) + goto done; + + /* + * Extra addresses are discarded, starting with + * the first. + */ + switch (excp->addrcnt) { + case 0: + excp->addr1 = cur; + excp->addrcnt = 1; + break; + case 1: + excp->addr2 = cur; + excp->addrcnt = 2; + break; + case 2: + excp->addr1 = excp->addr2; + excp->addr2 = cur; + break; + } + break; + } + + /* + * XXX + * This is probably not right behavior for savecursor -- need + * to figure out what the historical ex did for ";,;,;5p" or + * similar stupidity. + */ +done: if (savecursor_set) { + sp->lno = savecursor.lno; + sp->cno = savecursor.cno; + } + if (excp->addrcnt == 2 && + (excp->addr2.lno < excp->addr1.lno || + excp->addr2.lno == excp->addr1.lno && + excp->addr2.cno < excp->addr1.cno)) { + msgq(sp, M_ERR, + "The second address is smaller than the first."); + return (1); + } + *cmdp = cmd; + *cmdlenp = cmdlen; + return (0); +} + +/* + * Get a single line address specifier. + */ +static int +ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found) + SCR *sp; + EXF *ep; + MARK *cur; + char **cmdp; + size_t *cmdlenp; + int *addr_found; +{ + MARK m, *mp; + long total; + u_int flags; + size_t cmdlen; + char *cmd, *endp; + + *addr_found = 0; + + cmd = *cmdp; + cmdlen = *cmdlenp; + switch (*cmd) { + case '$': /* Last line in the file. */ + *addr_found = 1; + cur->cno = 0; + if (file_lline(sp, ep, &cur->lno)) + return (1); + ++cmd; + --cmdlen; + break; /* Absolute line number. */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *addr_found = 1; + /* + * The way the vi "previous context" mark worked was that + * "non-relative" motions set it. While vi wasn't totally + * consistent about this, ANY numeric address was considered + * non-relative, and set the value. Which is why we're + * hacking marks down here. + */ + if (IN_VI_MODE(sp)) { + m.lno = sp->lno; + m.cno = sp->cno; + if (mark_set(sp, ep, ABSMARK1, &m, 1)) + return (1); + } + cur->cno = 0; +/* 8-bit XXX */ cur->lno = strtol(cmd, &endp, 10); + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '\'': /* Use a mark. */ + *addr_found = 1; + if (cmdlen == 1) { + msgq(sp, M_ERR, "No mark name supplied."); + return (1); + } + if ((mp = mark_get(sp, ep, cmd[1])) == NULL) + return (1); + *cur = *mp; + cmd += 2; + cmdlen -= 2; + break; + case '\\': /* Search: forward/backward. */ + /* + * !!! + * I can't find any difference between // and \/ or + * between ?? and \?. Mark Horton doesn't remember + * there being any difference. C'est la vie. + */ + if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') { + msgq(sp, M_ERR, "\\ not followed by / or ?."); + return (1); + } + ++cmd; + --cmdlen; + if (cmd[0] == '/') + goto forward; + if (cmd[0] == '?') + goto backward; + /* NOTREACHED */ + case '/': /* Search forward. */ +forward: *addr_found = 1; + m.lno = sp->lno; + m.cno = sp->cno; + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET; + if (f_search(sp, ep, &m, &m, cmd, &endp, &flags)) + return (1); + cur->lno = m.lno; + cur->cno = m.cno; + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '?': /* Search backward. */ +backward: *addr_found = 1; + m.lno = sp->lno; + m.cno = sp->cno; + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET; + if (b_search(sp, ep, &m, &m, cmd, &endp, &flags)) + return (1); + cur->lno = m.lno; + cur->cno = m.cno; + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '.': /* Current position. */ + *addr_found = 1; + cur->cno = sp->cno; + + /* If an empty file, then '.' is 0, not 1. */ + if (sp->lno == 1) { + if (file_lline(sp, ep, &cur->lno)) + return (1); + if (cur->lno != 0) + cur->lno = 1; + } else + cur->lno = sp->lno; + ++cmd; + --cmdlen; + break; + } + + /* + * Evaluate any offset. Offsets are +/- any number, or any number + * of +/- signs, or any combination thereof. If no address found + * yet, offset is relative to ".". + */ + for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) { + if (!*addr_found) { + cur->lno = sp->lno; + cur->cno = sp->cno; + *addr_found = 1; + } + + if (cmdlen > 1 && isdigit(cmd[1])) { +/* 8-bit XXX */ total += strtol(cmd, &endp, 10); + cmdlen -= (endp - cmd); + cmd = endp; + } else { + total += cmd[0] == '-' ? -1 : 1; + --cmdlen; + ++cmd; + } + } + if (total < 0 && -total > cur->lno) { + msgq(sp, M_ERR, "Reference to a line number less than 0."); + return (1); + } + cur->lno += total; + + *cmdp = cmd; + *cmdlenp = cmdlen; + return (0); +} + +/* + * ex_is_abbrev - + * The vi text input routine needs to know if ex thinks this is + * an [un]abbreviate command, so it can turn off abbreviations. + * Usual ranting in the vi/v_ntext:txt_abbrev() routine. + */ +int +ex_is_abbrev(name, len) + char *name; + size_t len; +{ + EXCMDLIST const *cp; + + return ((cp = ex_comm_search(name, len)) != NULL && + (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE])); +} + +static inline EXCMDLIST const * +ex_comm_search(name, len) + char *name; + size_t len; +{ + EXCMDLIST const *cp; + + for (cp = cmds; cp->name != NULL; ++cp) { + if (cp->name[0] > name[0]) + return (NULL); + if (cp->name[0] != name[0]) + continue; + if (!memcmp(name, cp->name, len)) + return (cp); + } + return (NULL); +} diff --git a/usr.bin/vi/nex/ex_abbrev.c b/usr.bin/vi/nex/ex_abbrev.c new file mode 100644 index 000000000000..f28cbe45e490 --- /dev/null +++ b/usr.bin/vi/nex/ex_abbrev.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_abbrev.c 8.6 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * ex_abbr -- :abbreviate [key replacement] + * Create an abbreviation or display abbreviations. + */ +int +ex_abbr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch (cmdp->argc) { + case 0: + if (seq_dump(sp, SEQ_ABBREV, 0) == 0) + msgq(sp, M_INFO, "No abbreviations to display."); + return (0); + case 2: + break; + default: + abort(); + } + + if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len, + cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, 1)) + return (1); + F_SET(sp->gp, G_ABBREV); + return (0); +} + +/* + * ex_unabbr -- :unabbreviate key + * Delete an abbreviation. + */ +int +ex_unabbr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + + ap = cmdp->argv[0]; + if (!F_ISSET(sp->gp, G_ABBREV) || + seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) { + msgq(sp, M_ERR, "\"%s\" is not an abbreviation.", ap->bp); + return (1); + } + return (0); +} + +/* + * abbr_save -- + * Save the abbreviation sequences to a file. + */ +int +abbr_save(sp, fp) + SCR *sp; + FILE *fp; +{ + return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV)); +} diff --git a/usr.bin/vi/nex/ex_append.c b/usr.bin/vi/nex/ex_append.c new file mode 100644 index 000000000000..c6edfd57db3b --- /dev/null +++ b/usr.bin/vi/nex/ex_append.c @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_append.c 8.6 (Berkeley) 11/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +enum which {APPEND, CHANGE, INSERT}; + +static int aci __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_append -- :[line] a[ppend][!] + * Append one or more lines of new text after the specified line, + * or the current line if no address is specified. + */ +int +ex_append(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, APPEND)); +} + +/* + * ex_change -- :[line[,line]] c[hange][!] [count] + * Change one or more lines to the input text. + */ +int +ex_change(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, CHANGE)); +} + +/* + * ex_insert -- :[line] i[nsert][!] + * Insert one or more lines of new text before the specified line, + * or the current line if no address is specified. + */ +int +ex_insert(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, INSERT)); +} + +static int +aci(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + MARK m; + TEXT *tp; + recno_t cnt; + int rval, aiset; + + /* + * The ! flag turns off autoindent for append, change and + * insert. + */ + if (F_ISSET(cmdp, E_FORCE)) { + aiset = O_ISSET(sp, O_AUTOINDENT); + O_CLR(sp, O_AUTOINDENT); + } else + aiset = 0; + + /* + * If doing a change, replace lines as long as possible. + * Then, append more lines or delete remaining lines. + * Inserts are the same as appends to the previous line. + */ + rval = 0; + m = cmdp->addr1; + if (cmd == INSERT) { + --m.lno; + cmd = APPEND; + } + if (cmd == CHANGE) + for (;; ++m.lno) { + if (m.lno > cmdp->addr2.lno) { + cmd = APPEND; + --m.lno; + break; + } + switch (sp->s_get(sp, ep, &sp->tiq, 0, + TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) { + case INP_OK: + break; + case INP_EOF: + case INP_ERR: + rval = 1; + goto done; + } + tp = sp->tiq.cqh_first; + if (tp->len == 1 && tp->lb[0] == '.') { + for (cnt = + (cmdp->addr2.lno - m.lno) + 1; cnt--;) + if (file_dline(sp, ep, m.lno)) { + rval = 1; + goto done; + } + goto done; + } + if (file_sline(sp, ep, m.lno, tp->lb, tp->len)) { + rval = 1; + goto done; + } + } + + if (cmd == APPEND) + for (;; ++m.lno) { + switch (sp->s_get(sp, ep, &sp->tiq, 0, + TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) { + case INP_OK: + break; + case INP_EOF: + case INP_ERR: + rval = 1; + goto done; + } + tp = sp->tiq.cqh_first; + if (tp->len == 1 && tp->lb[0] == '.') + break; + if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) { + rval = 1; + goto done; + } + } + +done: if (rval == 0) + sp->lno = m.lno; + + if (aiset) + O_SET(sp, O_AUTOINDENT); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_args.c b/usr.bin/vi/nex/ex_args.c new file mode 100644 index 000000000000..2f96df727dd8 --- /dev/null +++ b/usr.bin/vi/nex/ex_args.c @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_args.c 8.13 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_next -- :next [files] + * Edit the next file, optionally setting the list of files. + * + * !!! + * The :next command behaved differently from the :rewind command in + * historic vi. See nvi/docs/autowrite for details, but the basic + * idea was that it ignored the force flag if the autowrite flag was + * set. This implementation handles them all identically. + */ +int +ex_next(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS **argv; + FREF *frp; + char *name; + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if (cmdp->argc) { + /* Mark all the current files as ignored. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + F_SET(frp, FR_IGNORE); + + /* Add the new files into the file list. */ + for (argv = cmdp->argv; argv[0]->len != 0; ++argv) + if (file_add(sp, NULL, argv[0]->bp, 0) == NULL) + return (1); + + if ((frp = file_first(sp)) == NULL) + return (1); + } else if ((frp = file_next(sp, sp->a_frp)) == NULL) { + msgq(sp, M_ERR, "No more files to edit."); + return (1); + } + + /* + * There's a tricky sequence, where the user edits two files, e.g. + * "x" and "y". While in "x", they do ":e y|:f foo", which changes + * the name of that FRP entry. Then, the :n command finds the file + * "y" with a name change. If the file name has been changed, get + * a new FREF for the original file name, and make it be the one that + * is displayed in the argument list, not the one with the name change. + */ + if (frp->cname != NULL) { + F_SET(frp, FR_IGNORE); + name = frp->name == NULL ? frp->tname : frp->name; + if ((frp = file_add(sp, sp->a_frp, name, 0)) == NULL) + return (1); + } + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_prev -- :prev + * Edit the previous file. + */ +int +ex_prev(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + char *name; + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if ((frp = file_prev(sp, sp->a_frp)) == NULL) { + msgq(sp, M_ERR, "No previous files to edit."); + return (1); + } + + /* See comment in ex_next(). */ + if (frp->cname != NULL) { + F_SET(frp, FR_IGNORE); + name = frp->name == NULL ? frp->tname : frp->name; + if ((frp = file_add(sp, frp, name, 0)) == NULL) + return (1); + } + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_rew -- :rew + * Re-edit the list of files. + */ +int +ex_rew(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp, *tfrp; + + /* + * !!! + * Historic practice -- you can rewind to the current file. + */ + if ((frp = file_first(sp)) == NULL) { + msgq(sp, M_ERR, "No previous files to rewind."); + return (1); + } + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + /* + * !!! + * Historic practice, turn off the edited bit. The :next and :prev + * code will discard any name changes, so ignore them here. Start + * at the beginning of the file, too. + */ + for (tfrp = sp->frefq.cqh_first; + tfrp != (FREF *)&sp->frefq; tfrp = tfrp->q.cqe_next) + F_CLR(tfrp, FR_CHANGEWRITE | FR_CURSORSET | FR_EDITED); + + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_args -- :args + * Display the list of files. + */ +int +ex_args(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + int cnt, col, iscur, len, nlen, sep; + char *name; + + /* + * !!! + * Ignore files that aren't in the "argument" list unless they are the + * one we're currently editing. I'm not sure this is right, but the + * historic vi behavior of not showing the current file if it was the + * result of a ":e" command, or if the file name was changed was wrong. + * This is actually pretty tricky, don't modify it without thinking it + * through. There have been a lot of problems in here. + * + * Also, historic practice was to display the original name of the file + * even if the user had used a file command to change the file name. + * Confusing, at best. We show both names: the original as that's what + * the user will get in a next, prev or rewind, and the new one since + * that's what the user is actually editing now. + * + * When we find the "argument" FREF, i.e. the current location in the + * user's argument list, if it's not the same as the current FREF, we + * display the current FREF as following the argument in the list. + * This means that if the user edits three files, "x", "y" and "z", and + * then does a :e command in the file "x" to edit "z", "z" will appear + * in the list twice. + */ + col = len = sep = 0; + for (cnt = 1, frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) { + iscur = 0; + /* + * If the last argument FREF structure, and we're editing + * it, set the current bit. Otherwise, we'll display it, + * then the file we're editing, and the latter will have + * the current bit set. + */ + if (frp == sp->a_frp) { + if (frp == sp->frp && frp->cname == NULL) + iscur = 1; + } else if (F_ISSET(frp, FR_IGNORE)) + continue; + name = frp->name == NULL ? frp->tname : frp->name; + /* + * Mistake. The user edited a temporary file (vi /tmp), then + * switched to another file (:e file). The argument FREF is + * pointing to the temporary file, but it doesn't have a name. + * Gracefully recover through the creative use of goto's. + */ + if (name == NULL) + goto testcur; +extra: nlen = strlen(name); + col += len = nlen + sep + (iscur ? 2 : 0); + if (col >= sp->cols - 1) { + col = len; + sep = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else if (cnt != 1) { + sep = 1; + (void)ex_printf(EXCOOKIE, " "); + } + ++cnt; + + if (iscur) + (void)ex_printf(EXCOOKIE, "[%s]", name); + else { + (void)ex_printf(EXCOOKIE, "%s", name); +testcur: if (frp == sp->a_frp) { + if (frp != sp->frp) + name = FILENAME(sp->frp); + else + name = frp->cname; + iscur = 1; + goto extra; + } + } + } + /* This should never happen; left in because it's been known to. */ + if (cnt == 1) + (void)ex_printf(EXCOOKIE, "No files.\n"); + else + (void)ex_printf(EXCOOKIE, "\n"); + return (0); +} diff --git a/usr.bin/vi/nex/ex_argv.c b/usr.bin/vi/nex/ex_argv.c new file mode 100644 index 000000000000..6ad76c61dfbc --- /dev/null +++ b/usr.bin/vi/nex/ex_argv.c @@ -0,0 +1,575 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_argv.c 8.26 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +static int argv_alloc __P((SCR *, size_t)); +static int argv_fexp __P((SCR *, EXCMDARG *, + char *, size_t, char *, size_t *, char **, size_t *, int)); +static int argv_sexp __P((SCR *, char **, size_t *, size_t *)); + +/* + * argv_init -- + * Build a prototype arguments list. + */ +int +argv_init(sp, ep, excp) + SCR *sp; + EXF *ep; + EXCMDARG *excp; +{ + EX_PRIVATE *exp; + + exp = EXP(sp); + exp->argsoff = 0; + argv_alloc(sp, 1); + + excp->argv = exp->args; + excp->argc = exp->argsoff; + return (0); +} + +/* + * argv_exp0 -- + * Put a string into an argv. + */ +int +argv_exp0(sp, ep, excp, cmd, cmdlen) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; +{ + EX_PRIVATE *exp; + + exp = EXP(sp); + argv_alloc(sp, cmdlen); + memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen); + exp->args[exp->argsoff]->bp[cmdlen] = '\0'; + exp->args[exp->argsoff]->len = cmdlen; + ++exp->argsoff; + excp->argv = exp->args; + excp->argc = exp->argsoff; + return (0); +} + +/* + * argv_exp1 -- + * Do file name expansion on a string, and leave it in a string. + */ +int +argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; + int is_bang; +{ + EX_PRIVATE *exp; + size_t blen, len; + char *bp; + + GET_SPACE_RET(sp, bp, blen, 512); + + len = 0; + exp = EXP(sp); + if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { + FREE_SPACE(sp, bp, blen); + return (1); + } + + argv_alloc(sp, len); + memmove(exp->args[exp->argsoff]->bp, bp, len); + exp->args[exp->argsoff]->bp[len] = '\0'; + exp->args[exp->argsoff]->len = len; + ++exp->argsoff; + excp->argv = exp->args; + excp->argc = exp->argsoff; + + FREE_SPACE(sp, bp, blen); + return (0); +} + +/* + * argv_exp2 -- + * Do file name and shell expansion on a string, and break + * it up into an argv. + */ +int +argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; + int is_bang; +{ + size_t blen, len, n; + int rval; + char *bp, *p; + + GET_SPACE_RET(sp, bp, blen, 512); + +#define SHELLECHO "echo " +#define SHELLOFFSET (sizeof(SHELLECHO) - 1) + memmove(bp, SHELLECHO, SHELLOFFSET); + p = bp + SHELLOFFSET; + len = SHELLOFFSET; + +#if defined(DEBUG) && 0 + TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); +#endif + + if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) { + rval = 1; + goto err; + } + +#if defined(DEBUG) && 0 + TRACE(sp, "before shell: %d: {%s}\n", len, bp); +#endif + + /* + * Do shell word expansion -- it's very, very hard to figure out + * what magic characters the user's shell expects. If it's not + * pure vanilla, don't even try. + */ + for (p = bp, n = len; n > 0; --n, ++p) + if (!isalnum(*p) && !isblank(*p) && *p != '/' && *p != '.') + break; + if (n > 0) { + if (argv_sexp(sp, &bp, &blen, &len)) { + rval = 1; + goto err; + } + p = bp; + } else { + p = bp + SHELLOFFSET; + len -= SHELLOFFSET; + } + +#if defined(DEBUG) && 0 + TRACE(sp, "after shell: %d: {%s}\n", len, bp); +#endif + + rval = argv_exp3(sp, ep, excp, p, len); + +err: FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * argv_exp3 -- + * Take a string and break it up into an argv. + */ +int +argv_exp3(sp, ep, excp, cmd, cmdlen) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; +{ + CHAR_T vlit; + EX_PRIVATE *exp; + size_t len; + int ch, off; + char *ap, *p; + + (void)term_key_ch(sp, K_VLNEXT, &vlit); + for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { + /* Skip any leading whitespace. */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen == 0) + break; + + /* + * Determine the length of this whitespace delimited + * argument. + * + * QUOTING NOTE: + * + * Skip any character preceded by the user's quoting + * character. + */ + for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) + if ((ch = *cmd) == vlit && cmdlen > 1) { + ++cmd; + --cmdlen; + } else if (isblank(ch)) + break; + + /* + * Copy the argument into place. + * + * QUOTING NOTE: + * + * Lose quote chars. + */ + argv_alloc(sp, len); + off = exp->argsoff; + exp->args[off]->len = len; + for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) + if (*ap == vlit) { + ++ap; + --exp->args[off]->len; + } + *p = '\0'; + } + excp->argv = exp->args; + excp->argc = exp->argsoff; + +#if defined(DEBUG) && 0 + for (cnt = 0; cnt < exp->argsoff; ++cnt) + TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); +#endif + return (0); +} + +/* + * argv_fexp -- + * Do file name and bang command expansion. + */ +static int +argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang) + SCR *sp; + EXCMDARG *excp; + char *cmd, *p, **bpp; + size_t cmdlen, *lenp, *blenp; + int is_bang; +{ + EX_PRIVATE *exp; + char *bp, *t; + size_t blen, len, tlen; + + /* Replace file name characters. */ + for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) + switch (*cmd) { + case '!': + if (!is_bang) + goto ins_ch; + exp = EXP(sp); + if (exp->lastbcomm == NULL) { + msgq(sp, M_ERR, + "No previous command to replace \"!\"."); + return (1); + } + len += tlen = strlen(exp->lastbcomm); + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, exp->lastbcomm, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '%': + if (sp->frp->cname == NULL && sp->frp->name == NULL) { + msgq(sp, M_ERR, + "No filename to substitute for %%."); + return (1); + } + tlen = strlen(t = FILENAME(sp->frp)); + len += tlen; + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, t, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '#': + /* + * Try the alternate file name first, then the + * previously edited file. + */ + if (sp->alt_name == NULL && (sp->p_frp == NULL || + sp->frp->cname == NULL && sp->frp->name == NULL)) { + msgq(sp, M_ERR, + "No filename to substitute for #."); + return (1); + } + if (sp->alt_name != NULL) + t = sp->alt_name; + else + t = FILENAME(sp->frp); + len += tlen = strlen(t); + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, t, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '\\': + /* + * QUOTING NOTE: + * + * Strip any backslashes that protected the file + * expansion characters. + */ + if (cmdlen > 1 && cmd[1] == '%' || cmd[1] == '#') + ++cmd; + /* FALLTHROUGH */ + default: +ins_ch: ++len; + ADD_SPACE_RET(sp, bp, blen, len); + *p++ = *cmd; + } + + /* Nul termination. */ + ++len; + ADD_SPACE_RET(sp, bp, blen, len); + *p = '\0'; + + /* Return the new string length, buffer, buffer length. */ + *lenp = len - 1; + *bpp = bp; + *blenp = blen; + return (0); +} + +/* + * argv_alloc -- + * Make more space for arguments. + */ +static int +argv_alloc(sp, len) + SCR *sp; + size_t len; +{ + ARGS *ap; + EX_PRIVATE *exp; + int cnt, off; + + /* + * Allocate room for another argument, always leaving + * enough room for an ARGS structure with a length of 0. + */ +#define INCREMENT 20 + exp = EXP(sp); + off = exp->argsoff; + if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { + cnt = exp->argscnt + INCREMENT; + REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); + if (exp->args == NULL) { + (void)argv_free(sp); + goto mem; + } + memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *)); + exp->argscnt = cnt; + } + + /* First argument. */ + if (exp->args[off] == NULL) { + CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); + if (exp->args[off] == NULL) + goto mem; + } + + /* First argument buffer. */ + ap = exp->args[off]; + ap->len = 0; + if (ap->blen < len + 1) { + ap->blen = len + 1; + REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); + if (ap->bp == NULL) { + ap->bp = NULL; + ap->blen = 0; + F_CLR(ap, A_ALLOCATED); +mem: msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(ap, A_ALLOCATED); + } + + /* Second argument. */ + if (exp->args[++off] == NULL) { + CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); + if (exp->args[off] == NULL) + goto mem; + } + /* 0 length serves as end-of-argument marker. */ + exp->args[off]->len = 0; + return (0); +} + +/* + * argv_free -- + * Free up argument structures. + */ +int +argv_free(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + int off; + + exp = EXP(sp); + if (exp->args != NULL) { + for (off = 0; off < exp->argscnt; ++off) { + if (exp->args[off] == NULL) + continue; + if (F_ISSET(exp->args[off], A_ALLOCATED)) + free(exp->args[off]->bp); + FREE(exp->args[off], sizeof(ARGS)); + } + FREE(exp->args, exp->argscnt * sizeof(ARGS *)); + } + exp->args = NULL; + exp->argscnt = 0; + exp->argsoff = 0; + return (0); +} + +/* + * argv_sexp -- + * Fork a shell, pipe a command through it, and read the output into + * a buffer. + */ +static int +argv_sexp(sp, bpp, blenp, lenp) + SCR *sp; + char **bpp; + size_t *blenp, *lenp; +{ + FILE *ifp; + pid_t pid; + size_t blen, len; + int ch, rval, output[2]; + char *bp, *p, *sh, *sh_path; + + bp = *bpp; + blen = *blenp; + + sh_path = O_STR(sp, O_SHELL); + if ((sh = strrchr(sh_path, '/')) == NULL) + sh = sh_path; + else + ++sh; + + /* + * There are two different processes running through this code. + * They are named the utility and the parent. The utility reads + * from standard input and writes to the parent. The parent reads + * from the utility and writes into the buffer. The parent reads + * from output[0], and the utility writes to output[1]. + */ + if (pipe(output) < 0) { + msgq(sp, M_SYSERR, "pipe"); + return (1); + } + if ((ifp = fdopen(output[0], "r")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + + /* + * Do the minimal amount of work possible, the shell is going + * to run briefly and then exit. Hopefully. + */ + switch (pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); +err: (void)close(output[0]); + (void)close(output[1]); + return (1); + case 0: /* Utility. */ + /* Redirect stdout/stderr to the write end of the pipe. */ + (void)dup2(output[1], STDOUT_FILENO); + (void)dup2(output[1], STDERR_FILENO); + + /* Close the utility's file descriptors. */ + (void)close(output[0]); + (void)close(output[1]); + + /* Assumes that all shells have -c. */ + execl(sh_path, sh, "-c", bp, NULL); + msgq(sp, M_ERR, + "Error: execl: %s: %s", sh_path, strerror(errno)); + _exit(127); + default: + /* Close the pipe end the parent won't use. */ + (void)close(output[1]); + break; + } + + rval = 0; + + /* + * Copy process output into a buffer. + * + * !!! + * Historic vi apparently discarded leading \n and \r's from + * the shell output stream. We don't on the grounds that any + * shell that does that is broken. + */ + for (p = bp, len = 0, ch = EOF; + (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len) + if (blen < 5) { + ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2); + p = bp + len; + blen = *blenp - len; + } + + /* Delete the final newline, nul terminate the string. */ + if (p > bp && p[-1] == '\n' || p[-1] == '\r') { + --len; + *--p = '\0'; + } else + *p = '\0'; + *lenp = len; + + if (ferror(ifp)) { + msgq(sp, M_ERR, "I/O error: %s", sh); +binc_err: rval = 1; + } + (void)fclose(ifp); + + *bpp = bp; /* *blenp is already updated. */ + + /* Wait for the process. */ + return (proc_wait(sp, (long)pid, sh, 0) | rval); +} diff --git a/usr.bin/vi/nex/ex_at.c b/usr.bin/vi/nex/ex_at.c new file mode 100644 index 000000000000..51d001f304a7 --- /dev/null +++ b/usr.bin/vi/nex/ex_at.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_at.c 8.16 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_at -- :@[@ | buffer] + * :*[* | buffer] + * + * Execute the contents of the buffer. + */ +int +ex_at(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + CB *cbp; + EX_PRIVATE *exp; + TEXT *tp; + int name, lmode; + + exp = EXP(sp); + + /* Historically, @@ and ** execute the last buffer. */ + name = cmdp->buffer; + if (name == cmdp->cmd->name[0]) { + if (!exp->at_lbuf_set) { + msgq(sp, M_ERR, "No previous buffer to execute."); + return (1); + } + name = exp->at_lbuf; + } + + CBNAME(sp, cbp, name); + if (cbp == NULL) { + msgq(sp, M_ERR, "Buffer %s is empty.", charname(sp, name)); + return (1); + } + + /* Save for reuse. */ + exp->at_lbuf = name; + exp->at_lbuf_set = 1; + + /* + * If the buffer was cut in line mode or had portions of more + * than one line, <newlines> are appended to each line as it + * is pushed onto the stack. + */ + tp = cbp->textq.cqh_last; + lmode = F_ISSET(cbp, CB_LMODE) || tp->q.cqe_prev != (void *)&cbp->textq; + for (; tp != (void *)&cbp->textq; tp = tp->q.cqe_prev) + if ((lmode || tp->q.cqe_prev != (void *)&cbp->textq) && + term_push(sp, "\n", 1, 0, 0) || + term_push(sp, tp->lb, tp->len, 0, CH_QUOTED)) + return (1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_bang.c b/usr.bin/vi/nex/ex_bang.c new file mode 100644 index 000000000000..4f7222b4e78c --- /dev/null +++ b/usr.bin/vi/nex/ex_bang.c @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_bang.c 8.22 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex/sex_screen.h" + +/* + * ex_bang -- :[line [,line]] ! command + * + * Pass the rest of the line after the ! character to the program named by + * the O_SHELL option. + * + * Historical vi did NOT do shell expansion on the arguments before passing + * them, only file name expansion. This means that the O_SHELL program got + * "$t" as an argument if that is what the user entered. Also, there's a + * special expansion done for the bang command. Any exclamation points in + * the user's argument are replaced by the last, expanded ! command. + * + * There's some fairly amazing slop in this routine to make the different + * ways of getting here display the right things. It took a long time to + * get get right (wrong?), so be careful. + */ +int +ex_bang(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum filtertype ftype; + ARGS *ap; + EX_PRIVATE *exp; + MARK rm; + recno_t lno; + size_t blen; + int rval; + char *bp, *msg; + + + ap = cmdp->argv[0]; + if (ap->len == 0) { + msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); + return (1); + } + + /* Swap commands. */ + exp = EXP(sp); + if (exp->lastbcomm != NULL) + FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1); + if ((exp->lastbcomm = strdup(ap->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + /* + * If the command was modified by the expansion, we redisplay it. + * Redisplaying it in vi mode is tricky, and handled separately + * in each case below. If we're in ex mode, it's easy, so we just + * do it here. + */ + bp = NULL; + if (F_ISSET(cmdp, E_MODIFY)) { + if (IN_EX_MODE(sp)) { + (void)ex_printf(EXCOOKIE, "!%s\n", ap->bp); + (void)ex_fflush(EXCOOKIE); + } + /* + * Vi: Display the command if modified. Historic vi displayed + * the command if it was modified due to file name and/or bang + * expansion. If piping lines, it was immediately overwritten + * by any error or line change reporting. We don't the user to + * have to page through the responses, so we only post it until + * it's erased by something else. Otherwise, pass it on to the + * ex_exec_proc routine to display after the screen has been + * cleaned up. + */ + if (IN_VI_MODE(sp)) { + GET_SPACE_RET(sp, bp, blen, ap->len + 2); + bp[0] = '!'; + memmove(bp + 1, ap->bp, ap->len + 1); + } + } + + /* + * If addresses were specified, pipe lines from the file through + * the command. + */ + if (cmdp->addrcnt != 0) { + if (bp != NULL) { + (void)sp->s_busy(sp, bp); + FREE_SPACE(sp, bp, blen); + } + /* + * !!! + * Historical vi permitted "!!" in an empty file. When it + * happens, we get called with two addresses of 1,1 and a + * bad attitude. + */ + ftype = FILTER; + if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) { + cmdp->addr1.lno = cmdp->addr2.lno = 0; + ftype = FILTER_READ; + } + } + if (filtercmd(sp, ep, + &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype)) + return (1); + sp->lno = rm.lno; + F_SET(exp, EX_AUTOPRINT); + return (0); + } + + /* + * If no addresses were specified, run the command. If the file + * has been modified and autowrite is set, write the file back. + * If the file has been modified, autowrite is not set and the + * warn option is set, tell the user about the file. + */ + msg = "\n"; + if (F_ISSET(ep, F_MODIFIED)) + if (O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) { + rval = 1; + goto ret; + } + } else if (O_ISSET(sp, O_WARN)) + if (IN_VI_MODE(sp) && F_ISSET(cmdp, E_MODIFY)) + msg = "\nFile modified since last write.\n"; + else + msg = "File modified since last write.\n"; + + /* Run the command. */ + rval = ex_exec_proc(sp, ap->bp, bp, msg); + + /* Ex terminates with a bang. */ + if (IN_EX_MODE(sp)) + (void)write(STDOUT_FILENO, "!\n", 2); + + /* Vi requires user permission to continue. */ + if (IN_VI_MODE(sp)) + F_SET(sp, S_CONTINUE); + + /* Free the extra space. */ +ret: if (bp != NULL) + FREE_SPACE(sp, bp, blen); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_cd.c b/usr.bin/vi/nex/ex_cd.c new file mode 100644 index 000000000000..6b51bc51c15e --- /dev/null +++ b/usr.bin/vi/nex/ex_cd.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_cd.c 8.3 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_cd -- :cd[!] [directory] + * Change directories. + */ +int +ex_cd(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + char *dir, buf[MAXPATHLEN]; + + switch (cmdp->argc) { + case 0: + if ((dir = getenv("HOME")) == NULL) { + msgq(sp, M_ERR, + "Environment variable HOME not set."); + return (1); + } + break; + case 1: + dir = cmdp->argv[0]->bp; + break; + default: + abort(); + } + + if (chdir(dir) < 0) { + msgq(sp, M_SYSERR, dir); + return (1); + } + if (getcwd(buf, sizeof(buf)) != NULL) + msgq(sp, M_INFO, "New directory: %s", buf); + return (0); +} diff --git a/usr.bin/vi/nex/ex_delete.c b/usr.bin/vi/nex/ex_delete.c new file mode 100644 index 000000000000..5b737e8f547d --- /dev/null +++ b/usr.bin/vi/nex/ex_delete.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_delete.c 8.6 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags] + * + * Delete lines from the file. + */ +int +ex_delete(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + /* Yank the lines. */ + if (cut(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &cmdp->addr2, CUT_DELETE | CUT_LINEMODE)) + return (1); + + /* Delete the lines. */ + if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1)) + return (1); + + /* Set the cursor to the line after the last line deleted. */ + sp->lno = cmdp->addr1.lno; + + /* Or the last line in the file if deleted to the end of the file. */ + if (file_lline(sp, ep, &lno)) + return (1); + if (sp->lno > lno) + sp->lno = lno; + return (0); +} diff --git a/usr.bin/vi/nex/ex_digraph.c b/usr.bin/vi/nex/ex_digraph.c new file mode 100644 index 000000000000..20f1bf327ded --- /dev/null +++ b/usr.bin/vi/nex/ex_digraph.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_digraph.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#ifndef NO_DIGRAPH +#include <sys/types.h> + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +static void do_digraph __P((SCR *, EXF *, int, u_char *)); + +/* This stuff is used to build the default digraphs table. */ +static u_char digtable[][4] = { +# ifdef CS_IBMPC + "C,\200", "u\"\1", "e'\2", "a^\3", + "a\"\4", "a`\5", "a@\6", "c,\7", + "e^\10", "e\"\211", "e`\12", "i\"\13", + "i^\14", "i`\15", "A\"\16", "A@\17", + "E'\20", "ae\21", "AE\22", "o^\23", + "o\"\24", "o`\25", "u^\26", "u`\27", + "y\"\30", "O\"\31", "U\"\32", "a'\240", + "i'!", "o'\"", "u'#", "n~$", + "N~%", "a-&", "o-'", "~?(", + "~!-", "\"<.", "\">/", +# ifdef CS_SPECIAL + "2/+", "4/,", "^+;", "^q<", + "^c=", "^r>", "^t?", "pp]", + "^^^", "oo_", "*a`", "*ba", + "*pc", "*Sd", "*se", "*uf", + "*tg", "*Ph", "*Ti", "*Oj", + "*dk", "*Hl", "*hm", "*En", + "*No", "eqp", "pmq", "ger", + "les", "*It", "*iu", "*/v", + "*=w", "sq{", "^n|", "^2}", + "^3~", "^_\377", +# endif /* CS_SPECIAL */ +# endif /* CS_IBMPC */ +# ifdef CS_LATIN1 + "~!!", "a-*", "\">+", "o-:", + "\"<>", "~??", + + "A`@", "A'A", "A^B", "A~C", + "A\"D", "A@E", "AEF", "C,G", + "E`H", "E'I", "E^J", "E\"K", + "I`L", "I'M", "I^N", "I\"O", + "-DP", "N~Q", "O`R", "O'S", + "O^T", "O~U", "O\"V", "O/X", + "U`Y", "U'Z", "U^[", "U\"\\", + "Y'_", + + "a``", "a'a", "a^b", "a~c", + "a\"d", "a@e", "aef", "c,g", + "e`h", "e'i", "e^j", "e\"k", + "i`l", "i'm", "i^n", "i\"o", + "-dp", "n~q", "o`r", "o's", + "o^t", "o~u", "o\"v", "o/x", + "u`y", "u'z", "u^{", "u\"|", + "y'~", +# endif /* CS_LATIN1 */ + "" +}; + +int +digraph_init(sp) + SCR *sp; +{ + int i; + + for (i = 0; *digtable[i]; i++) + do_digraph(sp, NULL, 0, digtable[i]); + do_digraph(sp, NULL, 0, NULL); + return (0); +} + +int +ex_digraph(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp); + return (0); +} + +static struct _DIG +{ + struct _DIG *next; + char key1; + char key2; + char dig; + char save; +} *digs; + +int +digraph(sp, key1, key2) + SCR *sp; + char key1; /* the underlying character */ + char key2; /* the second character */ +{ + int new_key; + register struct _DIG *dp; + + /* if digraphs are disabled, then just return the new char */ + if (O_ISSET(sp, O_DIGRAPH)) + { + return key2; + } + + /* remember the new key, so we can return it if this isn't a digraph */ + new_key = key2; + + /* sort key1 and key2, so that their original order won't matter */ + if (key1 > key2) + { + key2 = key1; + key1 = new_key; + } + + /* scan through the digraph chart */ + for (dp = digs; + dp && (dp->key1 != key1 || dp->key2 != key2); + dp = dp->next) + { + } + + /* if this combination isn't in there, just use the new key */ + if (!dp) + { + return new_key; + } + + /* else use the digraph key */ + return dp->dig; +} + +/* this function lists or defines digraphs */ +static void +do_digraph(sp, ep, bang, extra) + SCR *sp; + EXF *ep; + int bang; + u_char *extra; +{ + int dig; + register struct _DIG *dp; + struct _DIG *prev; + static int user_defined = 0; /* boolean: are all later digraphs user-defined? */ + char listbuf[8]; + + /* if "extra" is NULL, then we've reached the end of the built-ins */ + if (!extra) + { + user_defined = 1; + return; + } + + /* if no args, then display the existing digraphs */ + if (*extra < ' ') + { + listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' '; + listbuf[7] = '\0'; + for (dig = 0, dp = digs; dp; dp = dp->next) + { + if (dp->save || bang) + { + dig += 7; + if (dig >= sp->cno) + { + addch('\n'); + refresh(); + dig = 7; + } + listbuf[3] = dp->key1; + listbuf[4] = dp->key2; + listbuf[6] = dp->dig; + addstr(listbuf); + } + } + addch('\n'); + refresh(); + return; + } + + /* make sure we have at least two characters */ + if (!extra[1]) + { + msgq(sp, M_ERR, + "Digraphs must be composed of two characters"); + return; + } + + /* sort key1 and key2, so that their original order won't matter */ + if (extra[0] > extra[1]) + { + dig = extra[0]; + extra[0] = extra[1]; + extra[1] = dig; + } + + /* locate the new digraph character */ + for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++) + { + } + dig = extra[dig]; + if (!bang && dig) + { + dig |= 0x80; + } + + /* search for the digraph */ + for (prev = (struct _DIG *)0, dp = digs; + dp && (dp->key1 != extra[0] || dp->key2 != extra[1]); + prev = dp, dp = dp->next) + { + } + + /* deleting the digraph? */ + if (!dig) + { + if (!dp) + { +#ifndef CRUNCH + msgq(sp, M_ERR, + "%c%c not a digraph", extra[0], extra[1]); +#endif + return; + } + if (prev) + prev->next = dp->next; + else + digs = dp->next; + free(dp); + return; + } + + /* if necessary, create a new digraph struct for the new digraph */ + if (dig && !dp) + { + MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG)); + if (dp == NULL) + return; + if (prev) + prev->next = dp; + else + digs = dp; + dp->next = (struct _DIG *)0; + } + + /* assign it the new digraph value */ + dp->key1 = extra[0]; + dp->key2 = extra[1]; + dp->dig = dig; + dp->save = user_defined; +} + +void +digraph_save(sp, fd) + SCR *sp; + int fd; +{ + static char buf[] = "digraph! XX Y\n"; + register struct _DIG *dp; + + for (dp = digs; dp; dp = dp->next) + { + if (dp->save) + { + buf[9] = dp->key1; + buf[10] = dp->key2; + buf[12] = dp->dig; + write(fd, buf, (unsigned)14); + } + } +} +#endif diff --git a/usr.bin/vi/nex/ex_display.c b/usr.bin/vi/nex/ex_display.c new file mode 100644 index 000000000000..0cfef42f675b --- /dev/null +++ b/usr.bin/vi/nex/ex_display.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_display.c 8.14 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "tag.h" +#include "excmd.h" + +static int bdisplay __P((SCR *, EXF *)); +static void db __P((SCR *, CB *, char *)); + +/* + * ex_display -- :display b[uffers] | s[creens] | t[ags] + * + * Display buffers, tags or screens. + */ +int +ex_display(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch (cmdp->argv[0]->bp[0]) { + case 'b': + return (bdisplay(sp, ep)); + case 's': + return (ex_sdisplay(sp, ep)); + case 't': + return (ex_tagdisplay(sp, ep)); + } + msgq(sp, M_ERR, + "Unknown display argument %s, use b[uffers], s[creens], or t[ags].", + cmdp->argv[0]); + return (1); +} + +/* + * bdisplay -- + * + * Display buffers. + */ +static int +bdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + CB *cbp; + + if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) { + (void)ex_printf(EXCOOKIE, "No cut buffers to display."); + return (0); + } + + /* Buffers can be infinitely long, make it interruptible. */ + F_SET(sp, S_INTERRUPTIBLE); + + /* Display regular cut buffers. */ + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) { + if (isdigit(cbp->name)) + continue; + if (cbp->textq.cqh_first != (void *)&cbp->textq) + db(sp, cbp, NULL); + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + } + /* Display numbered buffers. */ + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) { + if (!isdigit(cbp->name)) + continue; + if (cbp->textq.cqh_first != (void *)&cbp->textq) + db(sp, cbp, NULL); + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + } + /* Display default buffer. */ + if ((cbp = sp->gp->dcbp) != NULL) + db(sp, cbp, "default buffer"); + return (0); +} + +/* + * db -- + * Display a buffer. + */ +static void +db(sp, cbp, name) + SCR *sp; + CB *cbp; + char *name; +{ + TEXT *tp; + size_t len; + char *p; + + (void)ex_printf(EXCOOKIE, "********** %s%s\n", + name == NULL ? charname(sp, cbp->name) : name, + F_ISSET(cbp, CB_LMODE) ? " (line mode)" : ""); + for (tp = cbp->textq.cqh_first; + tp != (void *)&cbp->textq; tp = tp->q.cqe_next) { + for (len = tp->len, p = tp->lb; len--;) { + (void)ex_printf(EXCOOKIE, "%s", charname(sp, *p++)); + if (F_ISSET(sp, S_INTERRUPTED)) + return; + } + (void)ex_printf(EXCOOKIE, "\n"); + } +} diff --git a/usr.bin/vi/nex/ex_edit.c b/usr.bin/vi/nex/ex_edit.c new file mode 100644 index 000000000000..eeafedaed778 --- /dev/null +++ b/usr.bin/vi/nex/ex_edit.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_edit.c 8.14 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_edit -- :e[dit][!] [+cmd] [file] + * :vi[sual][!] [+cmd] [file] + * + * Edit a file; if none specified, re-edit the current file. The second + * form of the command can only be executed while in vi mode. See the + * hack in ex.c:ex_cmd(). + * + * !!! + * Historic vi didn't permit the '+' command form without specifying + * a file name as well. + */ +int +ex_edit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + FREF *frp; + + frp = sp->frp; + switch (cmdp->argc) { + case 0: + /* + * If the name has been changed, we edit that file, not the + * original name. If the user was editing a temporary file, + * create another one. The reason for this is that we do + * special exit processing of temporary files, and reusing + * them is tricky. + */ + if (frp->cname != NULL) { + if ((frp = file_add(sp, frp, frp->cname, 1)) == NULL) + return (1); + set_alt_name(sp, sp->frp->cname); + } else if (frp->name == NULL) + if ((frp = file_add(sp, frp, NULL, 1)) == NULL) + return (1); + break; + case 1: + ap = cmdp->argv[0]; + if ((frp = file_add(sp, sp->frp, ap->bp, 1)) == NULL) + return (1); + set_alt_name(sp, ap->bp); + break; + default: + abort(); + } + + /* + * Check for modifications. + * + * !!! + * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit. + */ + if (F_ISSET(ep, F_MODIFIED) && + ep->refcnt <= 1 && !F_ISSET(cmdp, E_FORCE)) { + msgq(sp, M_ERR, + "Modified since last write; write or use ! to override."); + return (1); + } + + /* Switch files. */ + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + F_SET(sp, S_FSWITCH); + return (0); +} diff --git a/usr.bin/vi/nex/ex_equal.c b/usr.bin/vi/nex/ex_equal.c new file mode 100644 index 000000000000..f0a18bef41f4 --- /dev/null +++ b/usr.bin/vi/nex/ex_equal.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_equal.c 8.4 (Berkeley) 11/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_equal -- :address = + * Print out the line number matching the specified address, or the + * last line number in the file if no address specified. + */ +int +ex_equal(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + if (F_ISSET(cmdp, E_ADDRDEF)) { + if (file_lline(sp, ep, &lno)) + return (1); + (void)ex_printf(EXCOOKIE, "%ld\n", lno); + } else + (void)ex_printf(EXCOOKIE, "%ld\n", cmdp->addr1.lno); + return (0); +} diff --git a/usr.bin/vi/nex/ex_exit.c b/usr.bin/vi/nex/ex_exit.c new file mode 100644 index 000000000000..b0f929a5bbe3 --- /dev/null +++ b/usr.bin/vi/nex/ex_exit.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_exit.c 8.7 (Berkeley) 12/10/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_quit -- :quit[!] + * Quit. + */ +int +ex_quit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + force = F_ISSET(cmdp, E_FORCE); + + /* Check for modifications. */ + if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) { + msgq(sp, M_ERR, + "Modified since last write; write or use ! to override."); + return (1); + } + + /* + * !!! + * Historic practice: quit! or two quit's done in succession + * (where ZZ counts as a quit) didn't check for other files. + * + * Also check for related screens; if they exist, quit, the + * user will get the message on the last screen. + */ + if (!force && sp->ccnt != sp->q_ccnt + 1 && + ep->refcnt <= 1 && file_unedited(sp) != NULL) { + sp->q_ccnt = sp->ccnt; + msgq(sp, M_ERR, + "More files; use \":n\" to go to the next file, \":q!\" to quit."); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} diff --git a/usr.bin/vi/nex/ex_file.c b/usr.bin/vi/nex/ex_file.c new file mode 100644 index 000000000000..d556d5214e0e --- /dev/null +++ b/usr.bin/vi/nex/ex_file.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_file.c 8.7 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_file -- :f[ile] [name] + * Status line and change the file's name. + */ +int +ex_file(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + char *p, *t; + + switch (cmdp->argc) { + case 0: + break; + case 1: + frp = sp->frp; + + /* Make sure can allocate enough space. */ + if ((p = strdup(cmdp->argv[0]->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + /* If already have a file name, it becomes the alternate. */ + if ((t = FILENAME(frp)) != NULL) + set_alt_name(sp, t); + + /* Free any previously changed name. */ + if (frp->cname != NULL) + free(frp->cname); + frp->cname = p; + + /* The read-only bit follows the file name; clear it. */ + F_CLR(frp, FR_RDONLY); + + /* Have to force a write if the file exists, next time. */ + F_CLR(frp, FR_CHANGEWRITE); + break; + default: + abort(); + } + status(sp, ep, sp->lno, 1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_global.c b/usr.bin/vi/nex/ex_global.c new file mode 100644 index 000000000000..4f942a39f680 --- /dev/null +++ b/usr.bin/vi/nex/ex_global.c @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_global.c 8.29 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "interrupt.h" + +enum which {GLOBAL, VGLOBAL}; + +static int global __P((SCR *, EXF *, EXCMDARG *, enum which)); +static void global_intr __P((int)); + +/* + * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] + * Exec on lines matching a pattern. + */ +int +ex_global(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (global(sp, ep, + cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL)); +} + +/* + * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands] + * Exec on lines not matching a pattern. + */ +int +ex_vglobal(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (global(sp, ep, cmdp, VGLOBAL)); +} + +static int +global(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + DECLARE_INTERRUPTS; + RANGE *rp; + EX_PRIVATE *exp; + recno_t elno, lno; + regmatch_t match[1]; + regex_t *re, lre; + size_t clen, len; + int delim, eval, reflags, replaced, rval; + char *cb, *ptrn, *p, *t; + + /* + * Skip leading white space. Historic vi allowed any non- + * alphanumeric to serve as the global command delimiter. + */ + for (p = cmdp->argv[0]->bp; isblank(*p); ++p); + if (*p == '\0' || isalnum(*p)) { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + delim = *p++; + + /* + * Get the pattern string, toss escaped characters. + * + * QUOTING NOTE: + * Only toss an escaped character if it escapes a delimiter. + */ + for (ptrn = t = p;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + /* + * !!! + * Nul terminate the pattern string -- it's passed + * to regcomp which doesn't understand anything else. + */ + *t = '\0'; + break; + } + if (p[0] == '\\' && p[1] == delim) + ++p; + *t++ = *p++; + } + + /* If the pattern string is empty, use the last one. */ + if (*ptrn == '\0') { + if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + re = &sp->sre; + } else { + /* Set RE flags. */ + reflags = 0; + if (O_ISSET(sp, O_EXTENDED)) + reflags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + reflags |= REG_ICASE; + + /* Convert vi-style RE's to POSIX 1003.2 RE's. */ + if (re_conv(sp, &ptrn, &replaced)) + return (1); + + /* Compile the RE. */ + re = &lre; + eval = regcomp(re, ptrn, reflags); + + /* Free up any allocated memory. */ + if (replaced) + free(ptrn); + + if (eval) { + re_error(sp, eval, re); + return (1); + } + + /* + * Set saved RE. Historic practice is that + * globals set direction as well as the RE. + */ + sp->sre = lre; + sp->searchdir = FORWARD; + F_SET(sp, S_SRE_SET); + } + + /* Get a copy of the command string. */ + if ((clen = strlen(p)) == 0) { + msgq(sp, M_ERR, "No command string specified."); + return (1); + } + MALLOC_RET(sp, cb, char *, clen); + memmove(cb, p, clen); + + /* + * The global commands sets the substitute RE as well as + * the everything-else RE. + */ + sp->subre = sp->sre; + F_SET(sp, S_SUBRE_SET); + + /* Set the global flag, and set up interrupts. */ + F_SET(sp, S_GLOBAL); + SET_UP_INTERRUPTS(global_intr); + + /* + * For each line... The semantics of global matching are that we first + * have to decide which lines are going to get passed to the command, + * and then pass them to the command, ignoring other changes. There's + * really no way to do this in a single pass, since arbitrary line + * creation, deletion and movement can be done in the ex command. For + * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". + * What we do is create linked list of lines that are tracked through + * each ex command. There's a callback routine which the DB interface + * routines call when a line is created or deleted. This doesn't help + * the layering much. + */ + exp = EXP(sp); + for (rval = 0, lno = cmdp->addr1.lno, + elno = cmdp->addr2.lno; lno <= elno; ++lno) { + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) + goto interrupted; + + /* Get the line and search for a match. */ + if ((t = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + goto err; + } + match[0].rm_so = 0; + match[0].rm_eo = len; + switch(eval = regexec(re, t, 1, match, REG_STARTEND)) { + case 0: + if (cmd == VGLOBAL) + continue; + break; + case REG_NOMATCH: + if (cmd == GLOBAL) + continue; + break; + default: + re_error(sp, eval, re); + goto err; + } + + /* If follows the last entry, extend the last entry's range. */ + if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq && + rp->stop == lno - 1) { + ++rp->stop; + continue; + } + + /* Allocate a new range, and append it to the list. */ + CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE)); + if (rp == NULL) + goto err; + rp->start = rp->stop = lno; + CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q); + } + + exp = EXP(sp); + exp->range_lno = OOBLNO; + for (;;) { + /* + * Start at the beginning of the range each time, it may have + * been changed (or exhausted) if lines were inserted/deleted. + */ + if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq) + break; + if (rp->start > rp->stop) { + CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q); + free(rp); + continue; + } + + /* + * Execute the command, setting the cursor to the line so that + * relative addressing works. This means that the cursor moves + * to the last line sent to the command, by default, even if + * the command fails. + */ + exp->range_lno = sp->lno = rp->start++; + if (ex_cmd(sp, ep, cb, clen)) + goto err; + + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) { +interrupted: msgq(sp, M_INFO, "Interrupted."); + break; + } + } + + /* Set the cursor to the new value, making sure it exists. */ + if (exp->range_lno != OOBLNO) { + if (file_lline(sp, ep, &lno)) + return (1); + sp->lno = + lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno; + } + if (0) { +err: rval = 1; + } + +interrupt_err: + F_CLR(sp, S_GLOBAL); + TEAR_DOWN_INTERRUPTS; + + /* Free any remaining ranges and the command buffer. */ + while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) { + CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q); + free(rp); + } + free(cb); + return (rval); +} + +/* + * global_insdel -- + * Update the ranges based on an insertion or deletion. + */ +void +global_insdel(sp, ep, op, lno) + SCR *sp; + EXF *ep; + enum operation op; + recno_t lno; +{ + EX_PRIVATE *exp; + RANGE *nrp, *rp; + + exp = EXP(sp); + + switch (op) { + case LINE_APPEND: + return; + case LINE_DELETE: + for (rp = exp->rangeq.cqh_first; + rp != (void *)&exp->rangeq; rp = nrp) { + nrp = rp->q.cqe_next; + /* If range less than the line, ignore it. */ + if (rp->stop < lno) + continue; + /* If range greater than the line, decrement range. */ + if (rp->start > lno) { + --rp->start; + --rp->stop; + continue; + } + /* Lno is inside the range, decrement the end point. */ + if (rp->start > --rp->stop) { + CIRCLEQ_REMOVE(&exp->rangeq, rp, q); + free(rp); + } + } + break; + case LINE_INSERT: + for (rp = exp->rangeq.cqh_first; + rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) { + /* If range less than the line, ignore it. */ + if (rp->stop < lno) + continue; + /* If range greater than the line, increment range. */ + if (rp->start >= lno) { + ++rp->start; + ++rp->stop; + continue; + } + /* + * Lno is inside the range, so the range must be split. + * Since we're inserting a new element, neither range + * can be exhausted. + */ + CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE)); + if (nrp == NULL) { + F_SET(sp, S_INTERRUPTED); + return; + } + nrp->start = lno + 1; + nrp->stop = rp->stop + 1; + rp->stop = lno - 1; + CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q); + rp = nrp; + } + break; + case LINE_RESET: + return; + } + /* + * If the command deleted/inserted lines, the cursor moves to + * the line after the deleted/inserted line. + */ + exp->range_lno = lno; +} + +/* + * global_intr -- + * Set the interrupt bit in any screen that is running an interruptible + * global. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one global running, it will be hard to decide which one to + * stop. + */ +static void +global_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_GLOBAL) && F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} diff --git a/usr.bin/vi/nex/ex_init.c b/usr.bin/vi/nex/ex_init.c new file mode 100644 index 000000000000..26acf2f44c92 --- /dev/null +++ b/usr.bin/vi/nex/ex_init.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_init.c 8.11 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "tag.h" + +/* + * ex_screen_copy -- + * Copy ex screen. + */ +int +ex_screen_copy(orig, sp) + SCR *orig, *sp; +{ + EX_PRIVATE *oexp, *nexp; + + /* Create the private ex structure. */ + CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE)); + sp->ex_private = nexp; + + /* Initialize queues. */ + TAILQ_INIT(&nexp->tagq); + TAILQ_INIT(&nexp->tagfq); + CIRCLEQ_INIT(&nexp->rangeq); + + if (orig == NULL) { + nexp->at_lbuf_set = 0; + } else { + oexp = EXP(orig); + + nexp->at_lbuf = oexp->at_lbuf; + nexp->at_lbuf_set = oexp->at_lbuf_set; + + if (oexp->lastbcomm != NULL && + (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return(1); + } + + if (ex_tagcopy(orig, sp)) + return (1); + } + + nexp->lastcmd = &cmds[C_PRINT]; + return (0); +} + +/* + * ex_screen_end -- + * End a vi screen. + */ +int +ex_screen_end(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + int rval; + + rval = 0; + exp = EXP(sp); + + if (argv_free(sp)) + rval = 1; + + if (exp->ibp != NULL) + FREE(exp->ibp, exp->ibp_len); + + if (exp->lastbcomm != NULL) + FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1); + + /* Free private memory. */ + FREE(exp, sizeof(EX_PRIVATE)); + sp->ex_private = NULL; + + return (rval); +} + +/* + * ex_init -- + * Initialize ex. + */ +int +ex_init(sp, ep) + SCR *sp; + EXF *ep; +{ + size_t len; + + /* + * The default address is the last line of the file. If the address + * set bit is on for this file, load the address, ensuring that it + * exists. + */ + if (F_ISSET(sp->frp, FR_CURSORSET)) { + sp->lno = sp->frp->lno; + sp->cno = sp->frp->cno; + + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } else if (sp->cno >= len) + sp->cno = 0; + } else { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } + + /* Display the status line. */ + return (status(sp, ep, sp->lno, 0)); +} + +/* + * ex_end -- + * End ex session. + */ +int +ex_end(sp) + SCR *sp; +{ + /* Save the cursor location. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + return (0); +} + +/* + * ex_optchange -- + * Handle change of options for vi. + */ +int +ex_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_TAGS: + return (ex_tagalloc(sp, O_STR(sp, O_TAGS))); + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_join.c b/usr.bin/vi/nex/ex_join.c new file mode 100644 index 000000000000..33bace97500e --- /dev/null +++ b/usr.bin/vi/nex/ex_join.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_join.c 8.8 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_join -- :[line [,line]] j[oin][!] [count] [flags] + * Join lines. + */ +int +ex_join(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t from, to; + size_t blen, clen, len, tlen; + int echar, extra, first; + char *bp, *p, *tbp; + + from = cmdp->addr1.lno; + to = cmdp->addr2.lno; + + /* Check for no lines to join. */ + if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) { + msgq(sp, M_ERR, "No following lines to join."); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, 256); + + /* + * The count for the join command was off-by-one, + * historically, to other counts for other commands. + */ + if (F_ISSET(cmdp, E_COUNT)) + ++cmdp->addr2.lno; + + /* + * If only a single address specified, or, the same address + * specified twice, the from/two addresses will be the same. + */ + if (cmdp->addr1.lno == cmdp->addr2.lno) + ++cmdp->addr2.lno; + + clen = tlen = 0; + for (first = 1, from = cmdp->addr1.lno, + to = cmdp->addr2.lno; from <= to; ++from) { + /* + * Get next line. Historic versions of vi allowed "10J" while + * less than 10 lines from the end-of-file, so we do too. + */ + if ((p = file_gline(sp, ep, from, &len)) == NULL) { + cmdp->addr2.lno = from - 1; + break; + } + + /* Empty lines just go away. */ + if (len == 0) + continue; + + /* + * Get more space if necessary. Note, tlen isn't the length + * of the new line, it's roughly the amount of space needed. + * tbp - bp is the length of the new line. + */ + tlen += len + 2; + ADD_SPACE_RET(sp, bp, blen, tlen); + tbp = bp + clen; + + /* + * Historic practice: + * + * If force specified, join without modification. + * If the current line ends with whitespace, strip leading + * whitespace from the joined line. + * If the next line starts with a ), do nothing. + * If the current line ends with ., ? or !, insert two spaces. + * Else, insert one space. + * + * Echar is the last character in the last line joined. + */ + extra = 0; + if (!first && !F_ISSET(cmdp, E_FORCE)) { + if (isblank(echar)) + for (; len && isblank(*p); --len, ++p); + else if (p[0] != ')') { + if (strchr(".?!", echar)) { + *tbp++ = ' '; + ++clen; + extra = 1; + } + *tbp++ = ' '; + ++clen; + for (; len && isblank(*p); --len, ++p); + } + } + + if (len != 0) { + memmove(tbp, p, len); + tbp += len; + clen += len; + echar = p[len - 1]; + } else + echar = ' '; + + /* + * Historic practice for vi was to put the cursor at the first + * inserted whitespace character, if there was one, or the + * first character of the joined line, if there wasn't, or the + * last character of the line if joined to an empty line. If + * a count was specified, the cursor was moved as described + * for the first line joined, ignoring subsequent lines. If + * the join was a ':' command, the cursor was placed at the + * first non-blank character of the line unless the cursor was + * "attracted" to the end of line when the command was executed + * in which case it moved to the new end of line. There are + * probably several more special cases, but frankly, my dear, + * I don't give a damn. This implementation puts the cursor + * on the first inserted whitespace character, the first + * character of the joined line, or the last character of the + * line regardless. Note, if the cursor isn't on the joined + * line (possible with : commands), it is reset to the starting + * line. + */ + if (first) { + sp->cno = (tbp - bp) - (1 + extra); + first = 0; + } else + sp->cno = (tbp - bp) - len - (1 + extra); + } + sp->lno = cmdp->addr1.lno; + + /* Delete the joined lines. */ + for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to) + if (file_dline(sp, ep, to)) + goto err; + + /* Reset the original line. */ + if (file_sline(sp, ep, from, bp, tbp - bp)) { +err: FREE_SPACE(sp, bp, blen); + return (1); + } + FREE_SPACE(sp, bp, blen); + + sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1; + return (0); +} diff --git a/usr.bin/vi/nex/ex_map.c b/usr.bin/vi/nex/ex_map.c new file mode 100644 index 000000000000..0154c34699be --- /dev/null +++ b/usr.bin/vi/nex/ex_map.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_map.c 8.6 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * ex_map -- :map[!] [input] [replacement] + * Map a key/string or display mapped keys. + * + * Historical note: + * Historic vi maps were fairly bizarre, and likely to differ in + * very subtle and strange ways from this implementation. Two + * things worth noting are that vi would often hang or drop core + * if the map was strange enough (ex: map X "xy$@x^V), or, simply + * not work. One trick worth remembering is that if you put a + * mark at the start of the map, e.g. map X mx"xy ...), or if you + * put the map in a .exrc file, things would often work much better. + * No clue why. + */ +int +ex_map(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum seqtype stype; + CHAR_T *input; + size_t nlen; + int key; + char *name, buf[10]; + + stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND; + + switch (cmdp->argc) { + case 0: + if (seq_dump(sp, stype, 1) == 0) + msgq(sp, M_INFO, "No %s map entries.", + stype == SEQ_INPUT ? "input" : "command"); + return (0); + case 2: + input = cmdp->argv[0]->bp; + break; + default: + abort(); + } + + /* + * If the mapped string is #[0-9] (and wasn't quoted in any + * way, then map to a function key. + */ + nlen = 0; + if (input[0] == '#' && isdigit(input[1]) && !input[2]) { + key = atoi(input + 1); + nlen = snprintf(buf, sizeof(buf), "f%d", key); +#ifdef notdef + if (FKEY[key]) { /* CCC */ + input = FKEY[key]; + name = buf; + } else { + msgq(sp, M_ERR, "This terminal has no %s key.", buf); + return (1); + } +#else + name = NULL; +#endif + } else { + name = NULL; + + /* Some single keys may not be remapped in command mode. */ + if (stype == SEQ_COMMAND && input[1] == '\0') + switch (term_key_val(sp, input[0])) { + case K_COLON: + case K_CR: + case K_ESCAPE: + case K_NL: + msgq(sp, M_ERR, + "The %s character may not be remapped.", + charname(sp, input[0])); + return (1); + } + } + return (seq_set(sp, name, nlen, input, cmdp->argv[0]->len, + cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, 1)); +} + +/* + * ex_unmap -- (:unmap[!] key) + * Unmap a key. + */ +int +ex_unmap(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len, + F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) { + msgq(sp, M_INFO, "\"%s\" isn't mapped.", cmdp->argv[0]->bp); + return (1); + } + return (0); +} + +/* + * map_save -- + * Save the mapped sequences to a file. + */ +int +map_save(sp, fp) + SCR *sp; + FILE *fp; +{ + if (seq_save(sp, fp, "map ", SEQ_COMMAND)) + return (1); + return (seq_save(sp, fp, "map! ", SEQ_INPUT)); +} diff --git a/usr.bin/vi/nex/ex_mark.c b/usr.bin/vi/nex/ex_mark.c new file mode 100644 index 000000000000..34efe21648b1 --- /dev/null +++ b/usr.bin/vi/nex/ex_mark.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_mark.c 8.4 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +int +ex_mark(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (cmdp->argv[0]->len != 1) { + msgq(sp, M_ERR, "Mark names must be a single character."); + return (1); + } + return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1)); +} diff --git a/usr.bin/vi/nex/ex_mkexrc.c b/usr.bin/vi/nex/ex_mkexrc.c new file mode 100644 index 000000000000..b26c7f814532 --- /dev/null +++ b/usr.bin/vi/nex/ex_mkexrc.c @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_mkexrc.c 8.8 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "seq.h" +#include "pathnames.h" + +/* + * ex_mkexrc -- :mkexrc[!] [file] + * + * Create (or overwrite) a .exrc file with the current info. + */ +int +ex_mkexrc(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + struct stat sb; + FILE *fp; + int fd, sverrno; + char *fname; + + switch (cmdp->argc) { + case 0: + fname = _PATH_EXRC; + break; + case 1: + fname = cmdp->argv[0]->bp; + set_alt_name(sp, fname); + break; + default: + abort(); + } + + if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) { + msgq(sp, M_ERR, + "%s exists, not written; use ! to override.", fname); + return (1); + } + + /* Create with max permissions of rw-r--r--. */ + if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + msgq(sp, M_SYSERR, fname); + return (1); + } + + if ((fp = fdopen(fd, "w")) == NULL) { + sverrno = errno; + (void)close(fd); + errno = sverrno; + goto e2; + } + + if (abbr_save(sp, fp) || ferror(fp)) + goto e1; + if (map_save(sp, fp) || ferror(fp)) + goto e1; + if (opts_save(sp, fp) || ferror(fp)) + goto e1; +#ifndef NO_DIGRAPH + digraph_save(sp, fd); +#endif + if (fclose(fp)) + goto e2; + + msgq(sp, M_INFO, "New .exrc file: %s. ", fname); + return (0); + +e1: sverrno = errno; + (void)fclose(fp); + errno = sverrno; +e2: msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno)); + return (1); +} diff --git a/usr.bin/vi/nex/ex_move.c b/usr.bin/vi/nex/ex_move.c new file mode 100644 index 000000000000..965618b0834f --- /dev/null +++ b/usr.bin/vi/nex/ex_move.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_move.c 8.6 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +enum which {COPY, MOVE}; +static int cm __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_copy -- :[line [,line]] co[py] line [flags] + * Copy selected lines. + */ +int +ex_copy(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cm(sp, ep, cmdp, COPY)); +} + +/* + * ex_move -- :[line [,line]] co[py] line + * Move selected lines. + */ +int +ex_move(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cm(sp, ep, cmdp, MOVE)); +} + +static int +cm(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + CB cb; + MARK fm1, fm2, m, tm; + recno_t diff; + int rval; + + fm1 = cmdp->addr1; + fm2 = cmdp->addr2; + tm.lno = cmdp->lineno; + tm.cno = 0; + + /* Make sure the destination is valid. */ + if (cmd == MOVE && tm.lno >= fm1.lno && tm.lno < fm2.lno) { + msgq(sp, M_ERR, "Destination line is inside move range."); + return (1); + } + + /* Save the text to a cut buffer. */ + memset(&cb, 0, sizeof(cb)); + CIRCLEQ_INIT(&cb.textq); + if (cut(sp, ep, &cb, NULL, &fm1, &fm2, CUT_LINEMODE)) + return (1); + + /* If we're not copying, delete the old text and adjust tm. */ + if (cmd == MOVE) { + if (delete(sp, ep, &fm1, &fm2, 1)) { + rval = 1; + goto err; + } + if (tm.lno >= fm1.lno) + tm.lno -= (fm2.lno - fm1.lno) + 1; + } + + /* Add the new text. */ + if (put(sp, ep, &cb, NULL, &tm, &m, 1)) { + rval = 1; + goto err; + } + + /* + * Move and copy put the cursor on the last line moved or copied. + * The returned cursor from the put routine is the first line put, + * not the last, because that's the semantics of vi. + */ + diff = (fm2.lno - fm1.lno) + 1; + sp->lno = m.lno + (diff - 1); + sp->cno = 0; + + sp->rptlines[cmd == COPY ? L_COPIED : L_MOVED] += diff; + rval = 0; + +err: (void)text_lfree(&cb.textq); + return (rval); +} diff --git a/usr.bin/vi/nex/ex_open.c b/usr.bin/vi/nex/ex_open.c new file mode 100644 index 000000000000..77b418a41d6f --- /dev/null +++ b/usr.bin/vi/nex/ex_open.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_open.c 8.2 (Berkeley) 10/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_open -- :[line] o[pen] [/pattern/] [flags] + * + * Switch to single line "open" mode. + */ +int +ex_open(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* If open option off, disallow open command. */ + if (!O_ISSET(sp, O_OPEN)) { + msgq(sp, M_ERR, + "The open command requires that the open option be set."); + return (1); + } + + msgq(sp, M_ERR, "The open command is not yet implemented."); + return (1); +} diff --git a/usr.bin/vi/nex/ex_preserve.c b/usr.bin/vi/nex/ex_preserve.c new file mode 100644 index 000000000000..a7f35485698d --- /dev/null +++ b/usr.bin/vi/nex/ex_preserve.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_preserve.c 8.4 (Berkeley) 11/8/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_preserve -- :pre[serve] + * Push the file to recovery. + */ +int +ex_preserve(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + if (!F_ISSET(ep, F_RCV_ON)) { + msgq(sp, M_ERR, "Preservation of this file not possible."); + return (1); + } + + /* If recovery not initialized, do so. */ + if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep)) + return (1); + + /* Force the file to be read in, in case it hasn't yet. */ + if (file_lline(sp, ep, &lno)) + return (1); + + /* Sync to disk. */ + if (rcv_sync(sp, ep)) + return (1); + + /* Preserve the recovery files. */ + F_SET(ep, F_RCV_NORM); + + msgq(sp, M_INFO, "File preserved."); + return (0); +} diff --git a/usr.bin/vi/nex/ex_print.c b/usr.bin/vi/nex/ex_print.c new file mode 100644 index 000000000000..d25e9ff563b1 --- /dev/null +++ b/usr.bin/vi/nex/ex_print.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_print.c 8.6 (Berkeley) 11/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_list -- :[line [,line]] l[ist] [count] [flags] + * + * Display the addressed lines such that the output is unambiguous. + */ +int +ex_list(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, + &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_number -- :[line [,line]] nu[mber] [count] [flags] + * + * Display the addressed lines with a leading line number. + */ +int +ex_number(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, + &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_pr -- :[line [,line]] p[rint] [count] [flags] + * + * Display the addressed lines. + */ +int +ex_pr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_print -- + * Print the selected lines. + */ +int +ex_print(sp, ep, fp, tp, flags) + SCR *sp; + EXF *ep; + MARK *fp, *tp; + register int flags; +{ + register int ch, col, rlen; + recno_t from, to; + size_t len; + int cnt; + char *p, buf[10]; + + F_SET(sp, S_INTERRUPTIBLE); + for (from = fp->lno, to = tp->lno; from <= to; ++from) { + /* Display the line number. */ + if (LF_ISSET(E_F_HASH)) + col = ex_printf(EXCOOKIE, "%7ld ", from); + else + col = 0; + + /* + * Display the line. The format for E_F_PRINT isn't very good, + * especially in handling end-of-line tabs, but they're almost + * backward compatible. + */ + if ((p = file_gline(sp, ep, from, &len)) == NULL) { + GETLINE_ERR(sp, from); + return (1); + } + +#define WCHECK(ch) { \ + if (col == sp->cols) { \ + (void)ex_printf(EXCOOKIE, "\n"); \ + col = 0; \ + } \ + (void)ex_printf(EXCOOKIE, "%c", ch); \ + ++col; \ +} + for (rlen = len; rlen--;) { + ch = *p++; + if (LF_ISSET(E_F_LIST)) + if (ch != '\t' && isprint(ch)) { + WCHECK(ch); + } else if (ch & 0x80) { + (void)snprintf(buf, + sizeof(buf), "\\%03o", ch); + len = strlen(buf); + for (cnt = 0; cnt < len; ++cnt) + WCHECK(buf[cnt]); + } else { + WCHECK('^'); + WCHECK(ch + 0x40); + } + else { + ch &= 0x7f; + if (ch == '\t') { + while (col < sp->cols && + ++col % O_VAL(sp, O_TABSTOP)) + (void)ex_printf(EXCOOKIE, " "); + if (col == sp->cols) { + col = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } + } else if (isprint(ch)) { + WCHECK(ch); + } else if (ch == '\n') { + col = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else { + WCHECK('^'); + WCHECK(ch + 0x40); + } + } + } + if (LF_ISSET(E_F_LIST)) { + WCHECK('$'); + } else if (len == 0) { + /* + * If the line is empty, output a space + * to overwrite the colon prompt. + */ + WCHECK(' '); + } + (void)ex_printf(EXCOOKIE, "\n"); + + if (F_ISSET(sp, S_INTERRUPTED)) + break; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_put.c b/usr.bin/vi/nex/ex_put.c new file mode 100644 index 000000000000..3bc687fd1496 --- /dev/null +++ b/usr.bin/vi/nex/ex_put.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_put.c 8.4 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_put -- [line] pu[t] [buffer] + * + * Append a cut buffer into the file. + */ +int +ex_put(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + MARK m; + + m.lno = sp->lno; + m.cno = sp->cno; + if (put(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &m, 1)) + return (1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_read.c b/usr.bin/vi/nex/ex_read.c new file mode 100644 index 000000000000..9328b93c329a --- /dev/null +++ b/usr.bin/vi/nex/ex_read.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_read.c 8.21 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_read -- :read [file] + * :read [! cmd] + * Read from a file or utility. + */ +int +ex_read(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + struct stat sb; + FILE *fp; + MARK rm; + recno_t nlines; + size_t blen, len; + int rval; + char *bp, *name; + + /* + * If "read !", it's a pipe from a utility. + * + * !!! + * Historical vi wouldn't undo a filter read, for no apparent + * reason. + */ + if (F_ISSET(cmdp, E_FORCE)) { + /* Expand the user's argument. */ + if (argv_exp1(sp, ep, + cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0)) + return (1); + + /* If argc still 1, there wasn't anything to expand. */ + if (cmdp->argc == 1) { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + /* Redisplay the user's argument if it's changed. */ + if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) { + len = cmdp->argv[1]->len; + GET_SPACE_RET(sp, bp, blen, len + 2); + bp[0] = '!'; + memmove(bp + 1, cmdp->argv[1], cmdp->argv[1]->len + 1); + (void)sp->s_busy(sp, bp); + FREE_SPACE(sp, bp, blen); + } + + if (filtercmd(sp, ep, + &cmdp->addr1, NULL, &rm, cmdp->argv[1]->bp, FILTER_READ)) + return (1); + sp->lno = rm.lno; + return (0); + } + + /* Expand the user's argument. */ + if (argv_exp2(sp, ep, + cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0)) + return (1); + + switch (cmdp->argc) { + case 1: + /* + * No arguments, read the current file. + * Doesn't set the alternate file name. + */ + name = FILENAME(sp->frp); + break; + case 2: + /* + * One argument, read it. + * Sets the alternate file name. + */ + name = cmdp->argv[1]->bp; + set_alt_name(sp, name); + break; + default: + /* If expanded to more than one argument, object. */ + msgq(sp, M_ERR, + "%s expanded into too many file names", cmdp->argv[0]->bp); + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + /* + * !!! + * Historically, vi did not permit reads from non-regular files, + * nor did it distinguish between "read !" and "read!", so there + * was no way to "force" it. + */ + if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) { + msgq(sp, M_SYSERR, name); + return (1); + } + if (!S_ISREG(sb.st_mode)) { + (void)fclose(fp); + msgq(sp, M_ERR, "Only regular files may be read."); + return (1); + } + + rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1); + + /* + * Set the cursor to the first line read in, if anything read + * in, otherwise, the address. (Historic vi set it to the + * line after the address regardless, but since that line may + * not exist we don't bother.) + */ + sp->lno = cmdp->addr1.lno; + if (nlines) + ++sp->lno; + + F_SET(EXP(sp), EX_AUTOPRINT); + return (rval); +} + +/* + * ex_readfp -- + * Read lines into the file. + */ +int +ex_readfp(sp, ep, name, fp, fm, nlinesp, success_msg) + SCR *sp; + EXF *ep; + char *name; + FILE *fp; + MARK *fm; + recno_t *nlinesp; + int success_msg; +{ + EX_PRIVATE *exp; + u_long ccnt; + size_t len; + recno_t lno, nlines; + int rval; + + /* + * Add in the lines from the output. Insertion starts at the line + * following the address. + */ + ccnt = 0; + rval = 0; + exp = EXP(sp); + for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno) { + if (file_aline(sp, ep, 1, lno, exp->ibp, len)) { + rval = 1; + break; + } + ccnt += len; + } + + if (ferror(fp)) { + msgq(sp, M_SYSERR, name); + rval = 1; + } + + if (fclose(fp)) { + msgq(sp, M_SYSERR, name); + return (1); + } + + if (rval) + return (1); + + /* Return the number of lines read in. */ + nlines = lno - fm->lno; + if (nlinesp != NULL) + *nlinesp = nlines; + + if (success_msg) + msgq(sp, M_INFO, "%s: %lu line%s, %lu characters.", + name, nlines, nlines == 1 ? "" : "s", ccnt); + + return (0); +} diff --git a/usr.bin/vi/nex/ex_screen.c b/usr.bin/vi/nex/ex_screen.c new file mode 100644 index 000000000000..0fd000d4898a --- /dev/null +++ b/usr.bin/vi/nex/ex_screen.c @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_screen.c 8.10 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_split -- :s[plit] [file ...] + * Split the screen, optionally setting the file list. + */ +int +ex_split(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_split(sp, cmdp->argc ? cmdp->argv : NULL)); +} + +/* + * ex_bg -- :bg + * Hide the screen. + */ +int +ex_bg(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_bg(sp)); +} + +/* + * ex_fg -- :fg [file] + * Show the screen. + */ +int +ex_fg(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_fg(sp, cmdp->argc ? cmdp->argv[0]->bp : NULL)); +} + +/* + * ex_resize -- :resize [change] + * Change the screen size. + */ +int +ex_resize(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(cmdp, E_COUNT)) + cmdp->count = 1; + return (sp->s_rabs(sp, cmdp->count)); +} + +/* + * ex_sdisplay -- + * Display the list of screens. + */ +int +ex_sdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + int cnt, col, len, sep; + + if ((tsp = sp->gp->hq.cqh_first) == (void *)&sp->gp->hq) { + (void)ex_printf(EXCOOKIE, + "No backgrounded screens to display.\n"); + return (0); + } + + col = len = sep = 0; + for (cnt = 1; tsp != (void *)&sp->gp->hq; tsp = tsp->q.cqe_next) { + col += len = strlen(FILENAME(tsp->frp)) + sep; + if (col >= sp->cols - 1) { + col = len; + sep = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else if (cnt != 1) { + sep = 1; + (void)ex_printf(EXCOOKIE, " "); + } + (void)ex_printf(EXCOOKIE, "%s", FILENAME(tsp->frp)); + ++cnt; + } + (void)ex_printf(EXCOOKIE, "\n"); + return (0); +} diff --git a/usr.bin/vi/nex/ex_script.c b/usr.bin/vi/nex/ex_script.c new file mode 100644 index 000000000000..606e479d6af4 --- /dev/null +++ b/usr.bin/vi/nex/ex_script.c @@ -0,0 +1,570 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_script.c 8.10 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "script.h" + +/* + * XXX + */ +int openpty __P((int *, int *, char *, struct termios *, struct winsize *)); + +static int sscr_getprompt __P((SCR *, EXF *)); +static int sscr_init __P((SCR *, EXF *)); +static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *)); +static int sscr_setprompt __P((SCR *, char *, size_t)); + +/* + * ex_script -- : sc[ript][!] [file] + * + * Switch to script mode. + */ +int +ex_script(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* Vi only command. */ + if (!IN_VI_MODE(sp)) { + msgq(sp, M_ERR, + "The script command is only available in vi mode."); + return (1); + } + + /* Switch to the new file. */ + if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp)) + return (1); + + /* + * Create the shell, figure out the prompt. + * + * !!! + * The files just switched, use sp->ep. + */ + if (sscr_init(sp, sp->ep)) + return (1); + + return (0); +} + +/* + * sscr_init -- + * Create a pty setup for a shell. + */ +static int +sscr_init(sp, ep) + SCR *sp; + EXF *ep; +{ + SCRIPT *sc; + char *sh, *sh_path; + + MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT)); + sp->script = sc; + sc->sh_prompt = NULL; + sc->sh_prompt_len = 0; + + /* + * There are two different processes running through this code. + * They are the shell and the parent. + */ + sc->sh_master = sc->sh_slave = -1; + + if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) { + msgq(sp, M_SYSERR, "tcgetattr"); + goto err; + } + + /* + * Turn off output postprocessing and echo. + */ + sc->sh_term.c_oflag &= ~OPOST; + sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK); + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) { + msgq(sp, M_SYSERR, "tcgetattr"); + goto err; + } + + if (openpty(&sc->sh_master, + &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) { + msgq(sp, M_SYSERR, "openpty"); + goto err; + } + + /* + * Don't use vfork() here, because the signal semantics + * differ from implementation to implementation. + */ + switch (sc->sh_pid = fork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "fork"); +err: if (sc->sh_master != -1) + (void)close(sc->sh_master); + if (sc->sh_slave != -1) + (void)close(sc->sh_slave); + return (1); + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + /* + * XXX + * So that shells that do command line editing turn it off. + */ + (void)putenv("TERM=emacs"); + (void)putenv("TERMCAP=emacs:"); + (void)putenv("EMACS=t"); + + (void)setsid(); +#ifdef TIOCSCTTY + /* + * 4.4BSD allocates a controlling terminal using the TIOCSCTTY + * ioctl, not by opening a terminal device file. POSIX 1003.1 + * doesn't define a portable way to do this. If TIOCSCTTY is + * not available, hope that the open does it. + */ + (void)ioctl(sc->sh_slave, TIOCSCTTY, 0); +#endif + (void)close(sc->sh_master); + (void)dup2(sc->sh_slave, STDIN_FILENO); + (void)dup2(sc->sh_slave, STDOUT_FILENO); + (void)dup2(sc->sh_slave, STDERR_FILENO); + (void)close(sc->sh_slave); + + /* Assumes that all shells have -i. */ + sh_path = O_STR(sp, O_SHELL); + if ((sh = strrchr(sh_path, '/')) == NULL) + sh = sh_path; + else + ++sh; + execl(sh_path, sh, "-i", NULL); + msgq(sp, M_ERR, + "Error: execl: %s: %s", sh_path, strerror(errno)); + _exit(127); + default: + break; + } + + if (sscr_getprompt(sp, ep)) + return (1); + + F_SET(sp, S_REDRAW | S_SCRIPT); + return (0); + +} + +/* + * sscr_getprompt -- + * Eat lines printed by the shell until a line with no trailing + * carriage return comes; set the prompt from that line. + */ +static int +sscr_getprompt(sp, ep) + SCR *sp; + EXF *ep; +{ + struct timeval tv; + SCRIPT *sc; + fd_set fdset; + recno_t lline; + size_t llen, len; + u_int value; + int nr; + char *endp, *p, *t, buf[1024]; + + FD_ZERO(&fdset); + endp = buf; + len = sizeof(buf); + + /* Wait up to a second for characters to read. */ + tv.tv_sec = 5; + tv.tv_usec = 0; + sc = sp->script; + FD_SET(sc->sh_master, &fdset); + switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "select"); + goto prompterr; + case 0: /* Timeout */ + msgq(sp, M_ERR, "Error: timed out."); + goto prompterr; + case 1: /* Characters to read. */ + break; + } + + /* Read the characters. */ +more: len = sizeof(buf) - (endp - buf); + switch (nr = read(sc->sh_master, endp, len)) { + case 0: /* EOF. */ + msgq(sp, M_ERR, "Error: shell: EOF"); + goto prompterr; + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "shell"); + goto prompterr; + default: + endp += nr; + break; + } + + /* If any complete lines, push them into the file. */ + for (p = t = buf; p < endp; ++p) { + value = term_key_val(sp, *p); + if (value == K_CR || value == K_NL) { + if (file_lline(sp, ep, &lline) || + file_aline(sp, ep, 0, lline, t, p - t)) + goto prompterr; + t = p + 1; + } + } + if (p > buf) { + memmove(buf, t, endp - t); + endp = buf + (endp - t); + } + if (endp == buf) + goto more; + + /* Wait up 1/10 of a second to make sure that we got it all. */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "select"); + goto prompterr; + case 0: /* Timeout */ + break; + case 1: /* Characters to read. */ + goto more; + } + + /* Timed out, so theoretically we have a prompt. */ + llen = endp - buf; + endp = buf; + + /* Append the line into the file. */ + if (file_lline(sp, ep, &lline) || + file_aline(sp, ep, 0, lline, buf, llen)) { +prompterr: sscr_end(sp); + return (1); + } + + return (sscr_setprompt(sp, buf, llen)); +} + +/* + * sscr_exec -- + * Take a line and hand it off to the shell. + */ +int +sscr_exec(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SCRIPT *sc; + recno_t last_lno; + size_t blen, len, last_len, tlen; + int matchprompt, nw, rval; + char *bp, *p; + + /* If there's a prompt on the last line, append the command. */ + if (file_lline(sp, ep, &last_lno)) + return (1); + if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) { + GETLINE_ERR(sp, last_lno); + return (1); + } + if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) { + matchprompt = 1; + GET_SPACE_RET(sp, bp, blen, last_len + 128); + memmove(bp, p, last_len); + } else + matchprompt = 0; + + /* Get something to execute. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + goto err1; + if (lno == 0) + goto empty; + else + GETLINE_ERR(sp, lno); + goto err1; + } + + /* Empty lines aren't interesting. */ + if (len == 0) + goto empty; + + /* Delete any prompt. */ + if (sscr_matchprompt(sp, p, len, &tlen)) { + if (tlen == len) { +empty: msgq(sp, M_BERR, "Nothing to execute."); + goto err1; + } + p += (len - tlen); + len = tlen; + } + + /* Push the line to the shell. */ + sc = sp->script; + if ((nw = write(sc->sh_master, p, len)) != len) + goto err2; + rval = 0; + if (write(sc->sh_master, "\n", 1) != 1) { +err2: if (nw == 0) + errno = EIO; + msgq(sp, M_SYSERR, "shell"); + goto err1; + } + + if (matchprompt) { + ADD_SPACE_RET(sp, bp, blen, last_len + len); + memmove(bp + last_len, p, len); + if (file_sline(sp, ep, last_lno, bp, last_len + len)) +err1: rval = 1; + } + if (matchprompt) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * sscr_input -- + * Take a line from the shell and insert it into the file. + */ +int +sscr_input(sp) + SCR *sp; +{ + struct timeval tv; + SCRIPT *sc; + EXF *ep; + recno_t lno; + size_t blen, len, tlen; + u_int value; + int nr, rval; + char *bp, *endp, *p, *t; + + /* Find out where the end of the file is. */ + ep = sp->ep; + if (file_lline(sp, ep, &lno)) + return (1); + +#define MINREAD 1024 + GET_SPACE_RET(sp, bp, blen, MINREAD); + endp = bp; + + /* Read the characters. */ + rval = 1; + sc = sp->script; +more: switch (nr = read(sc->sh_master, endp, MINREAD)) { + case 0: /* EOF; shell just exited. */ + sscr_end(sp); + F_CLR(sp, S_SCRIPT); + rval = 0; + goto ret; + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "shell"); + goto ret; + default: + endp += nr; + break; + } + + /* Append the lines into the file. */ + for (p = t = bp; p < endp; ++p) { + value = term_key_val(sp, *p); + if (value == K_CR || value == K_NL) { + len = p - t; + if (file_aline(sp, ep, 1, lno++, t, len)) + goto ret; + t = p + 1; + } + } + if (p > t) { + len = p - t; + /* + * If the last thing from the shell isn't another prompt, wait + * up to 1/10 of a second for more stuff to show up, so that + * we don't break the output into two separate lines. Don't + * want to hang indefinitely because some program is hanging, + * confused the shell, or whatever. + */ + if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + FD_SET(sc->sh_master, &sp->rdfd); + FD_CLR(STDIN_FILENO, &sp->rdfd); + if (select(sc->sh_master + 1, + &sp->rdfd, NULL, NULL, &tv) == 1) { + memmove(bp, t, len); + endp = bp + len; + goto more; + } + } + if (sscr_setprompt(sp, t, len)) + return (1); + if (file_aline(sp, ep, 1, lno++, t, len)) + goto ret; + } + + /* The cursor moves to EOF. */ + sp->lno = lno; + sp->cno = len ? len - 1 : 0; + rval = sp->s_refresh(sp, ep); + +ret: FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * sscr_setprompt -- + * + * Set the prompt to the last line we got from the shell. + * + */ +static int +sscr_setprompt(sp, buf, len) + SCR *sp; + char* buf; + size_t len; +{ + SCRIPT *sc; + + sc = sp->script; + if (sc->sh_prompt) + FREE(sc->sh_prompt, sc->sh_prompt_len); + MALLOC(sp, sc->sh_prompt, char *, len + 1); + if (sc->sh_prompt == NULL) { + sscr_end(sp); + return (1); + } + memmove(sc->sh_prompt, buf, len); + sc->sh_prompt_len = len; + sc->sh_prompt[len] = '\0'; + return (0); +} + +/* + * sscr_matchprompt -- + * Check to see if a line matches the prompt. Nul's indicate + * parts that can change, in both content and size. + */ +static int +sscr_matchprompt(sp, lp, line_len, lenp) + SCR *sp; + char *lp; + size_t line_len, *lenp; +{ + SCRIPT *sc; + size_t prompt_len; + char *pp; + + sc = sp->script; + if (line_len < (prompt_len = sc->sh_prompt_len)) + return (0); + + for (pp = sc->sh_prompt; + prompt_len && line_len; --prompt_len, --line_len) { + if (*pp == '\0') { + for (; prompt_len && *pp == '\0'; --prompt_len, ++pp); + if (!prompt_len) + return (0); + for (; line_len && *lp != *pp; --line_len, ++lp); + if (!line_len) + return (0); + } + if (*pp++ != *lp++) + break; + } + + if (prompt_len) + return (0); + if (lenp != NULL) + *lenp = line_len; + return (1); +} + +/* + * sscr_end -- + * End the pipe to a shell. + */ +int +sscr_end(sp) + SCR *sp; +{ + SCRIPT *sc; + int rval; + + if ((sc = sp->script) == NULL) + return (0); + + /* Turn off the script flag. */ + F_CLR(sp, S_SCRIPT); + + /* Close down the parent's file descriptors. */ + if (sc->sh_master != -1) + (void)close(sc->sh_master); + if (sc->sh_slave != -1) + (void)close(sc->sh_slave); + + /* This should have killed the child. */ + rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0); + + /* Free memory. */ + FREE(sc->sh_prompt, sc->sh_prompt_len); + FREE(sc, sizeof(SCRIPT)); + sp->script = NULL; + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_set.c b/usr.bin/vi/nex/ex_set.c new file mode 100644 index 000000000000..a463524e5fa9 --- /dev/null +++ b/usr.bin/vi/nex/ex_set.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_set.c 8.2 (Berkeley) 10/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +int +ex_set(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch(cmdp->argc) { + case 0: + opts_dump(sp, CHANGED_DISPLAY); + break; + default: + opts_set(sp, cmdp->argv); + break; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_shell.c b/usr.bin/vi/nex/ex_shell.c new file mode 100644 index 000000000000..282cc8f4fbdc --- /dev/null +++ b/usr.bin/vi/nex/ex_shell.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_shell.c 8.17 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <curses.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "../svi/svi_screen.h" + +/* + * ex_shell -- :sh[ell] + * Invoke the program named in the SHELL environment variable + * with the argument -i. + */ +int +ex_shell(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + char buf[MAXPATHLEN]; + + (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)); + return (ex_exec_proc(sp, buf, "\n", NULL)); +} + +/* + * ex_exec_proc -- + * Run a separate process. + */ +int +ex_exec_proc(sp, cmd, p1, p2) + SCR *sp; + char *cmd, *p1, *p2; +{ + struct sigaction act, oact; + struct stat osb, sb; + struct termios term; + const char *name; + pid_t pid; + int isig, rval; + + /* Clear the rest of the screen. */ + if (sp->s_clear(sp)) + return (1); + + /* Save ex/vi terminal settings, and restore the original ones. */ + EX_LEAVE(sp, isig, act, oact, sb, osb, term); + + /* Put out various messages. */ + if (p1 != NULL) + (void)write(STDOUT_FILENO, p1, strlen(p1)); + if (p2 != NULL) + (void)write(STDOUT_FILENO, p2, strlen(p2)); + + + switch (pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); + rval = 1; + goto err; + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) + name = O_STR(sp, O_SHELL); + else + ++name; + execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL); + msgq(sp, M_ERR, "Error: execl: %s: %s", + O_STR(sp, O_SHELL), strerror(errno)); + _exit(127); + /* NOTREACHED */ + } + + rval = proc_wait(sp, (long)pid, cmd, 0); + + /* Restore ex/vi terminal settings. */ +err: EX_RETURN(sp, isig, act, oact, sb, osb, term); + + /* + * XXX + * EX_LEAVE/EX_RETURN only give us 1-second resolution on the tty + * changes. A fast '!' command, e.g. ":!pwd" can beat us to the + * refresh. When there's better resolution from the stat(2) timers, + * this can go away. + */ + F_SET(sp, S_REFRESH); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_shift.c b/usr.bin/vi/nex/ex_shift.c new file mode 100644 index 000000000000..8a0b858cbb6f --- /dev/null +++ b/usr.bin/vi/nex/ex_shift.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_shift.c 8.12 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +enum which {LEFT, RIGHT}; +static int shift __P((SCR *, EXF *, EXCMDARG *, enum which)); + +int +ex_shiftl(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (shift(sp, ep, cmdp, LEFT)); +} + +int +ex_shiftr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (shift(sp, ep, cmdp, RIGHT)); +} + +static int +shift(sp, ep, cmdp, rl) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which rl; +{ + recno_t from, to; + size_t blen, len, newcol, newidx, oldcol, oldidx, sw; + int curset; + char *p, *bp, *tbp; + + if (O_VAL(sp, O_SHIFTWIDTH) == 0) { + msgq(sp, M_INFO, "shiftwidth option set to 0"); + return (0); + } + + /* + * The historic version of vi permitted the user to string any number + * of '>' or '<' characters together, resulting in an indent of the + * appropriate levels. There's a special hack in ex_cmd() so that + * cmdp->argv[0] points to the string of '>' or '<' characters. + * + * Q: What's the difference between the people adding features + * to vi and the Girl Scouts? + * A: The Girl Scouts have mint cookies and adult supervision. + */ + for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p) + sw += O_VAL(sp, O_SHIFTWIDTH); + + GET_SPACE_RET(sp, bp, blen, 256); + + curset = 0; + for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) { + if ((p = file_gline(sp, ep, from, &len)) == NULL) + goto err; + if (!len) { + if (sp->lno == from) + curset = 1; + continue; + } + + /* + * Calculate the old indent amount and the number of + * characters it used. + */ + for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx) + if (p[oldidx] == ' ') + ++oldcol; + else if (p[oldidx] == '\t') + oldcol += O_VAL(sp, O_TABSTOP) - + oldcol % O_VAL(sp, O_TABSTOP); + else + break; + + /* Calculate the new indent amount. */ + if (rl == RIGHT) + newcol = oldcol + sw; + else { + newcol = oldcol < sw ? 0 : oldcol - sw; + if (newcol == oldcol) { + if (sp->lno == from) + curset = 1; + continue; + } + } + + /* Get a buffer that will hold the new line. */ + ADD_SPACE_RET(sp, bp, blen, newcol + len); + + /* + * Build a new indent string and count the number of + * characters it uses. + */ + for (tbp = bp, newidx = 0; + newcol >= O_VAL(sp, O_TABSTOP); ++newidx) { + *tbp++ = '\t'; + newcol -= O_VAL(sp, O_TABSTOP); + } + for (; newcol > 0; --newcol, ++newidx) + *tbp++ = ' '; + + /* Add the original line. */ + memmove(tbp, p + oldidx, len - oldidx); + + /* Set the replacement line. */ + if (file_sline(sp, ep, from, bp, (tbp + (len - oldidx)) - bp)) { +err: FREE_SPACE(sp, bp, blen); + return (1); + } + + /* + * !!! + * The shift command in historic vi had the usual bizarre + * collection of cursor semantics. If called from vi, the + * cursor was repositioned to the first non-blank character + * of the lowest numbered line shifted. If called from ex, + * the cursor was repositioned to the first non-blank of the + * highest numbered line shifted. Here, if the cursor isn't + * part of the set of lines that are moved, move it to the + * first non-blank of the last line shifted. (This makes + * ":3>>" in vi work reasonably.) If the cursor is part of + * the shifted lines, it doesn't get moved at all. This + * permits shifting of marked areas, i.e. ">'a." shifts the + * marked area twice, something that couldn't be done with + * historic vi. + */ + if (sp->lno == from) { + curset = 1; + if (newidx > oldidx) + sp->cno += newidx - oldidx; + else if (sp->cno >= oldidx - newidx) + sp->cno -= oldidx - newidx; + } + } + if (!curset) { + sp->lno = to; + sp->cno = 0; + (void)nonblank(sp, ep, to, &sp->cno); + } + + FREE_SPACE(sp, bp, blen); + + sp->rptlines[rl == RIGHT ? L_RSHIFT : L_LSHIFT] += + cmdp->addr2.lno - cmdp->addr1.lno + 1; + return (0); +} diff --git a/usr.bin/vi/nex/ex_source.c b/usr.bin/vi/nex/ex_source.c new file mode 100644 index 000000000000..bf2a9f1b275e --- /dev/null +++ b/usr.bin/vi/nex/ex_source.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_source.c 8.3 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_source -- :source file + * Execute ex commands from a file. + */ +int +ex_source(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (ex_cfile(sp, ep, cmdp->argv[0]->bp)); +} diff --git a/usr.bin/vi/nex/ex_stop.c b/usr.bin/vi/nex/ex_stop.c new file mode 100644 index 000000000000..26ec067c622a --- /dev/null +++ b/usr.bin/vi/nex/ex_stop.c @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_stop.c 8.4 (Berkeley) 10/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex/sex_screen.h" + +/* + * ex_stop -- :stop[!] + * :suspend[!] + * Suspend execution. + */ +int +ex_stop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* For some strange reason, the force flag turns off autowrite. */ + if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) && + !F_ISSET(cmdp, E_FORCE)) { + if (file_write((sp), (ep), NULL, NULL, NULL, FS_ALL)) + return (1); + if (sex_refresh(sp, ep)) + return (1); + } + return (sp->s_suspend(sp)); +} diff --git a/usr.bin/vi/nex/ex_subst.c b/usr.bin/vi/nex/ex_subst.c new file mode 100644 index 000000000000..6d132c56dd24 --- /dev/null +++ b/usr.bin/vi/nex/ex_subst.c @@ -0,0 +1,984 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_substitute.c 8.33 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "interrupt.h" + +#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */ +#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */ + +static int checkmatchsize __P((SCR *, regex_t *)); +static inline int regsub __P((SCR *, + char *, char **, size_t *, size_t *)); +static void subst_intr __P((int)); +static int substitute __P((SCR *, EXF *, + EXCMDARG *, char *, regex_t *, u_int)); + +/* + * ex_substitute -- + * [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]] + * + * Substitute on lines matching a pattern. + */ +int +ex_substitute(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + regex_t *re, lre; + size_t blen, len; + u_int flags; + int delim, eval, reflags, replaced; + char *bp, *ptrn, *rep, *p, *t; + + /* + * Skip leading white space. + * + * !!! + * Historic vi allowed any non-alphanumeric to serve as the + * substitution command delimiter. + * + * !!! + * If the arguments are empty, it's the same as &, i.e. we + * repeat the last substitution. + */ + for (p = cmdp->argv[0]->bp, + len = cmdp->argv[0]->len; len > 0; --len, ++p) { + if (!isblank(*p)) + break; + } + if (len == 0) + return (ex_subagain(sp, ep, cmdp)); + delim = *p++; + if (isalnum(delim)) + return (substitute(sp, ep, + cmdp, p, &sp->subre, SUB_MUSTSETR)); + + /* + * Get the pattern string, toss escaped characters. + * + * !!! + * Historic vi accepted any of the following forms: + * + * :s/abc/def/ change "abc" to "def" + * :s/abc/def change "abc" to "def" + * :s/abc/ delete "abc" + * :s/abc delete "abc" + * + * QUOTING NOTE: + * + * Only toss an escape character if it escapes a delimiter. + * This means that "s/A/\\\\f" replaces "A" with "\\f". It + * would be nice to be more regular, i.e. for each layer of + * escaping a single escape character is removed, but that's + * not how the historic vi worked. + */ + for (ptrn = t = p;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + /* + * !!! + * Nul terminate the pattern string -- it's passed + * to regcomp which doesn't understand anything else. + */ + *t = '\0'; + break; + } + if (p[0] == '\\' && p[1] == delim) + ++p; + *t++ = *p++; + } + + /* If the pattern string is empty, use the last one. */ + if (*ptrn == NULL) { + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, + "No previous regular expression."); + return (1); + } + re = &sp->subre; + flags = 0; + } else { + /* Set RE flags. */ + reflags = 0; + if (O_ISSET(sp, O_EXTENDED)) + reflags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + reflags |= REG_ICASE; + + /* Convert vi-style RE's to POSIX 1003.2 RE's. */ + if (re_conv(sp, &ptrn, &replaced)) + return (1); + + /* Compile the RE. */ + eval = regcomp(&lre, (char *)ptrn, reflags); + + /* Free up any allocated memory. */ + if (replaced) + FREE_SPACE(sp, ptrn, 0); + + if (eval) { + re_error(sp, eval, &lre); + return (1); + } + + /* + * Set saved RE. + * + * !!! + * Historic practice is that substitutes set the search + * direction as well as both substitute and search RE's. + */ + sp->searchdir = FORWARD; + sp->sre = lre; + F_SET(sp, S_SRE_SET); + sp->subre = lre; + F_SET(sp, S_SUBRE_SET); + + re = &lre; + flags = SUB_FIRST; + } + + /* + * Get the replacement string. + * + * The special character ~ (\~ if O_MAGIC not set) inserts the + * previous replacement string into this replacement string. + * + * The special character & (\& if O_MAGIC not set) matches the + * entire RE. No handling of & is required here, it's done by + * regsub(). + * + * QUOTING NOTE: + * + * Only toss an escape character if it escapes a delimiter or + * an escape character, or if O_MAGIC is set and it escapes a + * tilde. + */ + if (*p == '\0') { + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + sp->repl = NULL; + sp->repl_len = 0; + } else { + /* + * Count ~'s to figure out how much space we need. We could + * special case nonexistent last patterns or whether or not + * O_MAGIC is set, but it's probably not worth the effort. + */ + for (rep = p, len = 0; + p[0] != '\0' && p[0] != delim; ++p, ++len) + if (p[0] == '~') + len += sp->repl_len; + GET_SPACE_RET(sp, bp, blen, len); + for (t = bp, len = 0, p = rep;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + break; + } + if (p[0] == '\\') { + if (p[1] == '\\' || p[1] == delim) + ++p; + else if (p[1] == '~') { + ++p; + if (!O_ISSET(sp, O_MAGIC)) + goto tilde; + } + } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) { +tilde: ++p; + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + len += sp->repl_len; + continue; + } + *t++ = *p++; + ++len; + } + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + if ((sp->repl = malloc(len)) == NULL) { + msgq(sp, M_SYSERR, NULL); + FREE_SPACE(sp, bp, blen); + return (1); + } + memmove(sp->repl, bp, len); + sp->repl_len = len; + FREE_SPACE(sp, bp, blen); + } + + if (checkmatchsize(sp, &sp->subre)) + return (1); + return (substitute(sp, ep, cmdp, p, re, flags)); +} + +/* + * ex_subagain -- + * [line [,line]] & [cgr] [count] [#lp]] + * + * Substitute using the last substitute RE and replacement pattern. + */ +int +ex_subagain(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0)); +} + +/* + * ex_subtilde -- + * [line [,line]] ~ [cgr] [count] [#lp]] + * + * Substitute using the last RE and last substitute replacement pattern. + */ +int +ex_subtilde(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0)); +} + +/* + * The nasty part of the substitution is what happens when the replacement + * string contains newlines. It's a bit tricky -- consider the information + * that has to be retained for "s/f\(o\)o/^M\1^M\1/". The solution here is + * to build a set of newline offsets which we use to break the line up later, + * when the replacement is done. Don't change it unless you're pretty damned + * confident. + */ +#define NEEDNEWLINE(sp) { \ + if (sp->newl_len == sp->newl_cnt) { \ + sp->newl_len += 25; \ + REALLOC(sp, sp->newl, size_t *, \ + sp->newl_len * sizeof(size_t)); \ + if (sp->newl == NULL) { \ + sp->newl_len = 0; \ + return (1); \ + } \ + } \ +} + +#define BUILD(sp, l, len) { \ + if (lbclen + (len) > lblen) { \ + lblen += MAX(lbclen + (len), 256); \ + REALLOC(sp, lb, char *, lblen); \ + if (lb == NULL) { \ + lbclen = 0; \ + return (1); \ + } \ + } \ + memmove(lb + lbclen, l, len); \ + lbclen += len; \ +} + +#define NEEDSP(sp, len, pnt) { \ + if (lbclen + (len) > lblen) { \ + lblen += MAX(lbclen + (len), 256); \ + REALLOC(sp, lb, char *, lblen); \ + if (lb == NULL) { \ + lbclen = 0; \ + return (1); \ + } \ + pnt = lb + lbclen; \ + } \ +} + +/* + * substitute -- + * Do the substitution. This stuff is *really* tricky. There are + * lots of special cases, and general nastiness. Don't mess with it + * unless you're pretty confident. + */ +static int +substitute(sp, ep, cmdp, s, re, flags) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + char *s; + regex_t *re; + u_int flags; +{ + DECLARE_INTERRUPTS; + MARK from, to; + recno_t elno, lno; + size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset; + int didsub, do_eol_match, eflags, empty_ok, eval; + int linechanged, matched, quit, rval; + int cflag, gflag, lflag, nflag, pflag, rflag; + char *bp, *lb; + + /* + * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but + * it only displayed the last change. I'd disallow them, but they are + * useful in combination with the [v]global commands. In the current + * model the problem is combining them with the 'c' flag -- the screen + * would have to flip back and forth between the confirm screen and the + * ex print screen, which would be pretty awful. We do display all + * changes, though, for what that's worth. + * + * !!! + * Historic vi was fairly strict about the order of "options", the + * count, and "flags". I'm somewhat fuzzy on the difference between + * options and flags, anyway, so this is a simpler approach, and we + * just take it them in whatever order the user gives them. (The ex + * usage statement doesn't reflect this.) + */ + cflag = gflag = lflag = nflag = pflag = rflag = 0; + for (lno = OOBLNO; *s != '\0'; ++s) + switch (*s) { + case ' ': + case '\t': + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (lno != OOBLNO) + goto usage; + errno = 0; + lno = strtoul(s, &s, 10); + if (*s == '\0') /* Loop increment correction. */ + --s; + if (errno == ERANGE) { + if (lno == LONG_MAX) + msgq(sp, M_ERR, "Count overflow."); + else if (lno == LONG_MIN) + msgq(sp, M_ERR, "Count underflow."); + else + msgq(sp, M_SYSERR, NULL); + return (1); + } + /* + * In historic vi, the count was inclusive from the + * second address. + */ + cmdp->addr1.lno = cmdp->addr2.lno; + cmdp->addr2.lno += lno - 1; + break; + case '#': + nflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'g': + gflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + if (LF_ISSET(SUB_FIRST)) { + msgq(sp, M_ERR, + "Regular expression specified; r flag meaningless."); + return (1); + } + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, + "No previous regular expression."); + return (1); + } + rflag = 1; + break; + default: + goto usage; + } + + if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) { +usage: msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); + return (1); + } + + if (IN_VI_MODE(sp) && cflag && (lflag || nflag || pflag)) { + msgq(sp, M_ERR, + "The #, l and p flags may not be combined with the c flag in vi mode."); + return (1); + } + + if (!F_ISSET(sp, S_GLOBAL)) + SET_UP_INTERRUPTS(subst_intr); + + /* + * bp: if interactive, line cache + * blen: if interactive, line cache length + * lb: build buffer pointer. + * lbclen: current length of built buffer. + * lblen; length of build buffer. + */ + bp = lb = NULL; + blen = lbclen = lblen = 0; + + /* For each line... */ + for (matched = quit = 0, lno = cmdp->addr1.lno, + elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) { + + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) { + if (!F_ISSET(sp, S_GLOBAL)) + msgq(sp, M_INFO, "Interrupted."); + break; + } + + /* Get the line. */ + if ((s = file_gline(sp, ep, lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + /* + * Make a local copy if doing confirmation -- when calling + * the confirm routine we're likely to lose the cached copy. + */ + if (cflag) { + if (bp == NULL) { + GET_SPACE_RET(sp, bp, blen, llen); + } else + ADD_SPACE_RET(sp, bp, blen, llen); + memmove(bp, s, llen); + s = bp; + } + + /* Start searching from the beginning. */ + offset = 0; + len = llen; + + /* Reset the build buffer offset. */ + lbclen = 0; + + /* Reset empty match flag. */ + empty_ok = 1; + + /* + * We don't want to have to do a setline if the line didn't + * change -- keep track of whether or not this line changed. + * If doing confirmations, don't want to keep setting the + * line if change is refused -- keep track of substitutions. + */ + didsub = linechanged = 0; + + /* New line, do an EOL match. */ + do_eol_match = 1; + + /* It's not nul terminated, but we pretend it is. */ + eflags = REG_STARTEND; + + /* + * The search area is from s + offset to the EOL. + * + * Generally, sp->match[0].rm_so is the offset of the start + * of the match from the start of the search, and offset is + * the offset of the start of the last search. + */ +nextmatch: sp->match[0].rm_so = 0; + sp->match[0].rm_eo = len; + + /* Get the next match. */ + eval = regexec(re, + (char *)s + offset, re->re_nsub + 1, sp->match, eflags); + + /* + * There wasn't a match or if there was an error, deal with + * it. If there was a previous match in this line, resolve + * the changes into the database. Otherwise, just move on. + */ + if (eval == REG_NOMATCH) + goto endmatch; + if (eval != 0) { + re_error(sp, eval, re); + goto ret1; + } + matched = 1; + + /* Only the first search can match an anchored expression. */ + eflags |= REG_NOTBOL; + + /* + * !!! + * It's possible to match 0-length strings -- for example, the + * command s;a*;X;, when matched against the string "aabb" will + * result in "XbXbX", i.e. the matches are "aa", the space + * between the b's and the space between the b's and the end of + * the string. There is a similar space between the beginning + * of the string and the a's. The rule that we use (because vi + * historically used it) is that any 0-length match, occurring + * immediately after a match, is ignored. Otherwise, the above + * example would have resulted in "XXbXbX". Another example is + * incorrectly using " *" to replace groups of spaces with one + * space. + * + * The way we do this is that if we just had a successful match, + * the starting offset does not skip characters, and the match + * is empty, ignore the match and move forward. If there's no + * more characters in the string, we were attempting to match + * after the last character, so quit. + */ + if (!empty_ok && + sp->match[0].rm_so == 0 && sp->match[0].rm_eo == 0) { + empty_ok = 1; + if (len == 0) + goto endmatch; + BUILD(sp, s + offset, 1) + ++offset; + --len; + goto nextmatch; + } + + /* Confirm change. */ + if (cflag) { + /* + * Set the cursor position for confirmation. Note, + * if we matched on a '$', the cursor may be past + * the end of line. + * + * XXX + * We may want to "fix" this in the confirm routine, + * if the confirm routine should be able to display + * a cursor past EOL. + */ + from.lno = to.lno = lno; + from.cno = sp->match[0].rm_so + offset; + to.cno = sp->match[0].rm_eo; + if (llen == 0) + from.cno = to.cno = 0; + else { + if (to.cno >= llen) + to.cno = llen - 1; + if (from.cno >= llen) + from.cno = llen - 1; + } + switch (sp->s_confirm(sp, ep, &from, &to)) { + case CONF_YES: + break; + case CONF_NO: + didsub = 0; + BUILD(sp, s +offset, sp->match[0].rm_eo); + goto skip; + case CONF_QUIT: + /* Set the quit flag. */ + quit = 1; + + /* If interruptible, pass the info back. */ + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); + + /* + * If any changes, resolve them, otherwise + * return to the main loop. + */ + goto endmatch; + } + } + + /* Copy the bytes before the match into the build buffer. */ + BUILD(sp, s + offset, sp->match[0].rm_so); + + /* + * Cursor moves to last line changed, unless doing confirm, + * in which case don't move it. + * + * !!! + * Historic vi just put the cursor on the first non-blank + * of the last line changed. This might be better. + */ + if (!cflag) { + sp->lno = lno; + sp->cno = sp->match[0].rm_so + offset; + } + + /* Substitute the matching bytes. */ + didsub = 1; + if (regsub(sp, s + offset, &lb, &lbclen, &lblen)) + goto ret1; + + /* Set the change flag so we know this line was modified. */ + linechanged = 1; + + /* Move past the matched bytes. */ +skip: offset += sp->match[0].rm_eo; + len -= sp->match[0].rm_eo; + + /* A match cannot be followed by an empty pattern. */ + empty_ok = 0; + + /* + * If doing a global change with confirmation, we have to + * update the screen. The basic idea is to store the line + * so the screen update routines can find it, and restart. + */ + if (didsub && cflag && gflag) { + /* + * The new search offset will be the end of the + * modified line. + */ + saved_offset = lbclen; + + /* Copy the rest of the line. */ + if (len) + BUILD(sp, s + offset, len) + + /* Set the new offset. */ + offset = saved_offset; + + /* Store inserted lines, adjusting the build buffer. */ + last = 0; + if (sp->newl_cnt) { + for (cnt = 0; + cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { + if (file_iline(sp, ep, lno, + lb + last, sp->newl[cnt] - last)) + goto ret1; + last = sp->newl[cnt] + 1; + ++sp->rptlines[L_ADDED]; + } + lbclen -= last; + offset -= last; + sp->newl_cnt = 0; + } + + /* Store and retrieve the line. */ + if (file_sline(sp, ep, lno, lb + last, lbclen)) + goto ret1; + if ((s = file_gline(sp, ep, lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + goto ret1; + } + ADD_SPACE_RET(sp, bp, blen, llen) + memmove(bp, s, llen); + s = bp; + len = llen - offset; + + /* Restart the build. */ + lbclen = 0; + BUILD(sp, s, offset); + + /* + * If we haven't already done the after-the-string + * match, do one. Set REG_NOTEOL so the '$' pattern + * only matches once. + */ + if (!do_eol_match) + goto endmatch; + if (offset == len) { + do_eol_match = 0; + eflags |= REG_NOTEOL; + } + goto nextmatch; + } + + /* + * If it's a global: + * + * If at the end of the string, do a test for the after + * the string match. Set REG_NOTEOL so the '$' pattern + * only matches once. + */ + if (gflag && do_eol_match) { + if (len == 0) { + do_eol_match = 0; + eflags |= REG_NOTEOL; + } + goto nextmatch; + } + +endmatch: if (!linechanged) + continue; + + /* Copy any remaining bytes into the build buffer. */ + if (len) + BUILD(sp, s + offset, len) + + /* Store inserted lines, adjusting the build buffer. */ + last = 0; + if (sp->newl_cnt) { + for (cnt = 0; + cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { + if (file_iline(sp, ep, + lno, lb + last, sp->newl[cnt] - last)) + goto ret1; + last = sp->newl[cnt] + 1; + ++sp->rptlines[L_ADDED]; + } + lbclen -= last; + sp->newl_cnt = 0; + } + + /* Store the changed line. */ + if (file_sline(sp, ep, lno, lb + last, lbclen)) + goto ret1; + + /* Update changed line counter. */ + ++sp->rptlines[L_CHANGED]; + + /* Display as necessary. */ + if (lflag || nflag || pflag) { + from.lno = to.lno = lno; + from.cno = to.cno = 0; + if (lflag) + ex_print(sp, ep, &from, &to, E_F_LIST); + if (nflag) + ex_print(sp, ep, &from, &to, E_F_HASH); + if (pflag) + ex_print(sp, ep, &from, &to, E_F_PRINT); + } + } + + /* + * If not in a global command, and nothing matched, say so. + * Else, if none of the lines displayed, put something up. + */ + if (!matched) { + if (!F_ISSET(sp, S_GLOBAL)) + msgq(sp, M_INFO, "No match found."); + } else if (!lflag && !nflag && !pflag) + F_SET(EXP(sp), EX_AUTOPRINT); + + rval = 0; + if (0) { +ret1: rval = 1; + } + +interrupt_err: + if (!F_ISSET(sp, S_GLOBAL)) + TEAR_DOWN_INTERRUPTS; + + if (bp != NULL) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * regsub -- + * Do the substitution for a regular expression. + */ +static inline int +regsub(sp, ip, lbp, lbclenp, lblenp) + SCR *sp; + char *ip; /* Input line. */ + char **lbp; + size_t *lbclenp, *lblenp; +{ + enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv; + size_t lbclen, lblen; /* Local copies. */ + size_t mlen; /* Match length. */ + size_t rpl; /* Remaining replacement length. */ + char *rp; /* Replacement pointer. */ + int ch; + int no; /* Match replacement offset. */ + char *p, *t; /* Buffer pointers. */ + char *lb; /* Local copies. */ + + lb = *lbp; /* Get local copies. */ + lbclen = *lbclenp; + lblen = *lblenp; + + /* + * QUOTING NOTE: + * + * There are some special sequences that vi provides in the + * replacement patterns. + * & string the RE matched (\& if nomagic set) + * \# n-th regular subexpression + * \E end \U, \L conversion + * \e end \U, \L conversion + * \l convert the next character to lower-case + * \L convert to lower-case, until \E, \e, or end of replacement + * \u convert the next character to upper-case + * \U convert to upper-case, until \E, \e, or end of replacement + * + * Otherwise, since this is the lowest level of replacement, discard + * all escape characters. This (hopefully) follows historic practice. + */ +#define ADDCH(ch) { \ + CHAR_T __ch = (ch); \ + u_int __value = term_key_val(sp, __ch); \ + if (__value == K_CR || __value == K_NL) { \ + NEEDNEWLINE(sp); \ + sp->newl[sp->newl_cnt++] = lbclen; \ + } else if (conv != C_NOTSET) { \ + switch (conv) { \ + case C_ONELOWER: \ + conv = C_NOTSET; \ + /* FALLTHROUGH */ \ + case C_LOWER: \ + if (isupper(__ch)) \ + __ch = tolower(__ch); \ + break; \ + case C_ONEUPPER: \ + conv = C_NOTSET; \ + /* FALLTHROUGH */ \ + case C_UPPER: \ + if (islower(__ch)) \ + __ch = toupper(__ch); \ + break; \ + default: \ + abort(); \ + } \ + } \ + NEEDSP(sp, 1, p); \ + *p++ = __ch; \ + ++lbclen; \ +} + conv = C_NOTSET; + for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) { + switch (ch = *rp++) { + case '&': + if (O_ISSET(sp, O_MAGIC)) { + no = 0; + goto subzero; + } + break; + case '\\': + if (rpl == 0) + break; + --rpl; + switch (ch = *rp) { + case '&': + if (!O_ISSET(sp, O_MAGIC)) { + ++rp; + no = 0; + goto subzero; + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + no = *rp++ - '0'; +subzero: if (sp->match[no].rm_so == -1 || + sp->match[no].rm_eo == -1) + continue; + mlen = + sp->match[no].rm_eo - sp->match[no].rm_so; + for (t = ip + sp->match[no].rm_so; mlen--; ++t) + ADDCH(*t); + continue; + case 'e': + case 'E': + ++rp; + conv = C_NOTSET; + continue; + case 'l': + ++rp; + conv = C_ONELOWER; + continue; + case 'L': + ++rp; + conv = C_LOWER; + continue; + case 'u': + ++rp; + conv = C_ONEUPPER; + continue; + case 'U': + ++rp; + conv = C_UPPER; + continue; + default: + ++rp; + break; + } + } + ADDCH(ch); + } + + *lbp = lb; /* Update caller's information. */ + *lbclenp = lbclen; + *lblenp = lblen; + return (0); +} + +static int +checkmatchsize(sp, re) + SCR *sp; + regex_t *re; +{ + /* Build nsub array as necessary. */ + if (sp->matchsize < re->re_nsub + 1) { + sp->matchsize = re->re_nsub + 1; + REALLOC(sp, sp->match, + regmatch_t *, sp->matchsize * sizeof(regmatch_t)); + if (sp->match == NULL) { + sp->matchsize = 0; + return (1); + } + } + return (0); +} + +/* + * subst_intr -- + * Set the interrupt bit in any screen that is interruptible. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one substitute running, it will be hard to decide which one to + * stop. + */ +static void +subst_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} diff --git a/usr.bin/vi/nex/ex_tag.c b/usr.bin/vi/nex/ex_tag.c new file mode 100644 index 000000000000..c47ab0de9c44 --- /dev/null +++ b/usr.bin/vi/nex/ex_tag.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_tag.c 8.31 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "tag.h" + +static char *binary_search __P((char *, char *, char *)); +static int compare __P((char *, char *, char *)); +static char *linear_search __P((char *, char *, char *)); +static int search __P((SCR *, char *, char *, char **)); +static int tag_get __P((SCR *, char *, char **, char **, char **)); + +/* + * ex_tagfirst -- + * The tag code can be entered from main, i.e. "vi -t tag". + */ +int +ex_tagfirst(sp, tagarg) + SCR *sp; + char *tagarg; +{ + FREF *frp; + MARK m; + long tl; + u_int flags; + int sval; + char *p, *tag, *name, *search; + + /* Taglength may limit the number of characters. */ + if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl) + tagarg[tl] = '\0'; + + /* Get the tag information. */ + if (tag_get(sp, tagarg, &tag, &name, &search)) + return (1); + + /* Create the file entry. */ + if ((frp = file_add(sp, NULL, name, 0)) == NULL) + return (1); + if (file_init(sp, frp, NULL, 0)) + return (1); + + /* + * !!! + * Historic vi accepted a line number as well as a search + * string, and people are apparently still using the format. + */ + if (isdigit(search[0])) { + m.lno = atoi(search); + m.cno = 0; + } else { + /* + * Search for the tag; cheap fallback for C functions if + * the name is the same but the arguments have changed. + */ + m.lno = 1; + m.cno = 0; + flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; + sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); + if (sval && (p = strrchr(search, '(')) != NULL) { + p[1] = '\0'; + sval = f_search(sp, sp->ep, + &m, &m, search, NULL, &flags); + } + if (sval) + msgq(sp, M_ERR, "%s: search pattern not found.", tag); + } + + /* Set up the screen. */ + frp->lno = m.lno; + frp->cno = m.cno; + F_SET(frp, FR_CURSORSET); + + /* Might as well make this the default tag. */ + if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} + +/* + * ex_tagpush -- :tag [file] + * Move to a new tag. + * + * The tags stacks in nvi are a bit tricky. Each tag contains a file name, + * search string, and line/column numbers. The search string is only used + * for the first access and for user display. The first record on the stack + * is the place where we first did a tag, so it has no search string. The + * second record is the first tag, and so on. Note, this means that the + * "current" tag is always on the stack. Each tag has a line/column which is + * the location from which the user tagged the following TAG entry, and which + * is used as the return location. + */ +int +ex_tagpush(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum {TC_CHANGE, TC_CURRENT} which; + EX_PRIVATE *exp; + FREF *frp; + MARK m; + TAG *tp; + u_int flags; + int sval; + long tl; + char *name, *p, *search, *tag; + + exp = EXP(sp); + switch (cmdp->argc) { + case 1: + if (exp->tlast != NULL) + FREE(exp->tlast, strlen(exp->tlast) + 1); + if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + break; + case 0: + if (exp->tlast == NULL) { + msgq(sp, M_ERR, "No previous tag entered."); + return (1); + } + break; + default: + abort(); + } + + /* Taglength may limit the number of characters. */ + if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl) + exp->tlast[tl] = '\0'; + + /* Get the tag information. */ + if (tag_get(sp, exp->tlast, &tag, &name, &search)) + return (1); + + /* Get a new FREF structure. */ + if ((frp = file_add(sp, sp->frp, name, 1)) == NULL) { + FREE(tag, strlen(tag)); + return (1); + } + + /* + * Get a tag structure -- if this is the first tag, push it on the + * stack as a placeholder and get another tag structure. Set the + * line/column of the most recent element on the stack to be the + * current values, including the file pointer. Then push the new + * TAG onto the stack with the new file and search string for user + * display. + */ + CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); + if (tp != NULL && exp->tagq.tqh_first == NULL) { + TAILQ_INSERT_HEAD(&exp->tagq, tp, q); + CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); + } + if (exp->tagq.tqh_first != NULL) { + exp->tagq.tqh_first->frp = sp->frp; + exp->tagq.tqh_first->lno = sp->lno; + exp->tagq.tqh_first->cno = sp->cno; + } + if (tp != NULL) { + if ((tp->search = strdup(search)) == NULL) + msgq(sp, M_SYSERR, NULL); + else + tp->slen = strlen(search); + tp->frp = frp; + TAILQ_INSERT_HEAD(&exp->tagq, tp, q); + } + + /* Switch to the new file. */ + if (sp->frp == frp) + which = TC_CURRENT; + else { + MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, frp, NULL, 0)) { + if (tp != NULL) + FREE(tp, sizeof(TAG)); + FREE(tag, strlen(tag)); + return (1); + } + which = TC_CHANGE; + } + + /* + * !!! + * Historic vi accepted a line number as well as a search + * string, and people are apparently still using the format. + */ + if (isdigit(search[0])) { + m.lno = atoi(search); + m.cno = 0; + sval = 0; + } else { + /* + * Search for the tag; cheap fallback for C functions + * if the name is the same but the arguments have changed. + */ + m.lno = 1; + m.cno = 0; + flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; + sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); + if (sval && (p = strrchr(search, '(')) != NULL) { + p[1] = '\0'; + sval = f_search(sp, sp->ep, + &m, &m, search, NULL, &flags); + p[1] = '('; + } + if (sval) + msgq(sp, M_ERR, "%s: search pattern not found.", tag); + } + free(tag); + + switch (which) { + case TC_CHANGE: + frp->lno = m.lno; + frp->cno = m.cno; + F_SET(frp, FR_CURSORSET); + F_SET(sp, S_FSWITCH); + break; + case TC_CURRENT: + if (sval) + return (1); + sp->lno = m.lno; + sp->cno = m.cno; + break; + } + return (0); +} + +/* Free a tag or tagf structure from a queue. */ +#define FREETAG(tp) { \ + TAILQ_REMOVE(&exp->tagq, (tp), q); \ + if ((tp)->search != NULL) \ + free((tp)->search); \ + FREE((tp), sizeof(TAGF)); \ +} +#define FREETAGF(tfp) { \ + TAILQ_REMOVE(&exp->tagfq, (tfp), q); \ + free((tfp)->name); \ + FREE((tfp), sizeof(TAGF)); \ +} + +/* + * ex_tagpop -- :tagp[op][!] [number | file] + * Pop the tag stack. + */ +int +ex_tagpop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + EX_PRIVATE *exp; + TAG *ntp, *tp; + recno_t lno; + long off, saved_off; + size_t arglen, cno; + char *arg, *p, *t; + + /* Check for an empty stack. */ + exp = EXP(sp); + if (exp->tagq.tqh_first == NULL) { + msgq(sp, M_INFO, "The tags stack is empty."); + return (1); + } + + switch (cmdp->argc) { + case 0: /* Pop one tag. */ + tp = exp->tagq.tqh_first; + FREETAG(tp); + break; + case 1: /* Name or number. */ + arg = cmdp->argv[0]->bp; + saved_off = strtol(arg, &p, 10); + if (*p == '\0') { + if (saved_off < 1) + return (0); + for (tp = exp->tagq.tqh_first, off = saved_off; + tp != NULL && off-- > 1; tp = tp->q.tqe_next); + if (tp == NULL) { + msgq(sp, M_ERR, +"Less than %d entries on the tags stack; use :display to see the tags stack.", + saved_off); + return (1); + } + for (off = saved_off; off-- > 1;) { + tp = exp->tagq.tqh_first; + FREETAG(tp); + } + } else { + arglen = strlen(arg); + for (tp = exp->tagq.tqh_first; + tp != NULL; tp = tp->q.tqe_next) { + /* Use the user's original file name. */ + p = tp->frp->name; + if ((t = strrchr(p, '/')) == NULL) + t = p; + else + ++t; + if (!strncmp(arg, t, arglen)) { + ntp = tp; + break; + } + } + if (tp == NULL) { + msgq(sp, M_ERR, +"No file named %s on the tags stack; use :display to see the tags stack.", + arg); + return (1); + } + for (;;) { + tp = exp->tagq.tqh_first; + if (tp == ntp) + break; + FREETAG(tp); + } + } + break; + default: + abort(); + } + + /* Update the cursor from the saved TAG information. */ + tp = exp->tagq.tqh_first; + if (tp->frp == sp->frp) { + sp->lno = tp->lno; + sp->cno = tp->cno; + } else { + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, tp->frp, NULL, 0)) + return (1); + + tp->frp->lno = tp->lno; + tp->frp->cno = tp->cno; + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_FSWITCH); + } + + /* If returning to the first tag, the stack is now empty. */ + if (tp->q.tqe_next == NULL) + FREETAG(tp); + return (0); +} + +/* + * ex_tagtop -- :tagt[op][!] + * Clear the tag stack. + */ +int +ex_tagtop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + EX_PRIVATE *exp; + TAG *tp, tmp; + int found; + + /* Pop to oldest saved information. */ + exp = EXP(sp); + for (found = 0; (tp = exp->tagq.tqh_first) != NULL; found = 1) { + if (exp->tagq.tqh_first == NULL) + tmp = *tp; + FREETAG(tp); + } + + if (!found) { + msgq(sp, M_INFO, "The tags stack is empty."); + return (1); + } + + /* If not switching files, it's easy; else do the work. */ + if (tmp.frp == sp->frp) { + sp->lno = tmp.lno; + sp->cno = tmp.cno; + } else { + MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, tmp.frp, NULL, 0)) + return (1); + + tmp.frp->lno = tmp.lno; + tmp.frp->cno = tmp.cno; + + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_FSWITCH); + } + return (0); +} + +/* + * ex_tagdisplay -- + * Display the list of tags. + */ +int +ex_tagdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + EX_PRIVATE *exp; + TAG *tp; + size_t len, maxlen; + int cnt; + char *name; + + exp = EXP(sp); + if ((tp = exp->tagq.tqh_first) == NULL) { + (void)ex_printf(EXCOOKIE, "No tags to display.\n"); + return (0); + } + + /* + * Figure out the formatting. MNOC is the maximum + * number of file name columns before we split the line. + */ +#define MNOC 15 + for (maxlen = 0, + tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) { + len = strlen(name = tp->frp->name); /* The original name. */ + if (maxlen < len && len < MNOC) + maxlen = len; + } + + for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL; + ++cnt, tp = tp->q.tqe_next) { + len = strlen(name = tp->frp->name); /* The original name. */ + if (len > maxlen || len + tp->slen > sp->cols) + if (tp == NULL || tp->search == NULL) + (void)ex_printf(EXCOOKIE, + "%2d %s\n", cnt, name); + else + (void)ex_printf(EXCOOKIE, + "%2d %s\n** %*.*s %s\n", cnt, name, + (int)maxlen, (int)maxlen, "", tp->search); + else + if (tp == NULL || tp->search == NULL) + (void)ex_printf(EXCOOKIE, "%2d %*.*s\n", + cnt, (int)maxlen, (int)len, name); + else + (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n", + cnt, (int)maxlen, (int)len, name, + tp->search); + } + return (0); +} + +/* + * ex_tagalloc -- + * Create a new list of tag files. + */ +int +ex_tagalloc(sp, str) + SCR *sp; + char *str; +{ + EX_PRIVATE *exp; + TAGF *tp; + size_t len; + char *p, *t; + + /* Free current queue. */ + exp = EXP(sp); + while ((tp = exp->tagfq.tqh_first) != NULL) + FREETAGF(tp); + + /* Create new queue. */ + for (p = t = str;; ++p) { + if (*p == '\0' || isblank(*p)) { + if ((len = p - t) > 1) { + MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF)); + MALLOC(sp, tp->name, char *, len + 1); + if (tp->name == NULL) { + FREE(tp, sizeof(TAGF)); + return (1); + } + memmove(tp->name, t, len); + tp->name[len] = '\0'; + tp->flags = 0; + TAILQ_INSERT_TAIL(&exp->tagfq, tp, q); + } + t = p + 1; + } + if (*p == '\0') + break; + } + return (0); +} + /* Free previous queue. */ +/* + * ex_tagfree -- + * Free the tags file list. + */ +int +ex_tagfree(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + TAG *tp; + TAGF *tfp; + + /* Free up tag information. */ + exp = EXP(sp); + while ((tp = exp->tagq.tqh_first) != NULL) + FREETAG(tp); + while ((tfp = exp->tagfq.tqh_first) != NULL) + FREETAGF(tfp); + FREE(exp->tlast, strlen(exp->tlast) + 1); + return (0); +} + +/* + * ex_tagcopy -- + * Copy a screen's tag structures. + */ +int +ex_tagcopy(orig, sp) + SCR *orig, *sp; +{ + EX_PRIVATE *oexp, *nexp; + TAG *ap, *tp; + TAGF *atfp, *tfp; + + /* Copy tag stack. */ + oexp = EXP(orig); + nexp = EXP(sp); + for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) { + MALLOC(sp, tp, TAG *, sizeof(TAG)); + if (tp == NULL) + goto nomem; + *tp = *ap; + if (ap->search != NULL && + (tp->search = strdup(ap->search)) == NULL) + goto nomem; + TAILQ_INSERT_TAIL(&nexp->tagq, tp, q); + } + + /* Copy list of tag files. */ + for (atfp = oexp->tagfq.tqh_first; + atfp != NULL; atfp = atfp->q.tqe_next) { + MALLOC(sp, tfp, TAGF *, sizeof(TAGF)); + if (tfp == NULL) + goto nomem; + *tfp = *atfp; + if ((tfp->name = strdup(atfp->name)) == NULL) + goto nomem; + TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q); + } + + /* Copy the last tag. */ + if (oexp->tlast != NULL && + (nexp->tlast = strdup(oexp->tlast)) == NULL) { +nomem: msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} + +/* + * tag_get -- + * Get a tag from the tags files. + */ +static int +tag_get(sp, tag, tagp, filep, searchp) + SCR *sp; + char *tag, **tagp, **filep, **searchp; +{ + EX_PRIVATE *exp; + TAGF *tfp; + int dne; + char *p; + + /* + * Find the tag, only display missing file messages once, and + * then only if we didn't find the tag. + */ + dne = 0; + exp = EXP(sp); + for (p = NULL, tfp = exp->tagfq.tqh_first; + tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) { + errno = 0; + F_CLR(tfp, TAGF_DNE); + if (search(sp, tfp->name, tag, &p)) + if (errno == ENOENT) { + if (!F_ISSET(tfp, TAGF_DNE_WARN)) { + dne = 1; + F_SET(tfp, TAGF_DNE); + } + } else + msgq(sp, M_SYSERR, tfp->name); + } + + if (p == NULL) { + msgq(sp, M_ERR, "%s: tag not found.", tag); + if (dne) + for (tfp = exp->tagfq.tqh_first; + tfp != NULL; tfp = tfp->q.tqe_next) + if (F_ISSET(tfp, TAGF_DNE)) { + errno = ENOENT; + msgq(sp, M_SYSERR, tfp->name); + F_SET(tfp, TAGF_DNE_WARN); + } + return (1); + } + + /* + * Set the return pointers; tagp points to the tag, and, incidentally + * the allocated string, filep points to the nul-terminated file name, + * searchp points to the nul-terminated search string. + */ + for (*tagp = p; *p && !isblank(*p); ++p); + if (*p == '\0') + goto malformed; + for (*p++ = '\0'; isblank(*p); ++p); + for (*filep = p; *p && !isblank(*p); ++p); + if (*p == '\0') + goto malformed; + for (*p++ = '\0'; isblank(*p); ++p); + *searchp = p; + if (*p == '\0') { +malformed: free(*tagp); + msgq(sp, M_ERR, "%s: corrupted tag in %s.", tag, tfp->name); + return (1); + } + return (0); +} + +#define EQUAL 0 +#define GREATER 1 +#define LESS (-1) + +/* + * search -- + * Search a file for a tag. + */ +static int +search(sp, name, tname, tag) + SCR *sp; + char *name, *tname, **tag; +{ + struct stat sb; + int fd, len; + char *endp, *back, *front, *map, *p; + + if ((fd = open(name, O_RDONLY, 0)) < 0) + return (1); + + /* + * XXX + * We'd like to test if the file is too big to mmap. Since we don't + * know what size or type off_t's or size_t's are, what the largest + * unsigned integral type is, or what random insanity the local C + * compiler will perpetrate, doing the comparison in a portable way + * is flatly impossible. Hope that malloc fails if the file is too + * large. + */ + if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size, + PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) { + (void)close(fd); + return (1); + } + front = map; + back = front + sb.st_size; + + front = binary_search(tname, front, back); + front = linear_search(tname, front, back); + + if (front == NULL || (endp = strchr(front, '\n')) == NULL) { + *tag = NULL; + goto done; + } + + len = endp - front; + MALLOC(sp, p, char *, len + 1); + if (p == NULL) { + *tag = NULL; + goto done; + } + memmove(p, front, len); + p[len] = '\0'; + *tag = p; + +done: if (munmap(map, (size_t)sb.st_size)) + msgq(sp, M_SYSERR, "munmap"); + if (close(fd)) + msgq(sp, M_SYSERR, "close"); + return (0); +} + +/* + * Binary search for "string" in memory between "front" and "back". + * + * This routine is expected to return a pointer to the start of a line at + * *or before* the first word matching "string". Relaxing the constraint + * this way simplifies the algorithm. + * + * Invariants: + * front points to the beginning of a line at or before the first + * matching string. + * + * back points to the beginning of a line at or after the first + * matching line. + * + * Base of the Invariants. + * front = NULL; + * back = EOF; + * + * Advancing the Invariants: + * + * p = first newline after halfway point from front to back. + * + * If the string at "p" is not greater than the string to match, + * p is the new front. Otherwise it is the new back. + * + * Termination: + * + * The definition of the routine allows it return at any point, + * since front is always at or before the line to print. + * + * In fact, it returns when the chosen "p" equals "back". This + * implies that there exists a string is least half as long as + * (back - front), which in turn implies that a linear search will + * be no more expensive than the cost of simply printing a string or two. + * + * Trying to continue with binary search at this point would be + * more trouble than it's worth. + */ +#define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n'); + +static char * +binary_search(string, front, back) + register char *string, *front, *back; +{ + register char *p; + + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + + while (p != back) { + if (compare(string, p, back) == GREATER) + front = p; + else + back = p; + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + } + return (front); +} + +/* + * Find the first line that starts with string, linearly searching from front + * to back. + * + * Return NULL for no such line. + * + * This routine assumes: + * + * o front points at the first character in a line. + * o front is before or at the first line to be printed. + */ +static char * +linear_search(string, front, back) + char *string, *front, *back; +{ + while (front < back) { + switch (compare(string, front, back)) { + case EQUAL: /* Found it. */ + return (front); + break; + case LESS: /* No such string. */ + return (NULL); + break; + case GREATER: /* Keep going. */ + break; + } + SKIP_PAST_NEWLINE(front, back); + } + return (NULL); +} + +/* + * Return LESS, GREATER, or EQUAL depending on how the string1 compares + * with string2 (s1 ??? s2). + * + * o Matches up to len(s1) are EQUAL. + * o Matches up to len(s2) are GREATER. + * + * The string "s1" is null terminated. The string s2 is '\t', space, (or + * "back") terminated. + * + * !!! + * Reasonably modern ctags programs use tabs as separators, not spaces. + * However, historic programs did use spaces, and, I got complaints. + */ +static int +compare(s1, s2, back) + register char *s1, *s2, *back; +{ + for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) + if (*s1 != *s2) + return (*s1 < *s2 ? LESS : GREATER); + return (*s1 ? GREATER : s2 < back && + (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL); +} diff --git a/usr.bin/vi/nex/ex_undo.c b/usr.bin/vi/nex/ex_undo.c new file mode 100644 index 000000000000..c8dedd8d401a --- /dev/null +++ b/usr.bin/vi/nex/ex_undo.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_undo.c 8.4 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_undol -- U + * Undo changes to this line. + */ +int +ex_undol(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (log_setline(sp, ep)) + return (1); + + sp->cno = 0; + return (0); +} + +/* + * ex_undo -- u + * Undo the last change. + */ +int +ex_undo(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + MARK m; + + /* + * !!! + * Multiple undo isn't available in ex, as there's no '.' command. + * Whether 'u' is undo or redo is toggled each time, unless there + * was a change since the last undo, in which case it's an undo. + */ + if (!F_ISSET(ep, F_UNDO)) { + F_SET(ep, F_UNDO); + ep->lundo = FORWARD; + } + switch (ep->lundo) { + case BACKWARD: + if (log_forward(sp, ep, &m)) + return (1); + ep->lundo = FORWARD; + break; + case FORWARD: + if (log_backward(sp, ep, &m)) + return (1); + ep->lundo = BACKWARD; + break; + case NOTSET: + abort(); + } + sp->lno = m.lno; + sp->cno = m.cno; + return (0); +} diff --git a/usr.bin/vi/nex/ex_usage.c b/usr.bin/vi/nex/ex_usage.c new file mode 100644 index 000000000000..da84995d2993 --- /dev/null +++ b/usr.bin/vi/nex/ex_usage.c @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_usage.c 8.11 (Berkeley) 12/17/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * ex_help -- :help + * Display help message. + */ +int +ex_help(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + (void)ex_printf(EXCOOKIE, + "To see the list of vi commands, enter \":viusage<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "To see the list of ex commands, enter \":exusage<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "For a vi key usage statement enter \":viusage [key]<CR>\"\n"); + (void)ex_printf(EXCOOKIE, "To exit, enter \":q!\"\n"); + return (0); +} + +/* + * ex_usage -- :exusage [cmd] + * Display ex usage strings. + */ +int +ex_usage(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + EXCMDLIST const *cp; + + switch (cmdp->argc) { + case 1: + ap = cmdp->argv[0]; + for (cp = cmds; cp->name != NULL && + memcmp(ap->bp, cp->name, ap->len); ++cp); + if (cp->name == NULL) + (void)ex_printf(EXCOOKIE, + "The %.*s command is unknown.", + (int)ap->len, ap->bp); + else { + (void)ex_printf(EXCOOKIE, + "Command: %s\n Usage: %s\n", cp->help, cp->usage); + /* + * !!! + * The "visual" command has two modes, one from ex, + * one from the vi colon line. Don't ask. + */ + if (cp != &cmds[C_VISUAL_EX] && + cp != &cmds[C_VISUAL_VI]) + break; + if (cp == &cmds[C_VISUAL_EX]) + cp = &cmds[C_VISUAL_VI]; + else + cp = &cmds[C_VISUAL_EX]; + (void)ex_printf(EXCOOKIE, + "Command: %s\n Usage: %s\n", cp->help, cp->usage); + } + break; + case 0: + for (cp = cmds; cp->name != NULL; ++cp) + (void)ex_printf(EXCOOKIE, + "%*s: %s\n", MAXCMDNAMELEN, cp->name, cp->help); + break; + default: + abort(); + } + return (0); +} + +/* + * ex_viusage -- :viusage [key] + * Display vi usage strings. + */ +int +ex_viusage(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + VIKEYS const *kp; + int key; + + switch (cmdp->argc) { + case 1: + key = cmdp->argv[0]->bp[0]; + if (key > MAXVIKEY) + goto nokey; + + /* Special case: '[' and ']' commands. */ + if ((key == '[' || key == ']') && cmdp->argv[0]->bp[1] != key) + goto nokey; + + kp = &vikeys[key]; + if (kp->func == NULL) +nokey: (void)ex_printf(EXCOOKIE, + "The %s key has no current meaning", + charname(sp, key)); + else + (void)ex_printf(EXCOOKIE, + " Key:%s%s\nUsage: %s\n", + isblank(*kp->help) ? "" : " ", kp->help, kp->usage); + break; + case 0: + for (key = 0; key <= MAXVIKEY; ++key) { + kp = &vikeys[key]; + if (kp->help != NULL) + (void)ex_printf(EXCOOKIE, "%s\n", kp->help); + } + break; + default: + abort(); + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_util.c b/usr.bin/vi/nex/ex_util.c new file mode 100644 index 000000000000..fa7e70b9dfcc --- /dev/null +++ b/usr.bin/vi/nex/ex_util.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_util.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_getline -- + * Return a line from the terminal. + */ +int +ex_getline(sp, fp, lenp) + SCR *sp; + FILE *fp; + size_t *lenp; +{ + EX_PRIVATE *exp; + size_t off; + int ch; + char *p; + + exp = EXP(sp); + for (off = 0, p = exp->ibp;; ++off) { + ch = getc(fp); + if (off >= exp->ibp_len) { + BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1); + p = exp->ibp + off; + } + if (ch == EOF || ch == '\n') { + if (ch == EOF && !off) + return (1); + *lenp = off; + return (0); + } + *p++ = ch; + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/nex/ex_version.c b/usr.bin/vi/nex/ex_version.c new file mode 100644 index 000000000000..230632d2de3c --- /dev/null +++ b/usr.bin/vi/nex/ex_version.c @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_version.c 8.32 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_version -- :version + * Display the program version. + */ +int +ex_version(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + static time_t then = 759365451; + + (void)ex_printf(EXCOOKIE, +"Version 1.03, %sThe CSRG, University of California, Berkeley.\n", + ctime(&then)); + return (0); +} diff --git a/usr.bin/vi/nex/ex_visual.c b/usr.bin/vi/nex/ex_visual.c new file mode 100644 index 000000000000..d53152bde8e8 --- /dev/null +++ b/usr.bin/vi/nex/ex_visual.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_visual.c 8.7 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags] + * + * Switch to visual mode. + */ +int +ex_visual(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + size_t len; + int pos; + char buf[256]; + + /* If open option off, disallow visual command. */ + if (!O_ISSET(sp, O_OPEN)) { + msgq(sp, M_ERR, + "The visual command requires that the open option be set."); + return (1); + } + + /* If a line specified, move to that line. */ + if (cmdp->addrcnt) + sp->lno = cmdp->addr1.lno; + + /* + * Push a command based on the line position flags. If no + * flag specified, the line goes at the top of the screen. + */ + switch (F_ISSET(cmdp, E_F_CARAT | E_F_DASH | E_F_DOT | E_F_PLUS)) { + case E_F_CARAT: + pos = '^'; + break; + case E_F_DASH: + pos = '-'; + break; + case E_F_DOT: + pos = '.'; + break; + case E_F_PLUS: + default: + pos = '+'; + break; + } + + if (F_ISSET(cmdp, E_COUNT)) + len = snprintf(buf, sizeof(buf), + "%luz%c%lu", sp->lno, pos, cmdp->count); + else + len = snprintf(buf, sizeof(buf), "%luz%c", sp->lno, pos); + (void)term_push(sp, buf, len, 0, CH_NOMAP | CH_QUOTED); + + /* + * !!! + * Historically, if no line address was specified, the [p#l] flags + * caused the cursor to be moved to the last line of the file, which + * was then positioned as described above. This seems useless, so + * I haven't implemented it. + */ + switch (F_ISSET(cmdp, E_F_HASH | E_F_LIST | E_F_PRINT)) { + case E_F_HASH: + O_SET(sp, O_NUMBER); + break; + case E_F_LIST: + O_SET(sp, O_LIST); + break; + case E_F_PRINT: + break; + } + + /* Switch modes. */ + F_CLR(sp, S_SCREENS); + F_SET(sp, sp->saved_vi_mode); + + return (0); +} diff --git a/usr.bin/vi/nex/ex_write.c b/usr.bin/vi/nex/ex_write.c new file mode 100644 index 000000000000..5a51b6dff914 --- /dev/null +++ b/usr.bin/vi/nex/ex_write.c @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_write.c 8.19 (Berkeley) 12/18/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +enum which {WQ, WRITE, XIT}; + +static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_wq -- :wq[!] [>>] [file] + * Write to a file. + */ +int +ex_wq(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + if (exwr(sp, ep, cmdp, WQ)) + return (1); + + force = F_ISSET(cmdp, E_FORCE); + if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) { + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} + +/* + * ex_write -- :write[!] [>>] [file] + * :write [!] [cmd] + * Write to a file. + */ +int +ex_write(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (exwr(sp, ep, cmdp, WRITE)); +} + + +/* + * ex_xit -- :x[it]! [file] + * + * Write out any modifications and quit. + */ +int +ex_xit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT)) + return (1); + + force = F_ISSET(cmdp, E_FORCE); + if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) { + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} + +/* + * exwr -- + * The guts of the ex write commands. + */ +static int +exwr(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + EX_PRIVATE *exp; + MARK rm; + int flags; + char *name, *p; + + /* All write commands can have an associated '!'. */ + LF_INIT(FS_POSSIBLE); + if (F_ISSET(cmdp, E_FORCE)) + LF_SET(FS_FORCE); + + /* Skip any leading whitespace. */ + if (cmdp->argc != 0) + for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p); + + /* If no arguments, just write the file back. */ + if (cmdp->argc == 0 || *p == '\0') { + if (F_ISSET(cmdp, E_ADDR2_ALL)) + LF_SET(FS_ALL); + return (file_write(sp, ep, + &cmdp->addr1, &cmdp->addr2, NULL, flags)); + } + + /* If "write !" it's a pipe to a utility. */ + exp = EXP(sp); + if (cmd == WRITE && *p == '!') { + for (++p; *p && isblank(*p); ++p); + if (*p == '\0') { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + /* Expand the argument. */ + if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0)) + return (1); + if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2, + &rm, cmdp->argv[1]->bp, FILTER_WRITE)) + return (1); + sp->lno = rm.lno; + return (0); + } + + /* If "write >>" it's an append to a file. */ + if (cmd != XIT && p[0] == '>' && p[1] == '>') { + LF_SET(FS_APPEND); + + /* Skip ">>" and whitespace. */ + for (p += 2; *p && isblank(*p); ++p); + } + + /* Build an argv so we get an argument count and file expansion. */ + if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0)) + return (1); + + switch (cmdp->argc) { + case 1: + /* + * Nothing to expand, write the current file. + * XXX + * Should never happen, already checked this case. + */ + name = NULL; + break; + case 2: + /* One new argument, write it. */ + name = cmdp->argv[exp->argsoff - 1]->bp; + set_alt_name(sp, name); + break; + default: + /* If expanded to more than one argument, object. */ + msgq(sp, M_ERR, "%s expanded into too many file names", + cmdp->argv[0]->bp); + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + if (F_ISSET(cmdp, E_ADDR2_ALL)) + LF_SET(FS_ALL); + return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags)); +} + +/* + * ex_writefp -- + * Write a range of lines to a FILE *. + */ +int +ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch) + SCR *sp; + EXF *ep; + char *name; + FILE *fp; + MARK *fm, *tm; + u_long *nlno, *nch; +{ + register u_long ccnt, fline, tline; + size_t len; + char *p; + + fline = fm->lno; + tline = tm->lno; + + if (nlno != NULL) { + *nch = 0; + *nlno = 0; + } + ccnt = 0; + + /* + * The vi filter code has multiple processes running simultaneously, + * and one of them calls ex_writefp(). The "unsafe" function calls + * in this code are to file_gline() and msgq(). File_gline() is safe, + * see the comment in filter.c:filtercmd() for details. We don't call + * msgq if the multiple process bit in the EXF is set. + * + * !!! + * Historic vi permitted files of 0 length to be written. However, + * since the way vi got around dealing with "empty" files was to + * always have a line in the file no matter what, it wrote them as + * files of a single, empty line. We write empty files. + * + * "Alex, I'll take vi trivia for $1000." + */ + if (tline != 0) + for (; fline <= tline; ++fline) { + if ((p = file_gline(sp, ep, fline, &len)) == NULL) + break; + if (fwrite(p, 1, len, fp) != len) { + msgq(sp, M_SYSERR, name); + (void)fclose(fp); + return (1); + } + ccnt += len; + if (putc('\n', fp) != '\n') + break; + ++ccnt; + } + if (fclose(fp)) { + if (!F_ISSET(ep, F_MULTILOCK)) + msgq(sp, M_SYSERR, name); + return (1); + } + if (nlno != NULL) { + *nch = ccnt; + *nlno = tm->lno == 0 ? 0 : tm->lno - fm->lno + 1; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_yank.c b/usr.bin/vi/nex/ex_yank.c new file mode 100644 index 000000000000..0897034bf022 --- /dev/null +++ b/usr.bin/vi/nex/ex_yank.c @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_yank.c 8.3 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_yank -- :[line [,line]] ya[nk] [buffer] [count] + * + * Yank the lines into a buffer. + */ +int +ex_yank(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cut(sp, ep, NULL, + F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE)); +} diff --git a/usr.bin/vi/nex/ex_z.c b/usr.bin/vi/nex/ex_z.c new file mode 100644 index 000000000000..2842a22855a5 --- /dev/null +++ b/usr.bin/vi/nex/ex_z.c @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_z.c 8.4 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_z -- :[line] z [^-.+=] [count] [flags] + * + * Adjust window. + */ +int +ex_z(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t cnt, equals, lno; + int eofcheck; + + /* + * !!! + * If no count specified, use either two times the size of the + * scrolling region, or the size of the window option. POSIX + * 1003.2 claims that the latter is correct, but historic ex/vi + * documentation and practice appear to use the scrolling region. + * I'm using the window size as it means that the entire screen + * is used instead of losing a line to roundoff. Note, we drop + * a line from the cnt if using the window size to leave room for + * the next ex prompt. + */ + if (F_ISSET(cmdp, E_COUNT)) + cnt = cmdp->count; + else +#ifdef HISTORIC_PRACTICE + cnt = O_VAL(sp, O_SCROLL) * 2; +#else + cnt = O_VAL(sp, O_WINDOW) - 1; +#endif + + equals = 0; + eofcheck = 0; + lno = cmdp->addr1.lno; + + switch (F_ISSET(cmdp, + E_F_CARAT | E_F_DASH | E_F_DOT | E_F_EQUAL | E_F_PLUS)) { + case E_F_CARAT: /* Display cnt * 2 before the line. */ + eofcheck = 1; + if (lno > cnt * 2) + cmdp->addr1.lno = (lno - cnt * 2) + 1; + else + cmdp->addr1.lno = 1; + cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1; + break; + case E_F_DASH: /* Line goes at the bottom of the screen. */ + cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1; + cmdp->addr2.lno = lno; + break; + case E_F_DOT: /* Line goes in the middle of the screen. */ + /* + * !!! + * Historically, the "middleness" of the line overrode the + * count, so that "3z.19" or "3z.20" would display the first + * 12 lines of the file, i.e. (N - 1) / 2 lines before and + * after the specified line. + */ + eofcheck = 1; + cnt = (cnt - 1) / 2; + cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; + cmdp->addr2.lno = lno + cnt; + break; + case E_F_EQUAL: /* Center with hyphens. */ + /* + * !!! + * Strangeness. The '=' flag is like the '.' flag (see the + * above comment, it applies here as well) but with a special + * little hack. Print out lines of hyphens before and after + * the specified line. Additionally, the cursor remains set + * on that line. + */ + eofcheck = 1; + cnt = (cnt - 1) / 2; + cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; + cmdp->addr2.lno = lno - 1; + if (ex_pr(sp, ep, cmdp)) + return (1); + (void)ex_printf(EXCOOKIE, + "%s", "----------------------------------------\n"); + cmdp->addr2.lno = cmdp->addr1.lno = equals = lno; + if (ex_pr(sp, ep, cmdp)) + return (1); + (void)ex_printf(EXCOOKIE, + "%s", "----------------------------------------\n"); + cmdp->addr1.lno = lno + 1; + cmdp->addr2.lno = (lno + cnt) - 1; + break; + default: + /* If no line specified, move to the next one. */ + if (F_ISSET(cmdp, E_ADDRDEF)) + ++lno; + /* FALLTHROUGH */ + case E_F_PLUS: /* Line goes at the top of the screen. */ + eofcheck = 1; + cmdp->addr1.lno = lno; + cmdp->addr2.lno = (lno + cnt) - 1; + break; + } + + if (eofcheck) { + if (file_lline(sp, ep, &lno)) + return (1); + if (cmdp->addr2.lno > lno) + cmdp->addr2.lno = lno; + } + + if (ex_pr(sp, ep, cmdp)) + return (1); + if (equals) + sp->lno = equals; + return (0); +} diff --git a/usr.bin/vi/nex/excmd.c b/usr.bin/vi/nex/excmd.c new file mode 100644 index 000000000000..27ebc39eebe8 --- /dev/null +++ b/usr.bin/vi/nex/excmd.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)excmd.c 8.36 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * This array maps ex command names to command functions. + * + * The order in which command names are listed below is important -- + * ambiguous abbreviations are resolved to be the first possible match, + * e.g. "r" means "read", not "rewind", because "read" is listed before + * "rewind". + * + * The syntax of the ex commands is unbelievably irregular, and a special + * case from beginning to end. Each command has an associated "syntax + * script" which describes the "arguments" that are possible. The script + * syntax is as follows: + * + * ! -- ! flag + * 1 -- flags: [+-]*[pl#][+-]* + * 2 -- flags: [-.+^] + * 3 -- flags: [-.+^=] + * b -- buffer + * c[01+a] -- count (0-N, 1-N, signed 1-N, address offset) + * f[N#][or] -- file (a number or N, optional or required) + * l -- line + * S -- string with file name expansion + * s -- string + * W -- word string + * w[N#][or] -- word (a number or N, optional or required) + */ +EXCMDLIST const cmds[] = { +/* C_BANG */ + {"!", ex_bang, E_ADDR2_NONE|E_NORC, + "S", + "[line [,line]] ! command", + "filter lines through commands or run commands"}, +/* C_HASH */ + {"#", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] # [count] [l]", + "display numbered lines"}, +/* C_SUBAGAIN */ + {"&", ex_subagain, E_ADDR2|E_NORC, + "s", + "[line [,line]] & [cgr] [count] [#lp]", + "repeat the last subsitution"}, +/* C_STAR */ + {"*", ex_at, 0, + "b", + "* [buffer]", + "execute a buffer"}, +/* C_SHIFTL */ + {"<", ex_shiftl, E_ADDR2|E_AUTOPRINT|E_NORC, + "ca1", + "[line [,line]] <[<...] [count] [flags]", + "shift lines left"}, +/* C_EQUAL */ + {"=", ex_equal, E_ADDR1|E_NORC, + "1", + "[line] = [flags]", + "display line number"}, +/* C_SHIFTR */ + {">", ex_shiftr, E_ADDR2|E_AUTOPRINT|E_NORC, + "ca1", + "[line [,line]] >[>...] [count] [flags]", + "shift lines right"}, +/* C_AT */ + {"@", ex_at, 0, + "b", + "@ [buffer]", + "execute a buffer"}, +/* C_APPEND */ + {"append", ex_append, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF, + "!", + "[line] a[ppend][!]", + "append input to a line"}, +/* C_ABBR */ + {"abbreviate", ex_abbr, E_NOGLOBAL, + "W", + "ab[brev] word replace", + "specify an input abbreviation"}, +/* C_ARGS */ + {"args", ex_args, E_NOGLOBAL|E_NORC, + "", + "ar[gs]", + "display file argument list"}, +/* C_BG */ + {"bg", ex_bg, E_NOGLOBAL|E_NORC, + "", + "bg", + "background the current screen"}, +/* C_CHANGE */ + {"change", ex_change, E_ADDR2|E_NORC|E_ZERODEF, + "!ca", + "[line [,line]] c[hange][!] [count]", + "change lines to input"}, +/* C_CD */ + {"cd", ex_cd, E_NOGLOBAL, + "!f1o", + "cd[!] [directory]", + "change the current directory"}, +/* C_CHDIR */ + {"chdir", ex_cd, E_NOGLOBAL, + "!f1o", + "chd[ir][!] [directory]", + "change the current directory"}, +/* C_COPY */ + {"copy", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC, + "l1", + "[line [,line]] co[py] line [flags]", + "copy lines elsewhere in the file"}, +/* C_DELETE */ + {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC, + "bca1", + "[line [,line]] d[elete] [buffer] [count] [flags]", + "delete lines from the file"}, +/* C_DISPLAY */ + {"display", ex_display, E_NOGLOBAL|E_NORC, + "w1r", + "display b[uffers] | s[creens] | t[ags]", + "display buffers, screens or tags"}, +/* C_DIGRAPH */ + {"digraph", ex_digraph, E_NOGLOBAL|E_NOPERM|E_NORC, + "", + "digraph", + "specify digraphs (not implemented)"}, +/* C_EDIT */ + {"edit", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "e[dit][!] [+cmd] [file]", + "begin editing another file"}, +/* C_EX */ + {"ex", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "ex[!] [+cmd] [file]", + "begin editing another file"}, +/* C_EXUSAGE */ + {"exusage", ex_usage, E_NOGLOBAL|E_NORC, + "w1o", + "[exu]sage [command]", + "display ex command usage statement"}, +/* C_FILE */ + {"file", ex_file, E_NOGLOBAL|E_NORC, + "f1o", + "f[ile] [name]", + "display (and optionally set) file name"}, +/* C_FG */ + {"fg", ex_fg, E_NOGLOBAL|E_NORC, + "f1o", + "fg [file]", + "switch the current screen and a backgrounded screen"}, +/* C_GLOBAL */ + {"global", ex_global, E_ADDR2_ALL|E_NOGLOBAL|E_NORC, + "!s", + "[line [,line]] g[lobal][!] [;/]pattern[;/] [commands]", + "execute a global command on lines matching a pattern"}, +/* C_HELP */ + {"help", ex_help, E_NOGLOBAL|E_NORC, + "", + "he[lp]", + "display help statement"}, +/* C_INSERT */ + {"insert", ex_insert, E_ADDR1|E_NORC, + "!", + "[line] i[nsert][!]", + "insert input before a line"}, +/* C_JOIN */ + {"join", ex_join, E_ADDR2|E_AUTOPRINT|E_NORC, + "!ca1", + "[line [,line]] j[oin][!] [count] [flags]", + "join lines into a single line"}, +/* C_K */ + {"k", ex_mark, E_ADDR1|E_NORC, + "w1r", + "[line] k key", + "mark a line position"}, +/* C_LIST */ + {"list", ex_list, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] l[ist] [count] [#]", + "display lines in an unambiguous form"}, +/* C_MOVE */ + {"move", ex_move, E_ADDR2|E_AUTOPRINT|E_NORC, + "l", + "[line [,line]] m[ove] line", + "move lines elsewhere in the file"}, +/* C_MARK */ + {"mark", ex_mark, E_ADDR1|E_NORC, + "w1r", + "[line] ma[rk] key", + "mark a line position"}, +/* C_MAP */ + {"map", ex_map, 0, + "!W", + "map[!] [keys replace]", + "map input or commands to one or more keys"}, +/* C_MKEXRC */ + {"mkexrc", ex_mkexrc, E_NOGLOBAL|E_NORC, + "!f1r", + "mkexrc[!] file", + "write a .exrc file"}, +/* C_NEXT */ + {"next", ex_next, E_NOGLOBAL|E_NORC, + "!fN", + "n[ext][!] [file ...]", + "edit (and optionally specify) the next file"}, +/* C_NUMBER */ + {"number", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] nu[mber] [count] [l]", + "change display to number lines"}, +/* C_OPEN */ + {"open", ex_open, E_ADDR1, + "s", + "[line] o[pen] [/pattern/] [flags]", + "enter \"open\" mode (not implemented)"}, +/* C_PRINT */ + {"print", ex_pr, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] p[rint] [count] [#l]", + "display lines"}, +/* C_PRESERVE */ + {"preserve", ex_preserve, E_NOGLOBAL|E_NORC, + "", + "pre[serve]", + "preserve an edit session for recovery"}, +/* C_PREVIOUS */ + {"previous", ex_prev, E_NOGLOBAL|E_NORC, + "!", + "prev[ious][!]", + "edit the previous file in the file argument list"}, +/* C_PUT */ + {"put", ex_put, E_ADDR1|E_AUTOPRINT|E_NORC|E_ZERO, + "b", + "[line] pu[t] [buffer]", + "append a cut buffer to the line"}, +/* C_QUIT */ + {"quit", ex_quit, E_NOGLOBAL, + "!", + "q[uit][!]", + "exit ex/vi"}, +/* C_READ */ + {"read", ex_read, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF, + "!s", + "[line] r[ead] [!cmd | [file]]", + "append input from a command or file to the line"}, +/* C_RESIZE */ + {"resize", ex_resize, E_NOGLOBAL|E_NORC, + "c+", + "resize [change]", + "grow or shrink the current screen"}, +/* C_REWIND */ + {"rewind", ex_rew, E_NOGLOBAL|E_NORC, + "!", + "rew[ind][!]", + "re-edit all the files in the file argument list"}, +/* C_SUBSTITUTE */ + {"substitute", ex_substitute, E_ADDR2|E_NORC, + "s", +"[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]", + "substitute on lines matching a pattern"}, +/* C_SCRIPT */ + {"script", ex_script, E_NOGLOBAL|E_NORC, + "!f1o", + "sc[ript][!] [file]", + "run a shell in a screen"}, +/* C_SET */ + {"set", ex_set, E_NOGLOBAL, + "wN", + "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]", + "set options (use \":set all\" to see all options)"}, +/* C_SHELL */ + {"shell", ex_shell, E_NOGLOBAL|E_NORC, + "", + "sh[ell]", + "suspend editing and run a shell"}, +/* C_SOURCE */ + {"source", ex_source, E_NOGLOBAL, + "f1r", + "so[urce] file", + "read a file of ex commands"}, +/* C_SPLIT */ + {"split", ex_split, E_NOGLOBAL|E_NORC, + "fNo", + "sp[lit] [file ...]", + "split the current screen into two screens"}, +/* C_STOP */ + {"stop", ex_stop, E_NOGLOBAL|E_NORC, + "!", + "st[op][!]", + "suspend the edit session"}, +/* C_SUSPEND */ + {"suspend", ex_stop, E_NOGLOBAL|E_NORC, + "!", + "su[spend][!]", + "suspend the edit session"}, +/* C_T */ + {"t", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC, + "l1", + "[line [,line]] t line [flags]", + "move lines elsewhere in the file"}, +/* C_TAG */ + {"tag", ex_tagpush, E_NOGLOBAL, + "!w1o", + "ta[g][!] [string]", + "edit the file containing the tag"}, +/* C_TAGPOP */ + {"tagpop", ex_tagpop, E_NOGLOBAL|E_NORC, + "!w1o", + "tagp[op][!] [number | file]", + "return to a previous tag"}, +/* C_TAGTOP */ + {"tagtop", ex_tagtop, E_NOGLOBAL|E_NORC, + "!", + "tagt[op][!]", + "return to the first tag"}, +/* C_UNDOL */ + {"Undo", ex_undol, E_AUTOPRINT|E_NOGLOBAL|E_NORC, + "", + "U[ndo]", + "undo all the changes to this line"}, +/* C_UNDO */ + {"undo", ex_undo, E_AUTOPRINT|E_NOGLOBAL|E_NORC, + "", + "u[ndo]", + "undo the most recent change"}, +/* C_UNABBREVIATE */ + {"unabbreviate",ex_unabbr, E_NOGLOBAL, + "w1r", + "una[bbrev] word", + "delete an abbreviation"}, +/* C_UNMAP */ + {"unmap", ex_unmap, E_NOGLOBAL, + "!w1r", + "unm[ap][!] word", + "delete an input or command map"}, +/* C_VGLOBAL */ + {"vglobal", ex_vglobal, E_ADDR2_ALL|E_NOGLOBAL|E_NORC, + "s", + "[line [,line]] v[global] [;/]pattern[;/] [commands]", + "execute a global command on lines NOT matching a pattern"}, +/* C_VERSION */ + {"version", ex_version, E_NOGLOBAL|E_NORC, + "", + "version", + "display the program version information"}, +/* C_VISUAL_EX */ + {"visual", ex_visual, E_ADDR1|E_NOGLOBAL|E_NORC|E_ZERODEF, + "2c11", + "[line] vi[sual] [-|.|+|^] [window_size] [flags]", + "enter visual (vi) mode from ex mode"}, +/* C_VISUAL_VI */ + {"visual", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "vi[sual][!] [+cmd] [file]", + "edit another file (from vi mode only)"}, +/* C_VIUSAGE */ + {"viusage", ex_viusage, E_NOGLOBAL|E_NORC, + "w1o", + "[viu]sage [key]", + "display vi key usage statement"}, +/* C_WRITE */ + {"write", ex_write, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!s", + "[line [,line]] w[rite][!] [!cmd | [>>] [file]]", + "write the file"}, +/* C_WQ */ + {"wq", ex_wq, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!s", + "[line [,line]] wq[!] [>>] [file]", + "write the file and exit"}, +/* C_XIT */ + {"xit", ex_xit, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!f1o", + "[line [,line]] x[it][!] [file]", + "exit"}, +/* C_YANK */ + {"yank", ex_yank, E_ADDR2|E_NORC, + "bca", + "[line [,line]] ya[nk] [buffer] [count]", + "copy lines to a cut buffer"}, +/* C_Z */ + {"z", ex_z, E_ADDR1|E_NOGLOBAL|E_NORC, + "3c01", + "[line] z [-|.|+|^|=] [count] [flags]", + "display different screens of the file"}, +/* C_SUBTILDE */ + {"~", ex_subtilde, E_ADDR2|E_NORC, + "s", + "[line [,line]] ~ [cgr] [count] [#lp]", + "replace previous RE with previous replacement string,"}, + {NULL}, +}; diff --git a/usr.bin/vi/nex/excmd.h.stub b/usr.bin/vi/nex/excmd.h.stub new file mode 100644 index 000000000000..ce2715c8b086 --- /dev/null +++ b/usr.bin/vi/nex/excmd.h.stub @@ -0,0 +1,340 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)excmd.h.stub 8.44 (Berkeley) 12/29/93 + */ + +/* Ex command structure. */ +typedef struct _excmdlist { + char *name; /* Command name. */ + /* Underlying function. */ + int (*fn) __P((SCR *, EXF *, EXCMDARG *)); + +#define E_ADDR1 0x0000001 /* One address. */ +#define E_ADDR2 0x0000002 /* Two address. */ +#define E_ADDR2_ALL 0x0000004 /* Zero/two addresses; zero == all. */ +#define E_ADDR2_NONE 0x0000008 /* Zero/two addresses; zero == none. */ +#define E_ADDRDEF 0x0000010 /* Default addresses used. */ +#define E_AUTOPRINT 0x0000020 /* Command always sets autoprint. */ +#define E_BUFFER 0x0000040 /* Buffer name supplied. */ +#define E_COUNT 0x0000080 /* Count supplied. */ +#define E_FORCE 0x0000100 /* ! */ + +#define E_F_CARAT 0x0000200 /* ^ flag. */ +#define E_F_DASH 0x0000400 /* - flag. */ +#define E_F_DOT 0x0000800 /* . flag. */ +#define E_F_EQUAL 0x0001000 /* = flag. */ +#define E_F_HASH 0x0002000 /* # flag. */ +#define E_F_LIST 0x0004000 /* l flag. */ +#define E_F_PLUS 0x0008000 /* + flag. */ +#define E_F_PRINT 0x0010000 /* p flag. */ + +#define E_F_PRCLEAR 0x0020000 /* Clear the print (#, l, p) flags. */ +#define E_MODIFY 0x0040000 /* File name expansion modified arg. */ +#define E_NOGLOBAL 0x0080000 /* Not in a global. */ +#define E_NOPERM 0x0100000 /* Permission denied for now. */ +#define E_NORC 0x0200000 /* Not from a .exrc or EXINIT. */ +#define E_SETLAST 0x0400000 /* Reset last command. */ +#define E_ZERO 0x0800000 /* 0 is a legal addr1. */ +#define E_ZERODEF 0x1000000 /* 0 is default addr1 of empty files. */ + u_long flags; + char *syntax; /* Syntax script. */ + char *usage; /* Usage line. */ + char *help; /* Help line. */ +} EXCMDLIST; +#define MAXCMDNAMELEN 12 /* Longest command name. */ +extern EXCMDLIST const cmds[]; /* List of ex commands. */ + +/* Structure passed around to functions implementing ex commands. */ +struct _excmdarg { + EXCMDLIST const *cmd; /* Command entry in command table. */ + CHAR_T buffer; /* Named buffer. */ + recno_t lineno; /* Line number. */ + long count; /* Signed, specified count. */ + int addrcnt; /* Number of addresses (0, 1 or 2). */ + MARK addr1; /* 1st address. */ + MARK addr2; /* 2nd address. */ + ARGS **argv; /* Array of arguments. */ + int argc; /* Count of arguments. */ + u_int flags; /* Selected flags from EXCMDLIST. */ +}; + +/* Global ranges. */ +typedef struct _range RANGE; +struct _range { + CIRCLEQ_ENTRY(_range) q; /* Linked list of ranges. */ + recno_t start, stop; /* Start/stop of the range. */ +}; + +/* Ex private, per-screen memory. */ +typedef struct _ex_private { + ARGS **args; /* Arguments. */ + int argscnt; /* Argument count. */ + int argsoff; /* Offset into arguments. */ + + CHAR_T at_lbuf; /* Last executed at buffer's name. */ + int at_lbuf_set; /* If at_lbuf is set. */ + + char *ibp; /* Line input buffer. */ + size_t ibp_len; /* Line input buffer length. */ + + EXCMDLIST const *lastcmd; /* Last command. */ + + CHAR_T *lastbcomm; /* Last bang command. */ + + TAILQ_HEAD(_tagh, _tag) tagq; /* Tag stack. */ + TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag stack. */ + char *tlast; /* Saved last tag. */ + + /* Linked list of ranges. */ + CIRCLEQ_HEAD(_rangeh, _range) rangeq; + recno_t range_lno; /* Range set line number. */ + +#define EX_AUTOPRINT 0x01 /* Autoprint flag. */ + u_int flags; +} EX_PRIVATE; +#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private)) + +/* Macro to set up a command structure. */ +#define SETCMDARG(s, cmd_id, naddr, lno1, lno2, force, arg) { \ + ARGS *__ap[2], __a; \ + memset(&s, 0, sizeof(EXCMDARG)); \ + s.cmd = &cmds[cmd_id]; \ + s.addrcnt = (naddr); \ + s.addr1.lno = (lno1); \ + s.addr2.lno = (lno2); \ + s.addr1.cno = s.addr2.cno = 1; \ + if (force) \ + s.flags |= E_FORCE; \ + if ((__a.bp = arg) == NULL) { \ + s.argc = 0; \ + __a.len = 0; \ + } else { \ + s.argc = 1; \ + __a.len = strlen(arg); \ + } \ + __ap[0] = &__a; \ + __ap[1] = NULL; \ + s.argv = __ap; \ +} + +/* + * :next, :prev, :rewind, :tag, :tagpush, :tagpop modifications check. + * If force is set, the autowrite is skipped. + */ +#define MODIFY_CHECK(sp, ep, force) { \ + if (F_ISSET((ep), F_MODIFIED)) \ + if (O_ISSET((sp), O_AUTOWRITE)) { \ + if (!(force) && \ + file_write((sp), (ep), NULL, NULL, NULL, \ + FS_ALL | FS_POSSIBLE)) \ + return (1); \ + } else if (ep->refcnt <= 1 && !(force)) { \ + msgq(sp, M_ERR, \ + "Modified since last write; write or use ! to override."); \ + return (1); \ + } \ +} + +/* + * Macros to set and restore the terminal values, and note if the screen + * was modified. Specific to their uses in ex/filter.c and ex/ex_shell.c. + * + * The old terminal values almost certainly turn on VINTR, VQUIT and VSUSP. + * We don't want to interrupt the parent(s), so we ignore VINTR. VQUIT is + * ignored by main() because nvi never wants to catch it. A VSUSP handler + * have been installed by the screen code. + */ +#define EX_LEAVE(sp, isig, act, oact, sb, osb, term) \ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { \ + (act).sa_handler = SIG_IGN; \ + sigemptyset(&(act).sa_mask); \ + (act).sa_flags = 0; \ + if ((isig) = !sigaction(SIGINT, &(act), &(oact))) { \ + if (tcgetattr(STDIN_FILENO, &(term))) { \ + msgq(sp, M_SYSERR, "tcgetattr"); \ + rval = 1; \ + goto err; \ + } \ + if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, \ + &sp->gp->original_termios)) { \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + rval = 1; \ + goto err; \ + } \ + } \ + /* \ + * The process may write to the terminal. Save the \ + * access time (read) and modification time (write) \ + * of the tty; if they have changed when we restore \ + * the modes, will have to refresh the screen. \ + */ \ + sb.st_mtime = 1; \ + osb.st_mtime = 0; \ + (void)fstat(STDIN_FILENO, &osb); \ + } + +#define EX_RETURN(sp, isig, act, oact, sb, osb, term) \ + if (F_ISSET(sp->gp, G_ISFROMTTY) && (isig)) { \ + if (sigaction(SIGINT, &(oact), NULL)) { \ + msgq(sp, M_SYSERR, "signal"); \ + rval = 1; \ + } \ + if (tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &(term))) { \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + rval = 1; \ + } \ + /* If the terminal was used, refresh the screen. */ \ + (void)fstat(STDIN_FILENO, &(sb)); \ + if ((sb).st_mtime != (osb).st_mtime || \ + (sb).st_atime != (osb).st_atime) \ + F_SET(sp, S_REFRESH); \ + } + +/* + * Filter actions: + * + * FILTER Filter text through the utility. + * FILTER_READ Read from the utility into the file. + * FILTER_WRITE Write to the utility, display its output. + */ +enum filtertype { FILTER, FILTER_READ, FILTER_WRITE }; +int filtercmd __P((SCR *, EXF *, + MARK *, MARK *, MARK *, char *, enum filtertype)); + +/* Argument expansion routines. */ +int argv_init __P((SCR *, EXF *, EXCMDARG *)); +int argv_exp0 __P((SCR *, EXF *, EXCMDARG *, char *, size_t)); +int argv_exp1 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int)); +int argv_exp2 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int)); +int argv_exp3 __P((SCR *, EXF *, EXCMDARG *, char *, size_t)); +int argv_free __P((SCR *)); + +/* Ex function prototypes. */ +int ex __P((SCR *, EXF *)); +int ex_cfile __P((SCR *, EXF *, char *)); +int ex_cmd __P((SCR *, EXF *, char *, size_t)); +int ex_end __P((SCR *)); +int ex_exec_proc __P((SCR *, char *, char *, char *)); +int ex_gb __P((SCR *, EXF *, TEXTH *, int, u_int)); +int ex_getline __P((SCR *, FILE *, size_t *)); +int ex_icmd __P((SCR *, EXF *, char *, size_t)); +int ex_init __P((SCR *, EXF *)); +int ex_is_abbrev __P((char *, size_t)); +int ex_optchange __P((SCR *, int)); +int ex_print __P((SCR *, EXF *, MARK *, MARK *, int)); +int ex_readfp __P((SCR *, EXF *, char *, FILE *, MARK *, recno_t *, int)); +void ex_refresh __P((SCR *, EXF *)); +int ex_screen_copy __P((SCR *, SCR *)); +int ex_screen_end __P((SCR *)); +int ex_sdisplay __P((SCR *, EXF *)); +int ex_suspend __P((SCR *)); +int ex_tdisplay __P((SCR *, EXF *)); +int ex_writefp __P((SCR *, EXF *, + char *, FILE *, MARK *, MARK *, u_long *, u_long *)); +void global_insdel __P((SCR *, EXF *, enum operation, recno_t)); +int proc_wait __P((SCR *, long, const char *, int)); +int sscr_end __P((SCR *)); +int sscr_exec __P((SCR *, EXF *, recno_t)); +int sscr_input __P((SCR *)); + +#define EXPROTO(type, name) \ + type name __P((SCR *, EXF *, EXCMDARG *)) + +EXPROTO(int, ex_abbr); +EXPROTO(int, ex_append); +EXPROTO(int, ex_args); +EXPROTO(int, ex_at); +EXPROTO(int, ex_bang); +EXPROTO(int, ex_bg); +EXPROTO(int, ex_cd); +EXPROTO(int, ex_change); +EXPROTO(int, ex_color); +EXPROTO(int, ex_copy); +EXPROTO(int, ex_debug); +EXPROTO(int, ex_delete); +EXPROTO(int, ex_digraph); +EXPROTO(int, ex_display); +EXPROTO(int, ex_edit); +EXPROTO(int, ex_equal); +EXPROTO(int, ex_fg); +EXPROTO(int, ex_file); +EXPROTO(int, ex_global); +EXPROTO(int, ex_help); +EXPROTO(int, ex_insert); +EXPROTO(int, ex_join); +EXPROTO(int, ex_list); +EXPROTO(int, ex_map); +EXPROTO(int, ex_mark); +EXPROTO(int, ex_mkexrc); +EXPROTO(int, ex_move); +EXPROTO(int, ex_next); +EXPROTO(int, ex_number); +EXPROTO(int, ex_open); +EXPROTO(int, ex_pr); +EXPROTO(int, ex_preserve); +EXPROTO(int, ex_prev); +EXPROTO(int, ex_put); +EXPROTO(int, ex_quit); +EXPROTO(int, ex_read); +EXPROTO(int, ex_resize); +EXPROTO(int, ex_rew); +EXPROTO(int, ex_script); +EXPROTO(int, ex_set); +EXPROTO(int, ex_shell); +EXPROTO(int, ex_shiftl); +EXPROTO(int, ex_shiftr); +EXPROTO(int, ex_source); +EXPROTO(int, ex_split); +EXPROTO(int, ex_stop); +EXPROTO(int, ex_subagain); +EXPROTO(int, ex_substitute); +EXPROTO(int, ex_subtilde); +EXPROTO(int, ex_tagpop); +EXPROTO(int, ex_tagpush); +EXPROTO(int, ex_tagtop); +EXPROTO(int, ex_unabbr); +EXPROTO(int, ex_undo); +EXPROTO(int, ex_undol); +EXPROTO(int, ex_unmap); +EXPROTO(int, ex_usage); +EXPROTO(int, ex_validate); +EXPROTO(int, ex_version); +EXPROTO(int, ex_vglobal); +EXPROTO(int, ex_visual); +EXPROTO(int, ex_viusage); +EXPROTO(int, ex_wq); +EXPROTO(int, ex_write); +EXPROTO(int, ex_xit); +EXPROTO(int, ex_yank); +EXPROTO(int, ex_z); diff --git a/usr.bin/vi/nex/filter.c b/usr.bin/vi/nex/filter.c new file mode 100644 index 000000000000..d50e1e665d8d --- /dev/null +++ b/usr.bin/vi/nex/filter.c @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)filter.c 8.26 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +static int filter_ldisplay __P((SCR *, FILE *)); + +/* + * filtercmd -- + * Run a range of lines through a filter utility and optionally + * replace the original text with the stdout/stderr output of + * the utility. + */ +int +filtercmd(sp, ep, fm, tm, rp, cmd, ftype) + SCR *sp; + EXF *ep; + MARK *fm, *tm, *rp; + char *cmd; + enum filtertype ftype; +{ + struct sigaction act, oact; + struct stat osb, sb; + struct termios term; + FILE *ifp, *ofp; /* GCC: can't be uninitialized. */ + pid_t parent_writer_pid, utility_pid; + recno_t lno, nread; + int input[2], isig, output[2], rval; + char *name; + + /* Set return cursor position; guard against a line number of zero. */ + *rp = *fm; + if (fm->lno == 0) + rp->lno = 1; + + /* + * There are three different processes running through this code. + * They are the utility, the parent-writer and the parent-reader. + * The parent-writer is the process that writes from the file to + * the utility, the parent reader is the process that reads from + * the utility. + * + * Input and output are named from the utility's point of view. + * The utility reads from input[0] and the parent(s) write to + * input[1]. The parent(s) read from output[0] and the utility + * writes to output[1]. + * + * In the FILTER_READ case, the utility isn't expected to want + * input. Redirect its input from /dev/null. Otherwise open + * up utility input pipe. + */ + ifp = ofp = NULL; + input[0] = input[1] = output[0] = output[1] = -1; + if (ftype == FILTER_READ) { + if ((input[0] = open(_PATH_DEVNULL, O_RDONLY, 0)) < 0) { + msgq(sp, M_ERR, + "filter: %s: %s", _PATH_DEVNULL, strerror(errno)); + return (1); + } + } else { + if (pipe(input) < 0) { + msgq(sp, M_SYSERR, "pipe"); + goto err; + } + if ((ifp = fdopen(input[1], "w")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + } + + /* Open up utility output pipe. */ + if (pipe(output) < 0) { + msgq(sp, M_SYSERR, "pipe"); + goto err; + } + if ((ofp = fdopen(output[0], "r")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + + /* + * Save ex/vi terminal settings, and restore the original ones. + * Restoration so that users can do things like ":r! cat /dev/tty". + */ + EX_LEAVE(sp, isig, act, oact, sb, osb, term); + + /* Fork off the utility process. */ + switch (utility_pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); +err: if (input[0] != -1) + (void)close(input[0]); + if (ifp != NULL) + (void)fclose(ifp); + else if (input[1] != -1) + (void)close(input[1]); + if (ofp != NULL) + (void)fclose(ofp); + else if (output[0] != -1) + (void)close(output[0]); + if (output[1] != -1) + (void)close(output[1]); + rval = 1; + goto ret; + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + /* + * Redirect stdin from the read end of the input pipe, + * and redirect stdout/stderr to the write end of the + * output pipe. + */ + (void)dup2(input[0], STDIN_FILENO); + (void)dup2(output[1], STDOUT_FILENO); + (void)dup2(output[1], STDERR_FILENO); + + /* Close the utility's file descriptors. */ + (void)close(input[0]); + (void)close(input[1]); + (void)close(output[0]); + (void)close(output[1]); + + if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) + name = O_STR(sp, O_SHELL); + else + ++name; + + execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL); + msgq(sp, M_ERR, "Error: execl: %s: %s", + O_STR(sp, O_SHELL), strerror(errno)); + _exit (127); + /* NOTREACHED */ + default: /* Parent-reader, parent-writer. */ + /* Close the pipe ends neither parent will use. */ + (void)close(input[0]); + (void)close(output[1]); + break; + } + + /* + * FILTER_READ: + * + * Reading is the simple case -- we don't need a parent writer, + * so the parent reads the output from the read end of the output + * pipe until it finishes, then waits for the child. Ex_readfp + * appends to the MARK, and closes ofp. + * + * !!! + * Set the return cursor to the last line read in. Historically, + * this behaves differently from ":r file" command, which leaves + * the cursor at the first line read in. Check to make sure that + * it's not past EOF because we were reading into an empty file. + */ + if (ftype == FILTER_READ) { + rval = ex_readfp(sp, ep, "filter", ofp, fm, &nread, 0); + sp->rptlines[L_ADDED] += nread; + if (fm->lno == 0) + rp->lno = nread; + else + rp->lno += nread; + goto uwait; + } + + /* + * FILTER, FILTER_WRITE + * + * Here we need both a reader and a writer. Temporary files are + * expensive and we'd like to avoid disk I/O. Using pipes has the + * obvious starvation conditions. It's done as follows: + * + * fork + * child + * write lines out + * exit + * parent + * FILTER: + * read lines into the file + * delete old lines + * FILTER_WRITE + * read and display lines + * wait for child + * + * XXX + * We get away without locking the underlying database because we know + * that none of the records that we're reading will be modified until + * after we've read them. This depends on the fact that the current + * B+tree implementation doesn't balance pages or similar things when + * it inserts new records. When the DB code has locking, we should + * treat vi as if it were multiple applications sharing a database, and + * do the required locking. If necessary a work-around would be to do + * explicit locking in the line.c:file_gline() code, based on the flag + * set here. + */ + rval = 0; + F_SET(ep, F_MULTILOCK); + switch (parent_writer_pid = fork()) { + case -1: /* Error. */ + rval = 1; + msgq(sp, M_SYSERR, "fork"); + (void)close(input[1]); + (void)close(output[0]); + break; + case 0: /* Parent-writer. */ + /* + * Write the selected lines to the write end of the + * input pipe. Ifp is closed by ex_writefp. + */ + (void)close(output[0]); + _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL)); + + /* NOTREACHED */ + default: /* Parent-reader. */ + (void)close(input[1]); + if (ftype == FILTER_WRITE) + /* + * Read the output from the read end of the output + * pipe and display it. Filter_ldisplay closes ofp. + */ + rval = filter_ldisplay(sp, ofp); + else { + /* + * Read the output from the read end of the output + * pipe. Ex_readfp appends to the MARK and closes + * ofp. + */ + rval = ex_readfp(sp, ep, "filter", ofp, tm, &nread, 0); + sp->rptlines[L_ADDED] += nread; + } + + /* Wait for the parent-writer. */ + rval |= proc_wait(sp, + (long)parent_writer_pid, "parent-writer", 1); + + /* Delete any lines written to the utility. */ + if (ftype == FILTER && rval == 0) { + for (lno = tm->lno; lno >= fm->lno; --lno) + if (file_dline(sp, ep, lno)) { + rval = 1; + break; + } + if (rval == 0) + sp->rptlines[L_DELETED] += + (tm->lno - fm->lno) + 1; + } + /* + * If the filter had no output, we may have just deleted + * the cursor. Don't do any real error correction, we'll + * try and recover later. + */ + if (rp->lno > 1 && file_gline(sp, ep, rp->lno, NULL) == NULL) + --rp->lno; + break; + } + F_CLR(ep, F_MULTILOCK); + +uwait: rval |= proc_wait(sp, (long)utility_pid, cmd, 0); + + /* Restore ex/vi terminal settings. */ +ret: EX_RETURN(sp, isig, act, oact, sb, osb, term); + + return (rval); +} + +/* + * proc_wait -- + * Wait for one of the processes. + * + * !!! + * The pid_t type varies in size from a short to a long depending on the + * system. It has to be cast into something or the standard promotion + * rules get you. I'm using a long based on the belief that nobody is + * going to make it unsigned and it's unlikely to be a quad. + */ +int +proc_wait(sp, pid, cmd, okpipe) + SCR *sp; + long pid; + const char *cmd; + int okpipe; +{ + extern const char *const sys_siglist[]; + size_t len; + int pstat; + + /* Wait for the utility to finish. */ + (void)waitpid((pid_t)pid, &pstat, 0); + + /* + * Display the utility's exit status. Ignore SIGPIPE from the + * parent-writer, as that only means that the utility chose to + * exit before reading all of its input. + */ + if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) { + for (; isblank(*cmd); ++cmd); + len = strlen(cmd); + msgq(sp, M_ERR, "%.*s%s: received signal: %s%s.", + MIN(len, 20), cmd, len > 20 ? "..." : "", + sys_siglist[WTERMSIG(pstat)], + WCOREDUMP(pstat) ? "; core dumped" : ""); + return (1); + } + if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) { + for (; isblank(*cmd); ++cmd); + len = strlen(cmd); + msgq(sp, M_ERR, "%.*s%s: exited with status %d", + MIN(len, 20), cmd, len > 20 ? "..." : "", + WEXITSTATUS(pstat)); + return (1); + } + return (0); +} + +/* + * filter_ldisplay -- + * Display a line output from a utility. + * + * XXX + * This should probably be combined with some of the ex_print() + * routines into a single display routine. + */ +static int +filter_ldisplay(sp, fp) + SCR *sp; + FILE *fp; +{ + EX_PRIVATE *exp; + size_t len; + + exp = EXP(sp); + while (!ex_getline(sp, fp, &len)) { + (void)ex_printf(EXCOOKIE, "%.*s\n", (int)len, exp->ibp); + if (ferror(sp->stdfp)) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + } + if (fclose(fp)) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} diff --git a/usr.bin/vi/nex/script.h b/usr.bin/vi/nex/script.h new file mode 100644 index 000000000000..a04f14459165 --- /dev/null +++ b/usr.bin/vi/nex/script.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)script.h 8.1 (Berkeley) 12/19/93 + */ + +struct _script { + pid_t sh_pid; /* Shell pid. */ + int sh_master; /* Master pty fd. */ + int sh_slave; /* Slave pty fd. */ + char *sh_prompt; /* Prompt. */ + size_t sh_prompt_len; /* Prompt length. */ + char sh_name[64]; /* Pty name */ + struct winsize sh_win; /* Window size. */ + struct termios sh_term; /* Terminal information. */ +}; diff --git a/usr.bin/vi/nex/tag.h b/usr.bin/vi/nex/tag.h new file mode 100644 index 000000000000..f75d9b3fd466 --- /dev/null +++ b/usr.bin/vi/nex/tag.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tag.h 8.11 (Berkeley) 11/22/93 + */ + +struct _tagf { /* Tag file. */ + TAILQ_ENTRY(_tagf) q; /* Linked list of tag files. */ + char *name; /* Tag file name. */ + +#define TAGF_DNE 0x01 /* Didn't exist. */ +#define TAGF_DNE_WARN 0x02 /* DNE error reported. */ + u_char flags; +}; + +struct _tag { /* Tag stack. */ + TAILQ_ENTRY(_tag) q; /* Linked list of tags. */ + FREF *frp; /* Saved file name. */ + recno_t lno; /* Saved line number. */ + size_t cno; /* Saved column number. */ + char *search; /* Search string. */ + size_t slen; /* Search string length. */ +}; + +int ex_tagalloc __P((SCR *, char *)); +int ex_tagcopy __P((SCR *, SCR *)); +int ex_tagdisplay __P((SCR *, EXF *)); +int ex_tagfirst __P((SCR *, char *)); +int ex_tagfree __P((SCR *)); diff --git a/usr.bin/vi/nvi/getc.c b/usr.bin/vi/nvi/getc.c new file mode 100644 index 000000000000..d4ac2304b914 --- /dev/null +++ b/usr.bin/vi/nvi/getc.c @@ -0,0 +1,252 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getc.c 8.6 (Berkeley) 10/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * Character stream routines -- + * These routines return the file a character at a time. There are two + * special cases. First, the end of a line, end of a file, start of a + * file and empty lines are returned as special cases, and no character + * is returned. Second, empty lines include lines that have only white + * space in them, because the vi search functions don't care about white + * space, and this makes it easier for them to be consistent. + */ + +/* + * cs_init -- + * Initialize character stream routines. + */ +int +cs_init(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t lno; + + if ((csp->cs_bp = + file_gline(sp, ep, csp->cs_lno, &csp->cs_len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + msgq(sp, M_BERR, "Empty file."); + else + GETLINE_ERR(sp, csp->cs_lno); + return (1); + } + if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_ch = csp->cs_bp[csp->cs_cno]; + } + return (0); +} + +/* + * cs_next -- + * Retrieve the next character. + */ +int +cs_next(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t slno; + + switch (csp->cs_flags) { + case CS_EMP: /* EMP; get next line. */ + case CS_EOL: /* EOL; get next line. */ + slno = csp->cs_lno; /* Save current line. */ + if ((csp->cs_bp = + file_gline(sp, ep, ++csp->cs_lno, &csp->cs_len)) == NULL) { + csp->cs_lno = slno; + if (file_lline(sp, ep, &slno)) + return (1); + if (slno > csp->cs_lno) { + GETLINE_ERR(sp, csp->cs_lno); + return (1); + } + csp->cs_flags = CS_EOF; + } else if (csp->cs_len == 0 || + v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_ch = csp->cs_bp[csp->cs_cno = 0]; + } + break; + case 0: + if (csp->cs_cno == csp->cs_len - 1) + csp->cs_flags = CS_EOL; + else + csp->cs_ch = csp->cs_bp[++csp->cs_cno]; + break; + case CS_EOF: /* EOF; only returned once. */ + default: + abort(); + /* NOTREACHED */ + } + return (0); +} + +/* + * cs_fspace -- + * If on a space, eat forward until something other than a + * whitespace character. + * + * XXX + * Semantics of checking the current character were coded for the fword() + * function -- once the other word routines are converted, they may have + * to change. + */ +int +cs_fspace(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + if (csp->cs_flags != 0 || !isblank(csp->cs_ch)) + return (0); + for (;;) { + if (cs_next(sp, ep, csp)) + return (1); + if (csp->cs_flags != 0 || !isblank(csp->cs_ch)) + break; + } + return (0); +} + +/* + * cs_fblank -- + * Eat forward to the next non-whitespace character. + */ +int +cs_fblank(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + for (;;) { + if (cs_next(sp, ep, csp)) + return (1); + if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP || + csp->cs_flags == 0 && isblank(csp->cs_ch)) + continue; + break; + } + return (0); +} + +/* + * cs_prev -- + * Retrieve the previous character. + */ +int +cs_prev(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t slno; + + switch (csp->cs_flags) { + case CS_EMP: /* EMP; get previous line. */ + case CS_EOL: /* EOL; get previous line. */ + if (csp->cs_lno == 1) { /* SOF. */ + csp->cs_flags = CS_SOF; + break; + } + slno = csp->cs_lno; /* Save current line. */ + if ((csp->cs_bp = /* Line should exist. */ + file_gline(sp, ep, --csp->cs_lno, &csp->cs_len)) == NULL) { + GETLINE_ERR(sp, csp->cs_lno); + csp->cs_lno = slno; + return (1); + } + if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_cno = csp->cs_len - 1; + csp->cs_ch = csp->cs_bp[csp->cs_cno]; + } + break; + case 0: + if (csp->cs_cno == 0) + csp->cs_flags = CS_EOL; + else + csp->cs_ch = csp->cs_bp[--csp->cs_cno]; + break; + case CS_SOF: /* SOF; only returned once. */ + default: + abort(); + /* NOTREACHED */ + } + return (0); +} + +/* + * cs_bblank -- + * Eat backward to the next non-whitespace character. + */ +int +cs_bblank(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + for (;;) { + if (cs_prev(sp, ep, csp)) + return (1); + if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP || + csp->cs_flags == 0 && isblank(csp->cs_ch)) + continue; + break; + } + return (0); +} diff --git a/usr.bin/vi/nvi/v_again.c b/usr.bin/vi/nvi/v_again.c new file mode 100644 index 000000000000..7e9ea5650d83 --- /dev/null +++ b/usr.bin/vi/nvi/v_again.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_again.c 8.2 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_again -- & + * Repeat the previous substitution. + */ +int +v_again(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SUBAGAIN, 2, fm->lno, fm->lno, 1, ""); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_at.c b/usr.bin/vi/nvi/v_at.c new file mode 100644 index 000000000000..3b21fc3186d5 --- /dev/null +++ b/usr.bin/vi/nvi/v_at.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_at.c 8.3 (Berkeley) 8/25/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +int +v_at(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_AT, 0, OOBLNO, OOBLNO, 0, NULL); + cmd.buffer = vp->buffer; + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_ch.c b/usr.bin/vi/nvi/v_ch.c new file mode 100644 index 000000000000..40807d1db6b1 --- /dev/null +++ b/usr.bin/vi/nvi/v_ch.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ch.c 8.2 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> + +#include "vi.h" +#include "vcmd.h" + +#define NOPREV { \ + msgq(sp, M_BERR, "No previous F, f, T or t search."); \ + return (1); \ +} + +#define NOTFOUND(ch) { \ + msgq(sp, M_BERR, "%s not found.", charname(sp, ch)); \ + return (1); \ +} + +/* + * v_chrepeat -- [count]; + * Repeat the last F, f, T or t search. + */ +int +v_chrepeat(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + vp->character = sp->lastckey; + + switch (sp->csearchdir) { + case CNOTSET: + NOPREV; + case FSEARCH: + return (v_chF(sp, ep, vp, fm, tm, rp)); + case fSEARCH: + return (v_chf(sp, ep, vp, fm, tm, rp)); + case TSEARCH: + return (v_chT(sp, ep, vp, fm, tm, rp)); + case tSEARCH: + return (v_cht(sp, ep, vp, fm, tm, rp)); + default: + abort(); + } + /* NOTREACHED */ +} + +/* + * v_chrrepeat -- [count], + * Repeat the last F, f, T or t search in the reverse direction. + */ +int +v_chrrepeat(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + enum cdirection savedir; + + vp->character = sp->lastckey; + savedir = sp->csearchdir; + + switch (sp->csearchdir) { + case CNOTSET: + NOPREV; + case FSEARCH: + rval = v_chf(sp, ep, vp, fm, tm, rp); + break; + case fSEARCH: + rval = v_chF(sp, ep, vp, fm, tm, rp); + break; + case TSEARCH: + rval = v_cht(sp, ep, vp, fm, tm, rp); + break; + case tSEARCH: + rval = v_chT(sp, ep, vp, fm, tm, rp); + break; + default: + abort(); + } + sp->csearchdir = savedir; + return (rval); +} + +/* + * v_cht -- [count]tc + * Search forward in the line for the next occurrence of the character. + * Place the cursor on it if a motion command, to its left if its not. + */ +int +v_cht(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + + rval = v_chf(sp, ep, vp, fm, tm, rp); + if (!rval) + --rp->cno; /* XXX: Motion interaction with v_chf. */ + sp->csearchdir = tSEARCH; + return (rval); +} + +/* + * v_chf -- [count]fc + * Search forward in the line for the next occurrence of the character. + * Place the cursor to its right if a motion command, on it if its not. + */ +int +v_chf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t lno; + u_long cnt; + int key; + char *endp, *p, *startp; + + /* + * !!! + * If it's a dot command, it doesn't reset the key for which + * we're searching, e.g. in "df1|f2|.|;", the ';' searches + * for a '2'. + */ + key = vp->character; + if (!F_ISSET(vp, VC_ISDOT)) + sp->lastckey = key; + sp->csearchdir = fSEARCH; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NOTFOUND(key); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NOTFOUND(key); + + startp = p; + endp = p + len; + p += fm->cno; + for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + while (++p < endp && *p != key); + if (p == endp) + NOTFOUND(key); + } + rp->lno = fm->lno; + rp->cno = p - startp; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); +} + +/* + * v_chT -- [count]Tc + * Search backward in the line for the next occurrence of the character. + * Place the cursor to its right. + */ +int +v_chT(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + + rval = v_chF(sp, ep, vp, fm, tm, rp); + if (!rval) + ++rp->cno; + sp->csearchdir = TSEARCH; + return (0); +} + +/* + * v_chF -- [count]Fc + * Search backward in the line for the next occurrence of the character. + * Place the cursor on it. + */ +int +v_chF(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_long cnt; + int key; + char *p, *endp; + + /* + * !!! + * If it's a dot command, it doesn't reset the key for which + * we're searching, e.g. in "df1|f2|.|;", the ';' searches + * for a '2'. + */ + key = vp->character; + if (!F_ISSET(vp, VC_ISDOT)) + sp->lastckey = key; + sp->csearchdir = FSEARCH; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NOTFOUND(key); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NOTFOUND(key); + + endp = p - 1; + p += fm->cno; + for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + while (--p > endp && *p != key); + if (p == endp) + NOTFOUND(key); + } + rp->lno = fm->lno; + rp->cno = (p - endp) - 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_delete.c b/usr.bin/vi/nvi/v_delete.c new file mode 100644 index 000000000000..241ddf654f2e --- /dev/null +++ b/usr.bin/vi/nvi/v_delete.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_delete.c 8.7 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Delete -- [buffer][count]D + * Delete line command. + */ +int +v_Delete(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + return (0); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + return (0); + + tm->lno = fm->lno; + tm->cno = len; + + /* Yank the lines. */ + if (cut(sp, ep, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, CUT_DELETE)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + rp->lno = fm->lno; + rp->cno = fm->cno ? fm->cno - 1 : 0; + return (0); +} + +/* + * v_delete -- [buffer][count]d[count]motion + * Delete a range of text. + */ +int +v_delete(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t nlines; + size_t len; + int lmode; + + /* Yank the lines. */ + lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0; + if (cut(sp, ep, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, lmode | CUT_DELETE)) + return (1); + if (delete(sp, ep, fm, tm, lmode)) + return (1); + + /* Check for deleting the file. */ + if (file_lline(sp, ep, &nlines)) + return (1); + if (nlines == 0) { + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* + * If deleting lines, leave the cursor at the lowest line deleted, + * else, leave the cursor where it started. Always correct for EOL. + * + * The historic vi would delete the line the cursor was on (even if + * not in line mode) if the motion from the cursor was past the EOF + * and the cursor didn't originate on the last line of the file. A + * strange special case. We never delete the line the cursor is on. + * We'd have to pass a flag down to the delete() routine which would + * have to special case it. + */ + if (lmode) { + rp->lno = MIN(fm->lno, tm->lno); + if (rp->lno > nlines) + rp->lno = nlines; + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + return (0); + } + + rp->lno = fm->lno; + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (fm->cno >= len) + rp->cno = len ? len - 1 : 0; + else + rp->cno = fm->cno; + return (0); +} diff --git a/usr.bin/vi/nvi/v_ex.c b/usr.bin/vi/nvi/v_ex.c new file mode 100644 index 000000000000..631cd342d010 --- /dev/null +++ b/usr.bin/vi/nvi/v_ex.c @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ex.c 8.1 (Berkeley) 6/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_ex -- + * Run ex. + */ +int +v_ex(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_ex_run(sp, ep, rp)); +} diff --git a/usr.bin/vi/nvi/v_exit.c b/usr.bin/vi/nvi/v_exit.c new file mode 100644 index 000000000000..f308c480d3d7 --- /dev/null +++ b/usr.bin/vi/nvi/v_exit.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_exit.c 8.5 (Berkeley) 12/10/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_exit -- ZZ + * Save the file and exit. + */ +int +v_exit(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(ep, F_MODIFIED) && + file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + + /* + * !!! + * Historic practice: quit! or two quit's done in succession + * (where ZZ counts as a quit) didn't check for other files. + * + * Also check for related screens; if they exist, quit, the + * user will get the message on the last screen. + */ + if (sp->ccnt != sp->q_ccnt + 1 && + ep->refcnt <= 1 && file_unedited(sp) != NULL) { + sp->q_ccnt = sp->ccnt; + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, S_EXIT); + return (0); +} diff --git a/usr.bin/vi/nvi/v_exmode.c b/usr.bin/vi/nvi/v_exmode.c new file mode 100644 index 000000000000..150ec52ccf9c --- /dev/null +++ b/usr.bin/vi/nvi/v_exmode.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_exmode.c 8.3 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_exmode -- + * Put the editor in EX mode. + */ +int +v_exmode(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + sp->saved_vi_mode = F_ISSET(sp, S_VI_CURSES | S_VI_XAW); + F_CLR(sp, S_SCREENS); + F_SET(sp, S_EX); + return (0); +} diff --git a/usr.bin/vi/nvi/v_filter.c b/usr.bin/vi/nvi/v_filter.c new file mode 100644 index 000000000000..52ff2ae77b8d --- /dev/null +++ b/usr.bin/vi/nvi/v_filter.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_filter.c 8.10 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +/* + * v_filter -- [count]!motion command(s) + * Run range through shell commands, replacing text. + */ +int +v_filter(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + TEXT *tp; + + /* + * !!! + * Historical vi permitted "!!" in an empty file. This is + * handled as a special case in the ex_bang routine. Don't + * modify this setup without understanding that one. In + * particular, note that we're manipulating the ex argument + * structures behind ex's back. + */ + SETCMDARG(cmd, C_BANG, 2, fm->lno, tm->lno, 0, NULL); + EXP(sp)->argsoff = 0; /* XXX */ + if (F_ISSET(vp, VC_ISDOT)) { + if (argv_exp1(sp, ep, &cmd, "!", 1, 1)) + return (1); + } else { + /* Get the command from the user. */ + if (sp->s_get(sp, ep, &sp->tiq, + '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK) + return (1); + /* + * Len is 0 if backspaced over the prompt, + * 1 if only CR entered. + */ + tp = sp->tiq.cqh_first; + if (tp->len <= 1) + return (0); + + if (argv_exp1(sp, ep, &cmd, tp->lb + 1, tp->len - 1, 1)) + return (1); + } + cmd.argc = EXP(sp)->argsoff; /* XXX */ + cmd.argv = EXP(sp)->args; /* XXX */ + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_increment.c b/usr.bin/vi/nvi/v_increment.c new file mode 100644 index 000000000000..dfae29ea463e --- /dev/null +++ b/usr.bin/vi/nvi/v_increment.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_increment.c 8.6 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static char * const fmt[] = { +#define DEC 0 + "%ld", +#define SDEC 1 + "%+ld", +#define HEXC 2 + "%#0.*lX", +#define HEXL 3 + "%#0.*lx", +#define OCTAL 4 + "%#0.*lo", +}; + +/* + * v_increment -- [count]#[#+-] + * Increment/decrement a keyword number. + */ +int +v_increment(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + VI_PRIVATE *vip; + u_long ulval; + long lval; + size_t blen, len, nlen; + int rval; + char *bp, *ntype, *p, nbuf[100]; + + vip = VIP(sp); + + /* Do repeat operations. */ + if (vp->character == '#') + vp->character = vip->inc_lastch; + + /* Get new value. */ + if (F_ISSET(vp, VC_C1SET)) + vip->inc_lastval = vp->count; + + if (vp->character != '+' && vp->character != '-') { + msgq(sp, M_ERR, "usage: %s.", vp->kp->usage); + return (1); + } + vip->inc_lastch = vp->character; + + /* Figure out the resulting type and number. */ + p = vp->keyword; + len = vp->klen; + if (len > 1 && p[0] == '0') { + if (vp->character == '+') { + ulval = strtoul(vp->keyword, NULL, 0); + if (ULONG_MAX - ulval < vip->inc_lastval) + goto overflow; + ulval += vip->inc_lastval; + } else { + ulval = strtoul(vp->keyword, NULL, 0); + if (ulval < vip->inc_lastval) + goto underflow; + ulval -= vip->inc_lastval; + } + ntype = fmt[OCTAL]; + if (len > 2) + if (p[1] == 'X') + ntype = fmt[HEXC]; + else if (p[1] == 'x') + ntype = fmt[HEXL]; + nlen = snprintf(nbuf, sizeof(nbuf), ntype, len, ulval); + } else { + if (vp->character == '+') { + lval = strtol(vp->keyword, NULL, 0); + if (lval > 0 && LONG_MAX - lval < vip->inc_lastval) { +overflow: msgq(sp, M_ERR, "Resulting number too large."); + return (1); + } + lval += vip->inc_lastval; + } else { + lval = strtol(vp->keyword, NULL, 0); + if (lval < 0 && -(LONG_MIN - lval) < vip->inc_lastval) { +underflow: msgq(sp, M_ERR, "Resulting number too small."); + return (1); + } + lval -= vip->inc_lastval; + } + ntype = lval != 0 && + (*vp->keyword == '+' || *vp->keyword == '-') ? + fmt[SDEC] : fmt[DEC]; + nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval); + } + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, len + nlen); + memmove(bp, p, fm->cno); + memmove(bp + fm->cno, nbuf, nlen); + memmove(bp + fm->cno + nlen, + p + fm->cno + vp->klen, len - fm->cno - vp->klen); + len = len - vp->klen + nlen; + + rval = file_sline(sp, ep, fm->lno, bp, len); + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_init.c b/usr.bin/vi/nvi/v_init.c new file mode 100644 index 000000000000..f0d2facf4ea3 --- /dev/null +++ b/usr.bin/vi/nvi/v_init.c @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_init.c 8.18 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +static int v_comment __P((SCR *, EXF *)); + +/* + * v_screen_copy -- + * Copy vi screen. + */ +int +v_screen_copy(orig, sp) + SCR *orig, *sp; +{ + VI_PRIVATE *ovip, *nvip; + + /* Create the private vi structure. */ + CALLOC_RET(orig, nvip, VI_PRIVATE *, 1, sizeof(VI_PRIVATE)); + sp->vi_private = nvip; + + if (orig == NULL) { + nvip->inc_lastch = '+'; + nvip->inc_lastval = 1; + } else { + ovip = VIP(orig); + + /* User can replay the last input, but nothing else. */ + if (ovip->rep_len != 0) { + MALLOC(orig, nvip->rep, char *, ovip->rep_len); + if (nvip->rep != NULL) { + memmove(nvip->rep, ovip->rep, ovip->rep_len); + nvip->rep_len = ovip->rep_len; + } + } + + nvip->inc_lastch = ovip->inc_lastch; + nvip->inc_lastval = ovip->inc_lastval; + + if (ovip->paragraph != NULL && + (nvip->paragraph = strdup(ovip->paragraph)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + } + return (0); +} + +/* + * v_screen_end -- + * End a vi screen. + */ +int +v_screen_end(sp) + SCR *sp; +{ + VI_PRIVATE *vip; + + vip = VIP(sp); + + if (vip->rep != NULL) + FREE(vip->rep, vip->rep_len); + + if (vip->paragraph != NULL) + FREE(vip->paragraph, vip->paragraph_len); + + /* Free private memory. */ + FREE(vip, sizeof(VI_PRIVATE)); + sp->vi_private = NULL; + + return (0); +} + +/* + * v_init -- + * Initialize vi. + */ +int +v_init(sp, ep) + SCR *sp; + EXF *ep; +{ + size_t len; + + /* + * The default address is line 1, column 0. If the address set + * bit is on for this file, load the address, ensuring that it + * exists. + */ + if (F_ISSET(sp->frp, FR_CURSORSET)) { + sp->lno = sp->frp->lno; + sp->cno = sp->frp->cno; + + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (sp->lno != 1 || sp->cno != 0) { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } + } else if (sp->cno >= len) + sp->cno = 0; + + } else { + sp->lno = 1; + sp->cno = 0; + + if (O_ISSET(sp, O_COMMENT) && v_comment(sp, ep)) + return (1); + } + + /* Reset strange attraction. */ + sp->rcm = 0; + sp->rcmflags = 0; + + /* Make ex display to a special function. */ + if ((sp->stdfp = fwopen(sp, sp->s_ex_write)) == NULL) { + msgq(sp, M_SYSERR, "ex output"); + return (1); + } +#ifdef MAKE_EX_OUTPUT_LINE_BUFFERED + (void)setvbuf(sp->stdfp, NULL, _IOLBF, 0); +#endif + + /* Display the status line. */ + return (status(sp, ep, sp->lno, 0)); +} + +/* + * v_end -- + * End vi session. + */ +int +v_end(sp) + SCR *sp; +{ + /* Close down ex output file descriptor. */ + (void)fclose(sp->stdfp); + + return (0); +} + +/* + * v_optchange -- + * Handle change of options for vi. + */ +int +v_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_PARAGRAPHS: + case O_SECTIONS: + return (v_buildparagraph(sp)); + } + return (0); +} + +/* + * v_comment -- + * Skip the first comment. + */ +static int +v_comment(sp, ep) + SCR *sp; + EXF *ep; +{ + recno_t lno; + size_t len; + char *p; + + for (lno = 1; + (p = file_gline(sp, ep, lno, &len)) != NULL && len == 0; ++lno); + if (p == NULL || len <= 1 || memcmp(p, "/*", 2)) + return (0); + do { + for (; len; --len, ++p) + if (p[0] == '*' && len > 1 && p[1] == '/') { + sp->lno = lno; + return (0); + } + } while ((p = file_gline(sp, ep, ++lno, &len)) != NULL); + return (0); +} diff --git a/usr.bin/vi/nvi/v_join.c b/usr.bin/vi/nvi/v_join.c new file mode 100644 index 000000000000..c3f81d6d1a5c --- /dev/null +++ b/usr.bin/vi/nvi/v_join.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_join.c 8.3 (Berkeley) 8/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_join -- [count]J + * Join lines together. + */ +int +v_join(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + int lno; + + /* + * YASC. + * The general rule is that '#J' joins # lines, counting the current + * line. However, 'J' and '1J' are the same as '2J', i.e. join the + * current and next lines. This doesn't map well into the ex command + * (which takes two line numbers), so we handle it here. Note that + * we never test for EOF -- historically going past the end of file + * worked just fine. + */ + lno = fm->lno + 1; + if (F_ISSET(vp, VC_C1SET) && vp->count > 2) + lno = fm->lno + (vp->count - 1); + + SETCMDARG(cmd, C_JOIN, 2, fm->lno, lno, 0, NULL); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_left.c b/usr.bin/vi/nvi/v_left.c new file mode 100644 index 000000000000..cc80b379b06d --- /dev/null +++ b/usr.bin/vi/nvi/v_left.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_left.c 8.3 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_left -- [count]^H, [count]h + * Move left by columns. + */ +int +v_left(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t cnt; + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + if (fm->cno == 0) { + msgq(sp, M_BERR, "Already in the first column."); + return (1); + } + + rp->lno = fm->lno; + if (fm->cno > cnt) + rp->cno = fm->cno - cnt; + else + rp->cno = 0; + return (0); +} + +/* + * v_cfirst -- [count]_ + * + * Move to the first non-blank column on a line. + */ +int +v_cfirst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t cnt; + + /* + * A count moves down count - 1 rows, so, "3_" is the same as "2j_". + * + * !!! + * Historically, if the _ is a motion, it is always a line motion, + * and the line motion flag is set. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (cnt != 1) { + --vp->count; + if (v_down(sp, ep, vp, fm, tm, rp)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + F_SET(vp, VC_LMODE); + } else + rp->lno = fm->lno; + rp->cno = 0; + if (nonblank(sp, ep, rp->lno, &rp->cno)) + return (1); + return (0); +} + +/* + * v_first -- ^ + * Move to the first non-blank column on this line. + */ +int +v_first(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Yielding to none in our quest for compatibility with every + * historical blemish of vi, no matter how strange it might be, + * we permit the user to enter a count and then ignore it. + */ + rp->cno = 0; + if (nonblank(sp, ep, fm->lno, &rp->cno)) + return (1); + rp->lno = fm->lno; + return (0); +} + +/* + * v_ncol -- [count]| + * Move to column count or the first column on this line. If the + * requested column is past EOL, move to EOL. The nasty part is + * that we have to know character column widths to make this work. + */ +int +v_ncol(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_C1SET) && vp->count > 1) + rp->cno = + sp->s_chposition(sp, ep, fm->lno, (size_t)--vp->count); + else + rp->cno = 0; + rp->lno = fm->lno; + return (0); +} + +/* + * v_zero -- 0 + * Move to the first column on this line. + */ +int +v_zero(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + rp->lno = fm->lno; + rp->cno = 0; + return (0); +} diff --git a/usr.bin/vi/nvi/v_mark.c b/usr.bin/vi/nvi/v_mark.c new file mode 100644 index 000000000000..714242d9331e --- /dev/null +++ b/usr.bin/vi/nvi/v_mark.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_mark.c 8.3 (Berkeley) 10/31/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_mark -- m[a-z] + * Set a mark. + */ +int +v_mark(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + rp->lno = fm->lno; + rp->cno = fm->cno; + return (mark_set(sp, ep, vp->character, fm, 1)); +} + +/* + * v_gomark -- '['`a-z], or `['`a-z] + * Move to a mark. + * + * The single quote form moves to the first nonblank character of a line + * containing a mark. The back quote form moves to a mark, setting both + * row and column. We use a single routine for both forms, taking care + * of the nonblank behavior using a flag for the command. + * + * Although not commonly known, the "'`" and "'`" forms are historically + * valid. The behavior is determined by the first character, so "`'" is + * the same as "``". Remember this fact -- you'll be amazed at how many + * people don't know it and will be delighted that you are able to tell + * them. + */ +int +v_gomark(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + MARK *mp; + + if ((mp = mark_get(sp, ep, vp->character)) == NULL) + return (1); + *rp = *mp; + return (0); +} diff --git a/usr.bin/vi/nvi/v_match.c b/usr.bin/vi/nvi/v_match.c new file mode 100644 index 000000000000..963cca551fe8 --- /dev/null +++ b/usr.bin/vi/nvi/v_match.c @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_match.c 8.7 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_match -- % + * Search to matching character. + */ +int +v_match(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + register int cnt, matchc, startc; + VCS cs; + recno_t lno; + size_t len, off; + int (*gc)__P((SCR *, EXF *, VCS *)); + char *p; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + goto nomatch; + GETLINE_ERR(sp, fm->lno); + return (1); + } + + /* + * !!! + * Historical practice was to search in the forward direction only. + */ + for (off = fm->cno;; ++off) { + if (off >= len) { +nomatch: msgq(sp, M_BERR, "No match character on this line."); + return (1); + } + switch (startc = p[off]) { + case '(': + matchc = ')'; + gc = cs_next; + break; + case ')': + matchc = '('; + gc = cs_prev; + break; + case '[': + matchc = ']'; + gc = cs_next; + break; + case ']': + matchc = '['; + gc = cs_prev; + break; + case '{': + matchc = '}'; + gc = cs_next; + break; + case '}': + matchc = '{'; + gc = cs_prev; + break; + default: + continue; + } + break; + } + + cs.cs_lno = fm->lno; + cs.cs_cno = off; + if (cs_init(sp, ep, &cs)) + return (1); + for (cnt = 1;;) { + if (gc(sp, ep, &cs)) + return (1); + if (cs.cs_flags != 0) { + if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) + break; + continue; + } + if (cs.cs_ch == startc) + ++cnt; + else if (cs.cs_ch == matchc && --cnt == 0) + break; + } + if (cnt) { + msgq(sp, M_BERR, "Matching character not found."); + return (1); + } + rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * Movement commands go one space further. Increment the return + * MARK or from MARK depending on the direction of the search. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (len) + if (gc == cs_next) + ++rp->cno; + else + ++fm->cno; + } + return (0); +} diff --git a/usr.bin/vi/nvi/v_ntext.c b/usr.bin/vi/nvi/v_ntext.c new file mode 100644 index 000000000000..1c17ffc5bdd0 --- /dev/null +++ b/usr.bin/vi/nvi/v_ntext.c @@ -0,0 +1,1728 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ntext.c 8.80 (Berkeley) 1/13/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "seq.h" +#include "vcmd.h" +#include "excmd.h" + +static int txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T, int, int *, int *)); +static void txt_ai_resolve __P((SCR *, TEXT *)); +static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int)); +static void txt_err __P((SCR *, EXF *, TEXTH *)); +static int txt_hex __P((SCR *, TEXT *, int *, ARG_CHAR_T)); +static int txt_indent __P((SCR *, TEXT *)); +static int txt_margin __P((SCR *, TEXT *, int *, ARG_CHAR_T)); +static int txt_outdent __P((SCR *, TEXT *)); +static void txt_showmatch __P((SCR *, EXF *)); +static int txt_resolve __P((SCR *, EXF *, TEXTH *)); + +/* Cursor character (space is hard to track on the screen). */ +#if defined(DEBUG) && 0 +#undef CURSOR_CH +#define CURSOR_CH '+' +#endif + +/* Local version of BINC. */ +#define TBINC(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + goto err; \ +} + +/* + * newtext -- + * Read in text from the user. + * + * !!! + * Historic vi always used: + * + * ^D: autoindent deletion + * ^H: last character deletion + * ^W: last word deletion + * ^V: quote the next character + * + * regardless of the user's choices for these characters. The user's erase + * and kill characters worked in addition to these characters. Ex was not + * completely consistent with this, as it did map the scroll command to the + * user's EOF character. + * + * This implementation does not use fixed characters, but uses whatever the + * user specified as described by the termios structure. I'm getting away + * with something here, but I think I'm unlikely to get caught. + * + * !!! + * Historic vi did a special screen optimization for tab characters. For + * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of + * the string when it was displayed. Because this implementation redisplays + * the entire line on each keystroke, the "bcd" gets pushed to the right as + * we ignore that the user has "promised" to change the rest of the characters. + * Users have noticed, but this isn't worth fixing, and, the way that the + * historic vi did it results in an even worse bug. Given the keystrokes + * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears + * on the second <esc> key. + */ +int +v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + MARK *tm; /* To MARK. */ + const char *lp; /* Input line. */ + const size_t len; /* Input line length. */ + MARK *rp; /* Return MARK. */ + int prompt; /* Prompt to display. */ + recno_t ai_line; /* Line number to use for autoindent count. */ + u_int flags; /* TXT_ flags. */ +{ + /* State of abbreviation checks. */ + enum { A_NOTSET, A_SPACE, A_NOTSPACE } abb; + /* State of the "[^0]^D" sequences. */ + enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st; + /* State of the hex input character. */ + enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex; + /* State of quotation. */ + enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted; + CH ikey; /* Input character structure. */ + CHAR_T ch; /* Input character. */ + GS *gp; /* Global pointer. */ + TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */ + size_t rcol; /* 0-N: insert offset in the replay buffer. */ + size_t col; /* Current column. */ + u_long margin; /* Wrapmargin value. */ + u_int iflags; /* Input flags. */ + int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */ + int eval; /* Routine return value. */ + int replay; /* If replaying a set of input. */ + int showmatch; /* Showmatch set on this character. */ + int testnr; /* Test first character for nul replay. */ + int max, tmp; + char *p; + + /* + * Set the input flag, so tabs get displayed correctly + * and everyone knows that the text buffer is in use. + */ + F_SET(sp, S_INPUT); + + /* Local initialization. */ + eval = 0; + gp = sp->gp; + + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) If changing a line, + * copy it into the TEXT buffer. + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) { + text_lfree(tiqh); + goto newtp; + } + tp->ai = tp->insert = tp->offset = tp->owrite = 0; + if (lp != NULL) { + tp->len = len; + memmove(tp->lb, lp, len); + } else + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) + return (1); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + /* Set the starting line number. */ + tp->lno = sp->lno; + + /* + * Set the insert and overwrite counts. If overwriting characters, + * do insertion afterward. If not overwriting characters, assume + * doing insertion. If change is to a mark, emphasize it with an + * END_CH. + */ + if (len) { + if (LF_ISSET(TXT_OVERWRITE)) { + tp->owrite = tm->cno - sp->cno; + tp->insert = len - tm->cno; + } else + tp->insert = len - sp->cno; + + if (LF_ISSET(TXT_EMARK)) + tp->lb[tm->cno - 1] = END_CH; + } + + /* + * Many of the special cases in this routine are to handle autoindent + * support. Somebody decided that it would be a good idea if "^^D" + * and "0^D" deleted all of the autoindented characters. In an editor + * that takes single character input from the user, this wasn't a very + * good idea. Note also that "^^D" resets the next lines' autoindent, + * but "0^D" doesn't. + * + * We assume that autoindent only happens on empty lines, so insert + * and overwrite will be zero. If doing autoindent, figure out how + * much indentation we need and fill it in. Update input column and + * screen cursor as necessary. + */ + if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { + if (txt_auto(sp, ep, ai_line, NULL, 0, tp)) + return (1); + sp->cno = tp->ai; + } else { + /* + * The cc and S commands have a special feature -- leading + * <blank> characters are handled as autoindent characters. + * Beauty! + */ + if (LF_ISSET(TXT_AICHARS)) { + tp->offset = 0; + tp->ai = sp->cno; + } else + tp->offset = sp->cno; + } + + /* If getting a command buffer from the user, there may be a prompt. */ + if (LF_ISSET(TXT_PROMPT)) { + tp->lb[sp->cno++] = prompt; + ++tp->len; + ++tp->offset; + } + + /* + * If appending after the end-of-line, add a space into the buffer + * and move the cursor right. This space is inserted, i.e. pushed + * along, and then deleted when the line is resolved. Assumes that + * the cursor is already positioned at the end of the line. This + * avoids the nastiness of having the cursor reside on a magical + * column, i.e. a column that doesn't really exist. The only down + * side is that we may wrap lines or scroll the screen before it's + * strictly necessary. Not a big deal. + */ + if (LF_ISSET(TXT_APPENDEOL)) { + tp->lb[sp->cno] = CURSOR_CH; + ++tp->len; + ++tp->insert; + } + + /* + * Historic practice is that the wrapmargin value was a distance + * from the RIGHT-HAND column, not the left. It's more useful to + * us as a distance from the left-hand column. + * + * !!! + * Replay commands are not affected by wrapmargin values. What + * I found surprising was that people actually depend on it, as + * in this gem of a macro which centers lines: + * + * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p + * + * XXX + * Setting margin causes a significant performance hit. Normally + * we don't update the screen if there are keys waiting, but we + * have to if margin is set, otherwise the screen routines don't + * know where the cursor is. + */ + if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN)) + margin = 0; + else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) + margin = sp->cols - margin; + + /* Initialize abbreviations checks. */ + if (F_ISSET(gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) { + abb = A_NOTSPACE; + ab_cnt = ab_turnoff = 0; + } else + abb = A_NOTSET; + + /* + * Set up the dot command. Dot commands are done by saving the + * actual characters and replaying the input. We have to push + * the characters onto the key stack and then handle them normally, + * otherwise things like wrapmargin will fail. + * + * XXX + * It would be nice if we could swallow backspaces and such, but + * it's not all that easy to do. Another possibility would be to + * recognize full line insertions, which could be performed quickly, + * without replay. + */ +nullreplay: + rcol = 0; + if (replay = LF_ISSET(TXT_REPLAY)) { + /* + * !!! + * Historically, it wasn't an error to replay non-existent + * input. This test is necessary, we get here by the user + * doing an input command followed by a nul. + * + * !!! + * Historically, vi did not remap or reabbreviate replayed + * input. It did, however, beep at you if you changed an + * abbreviation and then replayed the input. We're not that + * compatible. + */ + if (VIP(sp)->rep == NULL) + return (0); + if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP)) + return (1); + testnr = 0; + abb = A_NOTSET; + LF_CLR(TXT_RECORD); + } else + testnr = 1; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (gp, showmatch = 0, + carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) { + /* + * Reset the line and update the screen. (The txt_showmatch() + * code refreshes the screen for us.) Don't refresh unless + * we're about to wait on a character or we need to know where + * the cursor really is. + */ + if (showmatch || margin || !KEYS_WAITING(sp)) { + if (sp->s_change(sp, ep, tp->lno, LINE_RESET)) + goto err; + if (showmatch) { + showmatch = 0; + txt_showmatch(sp, ep); + } else if (sp->s_refresh(sp, ep)) + goto err; + } + + /* Get the next character. */ +next_ch: if (term_key(sp, &ikey, iflags) != INP_OK) + goto err; + ch = ikey.ch; + + /* Abbreviation check. See comment in txt_abbrev(). */ +#define MAX_ABBREVIATION_EXPANSION 256 + if (ikey.flags & CH_ABBREVIATED) { + if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) { + term_ab_flush(sp, + "Abbreviation exceeded maximum number of characters"); + ab_cnt = 0; + continue; + } + } else + ab_cnt = 0; + + /* + * !!! + * Historic feature. If the first character of the input is + * a nul, replay the previous input. This isn't documented + * anywhere, and is a great test of vi clones. + */ + if (ch == '\0' && testnr) { + LF_SET(TXT_REPLAY); + goto nullreplay; + } + testnr = 0; + + /* + * Check to see if the character fits into the input (and + * replay, if necessary) buffers. It isn't necessary to + * have tp->len bytes, since it doesn't consider overwrite + * characters, but not worth fixing. + */ + if (LF_ISSET(TXT_RECORD)) { + TBINC(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1); + VIP(sp)->rep[rcol++] = ch; + } + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + + /* + * If the character was quoted, replace the last character + * (the literal mark) with the new character. If quoted + * by someone else, simply insert the character. + * + * !!! + * Extension -- if the quoted character is HEX_CH, enter hex + * mode. If the user enters "<HEX_CH>[isxdigit()]*" we will + * try to use the value as a character. Anything else resets + * hex mode. + */ + if (ikey.flags & CH_QUOTED) + goto ins_ch; + if (quoted == Q_THISCHAR) { + --sp->cno; + ++tp->owrite; + quoted = Q_NOTSET; + + if (ch == HEX_CH) + hex = H_NEXTCHAR; + goto ins_ch; + } + + switch (ikey.value) { + case K_CR: + case K_NL: /* New line. */ +#define LINE_RESOLVE { \ + /* \ + * Handle abbreviations. If there was one, \ + * discard the replay characters. \ + */ \ + if (abb == A_NOTSPACE && !replay) { \ + if (txt_abbrev(sp, tp, ch, \ + LF_ISSET(TXT_INFOLINE), &tmp, \ + &ab_turnoff)) \ + goto err; \ + if (tmp) { \ + if (LF_ISSET(TXT_RECORD)) \ + rcol -= tmp; \ + goto next_ch; \ + } \ + } \ + if (abb != A_NOTSET) \ + abb = A_SPACE; \ + /* Handle hex numbers. */ \ + if (hex == H_INHEX) { \ + if (txt_hex(sp, tp, &tmp, ch)) \ + goto err; \ + if (tmp) { \ + hex = H_NOTSET; \ + goto next_ch; \ + } \ + } \ + /* \ + * The 'R' command returns any overwriteable \ + * characters in the first line to the original \ + * characters. + */ \ + if (LF_ISSET(TXT_REPLACE) && tp->owrite && \ + tp == tiqh->cqh_first) { \ + memmove(tp->lb + sp->cno, \ + lp + sp->cno, tp->owrite); \ + tp->insert += tp->owrite; \ + tp->owrite = 0; \ + } \ + /* Delete any appended cursor. */ \ + if (LF_ISSET(TXT_APPENDEOL)) { \ + --tp->len; \ + --tp->insert; \ + } \ +} + LINE_RESOLVE; + + /* CR returns from the vi command line. */ + if (LF_ISSET(TXT_CR)) { + /* + * If a script window and not the colon + * line, push a <cr> so it gets executed. + */ + if (F_ISSET(sp, S_SCRIPT) && + !LF_ISSET(TXT_INFOLINE)) + (void)term_push(sp, + "\r", 1, 0, CH_NOMAP); + goto k_escape; + } + + /* + * Historic practice was to delete any <blank> + * characters following the inserted newline. + * This affects the 'R', 'c', and 's' commands. + */ + for (p = tp->lb + sp->cno + tp->owrite; + tp->insert && isblank(*p); + ++p, ++tp->owrite, --tp->insert); + + /* + * Move any remaining insert characters into + * a new TEXT structure. + */ + if ((ntp = text_init(sp, + tp->lb + sp->cno + tp->owrite, + tp->insert, tp->insert + 32)) == NULL) + goto err; + + /* Set bookkeeping for the new line. */ + ntp->lno = tp->lno + 1; + ntp->insert = tp->insert; + + /* + * Note if the user inserted any characters on this + * line. Done before calling txt_ai_resolve() because + * it changes the value of sp->cno without making the + * corresponding changes to tp->ai. + */ + tmp = sp->cno <= tp->ai; + + /* + * Resolve autoindented characters for the old line. + * Reset the autoindent line value. 0^D keeps the ai + * line from changing, ^D changes the level, even if + * there are no characters in the old line. Note, + * if using the current tp structure, use the cursor + * as the length, the user may have erased autoindent + * characters. + */ + if (LF_ISSET(TXT_AUTOINDENT)) { + txt_ai_resolve(sp, tp); + + if (carat_st == C_NOCHANGE) { + if (txt_auto(sp, ep, + OOBLNO, &ait, ait.ai, ntp)) + goto err; + FREE_SPACE(sp, ait.lb, ait.lb_len); + } else + if (txt_auto(sp, ep, + OOBLNO, tp, sp->cno, ntp)) + goto err; + carat_st = C_NOTSET; + } + + /* + * If the user hasn't entered any characters, delete + * any autoindent characters. + * + * !!! + * Historic vi didn't get the insert test right, if + * there were characters after the cursor, entering + * a <cr> left the autoindent characters on the line. + */ + if (tmp) + sp->cno = 0; + + /* Reset bookkeeping for the old line. */ + tp->len = sp->cno; + tp->ai = tp->insert = tp->owrite = 0; + + /* New cursor position. */ + sp->cno = ntp->ai; + + /* New lines are TXT_APPENDEOL if nothing to insert. */ + if (ntp->insert == 0) { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + LF_SET(TXT_APPENDEOL); + ntp->lb[sp->cno] = CURSOR_CH; + ++ntp->insert; + ++ntp->len; + } + + /* Update the old line. */ + if (sp->s_change(sp, ep, tp->lno, LINE_RESET)) + goto err; + + /* + * Swap old and new TEXT's, and insert the new TEXT + * into the queue. (DON'T insert until the old line + * has been updated, or the inserted line count in + * line.c:file_gline() will be wrong.) + */ + tp = ntp; + CIRCLEQ_INSERT_TAIL(tiqh, tp, q); + + /* Reset the cursor. */ + sp->lno = tp->lno; + + /* Update the new line. */ + if (sp->s_change(sp, ep, tp->lno, LINE_INSERT)) + goto err; + + /* Set the renumber bit. */ + F_SET(sp, S_RENUMBER); + + /* Refresh if nothing waiting. */ + if ((margin || !KEYS_WAITING(sp)) && + sp->s_refresh(sp, ep)) + goto err; + goto next_ch; + case K_ESCAPE: /* Escape. */ + if (!LF_ISSET(TXT_ESCAPE)) + goto ins_ch; + + LINE_RESOLVE; + + /* + * If there aren't any trailing characters in the line + * and the user hasn't entered any characters, delete + * the autoindent characters. + */ + if (!tp->insert && sp->cno <= tp->ai) { + tp->len = tp->owrite = 0; + sp->cno = 0; + } else if (LF_ISSET(TXT_AUTOINDENT)) + txt_ai_resolve(sp, tp); + + /* If there are insert characters, copy them down. */ +k_escape: if (tp->insert && tp->owrite) + memmove(tp->lb + sp->cno, + tp->lb + sp->cno + tp->owrite, tp->insert); + tp->len -= tp->owrite; + + /* + * Delete any lines that were inserted into the text + * structure and then erased. + */ + while (tp->q.cqe_next != (void *)tiqh) { + ntp = tp->q.cqe_next; + CIRCLEQ_REMOVE(tiqh, ntp, q); + text_free(ntp); + } + + /* + * If not resolving the lines into the file, end + * it with a nul. + * + * XXX + * This is wrong, should pass back a length. + */ + if (LF_ISSET(TXT_RESOLVE)) { + if (txt_resolve(sp, ep, tiqh)) + goto err; + /* + * Clear input flag -- input buffer no longer + * valid. + */ + F_CLR(sp, S_INPUT); + } else { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + tp->lb[tp->len] = '\0'; + } + + /* + * Set the return cursor position to rest on the last + * inserted character. + */ + if (rp != NULL) { + rp->lno = tp->lno; + rp->cno = sp->cno ? sp->cno - 1 : 0; + if (sp->s_change(sp, ep, rp->lno, LINE_RESET)) + goto err; + } + goto ret; + case K_CARAT: /* Delete autoindent chars. */ + if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai) + carat_st = C_CARATSET; + goto ins_ch; + case K_ZERO: /* Delete autoindent chars. */ + if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai) + carat_st = C_ZEROSET; + goto ins_ch; + case K_VEOF: /* Delete autoindent char. */ + /* + * If in the first column or no characters to erase, + * ignore the ^D (this matches historic practice). If + * not doing autoindent or already inserted non-ai + * characters, it's a literal. The latter test is done + * in the switch, as the CARAT forms are N + 1, not N. + */ + if (!LF_ISSET(TXT_AUTOINDENT)) + goto ins_ch; + if (sp->cno == 0 || tp->ai == 0) + break; + switch (carat_st) { + case C_CARATSET: /* ^^D */ + if (sp->cno > tp->ai + tp->offset + 1) + goto ins_ch; + + /* Save the ai string for later. */ + ait.lb = NULL; + ait.lb_len = 0; + TBINC(sp, ait.lb, ait.lb_len, tp->ai); + memmove(ait.lb, tp->lb, tp->ai); + ait.ai = ait.len = tp->ai; + + carat_st = C_NOCHANGE; + goto leftmargin; + case C_ZEROSET: /* 0^D */ + if (sp->cno > tp->ai + tp->offset + 1) + goto ins_ch; + carat_st = C_NOTSET; +leftmargin: tp->lb[sp->cno - 1] = ' '; + tp->owrite += sp->cno - tp->offset; + tp->ai = 0; + sp->cno = tp->offset; + break; + case C_NOTSET: /* ^D */ + if (sp->cno > tp->ai + tp->offset) + goto ins_ch; + (void)txt_outdent(sp, tp); + break; + default: + abort(); + } + break; + case K_VERASE: /* Erase the last character. */ + /* + * If can erase over the prompt, return. Len is 0 + * if backspaced over the prompt, 1 if only CR entered. + */ + if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) { + tp->len = 0; + goto ret; + } + + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + break; + } + + /* If nothing to erase, bell the user. */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* Drop back one character. */ + --sp->cno; + + /* + * Increment overwrite, decrement ai if deleted. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + ++tp->owrite; + if (sp->cno < tp->ai) + --tp->ai; + break; + case K_VINTR: + /* + * !!! + * Historically, <interrupt> exited the user from + * editing the infoline, and returned to the main + * screen. It also beeped the terminal, but that + * seems excessive. + */ + if (LF_ISSET(TXT_INFOLINE)) { + tp->lb[tp->len = 0] = '\0'; + goto ret; + } + goto ins_ch; + case K_VWERASE: /* Skip back one word. */ + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + } + + /* + * If at offset, nothing to erase so bell the user. + */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* + * First werase goes back to any autoindent + * and second werase goes back to the offset. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + if (tp->ai && sp->cno > tp->ai) + max = tp->ai; + else { + tp->ai = 0; + max = tp->offset; + } + + /* Skip over trailing space characters. */ + while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) { + --sp->cno; + ++tp->owrite; + } + if (sp->cno == max) + break; + /* + * There are three types of word erase found on UNIX + * systems. They can be identified by how the string + * /a/b/c is treated -- as 1, 3, or 6 words. Historic + * vi had two classes of characters, and strings were + * delimited by them and <blank>'s, so, 6 words. The + * historic tty interface used <blank>'s to delimit + * strings, so, 1 word. The algorithm offered in the + * 4.4BSD tty interface (as stty altwerase) treats it + * as 3 words -- there are two classes of characters, + * and strings are delimited by them and <blank>'s. + * The difference is that the type of the first erased + * character erased is ignored, which is exactly right + * when erasing pathname components. Here, the options + * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD + * tty interface and the historic tty driver behavior, + * respectively, and the default is the same as the + * historic vi behavior. + */ + if (LF_ISSET(TXT_TTYWERASE)) + while (sp->cno > max) { + --sp->cno; + ++tp->owrite; + if (isblank(tp->lb[sp->cno - 1])) + break; + } + else { + if (LF_ISSET(TXT_ALTWERASE)) { + --sp->cno; + ++tp->owrite; + if (isblank(tp->lb[sp->cno - 1])) + break; + } + if (sp->cno > max) + tmp = inword(tp->lb[sp->cno - 1]); + while (sp->cno > max) { + --sp->cno; + ++tp->owrite; + if (tmp != inword(tp->lb[sp->cno - 1]) + || isblank(tp->lb[sp->cno - 1])) + break; + } + } + break; + case K_VKILL: /* Restart this line. */ + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + } + + /* If at offset, nothing to erase so bell the user. */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* + * First kill goes back to any autoindent + * and second kill goes back to the offset. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + if (tp->ai && sp->cno > tp->ai) + max = tp->ai; + else { + tp->ai = 0; + max = tp->offset; + } + tp->owrite += sp->cno - max; + sp->cno = max; + break; + case K_CNTRLT: /* Add autoindent char. */ + if (!LF_ISSET(TXT_CNTRLT)) + goto ins_ch; + if (txt_indent(sp, tp)) + goto err; + goto ebuf_chk; + case K_CNTRLZ: + (void)sp->s_suspend(sp); + break; +#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT + case K_FORMFEED: + F_SET(sp, S_REFRESH); + break; +#endif + case K_RIGHTBRACE: + case K_RIGHTPAREN: + showmatch = LF_ISSET(TXT_SHOWMATCH); + goto ins_ch; + case K_VLNEXT: /* Quote the next character. */ + /* If in hex mode, see if we've entered a hex value. */ + if (hex == H_INHEX) { + if (txt_hex(sp, tp, &tmp, ch)) + goto err; + if (tmp) { + hex = H_NOTSET; + goto next_ch; + } + } + ch = '^'; + quoted = Q_NEXTCHAR; + /* FALLTHROUGH */ + default: /* Insert the character. */ +ins_ch: /* + * If entering a space character after a word, check + * for abbreviations. If there was one, discard the + * replay characters. + */ + if (isblank(ch) && abb == A_NOTSPACE && !replay) { + if (txt_abbrev(sp, tp, ch, + LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) + goto err; + if (tmp) { + if (LF_ISSET(TXT_RECORD)) + rcol -= tmp; + goto next_ch; + } + } + /* If in hex mode, see if we've entered a hex value. */ + if (hex == H_INHEX && !isxdigit(ch)) { + if (txt_hex(sp, tp, &tmp, ch)) + goto err; + if (tmp) { + hex = H_NOTSET; + goto next_ch; + } + } + /* Check to see if we've crossed the margin. */ + if (margin) { + if (sp->s_column(sp, ep, &col)) + goto err; + if (col >= margin) { + if (txt_margin(sp, tp, &tmp, ch)) + goto err; + if (tmp) + goto next_ch; + } + } + if (abb != A_NOTSET) + abb = isblank(ch) ? A_SPACE : A_NOTSPACE; + + if (tp->owrite) /* Overwrite a character. */ + --tp->owrite; + else if (tp->insert) { /* Insert a character. */ + ++tp->len; + if (tp->insert == 1) + tp->lb[sp->cno + 1] = tp->lb[sp->cno]; + else + memmove(tp->lb + sp->cno + 1, + tp->lb + sp->cno, tp->insert); + } + + tp->lb[sp->cno++] = ch; + + /* + * If we've reached the end of the buffer, then we + * need to switch into insert mode. This happens + * when there's a change to a mark and the user puts + * in more characters than the length of the motion. + */ +ebuf_chk: if (sp->cno >= tp->len) { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + LF_SET(TXT_APPENDEOL); + tp->lb[sp->cno] = CURSOR_CH; + ++tp->insert; + ++tp->len; + } + + if (hex == H_NEXTCHAR) + hex = H_INHEX; + if (quoted == Q_NEXTCHAR) + quoted = Q_THISCHAR; + break; + } +#if defined(DEBUG) && 1 + if (sp->cno + tp->insert + tp->owrite != tp->len) + msgq(sp, M_ERR, + "len %u != cno: %u ai: %u insert %u overwrite %u", + tp->len, sp->cno, tp->ai, tp->insert, tp->owrite); + tp->len = sp->cno + tp->insert + tp->owrite; +#endif + } + + /* Clear input flag. */ +ret: F_CLR(sp, S_INPUT); + + if (LF_ISSET(TXT_RECORD)) + VIP(sp)->rep_cnt = rcol; + return (eval); + + /* Error jump. */ +err: eval = 1; + txt_err(sp, ep, tiqh); + goto ret; +} + +/* + * txt_abbrev -- + * Handle abbreviations. + */ +static int +txt_abbrev(sp, tp, pushc, isinfoline, didsubp, turnoffp) + SCR *sp; + TEXT *tp; + ARG_CHAR_T pushc; + int isinfoline, *didsubp, *turnoffp; +{ + CHAR_T ch; + SEQ *qp; + size_t len, off; + char *p; + + /* Find the beginning of this "word". */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (isblank(*p)) { + ++p; + break; + } + ++len; + if (off == tp->ai || off == tp->offset) + break; + } + + /* + * !!! + * Historic vi exploded abbreviations on the command line. This has + * obvious problems in that unabbreviating the string can be extremely + * tricky, particularly if the string has, say, an embedded escape + * character. Personally, I think it's a stunningly bad idea. Other + * examples of problems this caused in historic vi are: + * :ab foo bar + * :ab foo baz + * results in "bar" being abbreviated to "baz", which wasn't what the + * user had in mind at all. Also, the commands: + * :ab foo bar + * :unab foo<space> + * resulted in an error message that "bar" wasn't mapped. Finally, + * since the string was already exploded by the time the unabbreviate + * command got it, all it knew was that an abbreviation had occurred. + * Cleverly, it checked the replacement string for its unabbreviation + * match, which meant that the commands: + * :ab foo1 bar + * :ab foo2 bar + * :unab foo2 + * unabbreviates "foo1", and the commands: + * :ab foo bar + * :ab bar baz + * unabbreviates "foo"! + * + * Anyway, people neglected to first ask my opinion before they wrote + * macros that depend on this stuff, so, we make this work as follows. + * When checking for an abbreviation on the command line, if we get a + * string which is <blank> terminated and which starts at the beginning + * of the line, we check to see it is the abbreviate or unabbreviate + * commands. If it is, turn abbreviations off and return as if no + * abbreviation was found. Not also, minor trickiness, so that if the + * user erases the line and starts another command, we go ahead an turn + * abbreviations back on. + * + * This makes the layering look like a Nachos Supreme. + */ + *didsubp = 0; + if (isinfoline) + if (off == tp->ai || off == tp->offset) + if (ex_is_abbrev(p, len)) { + *turnoffp = 1; + return (0); + } else + *turnoffp = 0; + else + if (*turnoffp) + return (0); + + /* Check for any abbreviations. */ + if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) + return (0); + + /* + * Push the abbreviation onto the tty stack. Historically, characters + * resulting from an abbreviation expansion were themselves subject to + * map expansions, O_SHOWMATCH matching etc. This means the expanded + * characters will be re-tested for abbreviations. It's difficult to + * know what historic practice in this case was, since abbreviations + * were applied to :colon command lines, so entering abbreviations that + * looped was tricky, although possible. In addition, obvious loops + * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will + * silently only implement and/or display the last abbreviation.) + * + * This implementation doesn't recover well from such abbreviations. + * The main input loop counts abbreviated characters, and, when it + * reaches a limit, discards any abbreviated characters on the queue. + * It's difficult to back up to the original position, as the replay + * queue would have to be adjusted, and the line state when an initial + * abbreviated character was received would have to be saved. + */ + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_ABBREVIATED)) + return (1); + if (term_push(sp, qp->output, qp->olen, 0, CH_ABBREVIATED)) + return (1); + + /* + * Move the cursor to the start of the abbreviation, + * adjust the length. + */ + sp->cno -= len; + tp->len -= len; + + /* Copy any insert characters back. */ + if (tp->insert) + memmove(tp->lb + sp->cno + tp->owrite, + tp->lb + sp->cno + tp->owrite + len, tp->insert); + + /* + * We return the length of the abbreviated characters. This is so + * the calling routine can replace the replay characters with the + * abbreviation. This means that subsequent '.' commands will produce + * the same text, regardless of intervening :[un]abbreviate commands. + * This is historic practice. + */ + *didsubp = len; + return (0); +} + +/* Offset to next column of stop size. */ +#define STOP_OFF(c, stop) (stop - (c) % stop) + +/* + * txt_ai_resolve -- + * When a line is resolved by <esc> or <cr>, review autoindent + * characters. + */ +static void +txt_ai_resolve(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long ts; + int del; + size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; + char *p; + + /* + * If the line is empty, has an offset, or no autoindent + * characters, we're done. + */ + if (!tp->len || tp->offset || !tp->ai) + return; + + /* + * The autoindent characters plus any leading <blank> characters + * in the line are resolved into the minimum number of characters. + * Historic practice. + */ + ts = O_VAL(sp, O_TABSTOP); + + /* Figure out the last <blank> screen column. */ + for (p = tp->lb, scno = 0, len = tp->len, + spaces = tab_after_sp = 0; len-- && isblank(*p); ++p) + if (*p == '\t') { + if (spaces) + tab_after_sp = 1; + scno += STOP_OFF(scno, ts); + } else { + ++spaces; + ++scno; + } + + /* + * If there are no spaces, or no tabs after spaces and less than + * ts spaces, it's already minimal. + */ + if (!spaces || !tab_after_sp && spaces < ts) + return; + + /* Count up spaces/tabs needed to get to the target. */ + for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs) + cno += STOP_OFF(cno, ts); + spaces = scno - cno; + + /* + * Figure out how many characters we're dropping -- if we're not + * dropping any, it's already minimal, we're done. + */ + old = p - tp->lb; + new = spaces + tabs; + if (old == new) + return; + + /* Shift the rest of the characters down, adjust the counts. */ + del = old - new; + memmove(p - del, p, tp->len - old); + sp->cno -= del; + tp->len -= del; + + /* Fill in space/tab characters. */ + for (p = tp->lb; tabs--;) + *p++ = '\t'; + while (spaces--) + *p++ = ' '; +} + +/* + * txt_auto -- + * Handle autoindent. If aitp isn't NULL, use it, otherwise, + * retrieve the line. + */ +int +txt_auto(sp, ep, lno, aitp, len, tp) + SCR *sp; + EXF *ep; + recno_t lno; + size_t len; + TEXT *aitp, *tp; +{ + size_t nlen; + char *p, *t; + + if (aitp == NULL) { + if ((p = t = file_gline(sp, ep, lno, &len)) == NULL) + return (0); + } else + p = t = aitp->lb; + for (nlen = 0; len; ++p) { + if (!isblank(*p)) + break; + /* If last character is a space, it counts. */ + if (--len == 0) { + ++p; + break; + } + } + + /* No indentation. */ + if (p == t) + return (0); + + /* Set count. */ + nlen = p - t; + + /* Make sure the buffer's big enough. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen); + + /* Copy the indentation into the new buffer. */ + memmove(tp->lb + nlen, tp->lb, tp->len); + memmove(tp->lb, t, nlen); + tp->len += nlen; + + /* Return the additional length. */ + tp->ai = nlen; + return (0); +} + +/* + * txt_backup -- + * Back up to the previously edited line. + */ +static TEXT * +txt_backup(sp, ep, tiqh, tp, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + TEXT *tp; + u_int flags; +{ + TEXT *ntp; + recno_t lno; + size_t total; + + /* Get a handle on the previous TEXT structure. */ + if ((ntp = tp->q.cqe_prev) == (void *)tiqh) { + msgq(sp, M_BERR, "Already at the beginning of the insert"); + return (tp); + } + + /* Make sure that we have enough space. */ + total = ntp->len + tp->insert; + if (LF_ISSET(TXT_APPENDEOL)) + ++total; + if (total > ntp->lb_len && + binc(sp, &ntp->lb, &ntp->lb_len, total)) + return (NULL); + + /* + * Append a cursor or copy inserted bytes to the end of the old line. + * Test for appending a cursor first, because the TEXT insert field + * will be 1 if we're appending a cursor. I don't think there's a + * third case, so abort() if there is. + */ + if (LF_ISSET(TXT_APPENDEOL)) { + ntp->lb[ntp->len] = CURSOR_CH; + ntp->insert = 1; + } else if (tp->insert) { + memmove(ntp->lb + ntp->len, tp->lb + tp->owrite, tp->insert); + ntp->insert = tp->insert; + } else + abort(); + + /* Set bookkeeping information. */ + sp->lno = ntp->lno; + sp->cno = ntp->len; + ntp->len += ntp->insert; + + /* Release the current TEXT. */ + lno = tp->lno; + CIRCLEQ_REMOVE(tiqh, tp, q); + text_free(tp); + + /* Update the old line on the screen. */ + if (sp->s_change(sp, ep, lno, LINE_DELETE)) + return (NULL); + + /* Return the old line. */ + return (ntp); +} + +/* + * txt_err -- + * Handle an error during input processing. + */ +static void +txt_err(sp, ep, tiqh) + SCR *sp; + EXF *ep; + TEXTH *tiqh; +{ + recno_t lno; + size_t len; + + /* + * The problem with input processing is that the cursor is at an + * indeterminate position since some input may have been lost due + * to a malloc error. So, try to go back to the place from which + * the cursor started, knowing that it may no longer be available. + * + * We depend on at least one line number being set in the text + * chain. + */ + for (lno = tiqh->cqh_first->lno; + file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno); + + sp->lno = lno == 0 ? 1 : lno; + sp->cno = 0; + + /* Redraw the screen, just in case. */ + F_SET(sp, S_REDRAW); +} + +/* + * txt_hex -- + * Let the user insert any character value they want. + * + * !!! + * This is an extension. The pattern "^Vx[0-9a-fA-F]*" is a way + * for the user to specify a character value which their keyboard + * may not be able to enter. + */ +static int +txt_hex(sp, tp, was_hex, pushc) + SCR *sp; + TEXT *tp; + int *was_hex; + ARG_CHAR_T pushc; +{ + CHAR_T ch, savec; + size_t len, off; + u_long value; + char *p, *wp; + + /* + * Null-terminate the string. Since nul isn't a legal hex value, + * this should be okay, and lets us use a local routine, which + * presumably understands the character set, to convert the value. + */ + savec = tp->lb[sp->cno]; + tp->lb[sp->cno] = 0; + + /* Find the previous HEX_CH. */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (*p == HEX_CH) { + wp = p + 1; + break; + } + ++len; + /* If not on this line, there's nothing to do. */ + if (off == tp->ai || off == tp->offset) + goto nothex; + } + + /* If no length, then it wasn't a hex value. */ + if (len == 0) + goto nothex; + + /* Get the value. */ + value = strtol(wp, NULL, 16); + if (value == LONG_MIN || value == LONG_MAX || value > MAX_CHAR_T) { +nothex: tp->lb[sp->cno] = savec; + *was_hex = 0; + return (0); + } + + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED)) + return (1); + ch = value; + if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED)) + return (1); + + tp->lb[sp->cno] = savec; + + /* Move the cursor to the start of the hex value, adjust the length. */ + sp->cno -= len + 1; + tp->len -= len + 1; + + /* Copy any insert characters back. */ + if (tp->insert) + memmove(tp->lb + sp->cno + tp->owrite, + tp->lb + sp->cno + tp->owrite + len + 1, tp->insert); + + *was_hex = 1; + return (0); +} + +/* + * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements + * to the next or previous shiftwidth value, i.e. for a 1-based numbering, + * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to + * the 10th column, and ^D moves it back. + * + * !!! + * The ^T and ^D characters in historical vi only had special meaning when + * they were the first characters typed after entering text input mode. + * Since normal erase characters couldn't erase autoindent (in this case + * ^T) characters, this meant that inserting text into previously existing + * text was quite strange, ^T only worked if it was the first keystroke, + * and then it could only be erased by using ^D. This implementation treats + * ^T specially anywhere it occurs in the input, and permits the standard + * erase characters to erase characters inserted using it. + * + * XXX + * Technically, txt_indent, txt_outdent should part of the screen interface, + * as they require knowledge of the size of a space character on the screen. + * (Not the size of tabs, because tabs are logically composed of spaces.) + * They're left in the text code because they're complicated, not to mention + * the gruesome awareness that if spaces aren't a single column on the screen + * for any language, we're into some serious, ah, for lack of a better word, + * "issues". + */ + +/* + * txt_indent -- + * Handle ^T indents. + */ +static int +txt_indent(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long sw, ts; + size_t cno, off, scno, spaces, tabs; + + ts = O_VAL(sp, O_TABSTOP); + sw = O_VAL(sp, O_SHIFTWIDTH); + + /* Get the current screen column. */ + for (off = scno = 0; off < sp->cno; ++off) + if (tp->lb[off] == '\t') + scno += STOP_OFF(scno, ts); + else + ++scno; + + /* Count up spaces/tabs needed to get to the target. */ + for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0; + cno + STOP_OFF(cno, ts) <= scno; ++tabs) + cno += STOP_OFF(cno, ts); + spaces = scno - cno; + + /* Put space/tab characters in place of any overwrite characters. */ + for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai) + tp->lb[sp->cno++] = '\t'; + for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai) + tp->lb[sp->cno++] = ' '; + + if (!tabs && !spaces) + return (0); + + /* Make sure there's enough room. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs); + + /* Move the insert characters out of the way. */ + if (tp->insert) + memmove(tp->lb + sp->cno + spaces + tabs, + tp->lb + sp->cno, tp->insert); + + /* Add new space/tab characters. */ + for (; tabs--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = '\t'; + for (; spaces--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = ' '; + return (0); +} + +/* + * txt_outdent -- + * Handle ^D outdents. + * + */ +static int +txt_outdent(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long sw, ts; + size_t cno, off, scno, spaces; + + ts = O_VAL(sp, O_TABSTOP); + sw = O_VAL(sp, O_SHIFTWIDTH); + + /* Get the current screen column. */ + for (off = scno = 0; off < sp->cno; ++off) + if (tp->lb[off] == '\t') + scno += STOP_OFF(scno, ts); + else + ++scno; + + /* Get the previous shiftwidth column. */ + for (cno = scno; --scno % sw != 0;); + + /* Decrement characters until less than or equal to that slot. */ + for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite) + if (tp->lb[--off] == '\t') + cno -= STOP_OFF(cno, ts); + else + --cno; + + /* Spaces needed to get to the target. */ + spaces = scno - cno; + + /* Maybe just a delete. */ + if (spaces == 0) + return (0); + + /* Make sure there's enough room. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces); + + /* Use up any overwrite characters. */ + for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite) + tp->lb[sp->cno++] = ' '; + + /* Maybe that was enough. */ + if (spaces == 0) + return (0); + + /* Move the insert characters out of the way. */ + if (tp->insert) + memmove(tp->lb + sp->cno + spaces, + tp->lb + sp->cno, tp->insert); + + /* Add new space characters. */ + for (; spaces--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = ' '; + return (0); +} + +/* + * txt_resolve -- + * Resolve the input text chain into the file. + */ +static int +txt_resolve(sp, ep, tiqh) + SCR *sp; + EXF *ep; + TEXTH *tiqh; +{ + TEXT *tp; + recno_t lno; + + /* The first line replaces a current line. */ + tp = tiqh->cqh_first; + if (file_sline(sp, ep, tp->lno, tp->lb, tp->len)) + return (1); + + /* All subsequent lines are appended into the file. */ + for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno) + if (file_aline(sp, ep, 0, lno, tp->lb, tp->len)) + return (1); + return (0); +} + +/* + * txt_showmatch -- + * Show a character match. + * + * !!! + * Historic vi tried to display matches even in the :colon command line. + * I think not. + */ +static void +txt_showmatch(sp, ep) + SCR *sp; + EXF *ep; +{ + struct timeval second; + VCS cs; + MARK m; + fd_set zero; + int cnt, endc, startc; + + /* + * Do a refresh first, in case the v_ntext() code hasn't done + * one in awhile, so the user can see what we're complaining + * about. + */ + if (sp->s_refresh(sp, ep)) + return; + /* + * We don't display the match if it's not on the screen. Find + * out what the first character on the screen is. + */ + if (sp->s_position(sp, ep, &m, 0, P_TOP)) + return; + + /* Initialize the getc() interface. */ + cs.cs_lno = sp->lno; + cs.cs_cno = sp->cno - 1; + if (cs_init(sp, ep, &cs)) + return; + startc = (endc = cs.cs_ch) == ')' ? '(' : '{'; + + /* Search for the match. */ + for (cnt = 1;;) { + if (cs_prev(sp, ep, &cs)) + return; + if (cs.cs_lno < m.lno || + cs.cs_lno == m.lno && cs.cs_cno < m.cno) + return; + if (cs.cs_flags != 0) { + if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) { + (void)sp->s_bell(sp); + return; + } + continue; + } + if (cs.cs_ch == endc) + ++cnt; + else if (cs.cs_ch == startc && --cnt == 0) + break; + } + + /* Move to the match. */ + m.lno = sp->lno; + m.cno = sp->cno; + sp->lno = cs.cs_lno; + sp->cno = cs.cs_cno; + (void)sp->s_refresh(sp, ep); + + /* + * Sleep(3) is eight system calls. Do it fast -- besides, + * I don't want to wait an entire second. + */ + FD_ZERO(&zero); + second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10; + second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L; + (void)select(0, &zero, &zero, &zero, &second); + + /* Return to the current location. */ + sp->lno = m.lno; + sp->cno = m.cno; + (void)sp->s_refresh(sp, ep); +} + +/* + * txt_margin -- + * Handle margin wrap. + * + * !!! + * Historic vi belled the user each time a character was entered after + * crossing the margin until a space was entered which could be used to + * break the line. I don't, it tends to wake the cats. + */ +static int +txt_margin(sp, tp, didbreak, pushc) + SCR *sp; + TEXT *tp; + int *didbreak; + ARG_CHAR_T pushc; +{ + CHAR_T ch; + size_t len, off, tlen; + char *p, *wp; + + /* Find the closest previous blank. */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (isblank(*p)) { + wp = p + 1; + break; + } + ++len; + /* If it's the beginning of the line, there's nothing to do. */ + if (off == tp->ai || off == tp->offset) { + *didbreak = 0; + return (0); + } + } + + /* + * Historic practice is to delete any trailing whitespace + * from the previous line. + */ + for (tlen = len;; --p, --off) { + if (!isblank(*p)) + break; + ++tlen; + if (off == tp->ai || off == tp->offset) + break; + } + + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_NOMAP)) + return (1); + if (len && term_push(sp, wp, len, 0, CH_NOMAP | CH_QUOTED)) + return (1); + ch = '\n'; + if (term_push(sp, &ch, 1, 0, CH_NOMAP)) + return (1); + + sp->cno -= tlen; + tp->owrite += tlen; + *didbreak = 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_paragraph.c b/usr.bin/vi/nvi/v_paragraph.c new file mode 100644 index 000000000000..00591cbe899e --- /dev/null +++ b/usr.bin/vi/nvi/v_paragraph.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_paragraph.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * Paragraphs are empty lines after text or values from the paragraph or + * section options. + */ + +/* + * v_paragraphf -- [count]} + * Move forward count paragraphs. + */ +int +v_paragraphf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { P_INTEXT, P_INBLANK } pstate; + size_t lastlen, len; + recno_t cnt, lastlno, lno; + char *p, *lp; + + /* Figure out what state we're currently in. */ + lno = fm->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) + goto eof; + + /* + * If we start in text, we want to switch states 2 * N - 1 + * times, in non-text, 2 * N times. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + cnt *= 2; + if (len == 0 || v_isempty(p, len)) + pstate = P_INBLANK; + else { + --cnt; + pstate = P_INTEXT; + } + + for (;;) { + lastlno = lno; + lastlen = len; + if ((p = file_gline(sp, ep, ++lno, &len)) == NULL) + goto eof; + switch (pstate) { + case P_INTEXT: + if (p[0] == '.' && len >= 2) + for (lp = VIP(sp)->paragraph; *lp; lp += 2) + if (lp[0] == p[1] && + (lp[1] == ' ' || lp[1] == p[2]) && + !--cnt) + goto found; + if (len == 0 || v_isempty(p, len)) { + if (!--cnt) + goto found; + pstate = P_INBLANK; + } + break; + case P_INBLANK: + if (len == 0 || v_isempty(p, len)) + break; + if (--cnt) { + pstate = P_INTEXT; + break; + } + /* + * Historically, a motion command was up to the end + * of the previous line, whereas the movement command + * was to the start of the new "paragraph". + */ +found: if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + rp->lno = lastlno; + rp->cno = lastlen ? lastlen + 1 : 0; + } else { + rp->lno = lno; + rp->cno = 0; + } + return (0); + default: + abort(); + } + } + + /* + * EOF is a movement sink, however, the } command historically + * moved to the end of the last line if repeatedly invoked. + */ +eof: if (fm->lno != lno - 1) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) + GETLINE_ERR(sp, fm->lno); + if (fm->cno != (len ? len - 1 : 0)) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + v_eof(sp, ep, NULL); + return (1); +} + +/* + * v_paragraphb -- [count]{ + * Move forward count paragraph. + */ +int +v_paragraphb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { P_INTEXT, P_INBLANK } pstate; + size_t len; + recno_t cnt, lno; + char *p, *lp; + + /* + * The { command historically moved to the beginning of the first + * line if invoked on the first line. + * + * Check for SOF. + */ + if (fm->lno <= 1) { + if (fm->cno == 0) { + v_sof(sp, NULL); + return (1); + } + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* Figure out what state we're currently in. */ + lno = fm->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) + goto sof; + + /* + * If we start in text, we want to switch states 2 * N - 1 + * times, in non-text, 2 * N times. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + cnt *= 2; + if (len == 0 || v_isempty(p, len)) + pstate = P_INBLANK; + else { + --cnt; + pstate = P_INTEXT; + } + + for (;;) { + if ((p = file_gline(sp, ep, --lno, &len)) == NULL) + goto sof; + switch (pstate) { + case P_INTEXT: + if (p[0] == '.' && len >= 2) + for (lp = VIP(sp)->paragraph; *lp; lp += 2) + if (lp[0] == p[1] && + (lp[1] == ' ' || lp[1] == p[2]) && + !--cnt) + goto found; + if (len == 0 || v_isempty(p, len)) { + if (!--cnt) + goto found; + pstate = P_INBLANK; + } + break; + case P_INBLANK: + if (len != 0 && !v_isempty(p, len)) { + if (!--cnt) { +found: rp->lno = lno; + rp->cno = 0; + return (0); + } + pstate = P_INTEXT; + } + break; + default: + abort(); + } + } + + /* SOF is a movement sink. */ +sof: rp->lno = 1; + rp->cno = 0; + return (0); +} + +/* + * v_buildparagraph -- + * Build the paragraph command search pattern. + */ +int +v_buildparagraph(sp) + SCR *sp; +{ + VI_PRIVATE *vip; + size_t p_len, s_len; + char *p, *p_p, *s_p; + + /* + * The vi paragraph command searches for either a paragraph or + * section option macro. + */ + p_len = (p_p = O_STR(sp, O_PARAGRAPHS)) == NULL ? 0 : strlen(p_p); + s_len = (s_p = O_STR(sp, O_SECTIONS)) == NULL ? 0 : strlen(s_p); + + if (p_len == 0 && s_len == 0) + return (0); + + MALLOC_RET(sp, p, char *, p_len + s_len + 1); + + vip = VIP(sp); + if (vip->paragraph != NULL) + FREE(vip->paragraph, vip->paragraph_len); + + if (p_p != NULL) + memmove(p, p_p, p_len + 1); + if (s_p != NULL) + memmove(p + p_len, s_p, s_len + 1); + vip->paragraph = p; + vip->paragraph_len = p_len + s_len + 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_put.c b/usr.bin/vi/nvi/v_put.c new file mode 100644 index 000000000000..60227ea4ccfd --- /dev/null +++ b/usr.bin/vi/nvi/v_put.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_put.c 8.6 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +static void inc_buf __P((SCR *, VICMDARG *)); + +/* + * v_Put -- [buffer]P + * Insert the contents of the buffer before the cursor. + */ +int +v_Put(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_ISDOT)) + inc_buf(sp, vp); + return (put(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 0)); +} + +/* + * v_put -- [buffer]p + * Insert the contents of the buffer after the cursor. + */ +int +v_put(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_ISDOT)) + inc_buf(sp, vp); + + return (put(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 1)); +} + +/* + * Historical whackadoo. The dot command `puts' the numbered buffer + * after the last one put. For example, `"4p.' would put buffer #4 + * and buffer #5. If the user continued to enter '.', the #9 buffer + * would be repeatedly output. This was not documented, and is a bit + * tricky to reconstruct. Historical versions of vi also dropped the + * contents of the default buffer after each put, so after `"4p' the + * default buffer would be empty. This makes no sense to me, so we + * don't bother. Don't assume sequential order of numeric characters. + * + * And, if that weren't exciting enough, failed commands don't normally + * set the dot command. Well, boys and girls, an exception is that + * the buffer increment gets done regardless of the success of the put. + */ +static void +inc_buf(sp, vp) + SCR *sp; + VICMDARG *vp; +{ + CHAR_T v; + + switch (vp->buffer) { + case '1': + v = '2'; + break; + case '2': + v = '3'; + break; + case '3': + v = '4'; + break; + case '4': + v = '5'; + break; + case '5': + v = '6'; + break; + case '6': + v = '7'; + break; + case '7': + v = '8'; + break; + case '8': + v = '9'; + break; + default: + return; + } + VIP(sp)->sdot.buffer = vp->buffer = v; +} diff --git a/usr.bin/vi/nvi/v_redraw.c b/usr.bin/vi/nvi/v_redraw.c new file mode 100644 index 000000000000..fca5ba33b713 --- /dev/null +++ b/usr.bin/vi/nvi/v_redraw.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_redraw.c 8.2 (Berkeley) 8/25/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_redraw -- + * Redraw the screen. + */ +int +v_redraw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + F_SET(sp, S_REFRESH); + return (0); +} diff --git a/usr.bin/vi/nvi/v_replace.c b/usr.bin/vi/nvi/v_replace.c new file mode 100644 index 000000000000..f9378e7ac4ed --- /dev/null +++ b/usr.bin/vi/nvi/v_replace.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_replace.c 8.12 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_replace -- [count]rc + * The r command in historic vi was almost beautiful in its badness. + * For example, "r<erase>" and "r<word erase>" beeped the terminal + * and deleted a single character. "Nr<carriage return>", where N + * was greater than 1, inserted a single carriage return. This may + * not be right, but at least it's not insane. + */ +int +v_replace(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + CH ikey; + TEXT *tp; + recno_t lno; + size_t blen, len; + u_long cnt; + int rval; + char *bp, *p; + + /* + * If the line doesn't exist, or it's empty, replacement isn't + * allowed. It's not hard to implement, but: + * + * 1: It's historic practice. + * 2: For consistency, this change would require that the more + * general case, "Nr", when the user is < N characters from + * the end of the line, also work. + * 3: Replacing a newline has somewhat odd semantics. + */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + goto nochar; + } + if (len == 0) { +nochar: msgq(sp, M_BERR, "No characters to replace"); + return (1); + } + + /* + * Figure out how many characters to be replace; for no particular + * reason other than that the semantics of replacing the newline + * are confusing, only permit the replacement of the characters in + * the current line. I suppose we could simply append the replacement + * characters to the line, but I see no compelling reason to do so. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + rp->cno = fm->cno + cnt - 1; + if (rp->cno > len - 1) { + v_eol(sp, ep, fm); + return (1); + } + + /* Get the character, literal escapes, escape terminates. */ + if (F_ISSET(vp, VC_ISDOT)) { + ikey.ch = VIP(sp)->rlast; + ikey.value = term_key_val(sp, ikey.ch); + } else { + if (term_key(sp, &ikey, 0) != INP_OK) + return (1); + switch (ikey.value) { + case K_ESCAPE: + *rp = *fm; + return (0); + case K_VLNEXT: + if (term_key(sp, &ikey, 0) != INP_OK) + return (1); + break; + } + VIP(sp)->rlast = ikey.ch; + } + + /* Copy the line. */ + GET_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, len); + p = bp; + + if (ikey.value == K_CR || ikey.value == K_NL) { + /* Set return line. */ + rp->lno = fm->lno + cnt; + + /* The first part of the current line. */ + if (file_sline(sp, ep, fm->lno, p, fm->cno)) + goto err_ret; + + /* + * The rest of the current line. And, of course, now it gets + * tricky. Any white space after the replaced character is + * stripped, and autoindent is applied. Put the cursor on the + * last indent character as did historic vi. + */ + for (p += fm->cno + cnt, len -= fm->cno + cnt; + len && isblank(*p); --len, ++p); + + if ((tp = text_init(sp, p, len, len)) == NULL) + goto err_ret; + if (txt_auto(sp, ep, fm->lno, NULL, 0, tp)) + goto err_ret; + rp->cno = tp->ai ? tp->ai - 1 : 0; + if (file_aline(sp, ep, 1, fm->lno, tp->lb, tp->len)) + goto err_ret; + text_free(tp); + + rval = 0; + + /* All of the middle lines. */ + while (--cnt) + if (file_aline(sp, ep, 1, fm->lno, "", 0)) { +err_ret: rval = 1; + break; + } + } else { + memset(bp + fm->cno, ikey.ch, cnt); + rval = file_sline(sp, ep, fm->lno, bp, len); + + rp->lno = fm->lno; + rp->cno = fm->cno + cnt - 1; + } + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_right.c b/usr.bin/vi/nvi/v_right.c new file mode 100644 index 000000000000..54b7be92d005 --- /dev/null +++ b/usr.bin/vi/nvi/v_right.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_right.c 8.3 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_right -- [count]' ', [count]l + * Move right by columns. + * + * Special case: the 'c' and 'd' commands can move past the end of line. + */ +int +v_right(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eol(sp, ep, NULL); + else + GETLINE_ERR(sp, fm->lno); + return (1); + } + + rp->lno = fm->lno; + if (len == 0 || fm->cno == len - 1) { + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + rp->cno = len; + return (0); + } + v_eol(sp, ep, NULL); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + rp->cno = fm->cno + cnt; + if (rp->cno > len - 1) + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + rp->cno = len; + else + rp->cno = len - 1; + return (0); +} + +/* + * v_dollar -- [count]$ + * Move to the last column. + */ +int +v_dollar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_long cnt; + + /* + * A count moves down count - 1 rows, so, "3$" is the same as "2j$". + * + * !!! + * Historically, if the $ is a motion, and deleting from at or before + * the first non-blank of the line, it's a line motion, and the line + * motion flag is set. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (cnt != 1) { + --vp->count; + if (v_down(sp, ep, vp, fm, tm, rp)) + return (1); + rp->cno = 0; + if (nonblank(sp, ep, rp->lno, &rp->cno)) + return (1); + if (fm->cno <= rp->cno && F_ISSET(vp, VC_C | VC_D | VC_Y)) + F_SET(vp, VC_LMODE); + } else + rp->lno = fm->lno; + + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eol(sp, ep, NULL); + else + GETLINE_ERR(sp, rp->lno); + return (1); + } + + if (len == 0) { + v_eol(sp, ep, NULL); + return (1); + } + + /* If it's a motion component, move one past the end of the line. */ + rp->cno = F_ISSET(vp, VC_C | VC_D | VC_Y) ? len : len - 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_screen.c b/usr.bin/vi/nvi/v_screen.c new file mode 100644 index 000000000000..986bfa4f29a2 --- /dev/null +++ b/usr.bin/vi/nvi/v_screen.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_screen.c 8.8 (Berkeley) 11/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_screen -- + * Switch screens. + */ +int +v_screen(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Try for the next lower screen, or, go back to the first + * screen on the stack. + */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) + sp->nextdisp = sp->q.cqe_next; + else if (sp->gp->dq.cqh_first == sp) { + msgq(sp, M_ERR, "No other screen to switch to."); + return (1); + } else + sp->nextdisp = sp->gp->dq.cqh_first; + + /* + * Display the old screen's status line so the user can + * find the screen they want. + */ + (void)status(sp, ep, fm->lno, 0); + + /* Save the old screen's cursor information. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_SSWITCH); + return (0); +} diff --git a/usr.bin/vi/nvi/v_scroll.c b/usr.bin/vi/nvi/v_scroll.c new file mode 100644 index 000000000000..f69ea0fb4e87 --- /dev/null +++ b/usr.bin/vi/nvi/v_scroll.c @@ -0,0 +1,354 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_scroll.c 8.8 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * The historic vi had a problem in that all movements were by physical + * lines, not by logical, or screen lines. Arguments can be made that this + * is the right thing to do. For example, single line movements, such as + * 'j' or 'k', should probably work on physical lines. Commands like "dj", + * or "j.", where '.' is a change command, make more sense for physical lines + * than they do for logical lines. + * + * These arguments, however, don't apply to scrolling commands like ^D and + * ^F -- if the window is fairly small, using physical lines can result in + * a half-page scroll repainting the entire screen, which is not what the + * user wanted. Second, if the line is larger than the screen, using physical + * lines can make it impossible to display parts of the line -- there aren't + * any commands that don't display the beginning of the line in historic vi, + * and if both the beginning and end of the line can't be on the screen at + * the same time, you lose. This is even worse in the case of the H, L, and + * M commands -- for large lines, they may all refer to the same line and + * will result in no movement at all. + * + * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the + * cursor positioning commands (H, L, M) commands using logical lines, not + * physical. + * + * Another issue is that page and half-page scrolling commands historically + * moved to the first non-blank character in the new line. If the line is + * approximately the same size as the screen, this loses because the cursor + * before and after a ^D, may refer to the same location on the screen. In + * this implementation, scrolling commands set the cursor to the first non- + * blank character if the line changes because of the scroll. Otherwise, + * the cursor is left alone. + */ + +/* + * v_lgoto -- [count]G + * Go to first non-blank character of the line count, the last line + * of the file by default. + */ +int +v_lgoto(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t last; + + if (file_lline(sp, ep, &last)) + return (1); + if (F_ISSET(vp, VC_C1SET)) { + if (last < vp->count) { + v_eof(sp, ep, fm); + return (1); + } + rp->lno = vp->count; + } else + rp->lno = last ? last : 1; + return (0); +} + +/* + * v_home -- [count]H + * Move to the first non-blank character of the logical line + * count from the top of the screen, 1 by default. + */ +int +v_home(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_position(sp, ep, rp, + F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_TOP)); +} + +/* + * v_middle -- M + * Move to the first non-blank character of the logical line + * in the middle of the screen. + */ +int +v_middle(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Yielding to none in our quest for compatibility with every + * historical blemish of vi, no matter how strange it might be, + * we permit the user to enter a count and then ignore it. + */ + return (sp->s_position(sp, ep, rp, 0, P_MIDDLE)); +} + +/* + * v_bottom -- [count]L + * Move to the first non-blank character of the logical line + * count from the bottom of the screen, 1 by default. + */ +int +v_bottom(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_position(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_BOTTOM)); +} + +/* + * v_up -- [count]^P, [count]k, [count]- + * Move up by lines. + */ +int +v_up(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + + lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + if (fm->lno <= lno) { + v_sof(sp, fm); + return (1); + } + rp->lno = fm->lno - lno; + return (0); +} + +/* + * v_cr -- [count]^M + * In a script window, send the line to the shell. + * In a regular window, move down by lines. + */ +int +v_cr(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * If it's a script window, exec the line, + * otherwise it's the same as v_down(). + */ + return (F_ISSET(sp, S_SCRIPT) ? + sscr_exec(sp, ep, fm->lno) : v_down(sp, ep, vp, fm, tm, rp)); +} + +/* + * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+ + * Move down by lines. + */ +int +v_down(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + + lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1); + + if (file_gline(sp, ep, lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + rp->lno = lno; + return (0); +} + +/* + * v_hpageup -- [count]^U + * Page up half screens. + */ +int +v_hpageup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Half screens always succeed unless already at SOF. Half screens + * set the scroll value, even if the command ultimately failed, in + * historic vi. It's probably a don't care. + */ + if (F_ISSET(vp, VC_C1SET)) + O_VAL(sp, O_SCROLL) = vp->count; + else + vp->count = O_VAL(sp, O_SCROLL); + + return (sp->s_down(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1)); +} + +/* + * v_hpagedown -- [count]^D + * Page down half screens. + */ +int +v_hpagedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Half screens always succeed unless already at EOF. Half screens + * set the scroll value, even if the command ultimately failed, in + * historic vi. It's probably a don't care. + */ + if (F_ISSET(vp, VC_C1SET)) + O_VAL(sp, O_SCROLL) = vp->count; + else + vp->count = O_VAL(sp, O_SCROLL); + + return (sp->s_up(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1)); +} + +/* + * v_pageup -- [count]^B + * Page up full screens. + * + * !!! + * Historic vi did not move to the SOF if the screen couldn't move, i.e. + * if SOF was already displayed on the screen. This implementation does + * move to SOF in that case, making ^B more like the the historic ^U. + */ +int +v_pageup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t count; + + /* Calculation from POSIX 1003.2/D8. */ + count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1); + + return (sp->s_down(sp, ep, rp, count, 1)); +} + +/* + * v_pagedown -- [count]^F + * Page down full screens. + * !!! + * Historic vi did not move to the EOF if the screen couldn't move, i.e. + * if EOF was already displayed on the screen. This implementation does + * move to EOF in that case, making ^F more like the the historic ^D. + */ +int +v_pagedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t count; + + /* Calculation from POSIX 1003.2/D8. */ + count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1); + + return (sp->s_up(sp, ep, rp, count, 1)); +} + +/* + * v_lineup -- [count]^Y + * Page up by lines. + */ +int +v_lineup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * The cursor moves down, staying with its original line, unless it + * reaches the bottom of the screen. + */ + return (sp->s_down(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0)); +} + +/* + * v_linedown -- [count]^E + * Page down by lines. + */ +int +v_linedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * The cursor moves up, staying with its original line, unless it + * reaches the top of the screen. + */ + return (sp->s_up(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0)); +} diff --git a/usr.bin/vi/nvi/v_search.c b/usr.bin/vi/nvi/v_search.c new file mode 100644 index 000000000000..25dcaf5e2e10 --- /dev/null +++ b/usr.bin/vi/nvi/v_search.c @@ -0,0 +1,364 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_search.c 8.16 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static int bcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int)); +static int fcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int)); +static int getptrn __P((SCR *, EXF *, int, char **)); + +/* + * v_searchn -- n + * Repeat last search. + */ +int +v_searchn(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + + flags = SEARCH_MSG; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + switch (sp->searchdir) { + case BACKWARD: + if (b_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case FORWARD: + if (f_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y| VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case NOTSET: + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + default: + abort(); + } + return (0); +} + +/* + * v_searchN -- N + * Reverse last search. + */ +int +v_searchN(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + + flags = SEARCH_MSG; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + switch (sp->searchdir) { + case BACKWARD: + if (f_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case FORWARD: + if (b_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case NOTSET: + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + default: + abort(); + } + return (0); +} + +/* + * v_searchw -- [count]^A + * Search for the word under the cursor. + */ +int +v_searchw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t blen, len; + u_int flags; + int rval; + char *bp; + + len = vp->kbuflen + sizeof(RE_WSTART) + sizeof(RE_WSTOP); + GET_SPACE_RET(sp, bp, blen, len); + (void)snprintf(bp, blen, "%s%s%s", RE_WSTART, vp->keyword, RE_WSTOP); + + flags = SEARCH_MSG; + rval = f_search(sp, ep, fm, rp, bp, NULL, &flags); + + FREE_SPACE(sp, bp, blen); + if (rval) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * v_searchb -- [count]?RE[? offset] + * Search backward. + */ +int +v_searchb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + char *ptrn; + + if (F_ISSET(vp, VC_ISDOT)) + ptrn = NULL; + else { + if (getptrn(sp, ep, '?', &ptrn)) + return (1); + if (ptrn == NULL) + return (0); + } + + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + if (b_search(sp, ep, fm, rp, ptrn, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * v_searchf -- [count]/RE[/ offset] + * Search forward. + */ +int +v_searchf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + char *ptrn; + + if (F_ISSET(vp, VC_ISDOT)) + ptrn = NULL; + else { + if (getptrn(sp, ep, '/', &ptrn)) + return (1); + if (ptrn == NULL) + return (0); + } + + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + if (f_search(sp, ep, fm, rp, ptrn, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * getptrn -- + * Get the search pattern. + */ +static int +getptrn(sp, ep, prompt, storep) + SCR *sp; + EXF *ep; + int prompt; + char **storep; +{ + TEXT *tp; + + if (sp->s_get(sp, ep, &sp->tiq, prompt, + TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK) + return (1); + + /* Len is 0 if backspaced over the prompt, 1 if only CR entered. */ + tp = sp->tiq.cqh_first; + if (tp->len == 0) + *storep = NULL; + else + *storep = tp->lb; + return (0); +} + +/* + * !!! + * Historically, commands didn't affect the line searched to if the motion + * command was a search and the pattern match was the start or end of the + * line. There were some special cases, however, concerning search to the + * start of end of a line. + * + * Vi was not, however, consistent, and it was fairly easy to confuse it. + * For example, given the two lines: + * + * abcdefghi + * ABCDEFGHI + * + * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h' + * 'k' and put would no longer work correctly. In any case, we try to do + * the right thing, but it's not likely exactly match historic practice. + */ + +/* + * bcorrect -- + * Handle command with a backward search as the motion. + */ +static int +bcorrect(sp, ep, vp, fm, rp, flags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + u_int flags; +{ + size_t len; + char *p; + + /* + * !!! + * Correct backward searches which start at column 0 to be one + * past the last column of the previous line. + * + * Backward searches become line mode operations if they start + * at column 0 and end at column 0 of another line. + */ + if (fm->lno > rp->lno && fm->cno == 0) { + if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (rp->cno == 0) + F_SET(vp, VC_LMODE); + fm->cno = len; + } + + /* + * !!! + * Commands would become line mode operations if there was a delta + * specified to the search pattern. + */ + if (LF_ISSET(SEARCH_DELTA)) { + F_SET(vp, VC_LMODE); + return (0); + } + return (0); +} + +/* + * fcorrect -- + * Handle command with a forward search as the motion. + */ +static int +fcorrect(sp, ep, vp, fm, rp, flags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + u_int flags; +{ + size_t len; + char *p; + + /* + * !!! + * Correct forward searches which end at column 0 to be one + * past the last column of the previous line. + * + * Forward searches become line mode operations if they start + * at column 0 and end at column 0 of another line. + */ + if (fm->lno < rp->lno && rp->cno == 0) { + if ((p = file_gline(sp, ep, --rp->lno, &len)) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (fm->cno == 0) + F_SET(vp, VC_LMODE); + rp->cno = len; + } + + /* + * !!! + * Commands would become line mode operations if there was a delta + * specified to the search pattern. + */ + if (LF_ISSET(SEARCH_DELTA)) { + F_SET(vp, VC_LMODE); + return (0); + } + + return (0); +} diff --git a/usr.bin/vi/nvi/v_section.c b/usr.bin/vi/nvi/v_section.c new file mode 100644 index 000000000000..8d97e2f10b4e --- /dev/null +++ b/usr.bin/vi/nvi/v_section.c @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_section.c 8.4 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * In historic vi, the section commands ignored empty lines, unlike the + * paragraph commands, which was probably okay. However, they also moved + * to the start of the last line when there where no more sections instead + * of the end of the last line like the paragraph commands. I've changed + * the latter behaviore to match the paragraphs command. + * + * In historic vi, a "function" was defined as the first character of the + * line being an open brace, which could be followed by anything. This + * implementation follows that historic practice. + */ + +/* Macro to do a check on each line. */ +#define CHECK { \ + if (len == 0) \ + continue; \ + if (p[0] == '{') { \ + if (!--cnt) { \ + rp->cno = 0; \ + rp->lno = lno; \ + return (0); \ + } \ + continue; \ + } \ + if (p[0] != '.' || len < 3) \ + continue; \ + for (lp = list; *lp; lp += 2) \ + if (lp[0] == p[1] && \ + (lp[1] == ' ' || lp[1] == p[2]) && !--cnt) { \ + rp->cno = 0; \ + rp->lno = lno; \ + return (0); \ + } \ +} + +/* + * v_sectionf -- [count]]] + * Move forward count sections/functions. + */ +int +v_sectionf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t cnt, lno; + char *p, *list, *lp; + + /* Get macro list. */ + if ((list = O_STR(sp, O_SECTIONS)) == NULL) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + for (lno = fm->lno; (p = file_gline(sp, ep, ++lno, &len)) != NULL;) + CHECK; + + /* EOF is a movement sink. */ + if (fm->lno != lno - 1) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + v_eof(sp, ep, NULL); + return (1); +} + +/* + * v_sectionb -- [count][[ + * Move backward count sections/functions. + */ +int +v_sectionb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t cnt, lno; + char *p, *list, *lp; + + /* Check for SOF. */ + if (fm->lno <= 1) { + v_sof(sp, NULL); + return (1); + } + + /* Get macro list. */ + if ((list = O_STR(sp, O_SECTIONS)) == NULL) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + for (lno = fm->lno; (p = file_gline(sp, ep, --lno, &len)) != NULL;) + CHECK; + + /* SOF is a movement sink. */ + rp->lno = 1; + rp->cno = 0; + return (0); +} diff --git a/usr.bin/vi/nvi/v_sentence.c b/usr.bin/vi/nvi/v_sentence.c new file mode 100644 index 000000000000..684952124f14 --- /dev/null +++ b/usr.bin/vi/nvi/v_sentence.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_sentence.c 8.7 (Berkeley) 8/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * In historic vi, a sentence was delimited by a '.', '?' or '!' character + * followed by TWO spaces or a newline. One or more empty lines was also + * treated as a separate sentence. The Berkeley documentation for historical + * vi states that any number of ')', ']', '"' and '\'' characters can be + * between the delimiter character and the spaces or end of line, however, + * the historical implementation did not handle additional '"' characters. + * We follow the documentation here, not the implementation. + * + * Once again, historical vi didn't do sentence movements associated with + * counts consistently, mostly in the presence of lines containing only + * white-space characters. + * + * This implementation also permits a single tab to delimit sentences, and + * treats lines containing only white-space characters as empty lines. + * And, tabs are eaten (along with spaces) when skipping to the start of the + * text follow a "sentence". + */ + +/* + * v_sentencef -- [count]) + * Move forward count sentences. + */ +int +v_sentencef(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { BLANK, NONE, PERIOD } state; + VCS cs; + u_long cnt; + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If in white-space, the next start of sentence counts as one. + * This may not handle " . " correctly, but it's real unclear + * what correctly means in that case. + */ + if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) { + if (cs_fblank(sp, ep, &cs)) + return (1); + if (--cnt == 0) { + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + return (1); + } + } + for (state = NONE;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + break; + if (cs.cs_flags == CS_EOL) { + if ((state == PERIOD || state == BLANK) && --cnt == 0) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == 0 && + isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs)) + return (1); + goto okret; + } + state = NONE; + continue; + } + if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */ + if (--cnt == 0) + goto okret; + if (cs_fblank(sp, ep, &cs)) + return (1); + if (--cnt == 0) + goto okret; + state = NONE; + continue; + } + switch (cs.cs_ch) { + case '.': + case '?': + case '!': + state = PERIOD; + break; + case ')': + case ']': + case '"': + case '\'': + if (state != PERIOD) + state = NONE; + break; + case '\t': + if (state == PERIOD) + state = BLANK; + /* FALLTHROUGH */ + case ' ': + if (state == PERIOD) { + state = BLANK; + break; + } + if (state == BLANK && --cnt == 0) { + if (cs_fblank(sp, ep, &cs)) + return (1); + goto okret; + } + /* FALLTHROUGH */ + default: + state = NONE; + break; + } + } + + /* EOF is a movement sink. */ + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + v_eof(sp, ep, NULL); + return (1); + +okret: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * Historic, uh, features, yeah, that's right, call 'em features. + * If the sentence movement is cutting an entire line, the buffer + * is in line mode. Reach up into the caller's VICMDARG structure, + * and whack the flags. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && + fm->cno == 0 && (rp->cno == 0 || cs.cs_flags != 0)) { + if (rp->cno == 0) + --rp->lno; + F_SET(vp, VC_LMODE); + } + return (0); +} + +/* + * v_sentenceb -- [count]( + * Move backward count sentences. + */ +int +v_sentenceb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + VCS cs; + recno_t slno; + size_t len, scno; + u_long cnt; + int last1, last2; + + if (fm->lno == 1 && fm->cno == 0) { + v_sof(sp, NULL); + return (1); + } + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If on an empty line, skip to the next previous + * non-white-space character. + */ + if (cs.cs_flags == CS_EMP) { + if (cs_bblank(sp, ep, &cs)) + return (1); + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags != CS_EOL) + break; + } + } + + for (last1 = last2 = 0;;) { + if (cs_prev(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */ + break; + if (cs.cs_flags == CS_EOL) { + last2 = last1; + last1 = 1; + continue; + } + if (cs.cs_flags == CS_EMP) { + if (--cnt == 0) + goto ret; + if (cs_bblank(sp, ep, &cs)) + return (1); + last1 = last2 = 0; + continue; + } + switch (cs.cs_ch) { + case '.': + case '?': + case '!': + if (!last1 || --cnt != 0) { + last2 = last1 = 0; + continue; + } + +ret: slno = cs.cs_lno; + scno = cs.cs_cno; + + /* + * Move to the start of the sentence, skipping blanks + * and special characters. + */ + do { + if (cs_next(sp, ep, &cs)) + return (1); + } while (!cs.cs_flags && + (cs.cs_ch == ')' || cs.cs_ch == ']' || + cs.cs_ch == '"' || cs.cs_ch == '\'')); + if ((cs.cs_flags || isblank(cs.cs_ch)) && + cs_fblank(sp, ep, &cs)) + return (1); + + /* + * If it was ". xyz", with the cursor on the 'x', or + * "end. ", with the cursor in the spaces, or the + * beginning of a sentence preceded by an empty line, + * we can end up where we started. Fix it. + */ + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + + /* + * Well, if an empty line preceded possible blanks + * and the sentence, it could be a real sentence. + */ + for (;;) { + if (cs_prev(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOL) + continue; + if (cs.cs_flags == 0 && isblank(cs.cs_ch)) + continue; + break; + } + if (cs.cs_flags == CS_EMP) + goto okret; + + /* But it wasn't; try again. */ + ++cnt; + cs.cs_lno = slno; + cs.cs_cno = scno; + last2 = last1 = 0; + break; + case '\t': + last1 = last2 = 1; + break; + default: + last2 = last1; + last1 = + cs.cs_flags == CS_EOL || isblank(cs.cs_ch) || + cs.cs_ch == ')' || cs.cs_ch == ']' || + cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0; + } + } + +okret: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * See comment in v_sentencef(). Ignore errors, they should + * never occur, and they'll get caught later. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && rp->cno == 0 && + file_gline(sp, ep, fm->lno, &len) != NULL && (len == 0 || + fm->cno == len - 1)) + F_SET(vp, VC_LMODE); + return (0); +} diff --git a/usr.bin/vi/nvi/v_shift.c b/usr.bin/vi/nvi/v_shift.c new file mode 100644 index 000000000000..2e984147a1c1 --- /dev/null +++ b/usr.bin/vi/nvi/v_shift.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_shift.c 8.3 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_shiftl -- [count]<motion + * Shift lines left. + */ +int +v_shiftl(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SHIFTL, 2, fm->lno, tm->lno, 0, "<"); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} + +/* + * v_shiftr -- [count]>motion + * Shift lines right. + */ +int +v_shiftr(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SHIFTR, 2, fm->lno, tm->lno, 0, ">"); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_status.c b/usr.bin/vi/nvi/v_status.c new file mode 100644 index 000000000000..9109c0196a80 --- /dev/null +++ b/usr.bin/vi/nvi/v_status.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_status.c 8.10 (Berkeley) 11/20/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_status -- ^G + * Show the file status. + */ +int +v_status(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + + /* + * ^G in historic vi reset the cursor column to the first + * non-blank character in the line. This doesn't seem of + * any usefulness whatsoever, so I don't bother. + */ + return (status(sp, ep, fm->lno, 1)); +} + +int +status(sp, ep, lno, showlast) + SCR *sp; + EXF *ep; + recno_t lno; + int showlast; +{ + recno_t last; + char *mo, *nc, *nf, *ro, *pid; +#ifdef DEBUG + char pbuf[50]; + + (void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid()); + pid = pbuf; +#else + pid = ""; +#endif + /* + * See nvi/exf.c:file_init() for a description of how and + * when the read-only bit is set. Possible displays are: + * + * new file + * new file, readonly + * [un]modified + * [un]modified, readonly + * name changed, [un]modified + * name changed, [un]modified, readonly + * + * !!! + * The historic display for "name changed" was "[Not edited]". + */ + if (F_ISSET(sp->frp, FR_NEWFILE)) { + F_CLR(sp->frp, FR_NEWFILE); + nf = "new file"; + mo = nc = ""; + } else { + nf = ""; + if (sp->frp->cname != NULL) { + nc = "name changed"; + mo = F_ISSET(ep, F_MODIFIED) ? + ", modified" : ", unmodified"; + } else { + nc = ""; + mo = F_ISSET(ep, F_MODIFIED) ? + "modified" : "unmodified"; + } + } + ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : ""; + if (showlast) { + if (file_lline(sp, ep, &last)) + return (1); + if (last >= 1) + msgq(sp, M_INFO, + "%s: %s%s%s%s: line %lu of %lu [%ld%%]%s", + FILENAME(sp->frp), nf, nc, mo, ro, lno, + last, (lno * 100) / last, pid); + else + msgq(sp, M_INFO, "%s: %s%s%s%s: empty file%s", + FILENAME(sp->frp), nf, nc, mo, ro, pid); + } else + msgq(sp, M_INFO, "%s: %s%s%s%s: line %lu%s", + FILENAME(sp->frp), nf, nc, mo, ro, lno, pid); + return (0); +} diff --git a/usr.bin/vi/nvi/v_stop.c b/usr.bin/vi/nvi/v_stop.c new file mode 100644 index 000000000000..2f64bfedf195 --- /dev/null +++ b/usr.bin/vi/nvi/v_stop.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_stop.c 8.5 (Berkeley) 10/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +int +v_stop(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* If autowrite is set, write out the file. */ + if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + if (sp->s_refresh(sp, ep)) + return (1); + } + return (sp->s_suspend(sp)); +} diff --git a/usr.bin/vi/nvi/v_switch.c b/usr.bin/vi/nvi/v_switch.c new file mode 100644 index 000000000000..d0d000facb2b --- /dev/null +++ b/usr.bin/vi/nvi/v_switch.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_switch.c 8.5 (Berkeley) 11/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +/* + * v_switch -- ^^ + * Switch to the previous file. + */ +int +v_switch(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + char *name; + + /* + * Try the alternate file name, then the previous file + * name. Use the real name, not the user's current name. + */ + if (sp->alt_name != NULL) + name = sp->alt_name; + else if (sp->p_frp != NULL) + name = sp->p_frp->name; + else { + msgq(sp, M_ERR, "No previous file to edit."); + return (1); + } + + /* If autowrite is set, write out the file. */ + if (F_ISSET(ep, F_MODIFIED)) + if (O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + } else { + msgq(sp, M_ERR, + "Modified since last write; write or use :edit! to override."); + return (1); + } + + SETCMDARG(cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, name); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_tag.c b/usr.bin/vi/nvi/v_tag.c new file mode 100644 index 000000000000..530e35279420 --- /dev/null +++ b/usr.bin/vi/nvi/v_tag.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_tag.c 8.2 (Berkeley) 12/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_tagpush -- ^[ + * Do a tag search on a the cursor keyword. + */ +int +v_tagpush(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_TAG, 0, OOBLNO, 0, 0, vp->keyword); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} + +/* + * v_tagpop -- ^T + * Pop the tags stack. + */ +/* ARGSUSED */ +int +v_tagpop(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_text.c b/usr.bin/vi/nvi/v_text.c new file mode 100644 index 000000000000..083b5b248e22 --- /dev/null +++ b/usr.bin/vi/nvi/v_text.c @@ -0,0 +1,827 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_text.c 8.23 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * !!! + * Repeated input in the historic vi is mostly wrong and this isn't very + * backward compatible. For example, if the user entered "3Aab\ncd" in + * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then + * appended to the result. There was also a hack which I don't remember + * right now, where "3o" would open 3 lines and then let the user fill them + * in, to make screen movements on 300 baud modems more tolerable. I don't + * think it's going to be missed. + */ + +#define SET_TXT_STD(sp, f) { \ + LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \ + TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \ + if (O_ISSET(sp, O_ALTWERASE)) \ + LF_SET(TXT_ALTWERASE); \ + if (O_ISSET(sp, O_AUTOINDENT)) \ + LF_SET(TXT_AUTOINDENT); \ + if (O_ISSET(sp, O_SHOWMATCH)) \ + LF_SET(TXT_SHOWMATCH); \ + if (O_ISSET(sp, O_WRAPMARGIN)) \ + LF_SET(TXT_WRAPMARGIN); \ + if (F_ISSET(sp, S_SCRIPT)) \ + LF_SET(TXT_CR); \ + if (O_ISSET(sp, O_TTYWERASE)) \ + LF_SET(TXT_TTYWERASE); \ +} + +/* + * !!! + * There's a problem with the way that we do logging for change commands with + * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the + * starting cursor position before the change command "moves" the cursor, the + * cursor position to which we return on undo will be where the user entered + * the change command, not the start of the change. Several of the following + * routines re-log the cursor to make this work correctly. Historic vi tried + * to do the same thing, and mostly got it right. (The only spectacular way + * it fails is if the user entered 'o' from anywhere but the last character of + * the line, the undo returned the cursor to the start of the line. If the + * user was on the last character of the line, the cursor returned to that + * position.) + */ + +static int v_CS __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *, u_int)); + +/* + * v_iA -- [count]A + * Append text to the end of the line. + */ +int +v_iA(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* Move the cursor to the end of the line + 1. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + } else { + /* Correct logging for implied cursor motion. */ + sp->cno = len == 0 ? 0 : len - 1; + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + /* Start the change after the line. */ + sp->cno = len; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_ia -- [count]a + * Append text to the cursor position. + */ +int +v_ia(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + u_int flags; + size_t len; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* + * Move the cursor one column to the right and + * repaint the screen. + */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + LF_SET(TXT_APPENDEOL); + } else if (len) { + if (len == sp->cno + 1) { + sp->cno = len; + LF_SET(TXT_APPENDEOL); + } else + ++sp->cno; + } else + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_iI -- [count]I + * Insert text at the first non-blank character in the line. + */ +int +v_iI(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* + * Move the cursor to the start of the line and repaint + * the screen. + */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + } else { + sp->cno = 0; + if (nonblank(sp, ep, lno, &sp->cno)) + return (1); + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + } + if (len == 0) + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_ii -- [count]i + * Insert text at the cursor position. + */ +int +v_ii(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + lno = 1; + len = 0; + } + /* If len == sp->cno, it's a replay caused by a count. */ + if (len == 0 || len == sp->cno) + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + /* + * On replay, if the line isn't empty, advance the insert + * by one (make it an append). + */ + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + if ((sp->cno = rp->cno) != 0) + ++sp->cno; + } + return (0); +} + +/* + * v_iO -- [count]O + * Insert text above this line. + */ +int +v_iO(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t ai_line, lno; + size_t len; + u_long cnt; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if (sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + goto insert; + p = NULL; + len = 0; + ai_line = OOBLNO; + } else { +insert: p = ""; + sp->cno = 0; + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + if (file_iline(sp, ep, sp->lno, p, 0)) + return (1); + if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) { + GETLINE_ERR(sp, sp->lno); + return (1); + } + ai_line = sp->lno + 1; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, ai_line, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_io -- [count]o + * Insert text after this line. + */ +int +v_io(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t ai_line, lno; + size_t len; + u_long cnt; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if (sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + goto insert; + p = NULL; + len = 0; + ai_line = OOBLNO; + } else { +insert: p = ""; + sp->cno = 0; + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + len = 0; + if (file_aline(sp, ep, 1, sp->lno, p, len)) + return (1); + if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) { + GETLINE_ERR(sp, sp->lno); + return (1); + } + ai_line = sp->lno - 1; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, ai_line, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_Change -- [buffer][count]C + * Change line command. + */ +int +v_Change(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (v_CS(sp, ep, vp, fm, tm, rp, 0)); +} + +/* + * v_Subst -- [buffer][count]S + * Line substitute command. + */ +int +v_Subst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + u_int flags; + + /* + * The S command is the same as a 'C' command from the beginning + * of the line. This is hard to do in the parser, so do it here. + * + * If autoindent is on, the change is from the first *non-blank* + * character of the line, not the first character. And, to make + * it just a bit more exciting, the initial space is handled as + * auto-indent characters. + */ + LF_INIT(0); + if (O_ISSET(sp, O_AUTOINDENT)) { + fm->cno = 0; + if (nonblank(sp, ep, fm->lno, &fm->cno)) + return (1); + LF_SET(TXT_AICHARS); + } else + fm->cno = 0; + sp->cno = fm->cno; + return (v_CS(sp, ep, vp, fm, tm, rp, flags)); +} + +/* + * v_CS -- + * C and S commands. + */ +static int +v_CS(sp, ep, vp, fm, tm, rp, iflags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; + u_int iflags; +{ + recno_t lno; + size_t len; + char *p; + u_int flags; + + SET_TXT_STD(sp, iflags); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + /* + * There are two cases -- if a count is supplied, we do a line + * mode change where we delete the lines and then insert text + * into a new line. Otherwise, we replace the current line. + */ + tm->lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0); + if (fm->lno != tm->lno) { + /* Make sure that the to line is real. */ + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + + /* Cut the lines. */ + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + + /* Insert a line while we still can... */ + if (file_iline(sp, ep, fm->lno, "", 0)) + return (1); + ++fm->lno; + ++tm->lno; + + /* Delete the lines. */ + if (delete(sp, ep, fm, tm, 1)) + return (1); + + /* Get the inserted line. */ + if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + tm = NULL; + sp->lno = fm->lno; + sp->cno = 0; + LF_SET(TXT_APPENDEOL); + } else { + /* The line may be empty, but that's okay. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, tm->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + tm->cno = len; + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + } + /* Correct logging for implied cursor motion. */ + log_cursor(sp, ep); + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); +} + +/* + * v_change -- [buffer][count]c[count]motion + * Change command. + */ +int +v_change(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t blen, len; + u_int flags; + int lmode, rval; + char *bp, *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + /* + * Move the cursor to the start of the change. Note, if autoindent + * is turned on, the cc command in line mode changes from the first + * *non-blank* character of the line, not the first character. And, + * to make it just a bit more exciting, the initial space is handled + * as auto-indent characters. + */ + lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0; + if (lmode) { + fm->cno = 0; + if (O_ISSET(sp, O_AUTOINDENT)) { + if (nonblank(sp, ep, fm->lno, &fm->cno)) + return (1); + LF_SET(TXT_AICHARS); + } + } + sp->lno = fm->lno; + sp->cno = fm->cno; + + /* Correct logging for implied cursor motion. */ + log_cursor(sp, ep); + + /* + * If changing within a single line, the line either currently has + * text or it doesn't. If it doesn't, just insert text. Otherwise, + * copy it and overwrite it. + */ + if (fm->lno == tm->lno) { + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (p == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, lmode)) + return (1); + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); + } + + /* + * It's trickier if changing over multiple lines. If we're in + * line mode we delete all of the lines and insert a replacement + * line which the user edits. If there was leading whitespace + * in the first line being changed, we copy it and use it as the + * replacement. If we're not in line mode, we just delete the + * text and start inserting. + * + * Copy the text. + */ + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, lmode)) + return (1); + + /* If replacing entire lines and there's leading text. */ + if (lmode && fm->cno) { + /* Get a copy of the first line changed. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + /* Copy the leading text elsewhere. */ + GET_SPACE_RET(sp, bp, blen, fm->cno); + memmove(bp, p, fm->cno); + } else + bp = NULL; + + /* Delete the text. */ + if (delete(sp, ep, fm, tm, lmode)) + return (1); + + /* If replacing entire lines, insert a replacement line. */ + if (lmode) { + if (file_iline(sp, ep, fm->lno, bp, fm->cno)) + return (1); + sp->lno = fm->lno; + len = sp->cno = fm->cno; + } + + /* Get the line we're editing. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + len = 0; + } + + /* Check to see if we're appending to the line. */ + if (fm->cno >= len) + LF_SET(TXT_APPENDEOL); + + /* No to mark. */ + tm = NULL; + + rval = v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags); + + if (bp != NULL) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * v_Replace -- [count]R + * Overwrite multiple characters. + */ +int +v_Replace(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_OVERWRITE | TXT_REPLACE); + } + tm->lno = rp->lno; + tm->cno = len ? len : 0; + if (v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)) + return (1); + + /* + * Special case. The historic vi handled [count]R badly, in that R + * would replace some number of characters, and then the count would + * append count-1 copies of the replacing chars to the replaced space. + * This seems wrong, so this version counts R commands. There is some + * trickiness in moving back to where the user stopped replacing after + * each R command. Basically, if the user ended with a newline, we + * want to use rp->cno (which will be 0). Otherwise, use the column + * after the returned cursor, unless it would be past the end of the + * line, in which case we append to the line. + */ + while (--cnt) { + if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) + GETLINE_ERR(sp, rp->lno); + SET_TXT_STD(sp, TXT_REPLAY); + + sp->lno = rp->lno; + + if (len == 0 || rp->cno == len - 1) { + sp->cno = len; + LF_SET(TXT_APPENDEOL); + } else { + sp->cno = rp->cno; + if (rp->cno != 0) + ++sp->cno; + LF_SET(TXT_OVERWRITE | TXT_REPLACE); + } + + if (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)) + return (1); + } + return (0); +} + +/* + * v_subst -- [buffer][count]s + * Substitute characters. + */ +int +v_subst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + + tm->lno = fm->lno; + tm->cno = fm->cno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1); + if (tm->cno > len) + tm->cno = len; + + if (p != NULL && cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); +} diff --git a/usr.bin/vi/nvi/v_ulcase.c b/usr.bin/vi/nvi/v_ulcase.c new file mode 100644 index 000000000000..12fd1c6626b9 --- /dev/null +++ b/usr.bin/vi/nvi/v_ulcase.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ulcase.c 8.3 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_ulcase -- [count]~ + * Toggle upper & lower case letters. + * + * !!! + * In historic vi, the count was ignored. It would have been better + * if there had been an associated motion, but it's too late to change + * it now. + */ +int +v_ulcase(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t blen, lcnt, len; + u_long cnt; + int ch, change, rval; + char *bp, *p; + + /* Figure out what memory to use. */ + GET_SPACE_RET(sp, bp, blen, 256); + + /* + * !!! + * Historic vi didn't permit ~ to cross newline boundaries. + * I can think of no reason why it shouldn't, which at least + * lets you auto-repeat through a paragraph. + */ + rval = 0; + for (change = -1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt;) { + /* Get the line; EOF is an infinite sink. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno >= fm->lno) { + GETLINE_ERR(sp, fm->lno); + rval = 1; + break; + } + if (change == -1) { + v_eof(sp, ep, NULL); + return (1); + } + break; + } + + /* Set current line number. */ + lno = fm->lno; + + /* Empty lines just decrement the count. */ + if (len == 0) { + --cnt; + ++fm->lno; + fm->cno = 0; + change = 0; + continue; + } + + /* Get a copy of the line. */ + ADD_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, len); + + /* Set starting pointer. */ + if (change == -1) + p = bp + fm->cno; + else + p = bp; + + /* + * Figure out how many characters get changed in this + * line. Set the final cursor column. + */ + if (fm->cno + cnt >= len) { + lcnt = len - fm->cno; + ++fm->lno; + fm->cno = 0; + } else + fm->cno += lcnt = cnt; + cnt -= lcnt; + + /* Change the line. */ + for (change = 0; lcnt--; ++p) { + ch = *(u_char *)p; + if (islower(ch)) { + *p = toupper(ch); + change = 1; + } else if (isupper(ch)) { + *p = tolower(ch); + change = 1; + } + } + + /* Update the line if necessary. */ + if (change && file_sline(sp, ep, lno, bp, len)) { + rval = 1; + break; + } + } + + /* If changed lines, could be on an illegal line. */ + if (fm->lno != lno && file_gline(sp, ep, fm->lno, &len) == NULL) { + --fm->lno; + fm->cno = len ? len - 1 : 0; + } + *rp = *fm; + + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_undo.c b/usr.bin/vi/nvi/v_undo.c new file mode 100644 index 000000000000..87c749b38179 --- /dev/null +++ b/usr.bin/vi/nvi/v_undo.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_undo.c 8.6 (Berkeley) 1/8/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Undo -- U + * Undo changes to this line. + */ +int +v_Undo(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Historically, U reset the cursor to the first column in the line + * (not the first non-blank). This seems a bit non-intuitive, but, + * considering that we may have undone multiple changes, anything + * else (including the cursor position stored in the logging records) + * is going to appear random. + */ + rp->lno = fm->lno; + rp->cno = 0; + + /* + * !!! + * Set up the flags so that an immediately subsequent 'u' will roll + * forward, instead of backward. In historic vi, a 'u' following a + * 'U' redid all of the changes to the line. Given that the user has + * explicitly discarded those changes by entering 'U', it seems likely + * that the user wants something between the original and end forms of + * the line, so starting to replay the changes seems the best way to + * get to there. + */ + F_SET(ep, F_UNDO); + ep->lundo = BACKWARD; + + return (log_setline(sp, ep)); +} + +/* + * v_undo -- u + * Undo the last change. + */ +int +v_undo(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* Set the command count. */ + VIP(sp)->u_ccnt = sp->ccnt; + + /* + * !!! + * In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u' + * undid the last undo. However, if there has been a change since + * the last undo/redo, we always do an undo. To make this work when + * the user can undo multiple operations, we leave the old semantic + * unchanged, but make '.' after a 'u' do another undo/redo operation. + * This has two problems. + * + * The first is that 'u' didn't set '.' in historic vi. So, if a + * user made a change, realized it was in the wrong place, does a + * 'u' to undo it, moves to the right place and then does '.', the + * change was reapplied. To make this work, we only apply the '.' + * to the undo command if it's the command immediately following an + * undo command. See vi/vi.c:getcmd() for the details. + * + * The second is that the traditional way to view the numbered cut + * buffers in vi was to enter the commands "1pu.u.u.u. which will + * no longer work because the '.' immediately follows the 'u' command. + * Since we provide a much better method of viewing buffers, and + * nobody can think of a better way of adding in multiple undo, this + * remains broken. + */ + if (!F_ISSET(ep, F_UNDO)) { + F_SET(ep, F_UNDO); + ep->lundo = BACKWARD; + } else if (!F_ISSET(vp, VC_ISDOT)) + ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD; + + switch (ep->lundo) { + case BACKWARD: + return (log_backward(sp, ep, rp)); + case FORWARD: + return (log_forward(sp, ep, rp)); + default: + abort(); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/nvi/v_util.c b/usr.bin/vi/nvi/v_util.c new file mode 100644 index 000000000000..83c4fb4ebbf6 --- /dev/null +++ b/usr.bin/vi/nvi/v_util.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_util.c 8.5 (Berkeley) 11/15/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_eof -- + * Vi end-of-file error. + */ +void +v_eof(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + u_long lno; + + if (mp == NULL) + msgq(sp, M_BERR, "Already at end-of-file."); + else { + if (file_lline(sp, ep, &lno)) + return; + if (mp->lno >= lno) + msgq(sp, M_BERR, "Already at end-of-file."); + else + msgq(sp, M_BERR, + "Movement past the end-of-file."); + } +} + +/* + * v_eol -- + * Vi end-of-line error. + */ +void +v_eol(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + size_t len; + + if (mp == NULL) + msgq(sp, M_BERR, "Already at end-of-line."); + else { + if (file_gline(sp, ep, mp->lno, &len) == NULL) { + GETLINE_ERR(sp, mp->lno); + return; + } + if (mp->cno == len - 1) + msgq(sp, M_BERR, "Already at end-of-line."); + else + msgq(sp, M_BERR, "Movement past the end-of-line."); + } +} + +/* + * v_sof -- + * Vi start-of-file error. + */ +void +v_sof(sp, mp) + SCR *sp; + MARK *mp; +{ + if (mp == NULL || mp->lno == 1) + msgq(sp, M_BERR, "Already at the beginning of the file."); + else + msgq(sp, M_BERR, "Movement past the beginning of the file."); +} + +/* + * v_isempty -- + * Return if the line contains nothing but white-space characters. + */ +int +v_isempty(p, len) + char *p; + size_t len; +{ + for (; len--; ++p) + if (!isblank(*p)) + return (0); + return (1); +} diff --git a/usr.bin/vi/nvi/v_word.c b/usr.bin/vi/nvi/v_word.c new file mode 100644 index 000000000000..8d917d68e088 --- /dev/null +++ b/usr.bin/vi/nvi/v_word.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_word.c 8.10 (Berkeley) 10/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * There are two types of "words". Bigwords are easy -- groups of anything + * delimited by whitespace. Normal words are trickier. They are either a + * group of characters, numbers and underscores, or a group of anything but, + * delimited by whitespace. When for a word, if you're in whitespace, it's + * easy, just remove the whitespace and go to the beginning or end of the + * word. Otherwise, figure out if the next character is in a different group. + * If it is, go to the beginning or end of that group, otherwise, go to the + * beginning or end of the current group. The historic version of vi didn't + * get this right, so, for example, there were cases where "4e" was not the + * same as "eeee". To get it right you have to resolve the cursor after each + * search so that the look-ahead to figure out what type of "word" the cursor + * is in will be correct. + * + * Empty lines, and lines that consist of only white-space characters count + * as a single word, and the beginning and end of the file counts as an + * infinite number of words. + * + * Movements associated with commands are different than movement commands. + * For example, in "abc def", with the cursor on the 'a', "cw" is from + * 'a' to 'c', while "w" is from 'a' to 'd'. In general, trailing white + * space is discarded from the change movement. Another example is that, + * in the same string, a "cw" on any white space character replaces that + * single character, and nothing else. Ain't nothin' in here that's easy. + * + * One historic note -- in the original vi, the 'w', 'W' and 'B' commands + * would treat groups of empty lines as individual words, i.e. the command + * would move the cursor to each new empty line. The 'e' and 'E' commands + * would treat groups of empty lines as a single word, i.e. the first use + * would move past the group of lines. The 'b' command would just beep at + * you. If the lines contained only white-space characters, the 'w' and 'W' + * commands will just beep at you, and the 'B', 'b', 'E' and 'e' commands + * will treat the group as a single word, and the 'B' and 'b' commands will + * treat the lines as individual words. This implementation treats both + * cases as a single white-space word. + */ + +#define FW(test) for (; len && (test); --len, ++p) +#define BW(test) for (; len && (test); --len, --p) + +enum which {BIGWORD, LITTLEWORD}; + +static int bword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int)); +static int eword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int)); +static int fword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, enum which)); + +/* + * v_wordw -- [count]w + * Move forward a word at a time. + */ +int +v_wordw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (fword(sp, ep, vp, fm, rp, LITTLEWORD)); +} + +/* + * v_wordW -- [count]W + * Move forward a bigword at a time. + */ +int +v_wordW(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (fword(sp, ep, vp, fm, rp, BIGWORD)); +} + +/* + * fword -- + * Move forward by words. + */ +static int +fword(sp, ep, vp, fm, rp, type) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + enum which type; +{ + enum { INWORD, NOTWORD } state; + VCS cs; + u_long cnt; + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If in white-space: + * If the count is 1, and it's a change command, we're done. + * Else, move to the first non-white-space character, which + * counts as a single word move. If it's a motion command, + * don't move off the end of the line. + */ + if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) { + if (cs.cs_flags != CS_EMP && cnt == 1) { + if (F_ISSET(vp, VC_C)) { + ++cs.cs_cno; + goto ret3; + } + if (F_ISSET(vp, VC_D | VC_Y)) { + if (cs_fspace(sp, ep, &cs)) + return (1); + goto ret1; + } + } + if (cs_fblank(sp, ep, &cs)) + return (1); + --cnt; + } + + /* + * Cyclically move to the next word -- this involves skipping + * over word characters and then any trailing non-word characters. + * Note, for the 'w' command, the definition of a word keeps + * switching. + */ + if (type == BIGWORD) + while (cnt--) { + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + break; + } + /* + * If a motion command and we're at the end of the + * last word, we're done. Delete and yank eat any + * trailing blanks, but we don't move off the end + * of the line regardless. + */ + if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (F_ISSET(vp, VC_D | VC_Y) && + cs_fspace(sp, ep, &cs)) + return (1); + break; + } + + /* Eat whitespace characters. */ + if (cs_fblank(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + } + else + while (cnt--) { + state = cs.cs_flags == 0 && + inword(cs.cs_ch) ? INWORD : NOTWORD; + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + break; + if (state == INWORD) { + if (!inword(cs.cs_ch)) + break; + } else + if (inword(cs.cs_ch)) + break; + } + /* See comment above. */ + if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (F_ISSET(vp, VC_D | VC_Y) && + cs_fspace(sp, ep, &cs)) + return (1); + break; + } + + /* Eat whitespace characters. */ + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + if (cs_fblank(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + } + + /* + * If a motion command, and eating the trailing non-word would + * move us off this line, don't do it. Move the return cursor + * to one past the EOL instead. + */ +ret1: if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOL) + ++cs.cs_cno; + + /* If we didn't move, we must be at EOF. */ +ret2: if (cs.cs_lno == fm->lno && cs.cs_cno == fm->cno) { + v_eof(sp, ep, fm); + return (1); + } + /* + * If at EOF, and it's a motion command, move the return cursor + * one past the EOF. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOF) + ++cs.cs_cno; +ret3: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + return (0); +} + +/* + * v_wordb -- [count]b + * Move backward a word at a time. + */ +int +v_wordb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (bword(sp, ep, vp, fm, rp, 0)); +} + +/* + * v_WordB -- [count]B + * Move backward a bigword at a time. + */ +int +v_wordB(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (bword(sp, ep, vp, fm, rp, 1)); +} + +/* + * bword -- + * Move backward by words. + */ +static int +bword(sp, ep, vp, fm, rp, spaceonly) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + int spaceonly; +{ + register char *p; + recno_t lno; + size_t len; + u_long cno, cnt; + char *startp; + + lno = fm->lno; + cno = fm->cno; + + /* Check for start of file. */ + if (lno == 1 && cno == 0) { + v_sof(sp, NULL); + return (1); + } + + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_sof(sp, NULL); + else + GETLINE_ERR(sp, lno); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Reset the length to the number of characters in the line; the + * first character is the current cursor position. + */ + len = cno ? cno + 1 : 0; + if (len == 0) + goto line; + for (startp = p, p += cno; cnt--;) { + if (spaceonly) { + if (!isblank(*p)) { + if (len < 2) + goto line; + --p; + --len; + } + BW(isblank(*p)); + if (len) + BW(!isblank(*p)); + else + goto line; + } else { + if (!isblank(*p)) { + if (len < 2) + goto line; + --p; + --len; + } + BW(isblank(*p)); + if (len) + if (inword(*p)) + BW(inword(*p)); + else + BW(!isblank(*p) && !inword(*p)); + else + goto line; + } + + if (cnt && len == 0) { + /* If we hit SOF, stay there (historic practice). */ +line: if (lno == 1) { + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* + * Get the line. If the line is empty, decrement + * count and get another one. + */ + if ((p = file_gline(sp, ep, --lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + if (len == 0) { + if (cnt == 0 || --cnt == 0) { + rp->lno = lno; + rp->cno = 0; + return (0); + } + goto line; + } + + /* + * Set the cursor to the end of the line. If the word + * at the end of this line has only a single character, + * we've already skipped over it. + */ + startp = p; + if (len) { + p += len - 1; + if (cnt && len > 1 && !isblank(p[0])) + if (inword(p[0])) { + if (!inword(p[-1])) + --cnt; + } else if (!isblank(p[-1]) && + !inword(p[-1])) + --cnt; + } + } else { + ++p; + ++len; + } + } + rp->lno = lno; + rp->cno = p - startp; + return (0); +} + +/* + * v_worde -- [count]e + * Move forward to the end of the word. + */ +int +v_worde(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (eword(sp, ep, vp, fm, rp, 0)); +} + +/* + * v_wordE -- [count]E + * Move forward to the end of the bigword. + */ +int +v_wordE(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (eword(sp, ep, vp, fm, rp, 1)); +} + +/* + * eword -- + * Move forward to the end of the word. + */ +static int +eword(sp, ep, vp, fm, rp, spaceonly) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + int spaceonly; +{ + register char *p; + recno_t lno; + size_t len, llen; + u_long cno, cnt; + int empty; + char *startp; + + lno = fm->lno; + cno = fm->cno; + + if ((p = file_gline(sp, ep, lno, &llen)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eof(sp, ep, NULL); + else + GETLINE_ERR(sp, lno); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Reset the length; the first character is the current cursor + * position. If no more characters in this line, may already + * be at EOF. + */ + len = llen - cno; + if (empty = llen == 0 || llen == cno + 1) + goto line; + + for (startp = p += cno; cnt--; empty = 0) { + if (spaceonly) { + if (!isblank(*p)) { + if (len < 2) + goto line; + ++p; + --len; + } + FW(isblank(*p)); + if (len) + FW(!isblank(*p)); + else + ++cnt; + } else { + if (!isblank(*p)) { + if (len < 2) + goto line; + ++p; + --len; + } + FW(isblank(*p)); + if (len) + if (inword(*p)) + FW(inword(*p)); + else + FW(!isblank(*p) && !inword(*p)); + else + ++cnt; + } + + if (cnt && len == 0) { + /* If we hit EOF, stay there (historic practice). */ +line: if ((p = file_gline(sp, ep, ++lno, &llen)) == NULL) { + /* + * If already at eof, complain, unless it's + * a change command or a delete command and + * there's something to delete. + */ + if (empty) { + if (F_ISSET(vp, VC_C) || + F_ISSET(vp, VC_D) && llen != 0) { + rp->lno = lno - 1; + rp->cno = llen ? llen : 1; + return (0); + } + v_eof(sp, ep, NULL); + return (1); + } + if ((p = + file_gline(sp, ep, --lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + rp->lno = lno; + rp->cno = llen ? llen - 1 : 0; + /* The 'c', 'd' and 'y' need one more space. */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); + } + len = llen; + cno = 0; + startp = p; + } else { + --p; + ++len; + } + } + rp->lno = lno; + rp->cno = cno + (p - startp); + + /* The 'c', 'd' and 'y' need one more space. */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); +} diff --git a/usr.bin/vi/nvi/v_xchar.c b/usr.bin/vi/nvi/v_xchar.c new file mode 100644 index 000000000000..019862240f26 --- /dev/null +++ b/usr.bin/vi/nvi/v_xchar.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_xchar.c 8.4 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +#define NODEL(sp) { \ + msgq(sp, M_BERR, "No characters to delete."); \ + return (1); \ +} + +/* + * v_xchar -- + * Deletes the character(s) on which the cursor sits. + */ +int +v_xchar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + MARK m; + recno_t lno; + u_long cnt; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NODEL(sp); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NODEL(sp); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Deleting from the cursor toward the end of line, w/o moving the + * cursor. Note, "2x" at EOL isn't the same as "xx" because the + * left movement of the cursor as part of the 'x' command isn't + * taken into account. Historically correct. + */ + tm->lno = fm->lno; + if (cnt < len - fm->cno) { + tm->cno = fm->cno + cnt; + m = *fm; + } else { + tm->cno = len; + m.lno = fm->lno; + m.cno = fm->cno ? fm->cno - 1 : 0; + } + + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + *rp = m; + return (0); +} + +/* + * v_Xchar -- + * Deletes the character(s) immediately before the current cursor + * position. + */ +int +v_Xchar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + u_long cnt; + + if (fm->cno == 0) { + msgq(sp, M_BERR, "Already at the left-hand margin."); + return (1); + } + + *tm = *fm; + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + fm->cno = cnt >= tm->cno ? 0 : tm->cno - cnt; + + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + *rp = *fm; + return (0); +} diff --git a/usr.bin/vi/nvi/v_yank.c b/usr.bin/vi/nvi/v_yank.c new file mode 100644 index 000000000000..7b2718e647a4 --- /dev/null +++ b/usr.bin/vi/nvi/v_yank.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_yank.c 8.11 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Yank -- [buffer][count]Y + * Yank lines of text into a cut buffer. + */ +int +v_Yank(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + + sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1; + return (0); +} + +/* + * v_yank -- [buffer][count]y[count][motion] + * Yank text (or lines of text) into a cut buffer. + */ +int +v_yank(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_LMODE)) { + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + } else if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + + /* + * !!! + * Historic vi moved the cursor to the from MARK if it was before the + * current cursor. This makes no sense. For example, "yj" moves the + * cursor but "yk" does not. Unfortunately, it's too late to change + * this now. Matching the historic semantics isn't easy. The line + * number was always changed and column movement was usually relative. + * However, "y'a" moved the cursor to the first non-blank of the line + * marked by a, while "y`a" moved the cursor to the line and column + * marked by a. + */ + if (F_ISSET(vp, VC_REVMOVE)) { + rp->lno = fm->lno; + if (vp->mkp == &vikeys['\'']) { + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + } else if (vp->mkp == &vikeys['`']) + rp->cno = fm->cno; + else + rp->cno = sp->s_relative(sp, ep, rp->lno); + } + + sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_z.c b/usr.bin/vi/nvi/v_z.c new file mode 100644 index 000000000000..31937ffc951c --- /dev/null +++ b/usr.bin/vi/nvi/v_z.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_z.c 8.8 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_z -- [count]z[count][-.+^<CR>] + * Move the screen. + */ +int +v_z(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t last, lno; + u_int value; + + /* + * The first count is the line to use. If the value doesn't + * exist, use the last line. + */ + if (F_ISSET(vp, VC_C1SET)) { + lno = vp->count; + if (file_lline(sp, ep, &last)) + return (1); + if (lno > last) + lno = last; + } else + lno = fm->lno; + + /* Set return cursor values. */ + rp->lno = lno; + rp->cno = fm->cno; + + /* + * The second count is the displayed window size, i.e. the 'z' + * command is another way to get artificially small windows. + * + * !!! + * A window size of 0 was historically allowed, and simply ignored. + * Also, this could be much more simply done by modifying the value + * of the O_WINDOW option, but that's not how it worked historically. + */ + if (F_ISSET(vp, VC_C2SET) && + vp->count2 != 0 && sp->s_rrel(sp, vp->count2)) + return (1); + + switch (vp->character) { + case '-': /* Put the line at the bottom. */ + if (sp->s_fill(sp, ep, lno, P_BOTTOM)) + return (1); + break; + case '.': /* Put the line in the middle. */ + if (sp->s_fill(sp, ep, lno, P_MIDDLE)) + return (1); + break; + default: /* Put the line at the top for <cr>. */ + value = term_key_val(sp, vp->character); + if (value != K_CR && value != K_NL) { + msgq(sp, M_ERR, "usage: %s.", vp->kp->usage); + return (1); + } + /* FALLTHROUGH */ + case '+': /* Put the line at the top. */ + if (sp->s_fill(sp, ep, lno, P_TOP)) + return (1); + break; + case '^': /* Print the screen before the z- screen. */ + /* + * !!! + * Historic practice isn't real clear on this one. It seems + * that the command "70z^" is the same as ":70<cr>z-z^" with + * an off-by-one difference. So, until I find documentation + * to the contrary, the z^ command in this implementation + * displays the screen immediately before the current one. + * Fill the screen with the selected line at the bottom, then, + * scroll the screen down a page, and move to the middle line + * of the screen. Historic vi moved the cursor to some random + * place in the screen, as far as I can tell. + */ + if (sp->s_fill(sp, ep, lno, P_BOTTOM)) + return (1); + if (sp->s_down(sp, ep, rp, sp->t_maxrows - 1, 1)) + return (1); + if (sp->s_position(sp, ep, rp, 0, P_MIDDLE)) + return (1); + break; + } + + /* If the map changes, have to redraw the entire screen. */ + F_SET(sp, S_REDRAW); + + return (0); +} diff --git a/usr.bin/vi/nvi/vcmd.c b/usr.bin/vi/nvi/vcmd.c new file mode 100644 index 000000000000..d7f1caf81d0c --- /dev/null +++ b/usr.bin/vi/nvi/vcmd.c @@ -0,0 +1,522 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)vcmd.c 8.22 (Berkeley) 1/8/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * This array maps keystrokes to vi command functions. It is known + * in ex/ex_usage.c that it takes four columns to name a vi character. + */ +VIKEYS const vikeys [MAXVIKEY + 1] = { +/* 000 NUL -- The code in vi.c expects key 0 to be undefined. */ + {NULL}, +/* 001 ^A */ + {v_searchw, V_ABS|V_CNT|V_MOVE|V_KEYW|V_RCM_SET, + "[count]^A", + "^A search forward for cursor word"}, +/* 002 ^B */ + {v_pageup, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^B", + "^B page up by screens"}, +/* 003 ^C */ + {NULL, 0, + "^C", + "^C interrupt a search or global command"}, +/* 004 ^D */ + {v_hpagedown, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^D", + "^D page down by half screens (setting count)"}, +/* 005 ^E */ + {v_linedown, V_CNT, + "[count]^E", + "^E page down by lines"}, +/* 006 ^F */ + {v_pagedown, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^F", + "^F page down by screens"}, +/* 007 ^G */ + {v_status, 0, + "^G", + "^G file status"}, +/* 010 ^H */ + {v_left, V_CNT|V_MOVE|V_RCM_SET, + "[count]^H", + "^H move left by columns"}, +/* 011 ^I */ + {NULL}, +/* 012 ^J */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^J", + "^J move down by lines"}, +/* 013 ^K */ + {NULL}, +/* 014 ^L */ + {v_redraw, 0, + "^L", + "^L redraw screen"}, +/* 015 ^M */ + {v_cr, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]^M", + "^M move down by lines (to first non-blank)"}, +/* 016 ^N */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^N", + "^N move down by lines"}, +/* 017 ^O */ + {NULL}, +/* 020 ^P */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^P", + "^P move up by lines"}, +/* 021 ^Q -- not available, used for hardware flow control. */ + {NULL}, +/* 022 ^R */ + {v_redraw, 0, + "^R", + "^R redraw screen"}, +/* 023 ^S -- not available, used for hardware flow control. */ + {NULL}, +/* 024 ^T */ + {v_tagpop, V_RCM_SET, + "^T", + "^T tag pop"}, +/* 025 ^U */ + {v_hpageup, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^U", + "^U half page up (set count)"}, +/* 026 ^V */ + {NULL, 0, + "^V", + "^V input a literal character"}, +/* 027 ^W */ + {v_screen, 0, + "^W", + "^W move to next screen"}, +/* 030 ^X */ + {NULL}, +/* 031 ^Y */ + {v_lineup, V_CNT, + "[count]^Y", + "^Y page up by lines"}, +/* 032 ^Z */ + {v_stop, 0, + "^Z", + "^Z suspend editor"}, +/* 033 ^[ */ + {NULL, 0, + "^[ <escape>", + "^[ <escape> leave input mode, return to command mode"}, +/* 034 ^\ */ + {NULL}, +/* 035 ^] */ + {v_tagpush, V_KEYW|V_RCM_SET, + "^]", + "^] tag push cursor word"}, +/* 036 ^^ */ + {v_switch, 0, + "^^", + "^^ switch to previous file"}, +/* 037 ^_ */ + {NULL}, +/* 040 ' ' */ + {v_right, V_CNT|V_MOVE|V_RCM_SET, + "[count]' '", + " <space> move right by columns"}, +/* 041 ! */ + {v_filter, V_CNT|V_DOT|V_MOTION|V_RCM_SET, + "[count]![count]motion command(s)", + " ! filter through command(s) to motion"}, +/* 042 " */ + {NULL}, +/* 043 # */ + {v_increment, V_CHAR|V_CNT|V_DOT|V_KEYNUM|V_RCM_SET, + "[count]#[#+-]", + " # number increment/decrement"}, +/* 044 $ */ + {v_dollar, V_CNT|V_MOVE|V_RCM_SETLAST, + " [count]$", + " $ move to last column"}, +/* 045 % */ + {v_match, V_ABS|V_MOVE|V_RCM_SET, + "%", + " % move to match"}, +/* 046 & */ + {v_again, 0, + "&", + " & repeat substitution"}, +/* 047 ' */ + {v_gomark, V_ABS|V_CHAR|V_LMODE|V_MOVE|V_RCM_SETFNB, + "'['a-z]", + " ' move to mark (to first non-blank)"}, +/* 050 ( */ + {v_sentenceb, V_CNT|V_MOVE|V_RCM_SET, + "[count](", + " ( move back sentence"}, +/* 051 ) */ + {v_sentencef, V_ABS|V_CNT|V_MOVE|V_RCM_SET, + "[count])", + " ) move forward sentence"}, +/* 052 * */ + {NULL}, +/* 053 + */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]+", + " + move down by lines (to first non-blank)"}, +/* 054 , */ + {v_chrrepeat, V_CNT|V_MOVE|V_RCM_SET, + "[count],", + " , reverse last F, f, T or t search"}, +/* 055 - */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]-", + " - move up by lines (to first non-blank)"}, +/* 056 . */ + {NULL, 0, + ".", + " . repeat the last command"}, +/* 057 / */ + {v_searchf, V_ABS|V_MOVE|V_RCM_SET, + "/RE[/ offset]", + " / search forward"}, +/* 060 0 */ + {v_zero, V_MOVE|V_RCM_SET, + "0", + " 0 move to first character"}, +/* 061 1 */ + {NULL}, +/* 062 2 */ + {NULL}, +/* 063 3 */ + {NULL}, +/* 064 4 */ + {NULL}, +/* 065 5 */ + {NULL}, +/* 066 6 */ + {NULL}, +/* 067 7 */ + {NULL}, +/* 070 8 */ + {NULL}, +/* 071 9 */ + {NULL}, +/* 072 : */ + {v_ex, 0, + ":command [| command] ...", + " : ex command"}, +/* 073 ; */ + {v_chrepeat, V_CNT|V_MOVE|V_RCM_SET, + "[count];", + " ; repeat last F, f, T or t search"}, +/* 074 < */ + {v_shiftl, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH, + "[count]<[count]motion", + " < shift lines left to motion"}, +/* 075 = */ + {NULL}, +/* 076 > */ + {v_shiftr, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH, + "[count]>[count]motion", + " > shift lines right to motion"}, +/* 077 ? */ + {v_searchb, V_ABS|V_MOVE|V_RCM_SET, + "?RE[? offset]", + " ? search backward"}, +/* 100 @ */ + {v_at, V_RBUF|V_RCM_SET, + "@buffer", + " @ execute buffer"}, +/* 101 A */ + {v_iA, V_CNT|V_DOT|V_RCM_SET, + "[count]A", + " A append to the line"}, +/* 102 B */ + {v_wordB, V_CNT|V_MOVE|V_RCM_SET, + "[count]B", + " B move back bigword"}, +/* 103 C */ + {v_Change, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]C", + " C change to end-of-line"}, +/* 104 D */ + {v_Delete, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]D", + " D delete to end-of-line"}, +/* 105 E */ + {v_wordE, V_CNT|V_MOVE|V_RCM_SET, + "[count]E", + " E move to end of bigword"}, +/* 106 F */ + {v_chF, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]F character", + " F character in line backward search"}, +/* 107 G */ + {v_lgoto, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]G", + " G move to line"}, +/* 110 H */ + {v_home, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "[count]H", + " H move to count lines from screen top"}, +/* 111 I */ + {v_iI, V_CNT|V_DOT|V_RCM_SET, + "[count]I", + " I insert at line beginning"}, +/* 112 J */ + {v_join, V_CNT|V_DOT|V_RCM_SET, + "[count]J", + " J join lines"}, +/* 113 K */ + {NULL}, +/* 114 L */ + {v_bottom, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "[count]L", + " L move to screen bottom"}, +/* 115 M */ + {v_middle, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "M", + " M move to screen middle"}, +/* 116 N */ + {v_searchN, V_ABS|V_MOVE|V_RCM_SET, + "n", + " N reverse last search"}, +/* 117 O */ + {v_iO, V_CNT|V_DOT|V_RCM_SET, + "[count]O", + " O insert above line"}, +/* 120 P */ + {v_Put, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer]P", + " P insert before cursor from buffer"}, +/* 121 Q */ + {v_exmode, 0, + "Q", + " Q switch to ex mode"}, +/* 122 R */ + {v_Replace, V_CNT|V_DOT|V_RCM_SET, + "[count]R", + " R replace characters"}, +/* 123 S */ + {v_Subst, V_CNT|V_DOT|V_LMODE|V_OBUF|V_RCM_SET, + "[buffer][count]S", + " S substitute for the line(s)"}, +/* 124 T */ + {v_chT, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]T character", + " T before character in line backward search"}, +/* 125 U */ + {v_Undo, V_RCM_SET, + "U", + " U Restore the current line"}, +/* 126 V */ + {NULL}, +/* 127 W */ + {v_wordW, V_CNT|V_MOVE|V_RCM_SET, + "[count]W", + " W move to next bigword"}, +/* 130 X */ + {v_Xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]X", + " X delete character before cursor"}, +/* 131 Y */ + {v_Yank, V_CNT|V_LMODE|V_OBUF, + "[buffer][count]Y", + " Y copy line"}, +/* 132 Z */ + {v_exit, 0, + "ZZ", + "ZZ save file and exit"}, +/* 133 [ */ + {v_sectionb, V_ABS|V_LMODE|V_MOVE|V_RCM_SET, + "[[", + "[[ move back section"}, +/* 134 \ */ + {NULL}, +/* 135 ] */ + {v_sectionf, V_ABS|V_LMODE|V_MOVE|V_RCM_SET, + "]]", + "]] move forward section"}, +/* 136 ^ */ + /* + * DON'T set the V_RCM_SETFNB flag, the function has to do + * the work anyway, in case it's a motion component. DO set + * V_RCM_SET, so that any motion that's part of a command is + * preserved. + */ + {v_first, V_CNT|V_MOVE|V_RCM_SET, + "^", + " ^ move to first non-blank"}, +/* 137 _ */ + /* + * DON'T set the V_RCM_SETFNB flag, the function has to do + * the work anyway, in case it's a motion component. DO set + * V_RCM_SET, so that any motion that's part of a command is + * preserved. + */ + {v_cfirst, V_CNT|V_MOVE|V_RCM_SET, + "_", + " _ move to first non-blank"}, +/* 140 ` */ + {v_gomark, V_ABS|V_CHAR|V_MOVE|V_RCM_SET, + "`[`a-z]", + " ` move to mark"}, +/* 141 a */ + {v_ia, V_CNT|V_DOT|V_RCM_SET, + "[count]a", + " a append after cursor"}, +/* 142 b */ + {v_wordb, V_CNT|V_MOVE|V_RCM_SET, + "[count]b", + " b move back word"}, +/* 143 c */ + {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_C, + "[buffer][count]c[count]motion", + " c change to motion"}, +/* 144 d */ + {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_D, + "[buffer][count]d[count]motion", + " d delete to motion"}, +/* 145 e */ + {v_worde, V_CNT|V_MOVE|V_RCM_SET, + "[count]e", + " e move to end of word"}, +/* 146 f */ + {v_chf, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]f character", + " f character in line forward search"}, +/* 147 g */ + {NULL}, +/* 150 h */ + {v_left, V_CNT|V_MOVE|V_RCM_SET, + "[count]h", + " h move left by columns"}, +/* 151 i */ + {v_ii, V_CNT|V_DOT|V_RCM_SET, + "[count]i", + " i insert before cursor"}, +/* 152 j */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]j", + " j move down by lines"}, +/* 153 k */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]k", + " k move up by lines"}, +/* 154 l */ + {v_right, V_CNT|V_MOVE|V_RCM_SET, + "[count]l", + " l move right by columns"}, +/* 155 m */ + {v_mark, V_CHAR, + "m[a-z]", + " m set mark"}, +/* 156 n */ + {v_searchn, V_ABS|V_MOVE|V_RCM_SET, + "n", + " n repeat last search"}, +/* 157 o */ + {v_io, V_CNT|V_DOT|V_RCM_SET, + "[count]o", + " o append after line"}, +/* 160 p */ + {v_put, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer]p", + " p insert after cursor from buffer"}, +/* 161 q */ + {NULL}, +/* 162 r */ + {v_replace, V_CNT|V_DOT|V_RCM_SET, + "[count]r character", + " r replace character"}, +/* 163 s */ + {v_subst, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]s", + " s substitute character"}, +/* 164 t */ + {v_cht, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]t character", + " t before character in line forward search"}, +/* 165 u */ + /* + * DON'T set the V_DOT flag, it' more complicated than that. + * See vi/vi.c for details. + */ + {v_undo, V_RCM_SET, + "u", + " u undo last change"}, +/* 166 v */ + {NULL}, +/* 167 w */ + {v_wordw, V_CNT|V_MOVE|V_RCM_SET, + "[count]w", + " w move to next word"}, +/* 170 x */ + {v_xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]x", + " x delete character"}, +/* 171 y */ + {v_yank, V_CNT|V_MOTION|V_OBUF|V_RCM_SET|VC_Y, + "[buffer][count]y[count]motion", + " y copy text to motion into a cut buffer"}, +/* 172 z */ + /* + * DON'T set the V_CHAR flag, the char isn't required, + * so it's handled specially in getcmd(). + */ + {v_z, V_CNT|V_RCM_SETFNB, + "[line]z[window_size][-|.|+|^|<CR>]", + " z redraw window"}, +/* 173 { */ + {v_paragraphb, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET, + "[count]{", + " { move back paragraph"}, +/* 174 | */ + {v_ncol, V_ABS|V_CNT|V_MOVE|V_RCM_SET, + "[count]|", + " | move to column"}, +/* 175 } */ + {v_paragraphf, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET, + "[count]}", + " } move forward paragraph"}, +/* 176 ~ */ + {v_ulcase, V_CNT|V_DOT|V_RCM_SET, + "[count]~", + " ~ reverse case"}, +}; diff --git a/usr.bin/vi/nvi/vcmd.h b/usr.bin/vi/nvi/vcmd.h new file mode 100644 index 000000000000..d48fa26acf5e --- /dev/null +++ b/usr.bin/vi/nvi/vcmd.h @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vcmd.h 8.23 (Berkeley) 1/8/94 + */ + +typedef struct _vikeys VIKEYS; + +/* Structure passed around to functions implementing vi commands. */ +typedef struct _vicmdarg { +#define vp_startzero buffer /* START ZERO OUT. */ + CHAR_T buffer; /* Buffer. */ + CHAR_T character; /* Character. */ + u_long count; /* Count. */ + u_long count2; /* Second count (only used by z). */ + int key; /* Command key. */ + VIKEYS const *kp; /* VIKEYS key. */ + VIKEYS const *mkp; /* VIKEYS motion key. */ + size_t klen; /* Keyword length. */ + +/* + * Historic vi allowed "dl" when the cursor was on the last column, deleting + * the last character, and similarly allowed "dw" when the cursor was on the + * last column of the file. It didn't allow "dh" when the cursor was on + * column 1, although these cases are not strictly analogous. The point is + * that some movements would succeed if they were associated with a motion + * command, and fail otherwise. This is part of the off-by-1 schizophrenia + * that plagued vi. Other examples are that "dfb" deleted everything up to + * and including the next 'b' character, but "d/b" only deleted everything + * up to the next 'b' character. While this implementation regularizes the + * interface to the extent possible, there are many special cases that can't + * be fixed. This is implemented by setting special flags per command so that + * the motion routines know what's really going on. + * + * Note, the VC_COMMASK flags are set in the vikeys array, and therefore + * must have values not used in the set of flags declared in the VIKEYS + * structure below. + */ +#define VC_C 0x0001 /* The 'c' command. */ +#define VC_D 0x0002 /* The 'd' command. */ +#define VC_SH 0x0004 /* The '>' command. */ +#define VC_Y 0x0008 /* The 'y' command. */ +#define VC_COMMASK 0x000f /* Mask for special flags. */ + +#define VC_BUFFER 0x0010 /* Buffer set. */ +#define VC_C1SET 0x0020 /* Count 1 set. */ +#define VC_C1RESET 0x0040 /* Reset the C1SET flag for dot commands. */ +#define VC_C2SET 0x0080 /* Count 2 set. */ +#define VC_LMODE 0x0100 /* Motion is line oriented. */ +#define VC_ISDOT 0x0200 /* Command was the dot command. */ +#define VC_REVMOVE 0x0400 /* Movement was before the cursor. */ + + u_int flags; + +#define vp_endzero keyword /* END ZERO OUT. */ + char *keyword; /* Keyword. */ + size_t kbuflen; /* Keyword buffer length. */ +} VICMDARG; + +/* Vi command structure. */ +struct _vikeys { /* Underlying function. */ + int (*func) __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *)); + +#define V_DONTUSE1 0x000001 /* VC_C */ +#define V_DONTUSE2 0x000002 /* VC_D */ +#define V_DONTUSE3 0x000004 /* VC_SH */ +#define V_DONTUSE4 0x000008 /* VC_Y */ +#define V_ABS 0x000010 /* Absolute movement, set '' mark. */ +#define V_CHAR 0x000020 /* Character (required, trailing). */ +#define V_CNT 0x000040 /* Count (optional, leading). */ +#define V_DOT 0x000080 /* On success, sets dot command. */ +#define V_KEYNUM 0x000100 /* Cursor referenced number. */ +#define V_KEYW 0x000200 /* Cursor referenced word. */ +#define V_LMODE 0x000400 /* Motion is line oriented. */ +#define V_MOTION 0x000800 /* Motion (required, trailing). */ +#define V_MOVE 0x001000 /* Command defines movement. */ +#define V_OBUF 0x002000 /* Buffer (optional, leading). */ +#define V_RBUF 0x004000 /* Buffer (required, trailing). */ +#define V_RCM 0x008000 /* Use relative cursor movment (RCM). */ +#define V_RCM_SET 0x010000 /* RCM: set to current position. */ +#define V_RCM_SETFNB 0x020000 /* RCM: set to first non-blank (FNB). */ +#define V_RCM_SETLAST 0x040000 /* RCM: set to last character. */ +#define V_RCM_SETLFNB 0x080000 /* RCM: set to FNB if line moved. */ +#define V_RCM_SETNNB 0x100000 /* RCM: set to next non-blank. */ + u_long flags; + char *usage; /* Usage line. */ + char *help; /* Help line. */ +}; +#define MAXVIKEY 126 /* List of vi commands. */ +extern VIKEYS const vikeys[MAXVIKEY + 1]; + +/* Definition of a "word". */ +#define inword(ch) (isalnum(ch) || (ch) == '_') + +/* Character stream structure, prototypes. */ +typedef struct _vcs { + recno_t cs_lno; /* Line. */ + size_t cs_cno; /* Column. */ + char *cs_bp; /* Buffer. */ + size_t cs_len; /* Length. */ + int cs_ch; /* Character. */ +#define CS_EMP 1 /* Empty line. */ +#define CS_EOF 2 /* End-of-file. */ +#define CS_EOL 3 /* End-of-line. */ +#define CS_SOF 4 /* Start-of-file. */ + int cs_flags; /* Return flags. */ +} VCS; + +int cs_bblank __P((SCR *, EXF *, VCS *)); +int cs_fblank __P((SCR *, EXF *, VCS *)); +int cs_fspace __P((SCR *, EXF *, VCS *)); +int cs_init __P((SCR *, EXF *, VCS *)); +int cs_next __P((SCR *, EXF *, VCS *)); +int cs_prev __P((SCR *, EXF *, VCS *)); + +/* Vi private, per-screen memory. */ +typedef struct _vi_private { + VICMDARG sdot; /* Saved dot, motion command. */ + VICMDARG sdotmotion; + + CHAR_T rlast; /* Last 'r' command character. */ + + char *rep; /* Input replay buffer. */ + size_t rep_len; /* Input replay buffer length. */ + size_t rep_cnt; /* Input replay buffer characters. */ + + CHAR_T inc_lastch; /* Last increment character. */ + long inc_lastval; /* Last increment value. */ + + char *paragraph; /* Paragraph search list. */ + size_t paragraph_len; /* Paragraph search list length. */ + + u_long u_ccnt; /* Undo command count. */ +} VI_PRIVATE; + +#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private)) + +/* Vi function prototypes. */ +int txt_auto __P((SCR *, EXF *, recno_t, TEXT *, size_t, TEXT *)); +int v_buildparagraph __P((SCR *)); +int v_end __P((SCR *)); +void v_eof __P((SCR *, EXF *, MARK *)); +void v_eol __P((SCR *, EXF *, MARK *)); +int v_exwrite __P((void *, const char *, int)); +int v_init __P((SCR *, EXF *)); +int v_isempty __P((char *, size_t)); +int v_msgflush __P((SCR *)); +int v_ntext __P((SCR *, EXF *, TEXTH *, MARK *, + const char *, const size_t, MARK *, int, recno_t, u_int)); +int v_optchange __P((SCR *, int)); +int v_screen_copy __P((SCR *, SCR *)); +int v_screen_end __P((SCR *)); +void v_sof __P((SCR *, MARK *)); +int vi __P((SCR *, EXF *)); + +#define VIPROTO(type, name) \ + type name __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *)) + +VIPROTO(int, v_again); +VIPROTO(int, v_at); +VIPROTO(int, v_bottom); +VIPROTO(int, v_cfirst); +VIPROTO(int, v_Change); +VIPROTO(int, v_change); +VIPROTO(int, v_chF); +VIPROTO(int, v_chf); +VIPROTO(int, v_chrepeat); +VIPROTO(int, v_chrrepeat); +VIPROTO(int, v_chT); +VIPROTO(int, v_cht); +VIPROTO(int, v_cr); +VIPROTO(int, v_Delete); +VIPROTO(int, v_delete); +VIPROTO(int, v_dollar); +VIPROTO(int, v_down); +VIPROTO(int, v_ex); +VIPROTO(int, v_exit); +VIPROTO(int, v_exmode); +VIPROTO(int, v_filter); +VIPROTO(int, v_first); +VIPROTO(int, v_gomark); +VIPROTO(int, v_home); +VIPROTO(int, v_hpagedown); +VIPROTO(int, v_hpageup); +VIPROTO(int, v_iA); +VIPROTO(int, v_ia); +VIPROTO(int, v_iI); +VIPROTO(int, v_ii); +VIPROTO(int, v_increment); +VIPROTO(int, v_iO); +VIPROTO(int, v_io); +VIPROTO(int, v_join); +VIPROTO(int, v_left); +VIPROTO(int, v_lgoto); +VIPROTO(int, v_linedown); +VIPROTO(int, v_lineup); +VIPROTO(int, v_mark); +VIPROTO(int, v_match); +VIPROTO(int, v_middle); +VIPROTO(int, v_ncol); +VIPROTO(int, v_pagedown); +VIPROTO(int, v_pageup); +VIPROTO(int, v_paragraphb); +VIPROTO(int, v_paragraphf); +VIPROTO(int, v_Put); +VIPROTO(int, v_put); +VIPROTO(int, v_redraw); +VIPROTO(int, v_Replace); +VIPROTO(int, v_replace); +VIPROTO(int, v_right); +VIPROTO(int, v_screen); +VIPROTO(int, v_searchb); +VIPROTO(int, v_searchf); +VIPROTO(int, v_searchN); +VIPROTO(int, v_searchn); +VIPROTO(int, v_searchw); +VIPROTO(int, v_sectionb); +VIPROTO(int, v_sectionf); +VIPROTO(int, v_sentenceb); +VIPROTO(int, v_sentencef); +VIPROTO(int, v_shiftl); +VIPROTO(int, v_shiftr); +VIPROTO(int, v_status); +VIPROTO(int, v_stop); +VIPROTO(int, v_Subst); +VIPROTO(int, v_subst); +VIPROTO(int, v_switch); +VIPROTO(int, v_tagpop); +VIPROTO(int, v_tagpush); +VIPROTO(int, v_ulcase); +VIPROTO(int, v_Undo); +VIPROTO(int, v_undo); +VIPROTO(int, v_up); +VIPROTO(int, v_wordB); +VIPROTO(int, v_wordb); +VIPROTO(int, v_wordE); +VIPROTO(int, v_worde); +VIPROTO(int, v_wordW); +VIPROTO(int, v_wordw); +VIPROTO(int, v_Xchar); +VIPROTO(int, v_xchar); +VIPROTO(int, v_Yank); +VIPROTO(int, v_yank); +VIPROTO(int, v_z); +VIPROTO(int, v_zero); diff --git a/usr.bin/vi/nvi/vi.c b/usr.bin/vi/nvi/vi.c new file mode 100644 index 000000000000..f969d78cfabf --- /dev/null +++ b/usr.bin/vi/nvi/vi.c @@ -0,0 +1,780 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)vi.c 8.45 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static int getcmd __P((SCR *, EXF *, + VICMDARG *, VICMDARG *, VICMDARG *, int *)); +static inline int + getcount __P((SCR *, ARG_CHAR_T, u_long *)); +static inline int + getkey __P((SCR *, CH *, u_int)); +static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int)); +static int getmotion __P((SCR *, EXF *, + VICMDARG *, VICMDARG *, MARK *, MARK *)); + +/* + * Side-effect: + * The dot structure can be set by the underlying vi functions, + * see v_Put() and v_put(). + */ +#define DOT (&VIP(sp)->sdot) +#define DOTMOTION (&VIP(sp)->sdotmotion) + +/* + * vi -- + * Main vi command loop. + */ +int +vi(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK abs, fm, tm, m; + VICMDARG cmd, *vp; + u_int flags, saved_mode; + int comcount, eval; + + /* Start vi. */ + if (v_init(sp, ep)) + return (1); + + /* Paint the screen. */ + if (sp->s_refresh(sp, ep)) { + (void)v_end(sp); + return (1); + } + + /* Command initialization. */ + memset(&cmd, 0, sizeof(VICMDARG)); + + for (eval = 0, vp = &cmd;;) { + if (!MAPPED_KEYS_WAITING(sp) && log_cursor(sp, ep)) + goto err; + + /* + * We get a command, which may or may not have an associated + * motion. If it does, we get it too, calling its underlying + * function to get the resulting mark. We then call the + * command setting the cursor to the resulting mark. + */ + if (getcmd(sp, ep, DOT, vp, NULL, &comcount)) + goto err; + + /* + * Historical practice: if a dot command gets a new count, + * any motion component goes away, i.e. "d3w2." deletes a + * total of 5 words. + */ + if (F_ISSET(vp, VC_ISDOT) && comcount) + DOTMOTION->count = 1; + + /* Get any associated keyword. */ + flags = vp->kp->flags; + if (LF_ISSET(V_KEYNUM | V_KEYW) && + getkeyword(sp, ep, vp, flags)) + goto err; + + /* If a non-relative movement, copy the future absolute mark. */ + if (LF_ISSET(V_ABS)) { + abs.lno = sp->lno; + abs.cno = sp->cno; + } + + /* + * Do any required motion; getmotion sets the from MARK + * and the line mode flag. + */ + if (LF_ISSET(V_MOTION)) { + if (getmotion(sp, ep, DOTMOTION, vp, &fm, &tm)) + goto err; + } else { + /* + * Set everything to the current cursor position. + * Line commands (ex: Y) default to the current line. + */ + tm.lno = fm.lno = sp->lno; + tm.cno = fm.cno = sp->cno; + + /* + * Set line mode flag, for example, "yy". + * + * If a count is set, we set the to MARK here relative + * to the cursor/from MARK. This is done for commands + * that take both counts and motions, i.e. "4yy" and + * "y%" -- there's no way the command can known which + * the user did, so we have to do it here. There are + * other commands that are line mode commands and take + * counts ("#G", "#H") and for which this calculation + * is either meaningless or wrong. Each command must + * do its own validity checking of the value. + */ + if (F_ISSET(vp->kp, V_LMODE)) { + F_SET(vp, VC_LMODE); + if (F_ISSET(vp, VC_C1SET)) { + tm.lno = sp->lno + vp->count - 1; + tm.cno = sp->cno; + } + } + } + + /* Increment the command count. */ + ++sp->ccnt; + + /* + * Call the function. Set the return cursor to the current + * cursor position first -- the underlying routines don't + * bother to do the work if it doesn't move. + */ + m.lno = sp->lno; + m.cno = sp->cno; + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + if ((vp->kp->func)(sp, ep, vp, &fm, &tm, &m)) + goto err; +#ifdef DEBUG + /* Make sure no function left the temporary space locked. */ + if (F_ISSET(sp->gp, G_TMP_INUSE)) { + msgq(sp, M_ERR, + "Error: vi: temporary buffer not released."); + return (1); + } +#endif + /* + * If that command took us out of vi or changed the screen, + * then exit the loop without further action. + */ + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) + break; + + /* Set the absolute mark. */ + if (LF_ISSET(V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1)) + goto err; + + /* Set the dot command structure. */ + if (LF_ISSET(V_DOT)) { + *DOT = cmd; + F_SET(DOT, VC_ISDOT); + /* + * If a count was supplied for both the command and + * its motion, the count was used only for the motion. + * Turn the count back on for the dot structure. + */ + if (F_ISSET(vp, VC_C1RESET)) + F_SET(DOT, VC_C1SET); + } + + /* + * Some vi row movements are "attracted" to the last position + * set, i.e. the V_RCM commands are moths to the V_RCM_SET + * commands' candle. It's broken into two parts. Here we deal + * with the command flags. In sp->relative(), we deal with the + * screen flags. If the movement is to the EOL the vi command + * handles it. If it's to the beginning, we handle it here. + * + * Note, some commands (e.g. _, ^) don't set the V_RCM_SETFNB + * flag, but do the work themselves. The reason is that they + * have to modify the column in case they're being used as a + * motion component. Other similar commands (e.g. +, -) don't + * have to modify the column because they are always line mode + * operations when used as motions, so the column number isn't + * of any interest. + * + * Does this totally violate the screen and editor layering? + * You betcha. As they say, if you think you understand it, + * you don't. + */ + switch (LF_ISSET(V_RCM | V_RCM_SETFNB | + V_RCM_SETLAST | V_RCM_SETLFNB | V_RCM_SETNNB)) { + case 0: + break; + case V_RCM: + m.cno = sp->s_relative(sp, ep, m.lno); + break; + case V_RCM_SETLAST: + sp->rcmflags = RCM_LAST; + break; + case V_RCM_SETLFNB: + if (fm.lno != m.lno) { + if (nonblank(sp, ep, m.lno, &m.cno)) + goto err; + sp->rcmflags = RCM_FNB; + } + break; + case V_RCM_SETFNB: + m.cno = 0; + /* FALLTHROUGH */ + case V_RCM_SETNNB: + if (nonblank(sp, ep, m.lno, &m.cno)) + goto err; + sp->rcmflags = RCM_FNB; + break; + default: + abort(); + } + + /* Update the cursor. */ + sp->lno = m.lno; + sp->cno = m.cno; + + if (!MAPPED_KEYS_WAITING(sp)) { + (void)msg_rpt(sp, 1); + + if (0) +err: term_map_flush(sp, "Vi error"); + } + + /* Refresh the screen. */ + if (sp->s_refresh(sp, ep)) { + eval = 1; + break; + } + + /* Set the new favorite position. */ + if (LF_ISSET(V_RCM_SET)) { + sp->rcmflags = 0; + (void)sp->s_column(sp, ep, &sp->rcm); + } + } + + return (v_end(sp) || eval); +} + +#define KEY(key, map) { \ + if (getkey(sp, &ikey, map)) \ + return (1); \ + key = ikey.ch; \ +} + +/* + * getcmd -- + * + * The command structure for vi is less complex than ex (and don't think + * I'm not grateful!) The command syntax is: + * + * [count] [buffer] [count] key [[motion] | [buffer] [character]] + * + * and there are several special cases. The motion value is itself a vi + * command, with the syntax: + * + * [count] key [character] + */ +static int +getcmd(sp, ep, dp, vp, ismotion, comcountp) + SCR *sp; + EXF *ep; + VICMDARG *dp, *vp; + VICMDARG *ismotion; /* Previous key if getting motion component. */ + int *comcountp; +{ + VIKEYS const *kp; + u_int flags; + CH ikey; + CHAR_T key; + + /* Refresh the command structure. */ + memset(&vp->vp_startzero, 0, + (char *)&vp->vp_endzero - (char *)&vp->vp_startzero); + + /* An escape bells the user if in command mode. */ + if (getkey(sp, &ikey, TXT_MAPCOMMAND)) { + if (ikey.value == K_ESCAPE && ismotion == NULL) + msgq(sp, M_BERR, "Already in command mode"); + return (1); + } + + key = ikey.ch; + if (key > MAXVIKEY) { + msgq(sp, M_BERR, "%s isn't a vi command", charname(sp, key)); + return (1); + } + + /* Pick up optional buffer. */ + if (key == '"') { + KEY(vp->buffer, 0); + F_SET(vp, VC_BUFFER); + KEY(key, TXT_MAPCOMMAND); + } + + /* + * Pick up optional count, where a leading 0 is not a count, + * it's a command. + */ + if (isdigit(key) && key != '0') { + if (getcount(sp, key, &vp->count)) + return (1); + F_SET(vp, VC_C1SET); + *comcountp = 1; + KEY(key, TXT_MAPCOMMAND); + } else + *comcountp = 0; + + /* Pick up optional buffer. */ + if (key == '"') { + if (F_ISSET(vp, VC_BUFFER)) { + msgq(sp, M_ERR, "Only one buffer can be specified."); + return (1); + } + KEY(vp->buffer, 0); + F_SET(vp, VC_BUFFER); + KEY(key, TXT_MAPCOMMAND); + } + + /* + * Find the command. The only legal command with no underlying + * function is dot. + */ + kp = vp->kp = &vikeys[vp->key = key]; + if (kp->func == NULL) { + if (key != '.') { + msgq(sp, M_ERR, + "%s isn't a command", charname(sp, key)); + return (1); + } + + /* If called for a motion command, stop now. */ + if (dp == NULL) + goto usage; + + /* A repeatable command must have been executed. */ + if (!F_ISSET(dp, VC_ISDOT)) { + msgq(sp, M_ERR, "No command to repeat."); + return (1); + } + + /* + * !!! + * If a '.' is immediately entered after an undo command, we + * replay the log instead of redoing the last command. This + * is necessary because 'u' can't set the dot command -- see + * vi/v_undo.c:v_undo for details. + */ + if (VIP(sp)->u_ccnt == sp->ccnt) { + vp->kp = &vikeys['u']; + F_SET(vp, VC_ISDOT); + return (0); + } + + /* Set new count/buffer, if any, and return. */ + if (F_ISSET(vp, VC_C1SET)) { + F_SET(dp, VC_C1SET); + dp->count = vp->count; + } + if (F_ISSET(vp, VC_BUFFER)) + dp->buffer = vp->buffer; + *vp = *dp; + return (0); + } + + flags = kp->flags; + + /* Check for illegal count. */ + if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) + goto usage; + + /* Illegal motion command. */ + if (ismotion == NULL) { + /* Illegal buffer. */ + if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) + goto usage; + + /* Required buffer. */ + if (LF_ISSET(V_RBUF)) + KEY(vp->buffer, 0); + + /* + * Special case: '[', ']' and 'Z' commands. Doesn't the + * fact that the *single* characters don't mean anything + * but the *doubled* characters do just frost your shorts? + */ + if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { + KEY(key, TXT_MAPCOMMAND); + if (vp->key != key) + goto usage; + } + /* Special case: 'z' command. */ + if (vp->key == 'z') { + KEY(vp->character, 0); + if (isdigit(vp->character)) { + if (getcount(sp, vp->character, &vp->count2)) + return (1); + F_SET(vp, VC_C2SET); + KEY(vp->character, 0); + } + } + } + + /* + * Commands that have motion components can be doubled to + * imply the current line. + */ + else if (ismotion->key != key && !LF_ISSET(V_MOVE)) { +usage: msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ? + vikeys[ismotion->key].usage : kp->usage); + return (1); + } + + /* Required character. */ + if (LF_ISSET(V_CHAR)) + KEY(vp->character, 0); + + return (0); +} + +/* + * getmotion -- + * + * Get resulting motion mark. + */ +static int +getmotion(sp, ep, dm, vp, fm, tm) + SCR *sp; + EXF *ep; + VICMDARG *dm, *vp; + MARK *fm, *tm; +{ + MARK m; + VICMDARG motion; + u_long cnt; + int notused; + + /* If '.' command, use the dot motion, else get the motion command. */ + if (F_ISSET(vp, VC_ISDOT)) { + motion = *dm; + F_SET(&motion, VC_ISDOT); + } else if (getcmd(sp, ep, NULL, &motion, vp, ¬used)) + return (1); + + /* + * A count may be provided both to the command and to the motion, in + * which case the count is multiplicative. For example, "3y4y" is the + * same as "12yy". This count is provided to the motion command and + * not to the regular function. + */ + cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1; + if (F_ISSET(vp, VC_C1SET)) { + motion.count *= vp->count; + F_SET(&motion, VC_C1SET); + + /* + * Set flags to restore the original values of the command + * structure so dot commands can change the count values, + * e.g. "2dw" "3." deletes a total of five words. + */ + F_CLR(vp, VC_C1SET); + F_SET(vp, VC_C1RESET); + } + + /* + * Some commands can be repeated to indicate the current line. In + * this case, or if the command is a "line command", set the flags + * appropriately. If not a doubled command, run the function to get + * the resulting mark. + */ + if (vp->key == motion.key) { + F_SET(vp, VC_LMODE); + + /* + * Set the end of the command; the column is after the line. + * + * If the current line is missing, i.e. the file is empty, + * historic vi permitted a "cc" or "!!" command to insert + * text. + */ + tm->lno = sp->lno + motion.count - 1; + if (file_gline(sp, ep, tm->lno, &tm->cno) == NULL) { + if (tm->lno != 1 || vp->key != 'c' && vp->key != '!') { + m.lno = sp->lno; + m.cno = sp->cno; + v_eof(sp, ep, &m); + return (1); + } + tm->cno = 0; + } + + /* Set the origin of the command. */ + fm->lno = sp->lno; + fm->cno = 0; + } else { + /* + * Motion commands change the underlying movement (*snarl*). + * For example, "l" is illegal at the end of a line, but "dl" + * is not. Set flags so the function knows the situation. + */ + F_SET(&motion, vp->kp->flags & VC_COMMASK); + + /* + * Everything starts at the current position. This permits + * commands like 'j' and 'k', that are line oriented motions + * and have special cursor suck semantics when they are used + * as standalone commands, to ignore column positioning. + */ + fm->lno = tm->lno = sp->lno; + fm->cno = tm->cno = sp->cno; + if ((motion.kp->func)(sp, ep, &motion, fm, NULL, tm)) + return (1); + + /* + * If the underlying motion was a line motion, set the flag + * in the command structure. Underlying commands can also + * flag the movement as a line motion (see v_sentence). + */ + if (F_ISSET(motion.kp, V_LMODE) || F_ISSET(&motion, VC_LMODE)) + F_SET(vp, VC_LMODE); + + /* + * If the motion is in the reverse direction, switch the from + * and to MARK's so that it's always in a forward direction. + * Because the motion is always from the from MARK to, but not + * including, the to MARK, the function may have modified the + * from MARK, so that it gets the one-past-the-place semantics + * we use; see v_match() for an example. Also set a flag so + * that the underlying function knows that we did this; v_yank, + * for example, has to know so it gets the return cursor right. + */ + if (tm->lno < fm->lno || + tm->lno == fm->lno && tm->cno < fm->cno) { + m = *fm; + *fm = *tm; + *tm = m; + F_SET(vp, VC_REVMOVE); + } + } + + /* + * If the command sets dot, save the motion structure. The + * motion count was changed above and needs to be reset, that's + * why this is done here, and not in the calling routine. + */ + if (F_ISSET(vp->kp, V_DOT)) { + *dm = motion; + dm->count = cnt; + } + + /* Let the underlying function know what motion command was used. */ + vp->mkp = motion.kp; + return (0); +} + +#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c)) + +/* + * getkeyword -- + * Get the "word" the cursor is on. + */ +static int +getkeyword(sp, ep, kp, flags) + SCR *sp; + EXF *ep; + VICMDARG *kp; + u_int flags; +{ + recno_t lno; + size_t beg, end, len; + char *p; + + if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eof(sp, ep, NULL); + else + GETLINE_ERR(sp, sp->lno); + return (1); + } + beg = sp->cno; + + /* May not be a keyword at all. */ + if (p == NULL || len == 0 || + LF_ISSET(V_KEYW) && !inword(p[beg]) || + LF_ISSET(V_KEYNUM) && !innum(p[beg]) && + p[beg] != '-' && p[beg] != '+') { +noword: msgq(sp, M_BERR, "Cursor not in a %s", + LF_ISSET(V_KEYW) ? "word" : "number"); + return (1); + } + + /* + * !!! + * Find the beginning/end of the keyword. Keywords (V_KEYW) are + * used for cursor-word searching and for tags. Historical vi + * only used the word in a tag search from the cursor to the end + * of the word, i.e. if the cursor was on the 'b' in " abc ", the + * tag was "bc". For no particular reason, we make cursor word + * searches follow the same rule. + */ + if (beg != 0) + if (LF_ISSET(V_KEYW)) { +#ifdef MOVE_TO_KEYWORD_BEGINNING + for (;;) { + --beg; + if (!inword(p[beg])) { + ++beg; + break; + } + if (beg == 0) + break; + } +#endif + } else { + for (;;) { + --beg; + if (!innum(p[beg])) { + if (beg > 0 && p[beg - 1] == '0' && + (p[beg] == 'X' || p[beg] == 'x')) + --beg; + else + ++beg; + break; + } + if (beg == 0) + break; + } + + /* Skip possible leading sign. */ + if (beg != 0 && p[beg] != '0' && + (p[beg - 1] == '+' || p[beg - 1] == '-')) + --beg; + } + + if (LF_ISSET(V_KEYW)) { + for (end = sp->cno; ++end < len && inword(p[end]);); + --end; + } else { + for (end = sp->cno; ++end < len;) { + if (p[end] == 'X' || p[end] == 'x') { + if (end != beg + 1 || p[beg] != '0') + break; + continue; + } + if (!innum(p[end])) + break; + } + + /* Just a sign isn't a number. */ + if (end == beg && (p[beg] == '+' || p[beg] == '-')) + goto noword; + --end; + } + + /* + * Getting a keyword implies moving the cursor to its beginning. + * Refresh now. + */ + if (beg != sp->cno) { + sp->cno = beg; + sp->s_refresh(sp, ep); + } + + /* + * XXX + * 8-bit clean problem. Numeric keywords are handled using strtol(3) + * and friends. This would have to be fixed in v_increment and here + * to not depend on a trailing NULL. + */ + len = (end - beg) + 2; /* XXX */ + kp->klen = (end - beg) + 1; + BINC_RET(sp, kp->keyword, kp->kbuflen, len); + memmove(kp->keyword, p + beg, kp->klen); + kp->keyword[kp->klen] = '\0'; /* XXX */ + return (0); +} + +/* + * getcount -- + * Return the next count. + */ +static inline int +getcount(sp, fkey, countp) + SCR *sp; + ARG_CHAR_T fkey; + u_long *countp; +{ + u_long count, tc; + CH ikey; + + ikey.ch = fkey; + count = tc = 0; + do { + /* Assume that overflow results in a smaller number. */ + tc = count * 10 + ikey.ch - '0'; + if (count > tc) { + /* Toss to the next non-digit. */ + do { + if (getkey(sp, &ikey, + TXT_MAPCOMMAND | TXT_MAPNODIGIT)) + return (1); + } while (isdigit(ikey.ch)); + msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX); + return (1); + } + count = tc; + if (getkey(sp, &ikey, TXT_MAPCOMMAND | TXT_MAPNODIGIT)) + return (1); + } while (isdigit(ikey.ch)); + *countp = count; + return (0); +} + +/* + * getkey -- + * Return the next key. + */ +static inline int +getkey(sp, ikeyp, map) + SCR *sp; + CH *ikeyp; + u_int map; +{ + switch (term_key(sp, ikeyp, map)) { + case INP_OK: + break; + case INP_EOF: + F_SET(sp, S_EXIT_FORCE); + /* FALLTHROUGH */ + case INP_ERR: + return (1); + } + return (ikeyp->value == K_ESCAPE); +} diff --git a/usr.bin/vi/options.c b/usr.bin/vi/options.c new file mode 100644 index 000000000000..9c74160c5c01 --- /dev/null +++ b/usr.bin/vi/options.c @@ -0,0 +1,822 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options.c 8.36 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +static int opts_abbcmp __P((const void *, const void *)); +static int opts_cmp __P((const void *, const void *)); +static OPTLIST const *opts_prefix __P((char *)); +static int opts_print __P((SCR *, OPTLIST const *, OPTION *)); + +/* + * O'Reilly noted options and abbreviations are from "Learning the VI Editor", + * Fifth Edition, May 1992. There's no way of knowing what systems they are + * actually from. + * + * HPUX noted options and abbreviations are from "The Ultimate Guide to the + * VI and EX Text Editors", 1990. + */ +static OPTLIST const optlist[] = { +/* O_ALTWERASE 4.4BSD */ + {"altwerase", f_altwerase, OPT_0BOOL, 0}, +/* O_AUTOINDENT 4BSD */ + {"autoindent", NULL, OPT_0BOOL, 0}, +/* O_AUTOPRINT 4BSD */ + {"autoprint", NULL, OPT_1BOOL, 0}, +/* O_AUTOWRITE 4BSD */ + {"autowrite", NULL, OPT_0BOOL, 0}, +/* O_BEAUTIFY 4BSD */ + {"beautify", NULL, OPT_0BOOL, 0}, +/* O_COLUMNS 4.4BSD */ + {"columns", f_columns, OPT_NUM, OPT_NOSAVE}, +/* O_COMMENT 4.4BSD */ + {"comment", NULL, OPT_0BOOL, 0}, +/* O_DIGRAPH XXX: Elvis */ + {"digraph", NULL, OPT_0BOOL, 0}, +/* O_DIRECTORY 4BSD */ + {"directory", NULL, OPT_STR, 0}, +/* O_EDCOMPATIBLE 4BSD */ + {"edcompatible",NULL, OPT_0BOOL, 0}, +/* O_ERRORBELLS 4BSD */ + {"errorbells", NULL, OPT_0BOOL, 0}, +/* O_EXRC System V (undocumented) */ + {"exrc", NULL, OPT_0BOOL, 0}, +/* O_EXTENDED 4.4BSD */ + {"extended", NULL, OPT_0BOOL, 0}, +/* O_FLASH HPUX */ + {"flash", NULL, OPT_1BOOL, 0}, +/* O_HARDTABS 4BSD */ + {"hardtabs", NULL, OPT_NUM, 0}, +/* O_IGNORECASE 4BSD */ + {"ignorecase", NULL, OPT_0BOOL, 0}, +/* O_KEYTIME 4.4BSD */ + {"keytime", f_keytime, OPT_NUM, 0}, +/* O_LEFTRIGHT 4.4BSD */ + {"leftright", f_leftright, OPT_0BOOL, 0}, +/* O_LINES 4.4BSD */ + {"lines", f_lines, OPT_NUM, OPT_NOSAVE}, +/* O_LISP 4BSD */ + {"lisp", f_lisp, OPT_0BOOL, 0}, +/* O_LIST 4BSD */ + {"list", f_list, OPT_0BOOL, 0}, +/* O_MAGIC 4BSD */ + {"magic", NULL, OPT_1BOOL, 0}, +/* O_MATCHTIME 4.4BSD */ + {"matchtime", f_matchtime, OPT_NUM, 0}, +/* O_MESG 4BSD */ + {"mesg", f_mesg, OPT_1BOOL, 0}, +/* O_MODELINE 4BSD */ + {"modeline", f_modeline, OPT_0BOOL, 0}, +/* O_NUMBER 4BSD */ + {"number", f_number, OPT_0BOOL, 0}, +/* O_OPEN 4BSD */ + {"open", NULL, OPT_1BOOL, 0}, +/* O_OPTIMIZE 4BSD */ + {"optimize", f_optimize, OPT_1BOOL, 0}, +/* O_PARAGRAPHS 4BSD */ + {"paragraphs", f_paragraph, OPT_STR, 0}, +/* O_PROMPT 4BSD */ + {"prompt", NULL, OPT_1BOOL, 0}, +/* O_READONLY 4BSD (undocumented) */ + {"readonly", f_readonly, OPT_0BOOL, 0}, +/* O_RECDIR 4.4BSD */ + {"recdir", NULL, OPT_STR, 0}, +/* O_REDRAW 4BSD */ + {"redraw", NULL, OPT_0BOOL, 0}, +/* O_REMAP 4BSD */ + {"remap", NULL, OPT_1BOOL, 0}, +/* O_REPORT 4BSD */ + {"report", NULL, OPT_NUM, OPT_NOSTR}, +/* O_RULER 4.4BSD */ + {"ruler", f_ruler, OPT_0BOOL, 0}, +/* O_SCROLL 4BSD */ + {"scroll", NULL, OPT_NUM, 0}, +/* O_SECTIONS 4BSD */ + {"sections", f_section, OPT_STR, 0}, +/* O_SHELL 4BSD */ + {"shell", NULL, OPT_STR, 0}, +/* O_SHIFTWIDTH 4BSD */ + {"shiftwidth", f_shiftwidth, OPT_NUM, 0}, +/* O_SHOWDIRTY 4.4BSD */ + {"showdirty", NULL, OPT_0BOOL, 0}, +/* O_SHOWMATCH 4BSD */ + {"showmatch", NULL, OPT_0BOOL, 0}, +/* O_SHOWMODE 4.4BSD */ + {"showmode", NULL, OPT_0BOOL, 0}, +/* O_SIDESCROLL 4.4BSD */ + {"sidescroll", f_sidescroll, OPT_NUM, 0}, +/* O_SLOWOPEN 4BSD */ + {"slowopen", NULL, OPT_0BOOL, 0}, +/* O_SOURCEANY 4BSD (undocumented) */ + {"sourceany", f_sourceany, OPT_0BOOL, 0}, +/* O_TABSTOP 4BSD */ + {"tabstop", f_tabstop, OPT_NUM, 0}, +/* O_TAGLENGTH 4BSD */ + {"taglength", NULL, OPT_NUM, OPT_NOSTR}, +/* O_TAGS 4BSD */ + {"tags", f_tags, OPT_STR, 0}, +/* O_TERM 4BSD */ + {"term", f_term, OPT_STR, OPT_NOSAVE}, +/* O_TERSE 4BSD */ + {"terse", NULL, OPT_0BOOL, 0}, +/* O_TIMEOUT 4BSD (undocumented) */ + {"timeout", NULL, OPT_1BOOL, 0}, +/* O_TTYWERASE 4.4BSD */ + {"ttywerase", f_ttywerase, OPT_0BOOL, 0}, +/* O_VERBOSE 4.4BSD */ + {"verbose", NULL, OPT_0BOOL, 0}, +/* O_W1200 4BSD */ + {"w1200", f_w1200, OPT_NUM, OPT_NEVER}, +/* O_W300 4BSD */ + {"w300", f_w300, OPT_NUM, OPT_NEVER}, +/* O_W9600 4BSD */ + {"w9600", f_w9600, OPT_NUM, OPT_NEVER}, +/* O_WARN 4BSD */ + {"warn", NULL, OPT_1BOOL, 0}, +/* O_WINDOW 4BSD */ + {"window", f_window, OPT_NUM, 0}, +/* O_WRAPMARGIN 4BSD */ + {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR}, +/* O_WRAPSCAN 4BSD */ + {"wrapscan", NULL, OPT_1BOOL, 0}, +/* O_WRITEANY 4BSD */ + {"writeany", NULL, OPT_0BOOL, 0}, + {NULL}, +}; + +typedef struct abbrev { + char *name; + int offset; +} OABBREV; + +static OABBREV const abbrev[] = { + {"ai", O_AUTOINDENT}, /* 4BSD */ + {"ap", O_AUTOPRINT}, /* 4BSD */ + {"aw", O_AUTOWRITE}, /* 4BSD */ + {"bf", O_BEAUTIFY}, /* 4BSD */ + {"co", O_COLUMNS}, /* 4.4BSD */ + {"dir", O_DIRECTORY}, /* 4BSD */ + {"eb", O_ERRORBELLS}, /* 4BSD */ + {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */ + {"ex", O_EXRC}, /* System V (undocumented) */ + {"ht", O_HARDTABS}, /* 4BSD */ + {"ic", O_IGNORECASE}, /* 4BSD */ + {"li", O_LINES}, /* 4.4BSD */ + {"modelines", O_MODELINE}, /* HPUX */ + {"nu", O_NUMBER}, /* 4BSD */ + {"opt", O_OPTIMIZE}, /* 4BSD */ + {"para", O_PARAGRAPHS}, /* 4BSD */ + {"re", O_REDRAW}, /* O'Reilly */ + {"ro", O_READONLY}, /* 4BSD (undocumented) */ + {"scr", O_SCROLL}, /* 4BSD (undocumented) */ + {"sect", O_SECTIONS}, /* O'Reilly */ + {"sh", O_SHELL}, /* 4BSD */ + {"slow", O_SLOWOPEN}, /* 4BSD */ + {"sm", O_SHOWMATCH}, /* 4BSD */ + {"sw", O_SHIFTWIDTH}, /* 4BSD */ + {"tag", O_TAGS}, /* 4BSD (undocumented) */ + {"tl", O_TAGLENGTH}, /* 4BSD */ + {"to", O_TIMEOUT}, /* 4BSD (undocumented) */ + {"ts", O_TABSTOP}, /* 4BSD */ + {"tty", O_TERM}, /* 4BSD (undocumented) */ + {"ttytype", O_TERM}, /* 4BSD (undocumented) */ + {"w", O_WINDOW}, /* O'Reilly */ + {"wa", O_WRITEANY}, /* 4BSD */ + {"wi", O_WINDOW}, /* 4BSD (undocumented) */ + {"wm", O_WRAPMARGIN}, /* 4BSD */ + {"ws", O_WRAPSCAN}, /* 4BSD */ + {NULL}, +}; + +/* + * opts_init -- + * Initialize some of the options. Since the user isn't really + * "setting" these variables, don't set their OPT_SET bits. + */ +int +opts_init(sp) + SCR *sp; +{ + ARGS *argv[2], a, b; + OPTLIST const *op; + u_long v; + int cnt; + char *s, b1[1024]; + + a.bp = b1; + a.len = 0; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b; + +#define SET_DEF(opt, str) { \ + if (str != b1) /* GCC puts strings in text-space. */ \ + (void)strcpy(b1, str); \ + a.len = strlen(b1); \ + if (opts_set(sp, argv)) { \ + msgq(sp, M_ERR, \ + "Unable to set default %s option", optlist[opt]); \ + return (1); \ + } \ + F_CLR(&sp->opts[opt], OPT_SET); \ +} + /* Set default values. */ + for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) + if (op->type == OPT_0BOOL) + O_CLR(sp, cnt); + else if (op->type == OPT_1BOOL) + O_SET(sp, cnt); + + /* + * !!! + * Vi historically stored temporary files in /var/tmp. We store them + * in /tmp by default, hoping it's a memory based file system. There + * are two ways to change this -- the user can set either the directory + * option or the TMPDIR environmental variable. + */ + (void)snprintf(b1, sizeof(b1), "directory=%s", + (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s); + SET_DEF(O_DIRECTORY, b1); + SET_DEF(O_KEYTIME, "keytime=6"); + SET_DEF(O_MATCHTIME, "matchtime=7"); + SET_DEF(O_REPORT, "report=5"); + SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp"); + (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE); + SET_DEF(O_RECDIR, b1); + (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2); + SET_DEF(O_SCROLL, b1); + SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh"); + (void)snprintf(b1, sizeof(b1), + "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s); + SET_DEF(O_SHELL, b1); + SET_DEF(O_SHIFTWIDTH, "shiftwidth=8"); + SET_DEF(O_SIDESCROLL, "sidescroll=16"); + SET_DEF(O_TABSTOP, "tabstop=8"); + (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS); + SET_DEF(O_TAGS, b1); + (void)snprintf(b1, sizeof(b1), + "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s); + SET_DEF(O_TERM, b1); + + /* + * The default window option values are: + * 8 if baud rate <= 600 + * 16 if baud rate <= 1200 + * LINES - 1 if baud rate > 1200 + */ + v = baud_from_bval(sp); + if (v <= 600) + v = 8; + else if (v <= 1200) + v = 16; + else + v = O_VAL(sp, O_LINES) - 1; + (void)snprintf(b1, sizeof(b1), "window=%lu", v); + SET_DEF(O_WINDOW, b1); + + SET_DEF(O_WRAPMARGIN, "wrapmargin=0"); + + /* + * By default, the historic vi always displayed information + * about two options, redraw and term. Term seems sufficient. + */ + F_SET(&sp->opts[O_TERM], OPT_SET); + return (0); +} + +/* + * opts_set -- + * Change the values of one or more options. + */ +int +opts_set(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + enum optdisp disp; + OABBREV atmp, *ap; + OPTLIST const *op; + OPTLIST otmp; + OPTION *spo; + u_long value, turnoff; + int ch, offset, rval; + char *endp, *equals, *name, *p; + + disp = NO_DISPLAY; + for (rval = 0; (*argv)->len != 0; ++argv) { + /* + * The historic vi dumped the options for each occurrence of + * "all" in the set list. Puhleeze. + */ + if (!strcmp(argv[0]->bp, "all")) { + disp = ALL_DISPLAY; + continue; + } + + /* Find equals sign or end of set, skipping backquoted chars. */ + for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p) + switch(ch) { + case '=': + equals = p; + break; + case '\\': + /* Historic vi just used the backslash. */ + if (p[1] == '\0') + break; + ++p; + break; + } + + turnoff = 0; + op = NULL; + if (equals) + *equals++ = '\0'; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Try the name without any leading "no". */ + if (name[0] == 'n' && name[1] == 'o') { + turnoff = 1; + name += 2; + } else + goto prefix; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Check for prefix match. */ +prefix: op = opts_prefix(name); + +found: if (op == NULL) { + msgq(sp, M_ERR, + "no %s option: 'set all' gives all option values", + name); + continue; + } + + /* Find current option values. */ + offset = op - optlist; + spo = sp->opts + offset; + + /* Set name, value. */ + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (equals) { + msgq(sp, M_ERR, + "set: [no]%s option doesn't take a value", + name); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, NULL, turnoff)) { + rval = 1; + break; + } + } else if (turnoff) + O_CLR(sp, offset); + else + O_SET(sp, offset); + goto change; + case OPT_NUM: + /* + * !!! + * Extension to historic vi. If the OPT_NOSTR flag is + * set, a numeric option may be turned off by using a + * "no" prefix, e.g. "nowrapmargin". (We assume that + * setting the value to 0 turns a numeric option off.) + */ + if (turnoff) { + if (F_ISSET(op, OPT_NOSTR)) { + value = 0; + goto nostr; + } + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + value = strtol(equals, &endp, 10); + if (*endp && !isblank(*endp)) { + msgq(sp, M_ERR, + "set %s: illegal number %s", name, equals); + break; + } +nostr: if (op->func != NULL) { + if (op->func(sp, spo, equals, value)) { + rval = 1; + break; + } + } else + O_VAL(sp, offset) = value; + goto change; + case OPT_STR: + if (turnoff) { + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, equals, (u_long)0)) { + rval = 1; + break; + } + } else { + if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED)) + free(O_STR(sp, offset)); + if ((O_STR(sp, offset) = + strdup(equals)) == NULL) { + msgq(sp, M_SYSERR, NULL); + rval = 1; + break; + } else + F_SET(&sp->opts[offset], OPT_ALLOCATED); + } +change: if (sp->s_optchange != NULL) + (void)sp->s_optchange(sp, offset); + F_SET(&sp->opts[offset], OPT_SET); + break; + default: + abort(); + } + } + if (disp) + opts_dump(sp, disp); + return (rval); +} + +/* + * opts_dump -- + * List the current values of selected options. + */ +void +opts_dump(sp, type) + SCR *sp; + enum optdisp type; +{ + OPTLIST const *op; + int base, b_num, cnt, col, colwidth, curlen, s_num; + int numcols, numrows, row; + int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT]; + char nbuf[20]; + + /* + * Options are output in two groups -- those that fit in a column and + * those that don't. Output is done on 6 character "tab" boundaries + * for no particular reason. (Since we don't output tab characters, + * we can ignore the terminal's tab settings.) Ignore the user's tab + * setting because we have no idea how reasonable it is. + */ +#define BOUND 6 + + /* Find a column width we can live with. */ + for (cnt = 6; cnt > 1; --cnt) { + colwidth = (sp->cols - 1) / cnt & ~(BOUND - 1); + if (colwidth >= 10) { + colwidth = (colwidth + BOUND) & ~(BOUND - 1); + break; + } + colwidth = 0; + } + + /* + * Two passes. First, get the set of options to list, entering them + * into the column list or the overflow list. No error checking, + * since we know that at least one option (O_TERM) has the OPT_SET bit + * set. + */ + for (b_num = s_num = 0, op = optlist; op->name; ++op) { + cnt = op - optlist; + + /* If OPT_NEVER set, it's never displayed. */ + if (F_ISSET(op, OPT_NEVER)) + continue; + + switch (type) { + case ALL_DISPLAY: /* Display all. */ + break; + case CHANGED_DISPLAY: /* Display changed. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SET)) + continue; + break; + case SELECT_DISPLAY: /* Display selected. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED)) + continue; + break; + default: + case NO_DISPLAY: + abort(); + /* NOTREACHED */ + } + F_CLR(&sp->opts[cnt], OPT_SELECTED); + + curlen = strlen(op->name); + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (!O_ISSET(sp, cnt)) + curlen += 2; + break; + case OPT_NUM: + (void)snprintf(nbuf, + sizeof(nbuf), "%ld", O_VAL(sp, cnt)); + curlen += strlen(nbuf); + break; + case OPT_STR: + curlen += strlen(O_STR(sp, cnt)) + 3; + break; + } + /* Offset by two so there's a gap. */ + if (curlen < colwidth - 2) + s_op[s_num++] = cnt; + else + b_op[b_num++] = cnt; + } + + numcols = (sp->cols - 1) / colwidth; + if (s_num > numcols) { + numrows = s_num / numcols; + if (s_num % numcols) + ++numrows; + } else + numrows = 1; + + for (row = 0; row < numrows;) { + for (base = row, col = 0; col < numcols; ++col) { + cnt = opts_print(sp, + &optlist[s_op[base]], &sp->opts[s_op[base]]); + if ((base += numrows) >= s_num) + break; + (void)ex_printf(EXCOOKIE, + "%*s", (int)(colwidth - cnt), ""); + } + if (++row < numrows || b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + + for (row = 0; row < b_num;) { + (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]); + if (++row < b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + (void)ex_printf(EXCOOKIE, "\n"); +} + +/* + * opts_print -- + * Print out an option. + */ +static int +opts_print(sp, op, spo) + SCR *sp; + OPTLIST const *op; + OPTION *spo; +{ + int curlen, offset; + + curlen = 0; + offset = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + curlen += ex_printf(EXCOOKIE, + "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name); + break; + case OPT_NUM: + curlen += ex_printf(EXCOOKIE, + "%s=%ld", op->name, O_VAL(sp, offset)); + break; + case OPT_STR: + curlen += ex_printf(EXCOOKIE, + "%s=\"%s\"", op->name, O_STR(sp, offset)); + break; + } + return (curlen); +} + +/* + * opts_save -- + * Write the current configuration to a file. + */ +int +opts_save(sp, fp) + SCR *sp; + FILE *fp; +{ + OPTION *spo; + OPTLIST const *op; + int ch, cnt; + char *p; + + for (spo = sp->opts, op = optlist; op->name; ++op) { + if (F_ISSET(op, OPT_NOSAVE)) + continue; + cnt = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (O_ISSET(sp, cnt)) + (void)fprintf(fp, "set %s\n", op->name); + else + (void)fprintf(fp, "set no%s\n", op->name); + break; + case OPT_NUM: + (void)fprintf(fp, + "set %s=%-3d\n", op->name, O_VAL(sp, cnt)); + break; + case OPT_STR: + (void)fprintf(fp, "set "); + for (p = op->name; (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('=', fp); + for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + break; + } + if (ferror(fp)) { + msgq(sp, M_ERR, "I/O error: %s", strerror(errno)); + return (1); + } + } + return (0); +} + +/* + * opts_prefix -- + * Check to see if the name is the prefix of one (and only one) + * option. If so, return the option. + */ +static OPTLIST const * +opts_prefix(name) + char *name; +{ + OPTLIST const *op, *save_op; + size_t len; + + save_op = NULL; + len = strlen(name); + for (op = optlist; op->name != NULL; ++op) { + if (op->name[0] < name[0]) + continue; + if (op->name[0] > name[0]) + break; + if (!memcmp(op->name, name, len)) { + if (save_op != NULL) + return (NULL); + save_op = op; + } + } + return (save_op); +} + +static int +opts_abbcmp(a, b) + const void *a, *b; +{ + return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name)); +} + +static int +opts_cmp(a, b) + const void *a, *b; +{ + return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name)); +} + +/* + * opts_free -- + * Free all option strings + */ +void +opts_free(sp) + SCR *sp; +{ + int cnt; + char *p; + + for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) { + p = O_STR(sp, cnt); + FREE(p, strlen(p) + 1); + } +} + +/* + * opts_copy -- + * Copy a screen's OPTION array. + */ +int +opts_copy(orig, sp) + SCR *orig, *sp; +{ + OPTION *op; + int cnt; + + /* Copy most everything without change. */ + memmove(sp->opts, orig->opts, sizeof(orig->opts)); + + /* + * Allocate copies of the strings -- keep trying to reallocate + * after ENOMEM failure, otherwise end up with more than one + * screen referencing the original memory. + */ + for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) && + (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) { + msgq(orig, M_SYSERR, NULL); + return (1); + } + return (0); +} diff --git a/usr.bin/vi/options.h.stub b/usr.bin/vi/options.h.stub new file mode 100644 index 000000000000..f5f52b52b3d3 --- /dev/null +++ b/usr.bin/vi/options.h.stub @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h.stub 8.14 (Berkeley) 12/20/93 + */ + +struct _option { + union { + u_long val; /* Value or boolean. */ + char *str; /* String. */ + } o_u; + size_t len; /* String length. */ + +#define OPT_ALLOCATED 0x01 /* Allocated space. */ +#define OPT_SELECTED 0x02 /* Selected for display. */ +#define OPT_SET 0x04 /* Set (display for the user). */ + u_char flags; +}; + +struct _optlist { + char *name; /* Name. */ + /* Change function. */ + int (*func) __P((SCR *, OPTION *, char *, u_long)); + /* Type of object. */ + enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type; + +#define OPT_NEVER 0x01 /* Never display the option. */ +#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */ +#define OPT_NOSTR 0x04 /* String that takes a "no". */ + u_int flags; +}; + +/* Clear, set, test boolean options. */ +#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1 +#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0 +#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val) + +/* Get option values. */ +#define O_LEN(sp, o) (sp)->opts[(o)].len +#define O_STR(sp, o) (sp)->opts[(o)].o_u.str +#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val + +/* Option routines. */ +enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY }; + +int opts_copy __P((SCR *, SCR *)); +void opts_dump __P((SCR *, enum optdisp)); +void opts_free __P((SCR *)); +int opts_init __P((SCR *)); +int opts_save __P((SCR *, FILE *)); +int opts_set __P((SCR *, ARGS *[])); + +/* Per-option change routines. */ +int f_altwerase __P((SCR *, OPTION *, char *, u_long)); +int f_columns __P((SCR *, OPTION *, char *, u_long)); +int f_keytime __P((SCR *, OPTION *, char *, u_long)); +int f_leftright __P((SCR *, OPTION *, char *, u_long)); +int f_lines __P((SCR *, OPTION *, char *, u_long)); +int f_lisp __P((SCR *, OPTION *, char *, u_long)); +int f_list __P((SCR *, OPTION *, char *, u_long)); +int f_matchtime __P((SCR *, OPTION *, char *, u_long)); +int f_mesg __P((SCR *, OPTION *, char *, u_long)); +int f_modeline __P((SCR *, OPTION *, char *, u_long)); +int f_number __P((SCR *, OPTION *, char *, u_long)); +int f_optimize __P((SCR *, OPTION *, char *, u_long)); +int f_paragraph __P((SCR *, OPTION *, char *, u_long)); +int f_readonly __P((SCR *, OPTION *, char *, u_long)); +int f_ruler __P((SCR *, OPTION *, char *, u_long)); +int f_section __P((SCR *, OPTION *, char *, u_long)); +int f_shiftwidth __P((SCR *, OPTION *, char *, u_long)); +int f_sidescroll __P((SCR *, OPTION *, char *, u_long)); +int f_sourceany __P((SCR *, OPTION *, char *, u_long)); +int f_tabstop __P((SCR *, OPTION *, char *, u_long)); +int f_tags __P((SCR *, OPTION *, char *, u_long)); +int f_term __P((SCR *, OPTION *, char *, u_long)); +int f_ttywerase __P((SCR *, OPTION *, char *, u_long)); +int f_w1200 __P((SCR *, OPTION *, char *, u_long)); +int f_w300 __P((SCR *, OPTION *, char *, u_long)); +int f_w9600 __P((SCR *, OPTION *, char *, u_long)); +int f_window __P((SCR *, OPTION *, char *, u_long)); +int f_wrapmargin __P((SCR *, OPTION *, char *, u_long)); diff --git a/usr.bin/vi/options_f.c b/usr.bin/vi/options_f.c new file mode 100644 index 000000000000..efd596f9585a --- /dev/null +++ b/usr.bin/vi/options_f.c @@ -0,0 +1,547 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options_f.c 8.25 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "tag.h" + +static int opt_putenv __P((char *)); + +#define DECL(f) \ + int \ + f(sp, op, str, val) \ + SCR *sp; \ + OPTION *op; \ + char *str; \ + u_long val; +#define CALL(f) \ + f(sp, op, str, val) + +#define turnoff val + +DECL(f_altwerase) +{ + if (turnoff) + O_CLR(sp, O_ALTWERASE); + else { + O_SET(sp, O_ALTWERASE); + O_CLR(sp, O_TTYWERASE); + } + return (0); +} + +DECL(f_ttywerase) +{ + if (turnoff) + O_CLR(sp, O_TTYWERASE); + else { + O_SET(sp, O_TTYWERASE); + O_CLR(sp, O_ALTWERASE); + } + return (0); +} + +DECL(f_columns) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_COLS) { + msgq(sp, M_ERR, "Screen columns too small, less than %d.", + MINIMUM_SCREEN_COLS); + return (1); + } + if (val < O_VAL(sp, O_SHIFTWIDTH)) { + msgq(sp, M_ERR, + "Screen columns too small, less than shiftwidth."); + return (1); + } + if (val < O_VAL(sp, O_SIDESCROLL)) { + msgq(sp, M_ERR, + "Screen columns too small, less than sidescroll."); + return (1); + } + if (val < O_VAL(sp, O_TABSTOP)) { + msgq(sp, M_ERR, + "Screen columns too small, less than tabstop."); + return (1); + } + if (val < O_VAL(sp, O_WRAPMARGIN)) { + msgq(sp, M_ERR, + "Screen columns too small, less than wrapmargin."); + return (1); + } +#ifdef XXX_NOT_RIGHT + /* + * This has to be checked by reaching down into the screen code. + */ + if (val < O_NUMBER_LENGTH) { + msgq(sp, M_ERR, + "Screen columns too small, less than number option."); + return (1); + } +#endif + /* Set the columns value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_COLUMNS) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_COLUMNS) = val; + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_keytime) +{ + O_VAL(sp, O_KEYTIME) = val; + return (0); +} + +DECL(f_leftright) +{ + if (turnoff) + O_CLR(sp, O_LEFTRIGHT); + else + O_SET(sp, O_LEFTRIGHT); + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_lines) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen lines too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Set the rows value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_LINES) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_LINES) = val; + + /* + * If no window value set, set a new default window and, + * optionally, a new scroll value. + */ + if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) { + O_VAL(sp, O_WINDOW) = val - 1; + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = val / 2; + } + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_lisp) +{ + msgq(sp, M_ERR, "The lisp option is not implemented."); + return (0); +} + +DECL(f_list) +{ + if (turnoff) + O_CLR(sp, O_LIST); + else + O_SET(sp, O_LIST); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_matchtime) +{ + O_VAL(sp, O_MATCHTIME) = val; + return (0); +} + +DECL(f_mesg) +{ + struct stat sb; + char *tty; + + /* Find the tty. */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) { + msgq(sp, M_ERR, "ttyname: %s.", strerror(errno)); + return (1); + } + + /* Save the tty mode for later; only save it once. */ + if (!F_ISSET(sp->gp, G_SETMODE)) { + F_SET(sp->gp, G_SETMODE); + if (stat(tty, &sb) < 0) { + msgq(sp, M_ERR, "%s: %s.", tty, strerror(errno)); + return (1); + } + sp->gp->origmode = sb.st_mode; + } + + if (turnoff) { + if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned off: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_CLR(sp, O_MESG); + } else { + if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned on: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_SET(sp, O_MESG); + } + return (0); +} + +/* + * f_modeline -- + * This has been documented in historical systems as both "modeline" + * and as "modelines". Regardless of the name, this option represents + * a security problem of mammoth proportions, not to mention a stunning + * example of what your intro CS professor referred to as the perils of + * mixing code and data. Don't add it, or I will kill you. + */ +DECL(f_modeline) +{ + if (!turnoff) + msgq(sp, M_ERR, "The modeline(s) option may never be set."); + return (0); +} + +DECL(f_number) +{ + if (turnoff) + O_CLR(sp, O_NUMBER); + else + O_SET(sp, O_NUMBER); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_optimize) +{ + msgq(sp, M_ERR, "The optimize option is not implemented."); + return (0); +} + +DECL(f_paragraph) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Paragraph options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED)) + free(O_STR(sp, O_PARAGRAPHS)); + if ((O_STR(sp, O_PARAGRAPHS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_readonly) +{ + if (turnoff) { + O_CLR(sp, O_READONLY); + if (sp->frp != NULL) + F_CLR(sp->frp, FR_RDONLY); + } else { + O_SET(sp, O_READONLY); + if (sp->frp != NULL) + F_SET(sp->frp, FR_RDONLY); + } + return (0); +} + +DECL(f_ruler) +{ + if (turnoff) + O_CLR(sp, O_RULER); + else + O_SET(sp, O_RULER); + return (0); +} + +DECL(f_section) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Section options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_SECTIONS], OPT_ALLOCATED)) + free(O_STR(sp, O_SECTIONS)); + if ((O_STR(sp, O_SECTIONS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_SECTIONS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_shiftwidth) +{ + if (val == 0) { + msgq(sp, M_ERR, "The shiftwidth can't be set to 0."); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Shiftwidth can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SHIFTWIDTH) = val; + return (0); +} + +DECL(f_sidescroll) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Sidescroll can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SIDESCROLL) = val; + return (0); +} + +/* + * f_sourceany -- + * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they + * were owned by the user. The sourceany option was an undocumented + * feature of historic vi which permitted the startup source'ing of + * .exrc files the user didn't own. This is an obvious security problem, + * and we ignore the option. + */ +DECL(f_sourceany) +{ + if (!turnoff) + msgq(sp, M_ERR, "The sourceany option may never be set."); + return (0); +} + +DECL(f_tabstop) +{ + if (val == 0) { + msgq(sp, M_ERR, "Tab stops can't be set to 0."); + return (1); + } +#define MAXTABSTOP 20 + if (val > MAXTABSTOP) { + msgq(sp, M_ERR, + "Tab stops can't be larger than %d.", MAXTABSTOP); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Tab stops can't be larger than screen size.", + MAXTABSTOP); + return (1); + } + O_VAL(sp, O_TABSTOP) = val; + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_tags) +{ + char *p; + + /* Copy for user display. */ + p = O_STR(sp, O_TAGS); + if ((O_STR(sp, O_TAGS) = strdup(str)) == NULL) { + O_STR(sp, O_TAGS) = p; + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (F_ISSET(&sp->opts[O_TAGS], OPT_ALLOCATED)) + FREE(p, strlen(p) + 1); + F_SET(&sp->opts[O_TAGS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_term) +{ + char buf[256]; + + if (F_ISSET(&sp->opts[O_TERM], OPT_ALLOCATED)) + free(O_STR(sp, O_TERM)); + if ((O_STR(sp, O_TERM) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_TERM], OPT_ALLOCATED | OPT_SET); + + /* Set the terminal value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "TERM=%s", str); + if (opt_putenv(buf)) + return (1); + + if (set_window_size(sp, 0, 0)) + return (1); + return (0); +} + +DECL(f_w300) +{ + /* Historical behavior for w300 was < 1200. */ + if (baud_from_bval(sp) >= 1200) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W300) = val; + return (0); +} + +DECL(f_w1200) +{ + u_long v; + + /* Historical behavior for w1200 was == 1200. */ + v = baud_from_bval(sp); + if (v < 1200 || v > 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W1200) = val; + return (0); +} + +DECL(f_w9600) +{ + speed_t v; + + /* Historical behavior for w9600 was > 1200. */ + v = baud_from_bval(sp); + if (v <= 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W9600) = val; + return (0); +} + +DECL(f_window) +{ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Window too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_WINDOW) = val; + O_VAL(sp, O_SCROLL) = val / 2; + + return (0); +} + +DECL(f_wrapmargin) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Wrapmargin value can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_WRAPMARGIN) = val; + return (0); +} + +/* + * opt_putenv -- + * Put a value into the environment. We use putenv(3) because it's + * more portable. The following hack is because some moron decided + * to keep a reference to the memory passed to putenv(3), instead of + * having it allocate its own. Someone clearly needs to get promoted + * into management. + */ +static int +opt_putenv(s) + char *s; +{ + char *t; + + /* Memory leak. */ + if ((t = strdup(s)) == NULL) + return (1); + return (putenv(t)); +} diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h new file mode 100644 index 000000000000..be6352b9dfc1 --- /dev/null +++ b/usr.bin/vi/pathnames.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.5 (Berkeley) 12/21/93 + */ + +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEVNULL "/dev/null" +#define _PATH_EXRC ".exrc" +#define _PATH_NEXRC ".nexrc" +#define _PATH_PRESERVE "/var/tmp/vi.recover" +#define _PATH_SENDMAIL "/usr/sbin/sendmail" +#define _PATH_SYSEXRC "/etc/vi.exrc" +#define _PATH_TAGS "tags /var/db/libc.tags /sys/kern/tags" +#define _PATH_TMP "/tmp" +#define _PATH_TTY "/dev/tty" diff --git a/usr.bin/vi/recover.c b/usr.bin/vi/recover.c new file mode 100644 index 000000000000..f9a483236abe --- /dev/null +++ b/usr.bin/vi/recover.c @@ -0,0 +1,622 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)recover.c 8.40 (Berkeley) 12/21/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> + +/* + * We include <sys/file.h>, because the flock(2) #defines were + * found there on historical systems. We also include <fcntl.h> + * because the open(2) #defines are found there on newer systems. + */ +#include <sys/file.h> + +#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "pathnames.h" + +/* + * Recovery code. + * + * The basic scheme is there's a btree file, whose name we specify. The first + * time a file is modified, and then at RCV_PERIOD intervals after that, the + * btree file is synced to disk. Each time a keystroke is requested for a file + * the terminal routines check to see if the file needs to be synced. This, of + * course means that the data structures had better be consistent each time the + * key routines are called. + * + * We don't use timers other than to flag that the file should be synced. This + * would require that the SCR and EXF data structures be locked, the dbopen(3) + * routines lock out the timers for each update, etc. It's just not worth it. + * The only way we can lose in the current scheme is if the file is saved, then + * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more. + * Not likely. + * + * When a file is first modified, a file which can be handed off to the mailer + * is created. The file contains normal headers, with two additions, which + * occur in THIS order, as the FIRST TWO headers: + * + * Vi-recover-file: file_name + * Vi-recover-path: recover_path + * + * Since newlines delimit the headers, this means that file names cannot + * have newlines in them, but that's probably okay. + * + * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX". + */ + +#define VI_FHEADER "Vi-recover-file: " +#define VI_PHEADER "Vi-recover-path: " + +static void rcv_alrm __P((int)); +static int rcv_mailfile __P((SCR *, EXF *)); +static void rcv_syncit __P((SCR *, int)); + +/* + * rcv_tmp -- + * Build a file name that will be used as the recovery file. + */ +int +rcv_tmp(sp, ep, name) + SCR *sp; + EXF *ep; + char *name; +{ + struct stat sb; + int fd; + char *dp, *p, path[MAXPATHLEN]; + + /* + * If the recovery directory doesn't exist, try and create it. As + * the recovery files are themselves protected from reading/writing + * by other than the owner, the worst that can happen is that a user + * would have permission to remove other users recovery files. If + * the sticky bit has the BSD semantics, that too will be impossible. + */ + dp = O_STR(sp, O_RECDIR); + if (stat(dp, &sb)) { + if (errno != ENOENT || mkdir(dp, 0)) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); + } + + /* Newlines delimit the mail messages. */ + for (p = name; *p; ++p) + if (*p == '\n') { + msgq(sp, M_ERR, + "Files with newlines in the name are unrecoverable."); + return (1); + } + + (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp); + + /* + * !!! + * We depend on mkstemp(3) setting the permissions correctly. + * GP's, we do it ourselves, to keep the window as small as + * possible. + */ + if ((fd = mkstemp(path)) == -1) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(path, S_IRUSR | S_IWUSR); + (void)close(fd); + + if ((ep->rcv_path = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(path); + return (1); + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_init -- + * Force the file to be snapshotted for recovery. + */ +int +rcv_init(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + struct sigaction act; + recno_t lno; + + F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON); + + /* Build file to mail to the user. */ + if (rcv_mailfile(sp, ep)) + goto err; + + /* Force read of entire file. */ + if (file_lline(sp, ep, &lno)) + goto err; + + /* Turn on a busy message, and sync it to backing store. */ + busy_on(sp, 1, "Copying file for recovery..."); + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Preservation failed: %s: %s", + ep->rcv_path, strerror(errno)); + busy_off(sp); + goto err; + } + busy_off(sp); + + if (!F_ISSET(sp->gp, G_RECOVER_SET)) { + /* Install the recovery timer handler. */ + act.sa_handler = rcv_alrm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, NULL); + + /* Start the recovery timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, NULL)) { + msgq(sp, M_ERR, + "Error: setitimer: %s", strerror(errno)); +err: msgq(sp, M_ERR, + "Recovery after system crash not possible."); + return (1); + } + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_alrm -- + * Recovery timer interrupt handler. + */ +static void +rcv_alrm(signo) + int signo; +{ + F_SET(__global_list, G_SIGALRM); +} + +/* + * rcv_mailfile -- + * Build the file to mail to the user. + */ +static int +rcv_mailfile(sp, ep) + SCR *sp; + EXF *ep; +{ + struct passwd *pw; + uid_t uid; + FILE *fp; + time_t now; + int fd; + char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN]; + + if ((pw = getpwuid(uid = getuid())) == NULL) { + msgq(sp, M_ERR, "Information on user id %u not found.", uid); + return (1); + } + + (void)snprintf(path, sizeof(path), + "%s/recover.XXXXXX", O_STR(sp, O_RECDIR)); + if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) { + msgq(sp, M_ERR, + "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + if (fd != -1) + (void)close(fd); + return (1); + } + + /* + * We keep an open lock on the file so that the recover option can + * distinguish between files that are live and those that need to + * be recovered. There's an obvious window between the mkstemp call + * and the lock, but it's pretty small. + */ + if ((ep->rcv_fd = dup(fd)) != -1) + (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB); + + if ((ep->rcv_mpath = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + + t = FILENAME(sp->frp); + if ((p = strrchr(t, '/')) == NULL) + p = t; + else + ++p; + (void)time(&now); + (void)gethostname(host, sizeof(host)); + (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n", + VI_FHEADER, p, /* Non-standard. */ + VI_PHEADER, ep->rcv_path, /* Non-standard. */ + "Reply-To: root", + "From: root (Nvi recovery program)", + "To: ", pw->pw_name, + "Subject: Nvi saved the file ", p, + "Precedence: bulk"); /* For vacation(1). */ + (void)fprintf(fp, "%s%.24s%s%s\n%s%s", + "On ", ctime(&now), + ", the user ", pw->pw_name, + "was editing a file named ", p); + if (p != t) + (void)fprintf(fp, " (%s)", t); + (void)fprintf(fp, "\n%s%s%s\n", + "on the machine ", host, ", when it was saved for\nrecovery."); + (void)fprintf(fp, "\n%s\n%s\n%s\n\n", + "You can recover most, if not all, of the changes", + "to this file using the -l and -r options to nvi(1)", + "or nex(1)."); + + if (fflush(fp) || ferror(fp)) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + return (0); +} + +/* + * rcv_sync -- + * Sync the backing file. + */ +int +rcv_sync(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Automatic file backup failed: %s: %s", + ep->rcv_path, strerror(errno)); + value.it_interval.tv_sec = value.it_interval.tv_usec = 0; + value.it_value.tv_sec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + F_CLR(ep, F_RCV_ON); + return (1); + } + return (0); +} + +/* + * rcv_hup -- + * Recovery SIGHUP interrupt handler. (Modem line dropped, or + * xterm window closed.) + */ +void +rcv_hup() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. Send email to the user for each file saved. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGHUP, SIG_DFL); + (void)kill(0, SIGHUP); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_term -- + * Recovery SIGTERM interrupt handler. (Reboot or halt is running.) + */ +void +rcv_term() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGTERM, SIG_DFL); + (void)kill(0, SIGTERM); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_syncit -- + * Sync the file, optionally send mail. + */ +static void +rcv_syncit(sp, email) + SCR *sp; + int email; +{ + EXF *ep; + char comm[1024]; + + if ((ep = sp->ep) == NULL || + !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON)) + return; + + (void)ep->db->sync(ep->db, R_RECNOSYNC); + F_SET(ep, F_RCV_NORM); + + /* + * !!! + * If you need to port this to a system that doesn't have sendmail, + * the -t flag being used causes sendmail to read the message for + * the recipients instead of us specifying them some other way. + */ + if (email) { + (void)snprintf(comm, sizeof(comm), + "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath); + (void)system(comm); + } + (void)file_end(sp, ep, 1); +} + +/* + * people making love + * never exactly the same + * just like a snowflake + * + * rcv_list -- + * List the files that can be recovered by this user. + */ +int +rcv_list(sp) + SCR *sp; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FILE *fp; + int found; + char *p, file[1024]; + + if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) { + (void)fprintf(stderr, + "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + for (found = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + if ((fp = fopen(dp->d_name, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the header, get the file name. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL) { + (void)fprintf(stderr, + "vi: %s: malformed recovery file.\n", dp->d_name); + goto next; + } + *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + (void)fprintf(stderr, + "vi: %s: %s\n", dp->d_name, strerror(errno)); + goto next; + } + + /* Display. */ + (void)printf("%s: %s", + file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime)); + found = 1; + +next: (void)fclose(fp); + } + if (found == 0) + (void)printf("vi: no files to recover.\n"); + (void)closedir(dirp); + return (0); +} + +/* + * rcv_read -- + * Start a recovered file as the file to edit. + */ +int +rcv_read(sp, name) + SCR *sp; + char *name; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FREF *frp; + FILE *fp; + time_t rec_mtime; + int found, requested; + char *p, *t, *recp, *pathp; + char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN]; + + if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) { + msgq(sp, M_ERR, + "%s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + recp = pathp = NULL; + for (found = requested = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + (void)snprintf(recpath, sizeof(recpath), + "%s/%s", O_STR(sp, O_RECDIR), dp->d_name); + if ((fp = fopen(recpath, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the headers. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL || + fgets(path, sizeof(path), fp) == NULL || + strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) || + (t = strchr(path, '\n')) == NULL) { + msgq(sp, M_ERR, + "%s: malformed recovery file.", recpath); + goto next; + } + ++found; + *t = *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + msgq(sp, M_ERR, + "vi: %s: %s", dp->d_name, strerror(errno)); + goto next; + } + + /* Check the file name. */ + if (strcmp(file + sizeof(VI_FHEADER) - 1, name)) + goto next; + + ++requested; + + /* If we've found more than one, take the most recent. */ + if (recp == NULL || rec_mtime < sb.st_mtime) { + p = recp; + t = pathp; + if ((recp = strdup(recpath)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + recp = p; + goto next; + } + if ((pathp = strdup(path)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + FREE(recp, strlen(recp) + 1); + recp = p; + pathp = t; + goto next; + } + if (p != NULL) { + FREE(p, strlen(p) + 1); + FREE(t, strlen(t) + 1); + } + rec_mtime = sb.st_mtime; + } + +next: (void)fclose(fp); + } + (void)closedir(dirp); + + if (recp == NULL) { + msgq(sp, M_INFO, + "No files named %s, owned by you, to edit.", name); + return (1); + } + if (found) { + if (requested > 1) + msgq(sp, M_INFO, + "There are older versions of this file for you to recover."); + if (found > requested) + msgq(sp, M_INFO, + "There are other files that you can recover."); + } + + /* Create the FREF structure, start the btree file. */ + if ((frp = file_add(sp, NULL, name, 0)) == NULL || + file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) { + FREE(recp, strlen(recp) + 1); + FREE(pathp, strlen(pathp) + 1); + return (1); + } + sp->ep->rcv_mpath = recp; + return (0); +} diff --git a/usr.bin/vi/screen.c b/usr.bin/vi/screen.c new file mode 100644 index 000000000000..e18bd02183dd --- /dev/null +++ b/usr.bin/vi/screen.c @@ -0,0 +1,296 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)screen.c 8.51 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "tag.h" + +/* + * screen_init -- + * Do the default initialization of an SCR structure. + */ +int +screen_init(orig, spp, flags) + SCR *orig, **spp; + u_int flags; +{ + SCR *sp; + size_t len; + + *spp = NULL; + CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR)); + *spp = sp; + +/* INITIALIZED AT SCREEN CREATE. */ + sp->gp = __global_list; /* All ref the GS structure. */ + + LIST_INIT(&sp->msgq); + CIRCLEQ_INIT(&sp->frefq); + + sp->ccnt = 2; /* Anything > 1 */ + + FD_ZERO(&sp->rdfd); + + CIRCLEQ_INIT(&sp->tiq); + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + sp->searchdir = NOTSET; + sp->csearchdir = CNOTSET; + + switch (flags & S_SCREENS) { + case S_EX: + if (sex_screen_init(sp)) + return (1); + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + return (1); + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + return (1); + break; + default: + abort(); + } + + sp->flags = flags; + } else { + if (orig->alt_name != NULL && + (sp->alt_name = strdup(orig->alt_name)) == NULL) + goto mem; + + /* Retain all searching/substitution information. */ + if (F_ISSET(orig, S_SRE_SET)) { + F_SET(sp, S_SRE_SET); + sp->sre = orig->sre; + } + if (F_ISSET(orig, S_SUBRE_SET)) { + F_SET(sp, S_SUBRE_SET); + sp->subre = orig->subre; + } + sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD; + sp->csearchdir = CNOTSET; + sp->lastckey = orig->lastckey; + + if (orig->matchsize) { + len = orig->matchsize * sizeof(regmatch_t); + MALLOC(sp, sp->match, regmatch_t *, len); + if (sp->match == NULL) + goto mem; + sp->matchsize = orig->matchsize; + memmove(sp->match, orig->match, len); + } + if (orig->repl_len) { + MALLOC(sp, sp->repl, char *, orig->repl_len); + if (sp->repl == NULL) + goto mem; + sp->repl_len = orig->repl_len; + memmove(sp->repl, orig->repl, orig->repl_len); + } + if (orig->newl_len) { + len = orig->newl_len * sizeof(size_t); + MALLOC(sp, sp->newl, size_t *, len); + if (sp->newl == NULL) + goto mem; + sp->newl_len = orig->newl_len; + sp->newl_cnt = orig->newl_cnt; + memmove(sp->newl, orig->newl, len); + } + + sp->saved_vi_mode = orig->saved_vi_mode; + + if (opts_copy(orig, sp)) { +mem: msgq(orig, M_SYSERR, "new screen attributes"); + (void)screen_end(sp); + return (1); + } + + sp->s_bell = orig->s_bell; + sp->s_bg = orig->s_bg; + sp->s_busy = orig->s_busy; + sp->s_change = orig->s_change; + sp->s_chposition = orig->s_chposition; + sp->s_clear = orig->s_clear; + sp->s_column = orig->s_column; + sp->s_confirm = orig->s_confirm; + sp->s_down = orig->s_down; + sp->s_edit = orig->s_edit; + sp->s_end = orig->s_end; + sp->s_ex_cmd = orig->s_ex_cmd; + sp->s_ex_run = orig->s_ex_run; + sp->s_ex_write = orig->s_ex_write; + sp->s_fg = orig->s_fg; + sp->s_fill = orig->s_fill; + sp->s_get = orig->s_get; + sp->s_key_read = orig->s_key_read; + sp->s_optchange = orig->s_optchange; + sp->s_position = orig->s_position; + sp->s_rabs = orig->s_rabs; + sp->s_refresh = orig->s_refresh; + sp->s_relative = orig->s_relative; + sp->s_rrel = orig->s_rrel; + sp->s_split = orig->s_split; + sp->s_suspend = orig->s_suspend; + sp->s_up = orig->s_up; + + F_SET(sp, F_ISSET(orig, S_SCREENS)); + } + + if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */ + return (1); + if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */ + return (1); + if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */ + return (1); + if (v_screen_copy(orig, sp)) /* Init vi. */ + return (1); + if (ex_screen_copy(orig, sp)) /* Init ex. */ + return (1); + + *spp = sp; + return (0); +} + +/* + * screen_end -- + * Release a screen. + */ +int +screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */ + rval = 1; + if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */ + rval = 1; + if (sex_screen_end(sp)) /* End S_EX screen. */ + rval = 1; + if (v_screen_end(sp)) /* End vi. */ + rval = 1; + if (ex_screen_end(sp)) /* End ex. */ + rval = 1; + + /* Free FREF's. */ + { FREF *frp; + while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) { + CIRCLEQ_REMOVE(&sp->frefq, frp, q); + if (frp->cname != NULL) + free(frp->cname); + if (frp->name != NULL) + free(frp->name); + if (frp->tname != NULL) + free(frp->tname); + FREE(frp, sizeof(FREF)); + } + } + + /* Free any text input. */ + text_lfree(&sp->tiq); + + /* Free any script information. */ + if (F_ISSET(sp, S_SCRIPT)) + sscr_end(sp); + + /* Free alternate file name. */ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + + /* Free up search information. */ + if (sp->match != NULL) + FREE(sp->match, sizeof(regmatch_t)); + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + if (sp->newl != NULL) + FREE(sp->newl, sp->newl_len); + + /* Free all the options */ + opts_free(sp); + + /* + * Free the message chain last, so previous failures have a place + * to put messages. Copy messages to (in order) a related screen, + * any screen, the global area. + */ + { SCR *c_sp; MSG *mp, *next; + if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = + sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else { + c_sp = NULL; + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(sp->gp, G_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, c_sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + FREE(mp->mbuf, mp->blen); + FREE(mp, sizeof(MSG)); + } + } + + /* Remove the screen from the displayed queue. */ + CIRCLEQ_REMOVE(&sp->gp->dq, sp, q); + + /* Free the screen itself. */ + FREE(sp, sizeof(SCR)); + + return (rval); +} diff --git a/usr.bin/vi/screen.h b/usr.bin/vi/screen.h new file mode 100644 index 000000000000..d95179352b48 --- /dev/null +++ b/usr.bin/vi/screen.h @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.h 8.81 (Berkeley) 12/29/93 + */ + +/* + * There are minimum values that vi has to have to display a screen. The + * row minimum is fixed at 1 line for the text, and 1 line for any error + * messages. The column calculation is a lot trickier. For example, you + * have to have enough columns to display the line number, not to mention + * guaranteeing that tabstop and shiftwidth values are smaller than the + * current column value. It's a lot simpler to have a fixed value and not + * worry about it. + * + * XXX + * MINIMUM_SCREEN_COLS is probably wrong. + */ +#define MINIMUM_SCREEN_ROWS 2 +#define MINIMUM_SCREEN_COLS 20 + /* Line operations. */ +enum operation { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET }; + /* Position values. */ +enum position { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP }; + +/* + * Structure for holding file references. Each SCR structure contains a + * linked list of these (the user's argument list) as well as pointers to + * the current and previous files. The structure contains the name of the + * file, along with the information that follows the name. A file has up + * to three "names". The tname field is the path of the temporary backing + * file, if any. The name field is the name the user originally used to + * specify the file to be edited. The cname field is the changed name if + * the user changed the name. + * + * Note that the read-only bit follows the file name, not the file itself. + * + * XXX + * The mtime field should be a struct timespec, but time_t is more portable. + */ +struct _fref { + CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */ + char *cname; /* Changed file name. */ + char *name; /* File name. */ + char *tname; /* Temporary file name. */ + + recno_t lno; /* 1-N: file cursor line. */ + size_t cno; /* 0-N: file cursor column. */ + time_t mtime; /* Last modification time. */ + +#define FR_CHANGEWRITE 0x001 /* Name changed and then written. */ +#define FR_CURSORSET 0x002 /* If lno/cno valid. */ +#define FR_EDITED 0x004 /* If the file was ever edited. */ +#define FR_IGNORE 0x008 /* File isn't part of argument list. */ +#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */ +#define FR_RDONLY 0x020 /* File is read-only. */ + u_int flags; +}; + +/* + * There's a file name hierarchy -- if the user has changed the name, we + * use it, otherwise, we use the original name, if there was one, othewise + * use the temporary name. + */ +#define FILENAME(frp) \ + ((frp)->cname != NULL) ? (frp)->cname : \ + ((frp)->name != NULL) ? (frp)->name : (frp)->tname + +/* + * SCR -- + * The screen structure. To the extent possible, all screen information + * is stored in the various private areas. The only information here + * is used by global routines or is shared by too many screens. + */ +struct _scr { +/* INITIALIZED AT SCREEN CREATE. */ + CIRCLEQ_ENTRY(_scr) q; /* Screens. */ + + GS *gp; /* Pointer to global area. */ + + SCR *nextdisp; /* Next display screen. */ + + EXF *ep; /* Screen's current EXF structure. */ + + MSGH msgq; /* Message list. */ + /* FREF list. */ + CIRCLEQ_HEAD(_frefh, _fref) frefq; + FREF *frp; /* FREF being edited. */ + FREF *a_frp; /* Last argument list FREF edited. */ + FREF *p_frp; /* Last FREF edited. */ + + u_long ccnt; /* Command count. */ + u_long q_ccnt; /* Quit or ZZ command count. */ + + /* Screen's: */ + size_t rows; /* 1-N: number of rows. */ + size_t cols; /* 1-N: number of columns. */ + size_t woff; /* 0-N: row offset in screen. */ + size_t t_rows; /* 1-N: cur number of text rows. */ + size_t t_maxrows; /* 1-N: max number of text rows. */ + size_t t_minrows; /* 1-N: min number of text rows. */ + + /* Cursor's: */ + recno_t lno; /* 1-N: file line. */ + size_t cno; /* 0-N: file character in line. */ + + size_t rcm; /* Vi: 0-N: Column suck. */ +#define RCM_FNB 0x01 /* Column suck: first non-blank. */ +#define RCM_LAST 0x02 /* Column suck: last. */ + u_int rcmflags; + +#define L_ADDED 0 /* Added lines. */ +#define L_CHANGED 1 /* Changed lines. */ +#define L_COPIED 2 /* Copied lines. */ +#define L_DELETED 3 /* Deleted lines. */ +#define L_JOINED 4 /* Joined lines. */ +#define L_MOVED 5 /* Moved lines. */ +#define L_PUT 6 /* Put lines. */ +#define L_LSHIFT 7 /* Left shift lines. */ +#define L_RSHIFT 8 /* Right shift lines. */ +#define L_YANKED 9 /* Yanked lines. */ + recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */ + + FILE *stdfp; /* Ex output file pointer. */ + + char *if_name; /* Ex input file name, for messages. */ + recno_t if_lno; /* Ex input file line, for messages. */ + + fd_set rdfd; /* Ex/vi: read fd select mask. */ + + TEXTH tiq; /* Ex/vi: text input queue. */ + + SCRIPT *script; /* Vi: script mode information .*/ + + char const *time_msg; /* ITIMER_REAL message. */ + struct itimerval time_value; /* ITIMER_REAL saved value. */ + struct sigaction time_handler; /* ITIMER_REAL saved handler. */ + + void *vi_private; /* Vi private area. */ + void *ex_private; /* Ex private area. */ + void *svi_private; /* Vi curses screen private area. */ + void *xaw_private; /* Vi XAW screen private area. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + char *alt_name; /* Ex/vi: alternate file name. */ + + /* Ex/vi: search/substitute info. */ + regex_t sre; /* Last search RE. */ + regex_t subre; /* Last substitute RE. */ + enum direction searchdir; /* File search direction. */ + enum cdirection csearchdir; /* Character search direction. */ + CHAR_T lastckey; /* Last search character. */ + regmatch_t *match; /* Substitute match array. */ + size_t matchsize; /* Substitute match array size. */ + char *repl; /* Substitute replacement. */ + size_t repl_len; /* Substitute replacement length.*/ + size_t *newl; /* Newline offset array. */ + size_t newl_len; /* Newline array size. */ + size_t newl_cnt; /* Newlines in replacement. */ + + u_int saved_vi_mode; /* Saved vi display type. */ + + OPTION opts[O_OPTIONCOUNT]; /* Options. */ + +/* + * SCREEN SUPPORT ROUTINES. + * + * A SCR * MUST be the first argument to these routines. + */ + /* Ring the screen bell. */ + void (*s_bell) __P((SCR *)); + /* Background the screen. */ + int (*s_bg) __P((SCR *)); + /* Put up a busy message. */ + int (*s_busy) __P((SCR *, char const *)); + /* Change a screen line. */ + int (*s_change) __P((SCR *, EXF *, recno_t, enum operation)); + /* Return column close to specified. */ + size_t (*s_chposition) __P((SCR *, EXF *, recno_t, size_t)); + /* Clear the screen. */ + int (*s_clear) __P((SCR *)); + /* Return the logical cursor column. */ + int (*s_column) __P((SCR *, EXF *, size_t *)); + enum confirm /* Confirm an action with the user. */ + (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *)); + /* Move down the screen. */ + int (*s_down) __P((SCR *, EXF *, MARK *, recno_t, int)); + /* Edit a file. */ + int (*s_edit) __P((SCR *, EXF *)); + /* End a screen. */ + int (*s_end) __P((SCR *)); + /* Run a single ex command. */ + int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *)); + /* Run user's ex commands. */ + int (*s_ex_run) __P((SCR *, EXF *, MARK *)); + /* Screen's ex write function. */ + int (*s_ex_write) __P((void *, const char *, int)); + /* Foreground the screen. */ + int (*s_fg) __P((SCR *, CHAR_T *)); + /* Fill the screen's map. */ + int (*s_fill) __P((SCR *, EXF *, recno_t, enum position)); + enum input /* Get a line from the user. */ + (*s_get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + enum input /* Get a key from the user. */ + (*s_key_read) __P((SCR *, int *, struct timeval *)); + /* Tell the screen an option changed. */ + int (*s_optchange) __P((SCR *, int)); + /* Return column at screen position. */ + int (*s_position) __P((SCR *, EXF *, + MARK *, u_long, enum position)); + /* Change the absolute screen size. */ + int (*s_rabs) __P((SCR *, long)); + /* Refresh the screen. */ + int (*s_refresh) __P((SCR *, EXF *)); + /* Return column close to last char. */ + size_t (*s_relative) __P((SCR *, EXF *, recno_t)); + /* Change the relative screen size. */ + int (*s_rrel) __P((SCR *, long)); + /* Split the screen. */ + int (*s_split) __P((SCR *, ARGS *[])); + /* Suspend the screen. */ + int (*s_suspend) __P((SCR *)); + /* Move up the screen. */ + int (*s_up) __P((SCR *, EXF *, MARK *, recno_t, int)); + +/* Editor screens. */ +#define S_EX 0x0000001 /* Ex screen. */ +#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */ +#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */ + +#define IN_EX_MODE(sp) /* If in ex mode. */ \ + (F_ISSET(sp, S_EX)) +#define IN_VI_MODE(sp) /* If in vi mode. */ \ + (F_ISSET(sp, S_VI_CURSES | S_VI_XAW)) +#define S_SCREENS /* Screens. */ \ + (S_EX | S_VI_CURSES | S_VI_XAW) + +/* Major screen/file changes. */ +#define S_EXIT 0x0000008 /* Exiting (not forced). */ +#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */ +#define S_FSWITCH 0x0000020 /* Switch files. */ +#define S_SSWITCH 0x0000040 /* Switch screens. */ +#define S_MAJOR_CHANGE /* Screen or file changes. */ \ + (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH) + +#define S_BELLSCHED 0x0000080 /* Bell scheduled. */ +#define S_CONTINUE 0x0000100 /* Need to ask the user to continue. */ +#define S_EXSILENT 0x0000200 /* Ex batch script. */ +#define S_GLOBAL 0x0000400 /* Doing a global command. */ +#define S_INPUT 0x0000800 /* Doing text input. */ +#define S_INTERRUPTED 0x0001000 /* If have been interrupted. */ +#define S_INTERRUPTIBLE 0x0002000 /* If can be interrupted. */ +#define S_REDRAW 0x0004000 /* Redraw the screen. */ +#define S_REFORMAT 0x0008000 /* Reformat the screen. */ +#define S_REFRESH 0x0010000 /* Refresh the screen. */ +#define S_RENUMBER 0x0020000 /* Renumber the screen. */ +#define S_RESIZE 0x0040000 /* Resize the screen. */ +#define S_SCRIPT 0x0080000 /* Window is a shell script. */ +#define S_SRE_SET 0x0100000 /* The search RE has been set. */ +#define S_SUBRE_SET 0x0200000 /* The substitute RE has been set. */ +#define S_TIMER_SET 0x0400000 /* If a busy timer is running. */ +#define S_UPDATE_MODE 0x0800000 /* Don't repaint modeline. */ + u_int flags; +}; + +/* Generic routines to start/stop a screen. */ +int screen_end __P((SCR *)); +int screen_init __P((SCR *, SCR **, u_int)); + +/* Public interfaces to the underlying screens. */ +int ex_screen_copy __P((SCR *, SCR *)); +int ex_screen_end __P((SCR *)); +int ex_screen_init __P((SCR *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_end __P((SCR *)); +int sex_screen_init __P((SCR *)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_end __P((SCR *)); +int svi_screen_init __P((SCR *)); +int v_screen_copy __P((SCR *, SCR *)); +int v_screen_end __P((SCR *)); +int v_screen_init __P((SCR *)); +int xaw_screen_copy __P((SCR *, SCR *)); +int xaw_screen_end __P((SCR *)); +int xaw_screen_init __P((SCR *)); diff --git a/usr.bin/vi/search.c b/usr.bin/vi/search.c new file mode 100644 index 000000000000..22aadef48751 --- /dev/null +++ b/usr.bin/vi/search.c @@ -0,0 +1,860 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)search.c 8.32 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "interrupt.h" + +static int check_delta __P((SCR *, EXF *, long, recno_t)); +static int ctag_conv __P((SCR *, char **, int *)); +static int get_delta __P((SCR *, char **, long *, u_int *)); +static int resetup __P((SCR *, regex_t **, enum direction, + char *, char **, long *, u_int *)); +static void search_intr __P((int)); + +/* + * resetup -- + * Set up a search for a regular expression. + */ +static int +resetup(sp, rep, dir, ptrn, epp, deltap, flagp) + SCR *sp; + regex_t **rep; + enum direction dir; + char *ptrn, **epp; + long *deltap; + u_int *flagp; +{ + u_int flags; + int delim, eval, re_flags, replaced; + char *p, *t; + + /* Set return information the default. */ + *deltap = 0; + + /* + * Use saved pattern if no pattern supplied, or if only a delimiter + * character is supplied. Only the pattern was saved, historic vi + * did not reuse any delta supplied. + */ + flags = *flagp; + if (ptrn == NULL) + goto prev; + if (ptrn[1] == '\0') { + if (epp != NULL) + *epp = ptrn + 1; + goto prev; + } + if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') { + if (epp != NULL) + *epp = ptrn + 2; +prev: if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + } + *rep = &sp->sre; + + /* Empty patterns set the direction. */ + if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + return (0); + } + + re_flags = 0; /* Set RE flags. */ + if (O_ISSET(sp, O_EXTENDED)) + re_flags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + re_flags |= REG_ICASE; + + if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */ + /* Set delimiter. */ + delim = *ptrn++; + + /* Find terminating delimiter, handling escaped delimiters. */ + for (p = t = ptrn;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + *t = '\0'; + break; + } + if (p[1] == delim && p[0] == '\\') + ++p; + *t++ = *p++; + } + + /* + * If characters after the terminating delimiter, it may + * be an error, or may be an offset. In either case, we + * return the end of the string, whatever it may be. + */ + if (*p) { + if (get_delta(sp, &p, deltap, flagp)) + return (1); + if (*p && LF_ISSET(SEARCH_TERM)) { + msgq(sp, M_ERR, + "Characters after search string and/or delta."); + return (1); + } + } + if (epp != NULL) + *epp = p; + + /* Check for "/ " or other such silliness. */ + if (*ptrn == '\0') + goto prev; + + if (re_conv(sp, &ptrn, &replaced)) + return (1); + } else if (LF_ISSET(SEARCH_TAG)) { + if (ctag_conv(sp, &ptrn, &replaced)) + return (1); + re_flags &= ~(REG_EXTENDED | REG_ICASE); + } + + /* Compile the RE. */ + if (eval = regcomp(*rep, ptrn, re_flags)) + re_error(sp, eval, *rep); + else if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + + /* Free up any extra memory. */ + if (replaced) + FREE_SPACE(sp, ptrn, 0); + return (eval); +} + +/* + * ctag_conv -- + * Convert a tags search path into something that the POSIX + * 1003.2 RE functions can handle. + */ +static int +ctag_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, len; + int lastdollar; + char *bp, *p, *t; + + *replacedp = 0; + + len = strlen(p = *ptrnp); + + /* Max memory usage is 2 times the length of the string. */ + GET_SPACE_RET(sp, bp, blen, len * 2); + + t = bp; + + /* The last charcter is a '/' or '?', we just strip it. */ + if (p[len - 1] == '/' || p[len - 1] == '?') + p[len - 1] = '\0'; + + /* The next-to-last character is a '$', and it's magic. */ + if (p[len - 2] == '$') { + lastdollar = 1; + p[len - 2] = '\0'; + } else + lastdollar = 0; + + /* The first character is a '/' or '?', we just strip it. */ + if (p[0] == '/' || p[0] == '?') + ++p; + + /* The second character is a '^', and it's magic. */ + if (p[0] == '^') + *t++ = *p++; + + /* + * Escape every other magic character we can find, stripping the + * backslashes ctags inserts to escape the search delimiter + * characters. + */ + while (p[0]) { + /* Ctags escapes the search delimiter characters. */ + if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) + ++p; + else if (strchr("^.[]$*", p[0])) + *t++ = '\\'; + *t++ = *p++; + } + if (lastdollar) + *t++ = '$'; + *t++ = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * search_intr -- + * Set the interrupt bit in any screen that is interruptible. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one search/global (see ex/ex_global.c) running, it will be hard + * to decide which one to stop. + */ +static void +search_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} + +#define EMPTYMSG "File empty; nothing to search." +#define EOFMSG "Reached end-of-file without finding the pattern." +#define NOTFOUND "Pattern not found." +#define SOFMSG "Reached top-of-file without finding the pattern." +#define WRAPMSG "Search wrapped." + +int +f_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* + * Start searching immediately after the cursor. If at the end of the + * line, start searching on the next line. This is incompatible (read + * bug fix) with the historic vi -- searches for the '$' pattern never + * moved forward, and "-t foo" didn't work if "foo" was the first thing + * in the file. + */ + if (LF_ISSET(SEARCH_FILE)) { + lno = 1; + coff = 0; + } else { + if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + if (fm->cno + 1 >= len) { + if (fm->lno == lno) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + return (1); + } + lno = 1; + } else + lno = fm->lno + 1; + coff = 0; + } else { + lno = fm->lno; + coff = fm->cno + 1; + } + } + + /* + * Set up busy message, interrupts. + * + * F_search is called from the ex_tagfirst() routine, which runs + * before the screen really exists. Make sure we don't step on + * anything. + */ + if (sp->s_position != NULL) + busy_on(sp, 1, "Searching..."); + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0;; ++lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno > fm->lno || + (l = file_gline(sp, ep, lno, &len)) == NULL) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + break; + } + lno = 0; + wrapped = 1; + continue; + } + + /* If already at EOL, just keep going. */ + if (len && coff == len) + continue; + + /* Set the termination. */ + match[0].rm_so = coff; + match[0].rm_eo = len; + +#if defined(DEBUG) && 0 + TRACE(sp, "F search: %lu from %u to %u\n", + lno, coff, len ? len - 1 : len); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + /* + * If an offset, see if it's legal. It's possible to match + * past the end of the line with $, so check for that case. + */ + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + rm->lno = lno; + rm->cno = match[0].rm_so; + + /* + * If a change command, it's possible to move beyond + * the end of a line. Historic vi generally got this + * wrong (try "c?$<cr>"). Not all that sure this gets + * it right, there are lots of strange cases. + */ + if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len) + rm->cno = len ? len - 1 : 0; + } + rval = 0; + break; + } + +interrupt_err: + + /* Turn off busy message, interrupts. */ + if (sp->s_position != NULL) + busy_off(sp); + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +int +b_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len, last; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* If in the first column, start searching on the previous line. */ + if (fm->cno == 0) { + if (fm->lno == 1) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + return (1); + } + } else + lno = fm->lno - 1; + } else + lno = fm->lno; + + /* Turn on busy message, interrupts. */ + busy_on(sp, 1, "Searching..."); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno < fm->lno || lno == 0) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + break; + } + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + break; + } + ++lno; + wrapped = 1; + continue; + } + + if ((l = file_gline(sp, ep, lno, &len)) == NULL) + goto err; + + /* Set the termination. */ + match[0].rm_so = 0; + match[0].rm_eo = coff ? coff : len; + +#if defined(DEBUG) && 0 + TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + /* + * Find the last acceptable one in this line. This + * is really painful, we need a cleaner interface to + * regexec to make this possible. + */ + for (;;) { + last = match[0].rm_so; + match[0].rm_so = match[0].rm_eo + 1; + if (match[0].rm_so >= len || + coff && match[0].rm_so >= coff) + break; + match[0].rm_eo = coff ? coff : len; + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | + REG_STARTEND); + if (eval == REG_NOMATCH) + break; + if (eval != 0) { + re_error(sp, eval, re); + goto err; + } + } + rm->lno = lno; + + /* See comment in f_search(). */ + if (!LF_ISSET(SEARCH_EOL) && last >= len) + rm->cno = len ? len - 1 : 0; + else + rm->cno = last; + } + rval = 0; + break; + } + + /* Turn off busy message, interrupts. */ +interrupt_err: +err: busy_off(sp); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +/* + * re_conv -- + * Convert vi's regular expressions into something that the + * the POSIX 1003.2 RE functions can handle. + * + * There are three conversions we make to make vi's RE's (specifically + * the global, search, and substitute patterns) work with POSIX RE's. + * + * 1: If O_MAGIC is not set, strip backslashes from the magic character + * set (.[]*~) that have them, and add them to the ones that don't. + * 2: If O_MAGIC is not set, the string "\~" is replaced with the text + * from the last substitute command's replacement string. If O_MAGIC + * is set, it's the string "~". + * 3: The pattern \<ptrn\> does "word" searches, convert it to use the + * new RE escapes. + */ +int +re_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, needlen; + int magic; + char *bp, *p, *t; + + /* + * First pass through, we figure out how much space we'll need. + * We do it in two passes, on the grounds that most of the time + * the user is doing a search and won't have magic characters. + * That way we can skip the malloc and memmove's. + */ + for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + magic = 1; + needlen += sizeof(RE_WSTART); + break; + case '>': + magic = 1; + needlen += sizeof(RE_WSTOP); + break; + case '~': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 1; + } + break; + default: + needlen += 2; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 2; + } + break; + default: + needlen += 1; + break; + } + + if (!magic) { + *replacedp = 0; + return (0); + } + + /* + * Get enough memory to hold the final pattern. + * + * XXX + * It's nul-terminated, for now. + */ + GET_SPACE_RET(sp, bp, blen, needlen + 1); + + for (p = *ptrnp, t = bp; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1); + t += sizeof(RE_WSTART) - 1; + break; + case '>': + memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1); + t += sizeof(RE_WSTOP) - 1; + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '~'; + else { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = '\\'; + *t++ = *p; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } else + *t++ = '~'; + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = *p; + break; + } + *t = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * get_delta -- + * Get a line delta. The trickiness is that the delta can be pretty + * complicated, i.e. "+3-2+3++- ++" is allowed. + * + * !!! + * In historic vi, if you had a delta on a search pattern which was used as + * a motion command, the command became a line mode command regardless of the + * cursor positions. A fairly common trick is to use a delta of "+0" to make + * the command a line mode command. This is the only place that knows about + * delta's, so we set the return flag information here. + */ +static int +get_delta(sp, dp, valp, flagp) + SCR *sp; + char **dp; + long *valp; + u_int *flagp; +{ + char *p; + long val, tval; + + for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) { + if (isblank(*p)) { + ++p; + continue; + } + if (*p == '+' || *p == '-') { + if (!isdigit(*(p + 1))) { + if (*p == '+') { + if (tval == LONG_MAX) + goto overflow; + ++tval; + } else { + if (tval == LONG_MIN) + goto underflow; + --tval; + } + ++p; + continue; + } + } else + if (!isdigit(*p)) + break; + + errno = 0; + val = strtol(p, &p, 10); + if (errno == ERANGE) { + if (val == LONG_MAX) +overflow: msgq(sp, M_ERR, "Delta value overflow."); + else if (val == LONG_MIN) +underflow: msgq(sp, M_ERR, "Delta value underflow."); + else + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (val >= 0) { + if (LONG_MAX - val < tval) + goto overflow; + } else + if (-(LONG_MIN - tval) > val) + goto underflow; + tval += val; + } + *dp = p; + *valp = tval; + return (0); +} + +/* + * check_delta -- + * Check a line delta to see if it's legal. + */ +static int +check_delta(sp, ep, delta, lno) + SCR *sp; + EXF *ep; + long delta; + recno_t lno; +{ + /* A delta can overflow a record number. */ + if (delta < 0) { + if (lno < LONG_MAX && delta >= (long)lno) { + msgq(sp, M_ERR, "Search offset before line 1."); + return (1); + } + } else { + if (ULONG_MAX - lno < delta) { + msgq(sp, M_ERR, "Delta value overflow."); + return (1); + } + if (file_gline(sp, ep, lno + delta, NULL) == NULL) { + msgq(sp, M_ERR, "Search offset past end-of-file."); + return (1); + } + } + return (0); +} + +/* + * re_error -- + * Report a regular expression error. + */ +void +re_error(sp, errcode, preg) + SCR *sp; + int errcode; + regex_t *preg; +{ + size_t s; + char *oe; + + s = regerror(errcode, preg, "", 0); + if ((oe = malloc(s)) == NULL) + msgq(sp, M_SYSERR, NULL); + else { + (void)regerror(errcode, preg, oe, s); + msgq(sp, M_ERR, "RE error: %s", oe); + } + free(oe); +} diff --git a/usr.bin/vi/search.h b/usr.bin/vi/search.h new file mode 100644 index 000000000000..00feff6027c8 --- /dev/null +++ b/usr.bin/vi/search.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)search.h 8.7 (Berkeley) 11/18/93 + */ + +#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */ +#define RE_WSTOP "[[:>:]]" + +#define SEARCH_DELTA 0x001 /* A delta part of the search.*/ +#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */ +#define SEARCH_FILE 0x004 /* Search the entire file. */ +#define SEARCH_MSG 0x008 /* Display search warning messages. */ +#define SEARCH_PARSE 0x010 /* Parse the search pattern. */ +#define SEARCH_SET 0x020 /* Set search direction. */ +#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */ +#define SEARCH_TERM 0x080 /* Search pattern should terminate. */ + +enum direction { NOTSET, FORWARD, BACKWARD }; +enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH }; + +/* Search functions. */ +int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int re_conv __P((SCR *, char **, int *)); +void re_error __P((SCR *, int, regex_t *)); diff --git a/usr.bin/vi/seq.c b/usr.bin/vi/seq.c new file mode 100644 index 000000000000..4d6d44956b0e --- /dev/null +++ b/usr.bin/vi/seq.c @@ -0,0 +1,299 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)seq.c 8.21 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * seq_set -- + * Internal version to enter a sequence. + */ +int +seq_set(sp, name, nlen, input, ilen, output, olen, stype, userdef) + SCR *sp; + char *name, *input, *output; + size_t nlen, ilen, olen; + enum seqtype stype; + int userdef; +{ + SEQ *lastqp, *qp; + CHAR_T *p; + +#if defined(DEBUG) && 0 + TRACE(sp, "seq_set: name {%s} input {%s} output {%s}\n", + name ? name : "", input, output); +#endif + /* Just replace the output field in any previous occurrence. */ + if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) { + if ((p = v_strdup(sp, output, olen)) == NULL) + goto mem1; + FREE(qp->output, qp->olen); + qp->olen = olen; + qp->output = p; + return (0); + } + + /* Allocate and initialize space. */ + CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ)); + if (qp == NULL) + goto mem1; + if (name == NULL) + qp->name = NULL; + else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) + goto mem2; + if ((qp->input = v_strdup(sp, input, ilen)) == NULL) + goto mem3; + if ((qp->output = v_strdup(sp, output, olen)) == NULL) { + FREE(qp->input, ilen); +mem3: if (qp->name != NULL) + FREE(qp->name, nlen); +mem2: FREE(qp, sizeof(SEQ)); +mem1: msgq(sp, M_SYSERR, NULL); + return (1); + } + + qp->stype = stype; + qp->nlen = nlen; + qp->ilen = ilen; + qp->olen = olen; + qp->flags = userdef ? S_USERDEF : 0; + + /* Link into the chain. */ + if (lastqp == NULL) { + LIST_INSERT_HEAD(&sp->gp->seqq, qp, q); + } else { + LIST_INSERT_AFTER(lastqp, qp, q); + } + + /* Set the fast lookup bit. */ + bit_set(sp->gp->seqb, qp->input[0]); + + return (0); +} + +/* + * seq_delete -- + * Delete a sequence. + */ +int +seq_delete(sp, input, ilen, stype) + SCR *sp; + char *input; + size_t ilen; + enum seqtype stype; +{ + SEQ *qp; + + if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL) + return (1); + + LIST_REMOVE(qp, q); + if (qp->name != NULL) + FREE(qp->name, qp->nlen); + FREE(qp->input, qp->ilen); + FREE(qp->output, qp->olen); + FREE(qp, sizeof(SEQ)); + return (0); +} + +/* + * seq_find -- + * Search the sequence list for a match to a buffer, if ispartial + * isn't NULL, partial matches count. + */ +SEQ * +seq_find(sp, lastqp, input, ilen, stype, ispartialp) + SCR *sp; + SEQ **lastqp; + char *input; + size_t ilen; + enum seqtype stype; + int *ispartialp; +{ + SEQ *lqp, *qp; + int diff; + + /* + * Ispartialp is a location where we return if there was a + * partial match, i.e. if the string were extended it might + * match something. + * + * XXX + * Overload the meaning of ispartialp; only the terminal key + * search doesn't want the search limited to complete matches, + * i.e. ilen may be longer than the match. + */ + if (ispartialp != NULL) + *ispartialp = 0; + for (lqp = NULL, qp = sp->gp->seqq.lh_first; + qp != NULL; lqp = qp, qp = qp->q.le_next) { + /* Fast checks on the first character and type. */ + if (qp->input[0] > input[0]) + break; + if (qp->input[0] < input[0] || qp->stype != stype) + continue; + + /* Check on the real comparison. */ + diff = memcmp(qp->input, input, MIN(qp->ilen, ilen)); + if (diff > 0) + break; + if (diff < 0) + continue; + /* + * If the entry is the same length as the string, return a + * match. If the entry is shorter than the string, return a + * match if called from the terminal key routine. Otherwise, + * keep searching for a complete match. + */ + if (qp->ilen <= ilen) { + if (qp->ilen == ilen || ispartialp != NULL) { + if (lastqp != NULL) + *lastqp = lqp; + return (qp); + } + continue; + } + /* + * If the entry longer than the string, return partial match + * if called from the terminal key routine. Otherwise, no + * match. + */ + if (ispartialp != NULL) + *ispartialp = 1; + break; + } + if (lastqp != NULL) + *lastqp = lqp; + return (NULL); +} + +/* + * seq_dump -- + * Display the sequence entries of a specified type. + */ +int +seq_dump(sp, stype, isname) + SCR *sp; + enum seqtype stype; + int isname; +{ + CHNAME const *cname; + SEQ *qp; + int cnt, len, olen, tablen; + char *p; + + cnt = 0; + cname = sp->gp->cname; + tablen = O_VAL(sp, O_TABSTOP); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (stype != qp->stype) + continue; + ++cnt; + for (p = qp->input, + olen = qp->ilen, len = 0; olen > 0; --olen, ++len) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + + for (p = qp->output, olen = qp->olen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + + if (isname && qp->name != NULL) { + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + for (p = qp->name, olen = qp->nlen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, + "%s", cname[*p++].name); + } + (void)ex_printf(EXCOOKIE, "\n"); + } + return (cnt); +} + +/* + * seq_save -- + * Save the sequence entries to a file. + */ +int +seq_save(sp, fp, prefix, stype) + SCR *sp; + FILE *fp; + char *prefix; + enum seqtype stype; +{ + CHAR_T esc; + SEQ *qp; + size_t olen; + int ch; + char *p; + + /* Write a sequence command for all keys the user defined. */ + (void)term_key_ch(sp, K_VLNEXT, &esc); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (!F_ISSET(qp, S_USERDEF)) + continue; + if (stype != qp->stype) + continue; + if (prefix) + (void)fprintf(fp, "%s", prefix); + for (p = qp->input, olen = qp->ilen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + isblank(ch) || term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc(' ', fp); + for (p = qp->output, olen = qp->olen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + } + return (0); +} diff --git a/usr.bin/vi/seq.h b/usr.bin/vi/seq.h new file mode 100644 index 000000000000..b9d9ac6cfd66 --- /dev/null +++ b/usr.bin/vi/seq.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)seq.h 8.7 (Berkeley) 12/2/93 + */ + +/* + * Map and abbreviation structures. + * + * The map structure is doubly linked list, sorted by input string and by + * input length within the string. (The latter is necessary so that short + * matches will happen before long matches when the list is searched.) + * Additionally, there is a bitmap which has bits set if there are entries + * starting with the corresponding character. This keeps us from walking + * the list unless it's necessary. + * + * XXX + * The fast-lookup bits are never turned off -- users don't usually unmap + * things, though, so it's probably not a big deal. + */ + /* Sequence type. */ +enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT }; + +struct _seq { + LIST_ENTRY(_seq) q; /* Linked list of all sequences. */ + enum seqtype stype; /* Sequence type. */ + char *name; /* Sequence name (if any). */ + size_t nlen; /* Name length. */ + char *input; /* Sequence input keys. */ + size_t ilen; /* Input keys length. */ + char *output; /* Sequence output keys. */ + size_t olen; /* Output keys length. */ + +#define S_USERDEF 0x01 /* If sequence user defined. */ + u_char flags; +}; + +int abbr_save __P((SCR *, FILE *)); +int map_save __P((SCR *, FILE *)); +int seq_delete __P((SCR *, char *, size_t, enum seqtype)); +int seq_dump __P((SCR *, enum seqtype, int)); +SEQ *seq_find __P((SCR *, SEQ **, char *, size_t, enum seqtype, int *)); +void seq_init __P((SCR *)); +int seq_save __P((SCR *, FILE *, char *, enum seqtype)); +int seq_set __P((SCR *, char *, size_t, + char *, size_t, char *, size_t, enum seqtype, int)); diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c new file mode 100644 index 000000000000..8b55fadedaae --- /dev/null +++ b/usr.bin/vi/sex/sex_confirm.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +enum confirm +sex_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + recno_t cnt; + + if (ex_print(sp, ep, fp, tp, 0)) + return (CONF_QUIT); + + for (cnt = fp->cno; cnt; --cnt) + (void)putc(' ', stdout); + for (cnt = tp->cno; cnt; --cnt) + (void)putc('^', stdout); + (void)fprintf(stdout, "[ynq]"); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c new file mode 100644 index 000000000000..54b7343608d1 --- /dev/null +++ b/usr.bin/vi/sex/sex_get.c @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_get.c 8.12 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <ctype.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +static void repaint __P((SCR *, int, char *, size_t)); + +#define DISPLAY(wval, ch, col) { \ + size_t __len; \ + int __ch; \ + if ((__ch = (ch)) == '\t') { \ + __len = O_VAL(sp, O_TABSTOP) - \ + ((col) % O_VAL(sp, O_TABSTOP)); \ + (col) += (wval) = __len; \ + while (__len--) \ + putc(' ', stdout); \ + } else { \ + (col) += (wval) = cname[(__ch)].len; \ + (void)fprintf(stdout, \ + "%.*s", cname[(__ch)].len, cname[(__ch)].name); \ + } \ +} + +#define ERASECH { \ + for (cnt = tp->wd[tp->len]; cnt > 0; --cnt, --col) \ + (void)fprintf(stdout, "%s", "\b \b"); \ +} + +/* + * sex_get -- + * Fill a buffer from the terminal for ex. + */ +enum input +sex_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + size_t cnt; + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + if (LF_ISSET(TXT_PROMPT) && O_ISSET(sp, O_PROMPT)) { + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + (void)fflush(stdout); + + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) { + ERASECH; + goto ins_ch; + } + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + repaint(sp, prompt, tp->lb, tp->len); + break; + case K_CR: + case K_NL: + if (LF_ISSET(TXT_NLECHO)) { + (void)putc('\r', stdout); + (void)putc('\n', stdout); + (void)fflush(stdout); + } + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) { + --tp->len; + ERASECH; + } + break; + case K_VKILL: + for (; tp->len; --tp->len) + ERASECH; + break; + case K_VLNEXT: + (void)fprintf(stdout, "%s%c", cname['^'].name, '\b'); + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + DISPLAY(tp->wd[tp->len], ikey.ch, col); + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * sex_get_notty -- + * Fill a buffer from the terminal for ex, but don't echo + * input. + */ +enum input +sex_get_notty(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) + goto ins_ch; + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + break; + case K_CR: + case K_NL: + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) + --tp->len; + break; + case K_VKILL: + tp->len = 0; + break; + case K_VLNEXT: + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * repaint -- + * Repaint the line. + */ +static void +repaint(sp, prompt, p, len) + SCR *sp; + int prompt; + char *p; + size_t len; +{ + CHNAME const *cname; + size_t col; + u_char width; + + cname = sp->gp->cname; + + (void)putc('\n', stdout); + if (prompt && O_ISSET(sp, O_PROMPT)) { /* Display prompt. */ + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + while (len--) + DISPLAY(width, *p++, col); +} diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c new file mode 100644 index 000000000000..0ba589299080 --- /dev/null +++ b/usr.bin/vi/sex/sex_refresh.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_refresh.c 8.5 (Berkeley) 11/18/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_refresh -- + * In ex, just display any messages. + */ +int +sex_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + MSG *mp; + + if (F_ISSET(sp, S_RESIZE)) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + F_CLR(sp, S_RESIZE); + } + + /* Ring the bell. */ + if (F_ISSET(sp, S_BELLSCHED)) { + sex_bell(sp); + F_CLR(sp, S_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) { + (void)fprintf(stdout, "%.*s\n", (int)mp->len, mp->mbuf); + F_SET(mp, M_EMPTY); + } + return (0); +} diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c new file mode 100644 index 000000000000..d087d9f17476 --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_screen.c 8.30 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" +#include "../svi/svi_screen.h" + +static void sex_abort __P((void)); +static int sex_noop __P((void)); +static int sex_nope __P((SCR *)); + +/* + * sex_screen_init -- + * Initialize the ex screen. + */ +int +sex_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = sex_bell; + sp->s_bg = (int (*)())sex_nope; + sp->s_busy = (int (*)())sex_busy; + sp->s_change = (int (*)())sex_noop; + sp->s_chposition = (size_t (*)())sex_abort; + sp->s_clear = (int (*)())sex_noop; + sp->s_column = (int (*)())sex_abort; + sp->s_confirm = sex_confirm; + sp->s_down = (int (*)())sex_abort; + sp->s_edit = sex_screen_edit; + sp->s_end = (int (*)())sex_noop; + sp->s_ex_cmd = (int (*)())sex_abort; + sp->s_ex_run = (int (*)())sex_abort; + sp->s_ex_write = (int (*)())sex_abort; + sp->s_fg = (int (*)())sex_nope; + sp->s_fill = (int (*)())sex_abort; + sp->s_get = F_ISSET(sp->gp, + G_ISFROMTTY) ? sex_get : sex_get_notty; + sp->s_key_read = sex_key_read; + sp->s_optchange = (int (*)())sex_noop; + sp->s_position = (int (*)())sex_abort; + sp->s_rabs = (int (*)())sex_nope; + sp->s_refresh = sex_refresh; + sp->s_relative = (size_t (*)())sex_abort; + sp->s_rrel = (int (*)())sex_nope; + sp->s_split = (int (*)())sex_nope; + sp->s_suspend = sex_suspend; + sp->s_up = (int (*)())sex_abort; + + return (0); +} + +/* + * sex_screen_copy -- + * Copy to a new screen. + */ +int +sex_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * sex_screen_end -- + * End a screen. + */ +int +sex_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * sex_screen_edit -- + * Main ex screen loop. The ex screen is relatively uncomplicated. + * As long as it has a stdio FILE pointer for output, it's happy. + */ +int +sex_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + struct termios rawt, t; + GS *saved_gp; + int force, rval; + + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SEX_RAW(t, rawt); + + /* Write to the terminal. */ + sp->stdfp = stdout; + + for (;;) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + + /* + * Run ex. If ex fails, sex data structures + * may be corrupted, be careful what you do. + */ + if (rval = ex(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); + (void)screen_end(sp); /* General SCR info. */ + break; + } + + saved_gp = sp->gp; + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force)) + break; + (void)screen_end(sp); /* General SCR info. */ + goto ret; + case 0: /* Changing from ex mode. */ + goto ret; + case S_FSWITCH: + F_CLR(sp, S_FSWITCH); + break; + case S_SSWITCH: + default: + abort(); + } + } + + /* Reset the terminal state. */ +ret: if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + return (rval); +} + +/* + * sex_abort -- + * Fake function. Die. + */ +static void +sex_abort() +{ + abort(); +} + +/* + * sex_noop -- + * Fake function. Do nothing. + */ +static int +sex_noop() +{ + return (0); +} + +/* + * sex_nope -- + * Fake function. Not in ex, you don't. + */ +static int +sex_nope(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "Command not applicable to ex mode."); + return (1); +} diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h new file mode 100644 index 000000000000..419a2f274ed1 --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sex_screen.h 8.12 (Berkeley) 11/29/93 + */ + +#define SEX_NORAW(t) \ + tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t)) + +#define SEX_RAW(t, rawt) { \ + if (tcgetattr(STDIN_FILENO, &(t))) \ + return (1); \ + (rawt) = (t); \ + (rawt).c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL); \ + (rawt).c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \ + (rawt).c_cc[VMIN] = 1; \ + (rawt).c_cc[VTIME] = 0; \ + if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(rawt))) \ + return (1); \ +} + +void sex_bell __P((SCR *)); +void sex_busy __P((SCR *, char const *)); +enum confirm + sex_confirm __P((SCR *, EXF *, MARK *, MARK *)); +enum input + sex_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_get_notty __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_key_read __P((SCR *, int *, struct timeval *)); +int sex_refresh __P((SCR *, EXF *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_edit __P((SCR *, EXF *)); +int sex_screen_end __P((SCR *)); +int sex_split __P((SCR *, char *[])); +int sex_suspend __P((SCR *)); diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c new file mode 100644 index 000000000000..4e3980f21efd --- /dev/null +++ b/usr.bin/vi/sex/sex_term.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_term.c 8.24 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "script.h" + +/* + * sex_key_read -- + * Read characters from the input. + */ +enum input +sex_key_read(sp, nrp, timeout) + SCR *sp; + int *nrp; + struct timeval *timeout; +{ + struct timeval t, *tp; + IBUF *tty; + SCR *tsp; + int maxfd, nr; + + *nrp = 0; + tty = sp->gp->tty; + + /* + * We're about to block; check for signals. If a signal received, + * clear it immediately, so that if it's reset while being serviced + * we won't miss it. + * + * Signal recipients set global flags. If one is set, we either + * set local flags or call handling routines. None of this has + * anything to do with input keys, but it's something that can't + * be done asynchronously without doing a lot of locking to handle + * race conditions, and which needs to be done periodically. + */ +sigchk: while (F_ISSET(sp->gp, + G_SIGALRM | G_SIGHUP | G_SIGTERM | G_SIGWINCH)) { + if (F_ISSET(sp->gp, G_SIGALRM)) { + F_CLR(sp->gp, G_SIGALRM); + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp->ep != NULL && + F_ISSET(tsp->ep, F_RCV_ON)) + F_SET(tsp->ep, F_RCV_ALRM); + } + if (F_ISSET(sp->ep, F_RCV_ALRM)) { + F_CLR(sp->ep, F_RCV_ALRM); + (void)rcv_sync(sp, sp->ep); + } + if (F_ISSET(sp->gp, G_SIGHUP)) { + F_CLR(sp->gp, G_SIGHUP); + rcv_hup(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGTERM)) { + F_CLR(sp->gp, G_SIGTERM); + rcv_term(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGWINCH)) { + F_CLR(sp->gp, G_SIGWINCH); + set_window_size(sp, 0, 1); + F_SET(sp, S_RESIZE); + (void)sp->s_refresh(sp, sp->ep); + } + } + + /* + * There are three cases here: + * + * 1: A read from a file or a pipe. In this case, the reads + * never timeout regardless. This means that we can hang + * when trying to complete a map, but we're going to hang + * on the next read anyway. + */ + if (!F_ISSET(sp->gp, G_ISFROMTTY)) { + if ((nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) > 0) { + tty->cnt += *nrp = nr; + return (INP_OK); + } + return (INP_EOF); + } + + /* + * 2: A read with an associated timeout. In this case, we are trying + * to complete a map sequence. Ignore script windows and timeout + * as specified. If input arrives, we fall into #3, but because + * timeout isn't NULL, don't read anything but command input. + * + * If interrupted, go back and check to see what it was. + */ + if (timeout != NULL) { + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + FD_SET(STDIN_FILENO, &sp->rdfd); + for (;;) { + switch (select(STDIN_FILENO + 1, + &sp->rdfd, NULL, NULL, timeout)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + goto err; + case 1: /* Characters ready. */ + break; + case 0: /* Timeout. */ + return (INP_OK); + } + break; + } + } + + /* + * 3: At this point, we'll take anything that comes. Select on the + * command file descriptor and the file descriptor for the script + * window if there is one. Poll the fd's, increasing the timeout + * each time each time we don't get anything until we're blocked + * on I/O. + * + * If interrupted, go back and check to see what it was. + */ + for (t.tv_sec = t.tv_usec = 0;;) { + /* + * Reset each time -- sscr_input() may call other + * routines which could reset bits. + */ + if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) { + tp = &t; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) { + FD_SET(sp->script->sh_master, &sp->rdfd); + maxfd = + MAX(STDIN_FILENO, sp->script->sh_master); + } else + maxfd = STDIN_FILENO; + } else { + tp = NULL; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + maxfd = STDIN_FILENO; + } + + switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; +err: msgq(sp, M_SYSERR, "select"); + return (INP_ERR); + case 0: /* Timeout. */ + if (t.tv_usec) { + ++t.tv_sec; + t.tv_usec = 0; + } else + t.tv_usec += 500000; + continue; + } + + if (timeout == NULL && F_ISSET(sp, S_SCRIPT) && + FD_ISSET(sp->script->sh_master, &sp->rdfd)) { + sscr_input(sp); + continue; + } + + switch (nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) { + case 0: /* EOF. */ + return (INP_EOF); + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + msgq(sp, M_SYSERR, "read"); + return (INP_ERR); + default: + tty->cnt += *nrp = nr; + return (INP_OK); + } + /* NOTREACHED */ + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c new file mode 100644 index 000000000000..25eb17f049c3 --- /dev/null +++ b/usr.bin/vi/sex/sex_util.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_util.c 8.9 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_bell -- + * Ring the bell. + */ +void +sex_bell(sp) + SCR *sp; +{ + (void)write(STDOUT_FILENO, "\07", 1); /* \a */ +} + +void +sex_busy(sp, msg) + SCR *sp; + char const *msg; +{ + (void)fprintf(stdout, "%s\n", msg); + (void)fflush(stdout); +} + +/* + * sex_suspend -- + * Suspend an ex screen. + */ +int +sex_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* Save ex/vi terminal settings, and restore the original ones. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSADRAIN, &sp->gp->original_termios); + } + + /* Kill the process group. */ + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(0, SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + /* Restore ex/vi terminal settings. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &t); + + return (rval); +} diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c new file mode 100644 index 000000000000..01546d7d7716 --- /dev/null +++ b/usr.bin/vi/svi/svi_confirm.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> + +#include "vi.h" +#include "svi_screen.h" + +enum confirm +svi_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + size_t oldy, oldx; + + /* + * Refresh the cursor first -- this means that we won't have to + * set S_UPDATE_MODE to keep refresh from erasing the mode line + * or SVI_CUR_INVALID because we sneaked the cursor off somewhere + * else. + */ + sp->lno = fp->lno; + sp->cno = fp->cno; + if (svi_paint(sp, ep)) + return (CONF_QUIT); + + getyx(stdscr, oldy, oldx); + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + ADDNSTR(CONFSTRING, sizeof(CONFSTRING) - 1); + MOVEA(sp, oldy, oldx); + refresh(); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c new file mode 100644 index 000000000000..7a83d409b3cd --- /dev/null +++ b/usr.bin/vi/svi/svi_ex.c @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_ex.c 8.36 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "../sex/sex_screen.h" + +static int svi_ex_done __P((SCR *, EXF *, MARK *)); +static int svi_ex_scroll __P((SCR *, int, int, CH *)); + +/* + * svi_ex_cmd -- + * Execute an ex command. + */ +int +svi_ex_cmd(sp, ep, exp, rp) + SCR *sp; + EXF *ep; + EXCMDARG *exp; + MARK *rp; +{ + SVI_PRIVATE *svp; + int rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + (void)svi_busy(sp, NULL); + rval = exp->cmd->fn(sp, ep, exp); + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + (void)msg_rpt(sp, 0); + (void)ex_fflush(EXCOOKIE); + + /* + * If displayed anything, figure out if we have to wait. If the + * screen wasn't trashed, only one line and there are no waiting + * messages, don't wait, but don't overwrite it with mode information + * either. If there's a screen under this one, change the line to + * inverse video. + */ + if (svp->extotalcount > 0) + if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY))) + F_SET(sp, S_UPDATE_MODE); + else + (void)svi_ex_scroll(sp, 1, 0, NULL); + return (svi_ex_done(sp, ep, rp) || rval); +} + +/* + * svi_ex_run -- + * Execute strings of ex commands. + */ +int +svi_ex_run(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + enum input (*get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + struct termios rawt, t; + CH ikey; + SVI_PRIVATE *svp; + TEXT *tp; + int flags, in_exmode, rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + /* + * There's some tricky stuff going on here to handle when a user has + * mapped a key to multiple ex commands. Historic practice was that + * vi ran without any special actions, as if the user were entering + * the characters, until ex trashed the screen, e.g. something like a + * '!' command. At that point, we no longer know what the screen + * looks like, so we can't afford to overwrite anything. The solution + * is to go into real ex mode until we get to the end of the command + * strings. + */ + get = svi_get; + flags = TXT_BS | TXT_PROMPT; + for (in_exmode = rval = 0;;) { + if (get(sp, ep, &sp->tiq, ':', flags) != INP_OK) { + rval = 1; + break; + } + + /* + * Len is 0 if the user backspaced over the prompt, + * 1 if only a CR was entered. + */ + tp = sp->tiq.cqh_first; + if (tp->len == 0) + break; + + if (!in_exmode) + (void)svi_busy(sp, NULL); + + (void)ex_icmd(sp, ep, tp->lb, tp->len); + (void)ex_fflush(EXCOOKIE); + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + break; + + /* + * If continue not required, and one or no lines, and there + * are no waiting messages, don't wait, but don't overwrite + * it with mode information either. If there's a screen under + * this one, change the line to inverse video. + */ + if (!F_ISSET(sp, S_CONTINUE) && + (svp->extotalcount == 0 || svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY)))) { + if (svp->extotalcount == 1) + F_SET(sp, S_UPDATE_MODE); + break; + } + + /* If the screen is trashed, go into ex mode. */ + if (!in_exmode && F_ISSET(sp, S_REFRESH)) { + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + SEX_RAW(t, rawt); + get = sex_get; + } else + get = sex_get_notty; + flags = TXT_CR | TXT_NLECHO | TXT_PROMPT; + in_exmode = 1; + } + + /* + * If the user hasn't already indicated that they're done, + * they may continue in ex mode by entering a ':'. + */ + if (F_ISSET(sp, S_INTERRUPTED)) + break; + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + if (in_exmode) { + (void)write(STDOUT_FILENO, + CONTMSG, sizeof(CONTMSG) - 1); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) { + rval = 1; + goto ret; + } + if (ikey.ch == ' ' || ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + sex_bell(sp); + } + } else + (void)svi_ex_scroll(sp, 1, 1, &ikey); + if (ikey.ch != ':') + break; + + if (in_exmode) + (void)write(STDOUT_FILENO, "\r\n", 2); + else { + ++svp->extotalcount; + ++svp->exlinecount; + } + } + +ret: if (in_exmode) { + /* Reset the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + F_SET(sp, S_REFRESH); + } else + if (svi_ex_done(sp, ep, rp)) + rval = 1; + F_CLR(sp, S_CONTINUE); + return (rval); +} + +/* + * svi_ex_done -- + * Cleanup from dipping into ex. + */ +static int +svi_ex_done(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + SMAP *smp; + SVI_PRIVATE *svp; + recno_t lno; + size_t cnt, len; + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + return (0); + + /* + * Otherwise, the only cursor modifications will be real, however, the + * underlying line may have changed; don't trust anything. This code + * has been a remarkably fertile place for bugs. + * + * Repaint the entire screen if at least half the screen is trashed. + * Else, repaint only over the overwritten lines. The "-2" comes + * from one for the mode line and one for the fact that it's an offset. + * Note the check for small screens. + * + * Don't trust ANYTHING. + */ + svp = SVP(sp); + if (svp->extotalcount >= HALFTEXT(sp)) + F_SET(sp, S_REDRAW); + else + for (cnt = sp->rows - 2; svp->extotalcount--; --cnt) + if (cnt > sp->t_rows) { + MOVE(sp, cnt, 0); + clrtoeol(); + } else { + smp = HMAP + cnt; + SMAP_FLUSH(smp); + if (svi_line(sp, ep, smp, NULL, NULL)) + return (1); + } + /* + * Do a reality check on a cursor value, and make sure it's okay. + * If necessary, change it. Ex keeps track of the line number, + * but ex doesn't care about the column and it may have disappeared. + */ + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + GETLINE_ERR(sp, sp->lno); + sp->lno = 1; + sp->cno = 0; + } else if (sp->cno >= len) + sp->cno = len ? len - 1 : 0; + + rp->lno = sp->lno; + rp->cno = sp->cno; + return (0); +} + +/* + * svi_ex_write -- + * Write out the ex messages. + * + * There is no tab or character translation going on, so, whatever the ex + * and/or curses routines do with special characters is all that gets done. + * This is probably okay, I don't see any reason that user's tab settings + * should affect ex output, and ex should have displayed everything else + * exactly as it wanted it on the screen. + */ +int +svi_ex_write(cookie, line, llen) + void *cookie; + const char *line; + int llen; +{ + SCR *sp; + SVI_PRIVATE *svp; + size_t oldy, oldx; + int len, rlen; + const char *p; + + /* + * XXX + * If it's a 4.4BSD system, we could just use fpurge(3). + * This shouldn't be too expensive, though. + */ + sp = cookie; + svp = SVP(sp); + if (F_ISSET(sp, S_INTERRUPTED)) + return (llen); + + p = line; /* In case of a write of 0. */ + for (rlen = llen; llen;) { + /* Get the next line. */ + if ((p = memchr(line, '\n', llen)) == NULL) + len = llen; + else + len = p - line; + + /* + * The max is sp->cols characters, and we may + * have already written part of the line. + */ + if (len + svp->exlcontinue > sp->cols) + len = sp->cols - svp->exlcontinue; + + /* + * If the first line output, do nothing. + * If the second line output, draw the divider line. + * If drew a full screen, remove the divider line. + * If it's a continuation line, move to the continuation + * point, else, move the screen up. + */ + if (svp->exlcontinue == 0) { + if (svp->extotalcount == 1) { + MOVE(sp, INFOLINE(sp) - 1, 0); + clrtoeol(); + if (svi_divider(sp)) + return (-1); + F_SET(svp, SVI_DIVIDER); + ++svp->extotalcount; + ++svp->exlinecount; + } + if (svp->extotalcount == sp->t_maxrows && + F_ISSET(svp, SVI_DIVIDER)) { + --svp->extotalcount; + --svp->exlinecount; + F_CLR(svp, SVI_DIVIDER); + } + if (svp->extotalcount != 0 && + svi_ex_scroll(sp, 0, 0, NULL)) + return (-1); + MOVE(sp, INFOLINE(sp), 0); + ++svp->extotalcount; + ++svp->exlinecount; + if (F_ISSET(sp, S_INTERRUPTIBLE) && + F_ISSET(sp, S_INTERRUPTED)) + break; + } else + MOVE(sp, INFOLINE(sp), svp->exlcontinue); + + /* Display the line. */ + if (len) + ADDNSTR(line, len); + + /* Clear to EOL. */ + getyx(stdscr, oldy, oldx); + if (oldx < sp->cols) + clrtoeol(); + + /* If we loop, it's a new line. */ + svp->exlcontinue = 0; + + /* Reset for the next line. */ + line += len; + llen -= len; + if (p != NULL) { + ++line; + --llen; + } + } + /* Refresh the screen, even if it's a partial. */ + refresh(); + + /* Set up next continuation line. */ + if (p == NULL) + getyx(stdscr, oldy, svp->exlcontinue); + return (rlen); +} + +/* + * svi_ex_scroll -- + * Scroll the screen for ex output. + */ +static int +svi_ex_scroll(sp, mustwait, colon_ok, chp) + SCR *sp; + int mustwait, colon_ok; + CH *chp; +{ + CH ikey; + SVI_PRIVATE *svp; + + /* + * Scroll the screen. Instead of scrolling the entire screen, delete + * the line above the first line output so preserve the maximum amount + * of the screen. + */ + svp = SVP(sp); + if (svp->extotalcount >= sp->rows) { + MOVE(sp, 0, 0); + } else + MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0); + + deleteln(); + + /* If there are screens below us, push them back into place. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) { + MOVE(sp, INFOLINE(sp), 0); + insertln(); + } + + /* If just displayed a full screen, wait. */ + if (mustwait || svp->exlinecount == sp->t_maxrows) { + MOVE(sp, INFOLINE(sp), 0); + if (F_ISSET(sp, S_INTERRUPTIBLE)) { + ADDNSTR(CONTMSG_I, (int)sizeof(CONTMSG_I) - 1); + } else { + ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1); + } + clrtoeol(); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + return (-1); + if (ikey.ch == ' ') + break; + if (colon_ok && ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + if (ikey.ch == QUIT_CH && + F_ISSET(sp, S_INTERRUPTIBLE)) { + F_SET(sp, S_INTERRUPTED); + break; + } + svi_bell(sp); + } + if (chp != NULL) + *chp = ikey; + svp->exlinecount = 0; + } + return (0); +} diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c new file mode 100644 index 000000000000..46f5f0e240cb --- /dev/null +++ b/usr.bin/vi/svi/svi_get.c @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_get.c 8.19 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +/* + * svi_get -- + * Fill a buffer from the terminal for vi. + */ +enum input +svi_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + MARK save; + SMAP *esmp; + recno_t bot_lno, top_lno; + size_t bot_off, cnt, top_off; + int eval; + + /* + * The approach used is to fake like the user is doing input on + * the last line of the screen. This makes all of the scrolling + * work correctly, and allows us the use of the vi text editing + * routines, not to mention practically infinite length ex commands. + * + * Save the current location. + */ + bot_lno = TMAP->lno; + bot_off = TMAP->off; + top_lno = HMAP->lno; + top_off = HMAP->off; + save.lno = sp->lno; + save.cno = sp->cno; + + /* + * If it's a small screen, TMAP may be small for the screen. + * Fix it, filling in fake lines as we go. + */ + if (ISSMALLSCREEN(sp)) + for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + } + + /* Build the fake entry. */ + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + SMAP_FLUSH(&TMAP[1]); + ++TMAP; + + /* Move to it. */ + sp->lno = TMAP[0].lno; + sp->cno = 0; + + if (O_ISSET(sp, O_ALTWERASE)) + LF_SET(TXT_ALTWERASE); + if (O_ISSET(sp, O_TTYWERASE)) + LF_SET(TXT_TTYWERASE); + LF_SET(TXT_APPENDEOL | + TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); + + /* Don't update the modeline for now. */ + F_SET(SVP(sp), SVI_INFOLINE); + + eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags); + + F_CLR(SVP(sp), SVI_INFOLINE); + + /* Put it all back. */ + --TMAP; + sp->lno = save.lno; + sp->cno = save.cno; + + /* + * If it's a small screen, TMAP may be wrong. Clear any + * lines that might have been overwritten. + */ + if (ISSMALLSCREEN(sp)) { + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } + + /* + * The map may be wrong if the user entered more than one + * (logical) line. Fix it. If the user entered a whole + * screen, this will be slow, but it's not worth caring. + */ + while (bot_lno != TMAP->lno || bot_off != TMAP->off) + if (svi_sm_1down(sp, ep)) + return (INP_ERR); + + /* + * Invalidate the cursor, the line never really existed. This fixes + * a bug where the user searches for the last line on the screen + 1 + * and the refresh routine thinks that's where we just were. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + return (eval ? INP_ERR : INP_OK); +} diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c new file mode 100644 index 000000000000..2370a0f5eea8 --- /dev/null +++ b/usr.bin/vi/svi/svi_line.c @@ -0,0 +1,421 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_line.c 8.18 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +#if defined(DEBUG) && 0 +#define TABCH '-' +#define TABSTR "--------------------" +#else +#define TABSTR " " +#define TABCH ' ' +#endif + +/* + * svi_line -- + * Update one line on the screen. + */ +int +svi_line(sp, ep, smp, yp, xp) + SCR *sp; + EXF *ep; + SMAP *smp; + size_t *xp, *yp; +{ + CHNAME const *cname; + SMAP *tsmp; + size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens; + size_t offset_in_char, offset_in_line; + size_t oldy, oldx; + int ch, is_cached, is_infoline, is_partial, is_tab, listset; + char *p, nbuf[10]; + +#if defined(DEBUG) && 0 + TRACE(sp, "svi_line: row %u: line: %u off: %u\n", + smp - HMAP, smp->lno, smp->off); +#endif + + /* + * Assume that, if the cache entry for the line is filled in, the + * line is already on the screen, and all we need to do is return + * the cursor position. If the calling routine doesn't need the + * cursor position, we can just return. + */ + is_cached = SMAP_CACHE(smp); + if (yp == NULL && is_cached) + return (0); + + /* + * A nasty side effect of this routine is that it returns the screen + * position for the "current" character. Not pretty, but this is the + * only routine that really knows what's out there. + * + * Move to the line. This routine can be called by svi_sm_position(), + * which uses it to fill in the cache entry so it can figure out what + * the real contents of the screen are. Because of this, we have to + * return to whereever we started from. + */ + getyx(stdscr, oldy, oldx); + MOVE(sp, smp - HMAP, 0); + + /* Get the character map. */ + cname = sp->gp->cname; + + /* Get a copy of the line. */ + p = file_gline(sp, ep, smp->lno, &len); + + /* + * Special case if we're printing the info/mode line. Skip printing + * the leading number, as well as other minor setup. If painting the + * line between two screens, it's always in reverse video. The only + * time this code paints the mode line is when the user is entering + * text for a ":" command, so we can put the code here instead of + * dealing with the empty line logic below. This is a kludge, but it's + * pretty much confined to this module. + * + * Set the number of screens to skip until a character is displayed. + * Left-right screens are special, because we don't bother building + * a buffer to be skipped over. + * + * Set the number of columns for this screen. + */ + cols_per_screen = sp->cols; + if (is_infoline = ISINFOLINE(sp, smp)) { + listset = 0; + if (O_ISSET(sp, O_LEFTRIGHT)) + skip_screens = 0; + else + skip_screens = smp->off - 1; + } else { + listset = O_ISSET(sp, O_LIST); + skip_screens = smp->off - 1; + + /* + * If O_NUMBER is set and it's line number 1 or the line exists + * and this is the first screen of a folding line or any left- + * right line, display the line number. + */ + if (O_ISSET(sp, O_NUMBER)) { + cols_per_screen -= O_NUMBER_LENGTH; + if ((smp->lno == 1 || p != NULL) && skip_screens == 0) { + (void)snprintf(nbuf, + sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + } + } + + /* + * Special case non-existent lines and the first line of an empty + * file. In both cases, the cursor position is 0, but corrected + * for the O_NUMBER field if it was displayed. + */ + if (p == NULL || len == 0) { + /* Fill in the cursor. */ + if (yp != NULL && smp->lno == sp->lno) { + *yp = smp - HMAP; + *xp = sp->cols - cols_per_screen; + } + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + + /* Set line cacheing information. */ + smp->c_sboff = smp->c_eboff = 0; + smp->c_scoff = smp->c_eclen = 0; + + if (p == NULL) { + if (smp->lno != 1) + ADDCH(listset && skip_screens == 0 ? '$' : '~'); + } else if (listset && skip_screens == 0) + ADDCH('$'); + + clrtoeol(); + MOVEA(sp, oldy, oldx); + return (0); + } + + /* + * If we wrote a line that's this or a previous one, we can do this + * much more quickly -- we cached the starting and ending positions + * of that line. The way it works is we keep information about the + * lines displayed in the SMAP. If we're painting the screen in + * the forward, this saves us from reformatting the physical line for + * every line on the screen. This wins big on binary files with 10K + * lines. + * + * Test for the first screen of the line, then the current screen line, + * then the line behind us, then do the hard work. Note, it doesn't + * do us any good to have a line in front of us -- it would be really + * hard to try and figure out tabs in the reverse direction, i.e. how + * many spaces a tab takes up in the reverse direction depends on + * what characters preceded it. + */ + if (smp->off == 1) { + smp->c_sboff = offset_in_line = 0; + smp->c_scoff = offset_in_char = 0; + p = &p[offset_in_line]; + } else if (is_cached) { + offset_in_line = smp->c_sboff; + offset_in_char = smp->c_scoff; + p = &p[offset_in_line]; + if (skip_screens != 0) + cols_per_screen = sp->cols; + } else if (smp != HMAP && + SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) { + if (tsmp->c_eclen != tsmp->c_ecsize) { + offset_in_line = tsmp->c_eboff; + offset_in_char = tsmp->c_eclen; + } else { + offset_in_line = tsmp->c_eboff + 1; + offset_in_char = 0; + } + + /* Put starting info for this line in the cache. */ + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + p = &p[offset_in_line]; + if (skip_screens != 0) + cols_per_screen = sp->cols; + } else { + offset_in_line = 0; + offset_in_char = 0; + + /* This is the loop that skips through screens. */ + if (skip_screens == 0) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + } else for (scno = 0; offset_in_line < len; ++offset_in_line) { + scno += chlen = + (ch = *(u_char *)p++) == '\t' && !listset ? + TAB_OFF(sp, scno) : cname[ch].len; + if (scno < cols_per_screen) + continue; + /* + * Reset cols_per_screen to second and subsequent line + * length. + */ + scno -= cols_per_screen; + cols_per_screen = sp->cols; + + /* + * If crossed the last skipped screen boundary, start + * displaying the characters. + */ + if (--skip_screens) + continue; + + /* Put starting info for this line in the cache. */ + if (scno) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char = chlen - scno; + --p; + } else { + smp->c_sboff = ++offset_in_line; + smp->c_scoff = 0; + } + break; + } + } + + /* + * Set the number of characters to skip before reaching the cursor + * character. Offset by 1 and use 0 as a flag value. Svi_line is + * called repeatedly with a valid pointer to a cursor position. + * Don't fill anything in unless it's the right line and the right + * character, and the right part of the character... + */ + if (yp == NULL || + smp->lno != sp->lno || sp->cno < offset_in_line || + offset_in_line + cols_per_screen < sp->cno) { + cno_cnt = 0; + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } else + cno_cnt = (sp->cno - offset_in_line) + 1; + + /* This is the loop that actually displays characters. */ + for (is_partial = 0, scno = 0; + offset_in_line < len; ++offset_in_line, offset_in_char = 0) { + if ((ch = *(u_char *)p++) == '\t' && !listset) { + scno += chlen = TAB_OFF(sp, scno) - offset_in_char; + is_tab = 1; + } else { + scno += chlen = cname[ch].len - offset_in_char; + is_tab = 0; + } + + /* + * Only display up to the right-hand column. Set a flag if + * the entire character wasn't displayed for use in setting + * the cursor. If reached the end of the line, set the cache + * info for the screen. Don't worry about there not being + * characters to display on the next screen, its lno/off won't + * match up in that case. + */ + if (scno >= cols_per_screen) { + smp->c_ecsize = chlen; + chlen -= scno - cols_per_screen; + smp->c_eclen = chlen; + smp->c_eboff = offset_in_line; + if (scno > cols_per_screen) + is_partial = 1; + + /* Terminate the loop. */ + offset_in_line = len; + } + + /* + * If the caller wants the cursor value, and this was the + * cursor character, set the value. There are two ways to + * put the cursor on a character -- if it's normal display + * mode, it goes on the last column of the character. If + * it's input mode, it goes on the first. In normal mode, + * set the cursor only if the entire character was displayed. + */ + if (cno_cnt && + --cno_cnt == 0 && (F_ISSET(sp, S_INPUT) || !is_partial)) { + *yp = smp - HMAP; + if (F_ISSET(sp, S_INPUT)) + *xp = scno - chlen; + else + *xp = scno - 1; + if (O_ISSET(sp, O_NUMBER) && + !is_infoline && smp->off == 1) + *xp += O_NUMBER_LENGTH; + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } + + /* If the line is on the screen, don't display anything. */ + if (is_cached) + continue; + + /* + * Display the character. If it's a tab and tabs aren't some + * ridiculous length, do it fast. (We do tab expansion here + * because curses doesn't have a way to set the tab length.) + */ + if (is_tab) { + if (chlen <= sizeof(TABSTR) - 1) { + ADDNSTR(TABSTR, chlen); + } else + while (chlen--) + ADDCH(TABCH); + } else + ADDNSTR(cname[ch].name + offset_in_char, chlen); + } + + if (scno < cols_per_screen) { + /* If didn't paint the whole line, update the cache. */ + smp->c_ecsize = smp->c_eclen = cname[ch].len; + smp->c_eboff = len - 1; + + /* + * If not the info/mode line, and O_LIST set, and at the + * end of the line, and the line ended on this screen, + * add a trailing $. + */ + if (listset) { + ++scno; + ADDCH('$'); + } + + /* If still didn't paint the whole line, clear the rest. */ + if (scno < cols_per_screen) + clrtoeol(); + } + +ret: MOVEA(sp, oldy, oldx); + return (0); +} + +/* + * svi_number -- + * Repaint the numbers on all the lines. + */ +int +svi_number(sp, ep) + SCR *sp; + EXF *ep; +{ + SMAP *smp; + recno_t lno; + size_t oldy, oldx; + char *lp, *p, nbuf[10]; + + /* + * Try and avoid getting the last line in the file, by getting the + * line after the last line in the screen -- if it exists, we know + * we have to to number all the lines in the screen. Get the one + * after the last instead of the last, so that the info line doesn't + * fool us. + * + * If that test fails, we have to check each line for existence. + * + * XXX + * The problem is that file_lline will lie, and tell us that the + * info line is the last line in the file. + */ + lp = file_gline(sp, ep, TMAP->lno + 1, NULL); + + getyx(stdscr, oldy, oldx); + for (smp = HMAP; smp <= TMAP; ++smp) { + if (smp->off != 1) + continue; + if (ISINFOLINE(sp, smp)) + break; + if (smp->lno != 1 && lp == NULL && + (p = file_gline(sp, ep, smp->lno, NULL)) == NULL) + break; + MOVE(sp, smp - HMAP, 0); + (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + MOVEA(sp, oldy, oldx); + return (0); +} diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c new file mode 100644 index 000000000000..353c08178e20 --- /dev/null +++ b/usr.bin/vi/svi/svi_refresh.c @@ -0,0 +1,839 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_refresh.c 8.43 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static int svi_modeline __P((SCR *, EXF *)); +static int svi_msgflush __P((SCR *)); + +int +svi_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + u_int paintbits; + + /* + * 1: Resize the screen. + * + * Notice that a resize is requested, and set up everything so that + * the file gets reinitialized. Done here, instead of in the vi loop + * because there may be other initialization that other screens need + * to do. The actual changing of the row/column values was done by + * calling the ex options code which put them into the environment, + * which is used by curses. Stupid, but ugly. + */ + if (F_ISSET(sp, S_RESIZE)) { + /* Reinitialize curses. */ + if (svi_curses_end(sp) || svi_curses_init(sp)) + return (1); + + /* Lose any svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* + * Fill the map, incidentally losing any svi_line() + * cached information. + */ + if (sp->s_fill(sp, ep, sp->lno, P_FILL)) + return (1); + F_CLR(sp, S_RESIZE | S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: S_REFRESH + * + * If S_REFRESH is set in the current screen, repaint everything + * that we can find. + */ + if (F_ISSET(sp, S_REFRESH)) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp) + F_SET(tsp, S_REDRAW); + /* + * 3: Related or dirtied screens, or screens with messages. + * + * If related screens share a view into a file, they may have been + * modified as well. Refresh any screens with paint or dirty bits + * set, or where messages are waiting. Finally, if we refresh any + * screens other than the current one, the cursor will be trashed. + */ + paintbits = S_REDRAW | S_REFORMAT | S_REFRESH; + if (O_ISSET(sp, O_NUMBER)) + paintbits |= S_RENUMBER; + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp && + (F_ISSET(tsp, paintbits) || + F_ISSET(SVP(tsp), SVI_SCREENDIRTY) || + tsp->msgq.lh_first != NULL && + !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) { + (void)svi_paint(tsp, tsp->ep); + F_CLR(SVP(tsp), SVI_SCREENDIRTY); + F_SET(SVP(sp), SVI_CUR_INVALID); + } + + /* + * 4: Refresh the current screen. + * + * Always refresh the current screen, it may be a cursor movement. + * Also, always do it last -- that way, S_REFRESH can be set in + * the current screen only, and the screen won't flash. + */ + F_CLR(sp, SVI_SCREENDIRTY); + return (svi_paint(sp, ep)); +} + +/* + * svi_paint -- + * This is the guts of the vi curses screen code. The idea is that + * the SCR structure passed in contains the new coordinates of the + * screen. What makes this hard is that we don't know how big + * characters are, doing input can put the cursor in illegal places, + * and we're frantically trying to avoid repainting unless it's + * absolutely necessary. If you change this code, you'd better know + * what you're doing. It's subtle and quick to anger. + */ +int +svi_paint(sp, ep) + SCR *sp; + EXF *ep; +{ + CHNAME const *cname; + SMAP *smp, tmp; + SVI_PRIVATE *svp; + recno_t lastline, lcnt; + size_t cwtotal, cnt, len, x, y; + int ch, didpaint; + char *p; + +#define LNO sp->lno +#define OLNO svp->olno +#define CNO sp->cno +#define OCNO svp->ocno +#define SCNO svp->sc_col + + didpaint = 0; + svp = SVP(sp); + + /* + * 1: Reformat the lines. + * + * If the lines themselves have changed (:set list, for example), + * fill in the map from scratch. Adjust the screen that's being + * displayed if the leftright flag is set. + */ + if (F_ISSET(sp, S_REFORMAT)) { + /* Toss svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* Toss svi_line() cached information. */ + if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP)) + return (1); + if (O_ISSET(sp, O_LEFTRIGHT) && + (cnt = svi_screens(sp, ep, LNO, &CNO)) != 1) + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + F_CLR(sp, S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: Line movement. + * + * Line changes can cause the top line to change as well. As + * before, if the movement is large, the screen is repainted. + * + * 2a: Tiny screens. + * + * Tiny screens cannot be permitted into the "scrolling" parts of + * the smap code for two reasons. If the screen size is 1 line, + * HMAP == TMAP and the code will quickly drop core. If the screen + * size is 2, none of the divisions by 2 will work, and scrolling + * won't work. In fact, because no line change will be less than + * HALFTEXT(sp), we always ending up "filling" the map, with a + * P_MIDDLE flag, which isn't what the user wanted. Tiny screens + * can go into the "fill" portions of the smap code, however. + */ + if (sp->t_rows <= 2) { + if (LNO < HMAP->lno) { + if (svi_sm_fill(sp, ep, LNO, P_TOP)) + return (1); + } else if (LNO > TMAP->lno) + if (svi_sm_fill(sp, ep, LNO, P_BOTTOM)) + return (1); + if (sp->t_rows == 1) { + HMAP->off = svi_screens(sp, ep, LNO, &CNO); + goto paint; + } + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * 2b: Small screens. + * + * Users can use the window, w300, w1200 and w9600 options to make + * the screen artificially small. The behavior of these options + * in the historic vi wasn't all that consistent, and, in fact, it + * was never documented how various screen movements affected the + * screen size. Generally, one of three things would happen: + * 1: The screen would expand in size, showing the line + * 2: The screen would scroll, showing the line + * 3: The screen would compress to its smallest size and + * repaint. + * In general, scrolling didn't cause compression (200^D was handled + * the same as ^D), movement to a specific line would (:N where N + * was 1 line below the screen caused a screen compress), and cursor + * movement would scroll if it was 11 lines or less, and compress if + * it was more than 11 lines. (And, no, I have no idea where the 11 + * comes from.) + * + * What we do is try and figure out if the line is less than half of + * a full screen away. If it is, we expand the screen if there's + * room, and then scroll as necessary. The alternative is to compress + * and repaint. + * + * !!! + * This code is a special case from beginning to end. Unfortunately, + * home modems are still slow enough that it's worth having. + * + * XXX + * If the line a really long one, i.e. part of the line is on the + * screen but the column offset is not, we'll end up in the adjust + * code, when we should probably have compressed the screen. + */ + if (ISSMALLSCREEN(sp)) + if (LNO < HMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + } + else + goto small_fill; + } else if (LNO > TMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, TMAP + 1)) + return (1); + ++TMAP; + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + else { +small_fill: MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; sp->t_rows > sp->t_minrows; + --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + if (svi_sm_fill(sp, ep, LNO, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + } + + /* + * 3a: Line down. + */ + if (LNO >= HMAP->lno) { + if (LNO <= TMAP->lno) + goto adjust; + + /* + * If less than half a screen away, scroll down until the + * line is on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1up(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than a full screen from the bottom of the file, put + * the last line of the file on the bottom of the screen. The + * calculation is safe because we know there's at least one + * full screen of lines, otherwise couldn't have gotten here. + */ + if (file_lline(sp, ep, &lastline)) + return (1); + tmp.lno = LNO; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows); + if (lcnt < sp->t_rows) { + if (svi_sm_fill(sp, ep, lastline, P_BOTTOM)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * If more than a full screen from the last line of the file, + * put the new line in the middle of the screen. + */ + goto middle; + } + + /* + * 3b: Line up. + * + * If less than half a screen away, scroll up until the line is + * the first line on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1down(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than half a screen from the top of the file, put the first + * line of the file at the top of the screen. Otherwise, put the line + * in the middle of the screen. + */ + tmp.lno = 1; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + if (svi_sm_fill(sp, ep, 1, P_TOP)) + return (1); + } else +middle: if (svi_sm_fill(sp, ep, LNO, P_MIDDLE)) + return (1); + F_SET(sp, S_REDRAW); + + /* + * At this point we know part of the line is on the screen. Since + * scrolling is done using logical lines, not physical, all of the + * line may not be on the screen. While that's not necessarily bad, + * if the part the cursor is on isn't there, we're going to lose. + * This can be tricky; if the line covers the entire screen, lno + * may be the same as both ends of the map, that's why we test BOTH + * the top and the bottom of the map. This isn't a problem for + * left-right scrolling, the cursor movement code handles the problem. + * + * There's a performance issue here if editing *really* long lines. + * This gets to the right spot by scrolling, and, in a binary, by + * scrolling hundreds of lines. If the adjustment looks like it's + * going to be a serious problem, refill the screen and repaint. + */ +adjust: if (!O_ISSET(sp, O_LEFTRIGHT) && + (LNO == HMAP->lno || LNO == TMAP->lno)) { + cnt = svi_screens(sp, ep, LNO, &CNO); + if (LNO == HMAP->lno && cnt < HMAP->off) + if ((HMAP->off - cnt) > HALFTEXT(sp)) { + HMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_TOP); + F_SET(sp, S_REDRAW); + } else + while (cnt < HMAP->off) + if (svi_sm_1down(sp, ep)) + return (1); + if (LNO == TMAP->lno && cnt > TMAP->off) + if ((cnt - TMAP->off) > HALFTEXT(sp)) { + TMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM); + F_SET(sp, S_REDRAW); + } else + while (cnt > TMAP->off) + if (svi_sm_1up(sp, ep)) + return (1); + } + + /* If the screen needs to be repainted, skip cursor optimization. */ + if (F_ISSET(sp, S_REDRAW)) + goto paint; + + /* + * 4: Cursor movements. + * + * Decide cursor position. If the line has changed, the cursor has + * moved over a tab, or don't know where the cursor was, reparse the + * line. Note, if we think that the cursor "hasn't moved", reparse + * the line. This is 'cause if it hasn't moved, we've almost always + * lost track of it. + * + * Otherwise, we've just moved over fixed-width characters, and can + * calculate the left/right scrolling and cursor movement without + * reparsing the line. Note that we don't know which (if any) of + * the characters between the old and new cursor positions changed. + * + * XXX + * With some work, it should be possible to handle tabs quickly, at + * least in obvious situations, like moving right and encountering + * a tab, without reparsing the whole line. + */ + + /* If the line we're working with has changed, reparse. */ + if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) { + F_CLR(SVP(sp), SVI_CUR_INVALID); + goto slow; + } + + /* Otherwise, if nothing's changed, go fast. */ + if (CNO == OCNO) + goto fast; + + /* + * Get the current line. If this fails, we either have an empty + * file and can just repaint, or there's a real problem. This + * isn't a performance issue because there aren't any ways to get + * here repeatedly. + */ + if ((p = file_gline(sp, ep, LNO, &len)) == NULL) { + if (file_lline(sp, ep, &lastline)) + return (1); + if (lastline == 0) + goto slow; + GETLINE_ERR(sp, LNO); + return (1); + } + +#ifdef DEBUG + /* This is just a test. */ + if (CNO >= len && len != 0) { + msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)", + tail(__FILE__), __LINE__, CNO, len); + return (1); + } +#endif + /* + * The basic scheme here is to look at the characters in between + * the old and new positions and decide how big they are on the + * screen, and therefore, how many screen positions to move. + */ + cname = sp->gp->cname; + if (CNO < OCNO) { + /* + * 4a: Cursor moved left. + * + * Point to the old character. The old cursor position can + * be past EOL if, for example, we just deleted the rest of + * the line. In this case, since we don't know the width of + * the characters we traversed, we have to do it slowly. + */ + p += OCNO; + cnt = (OCNO - CNO) + 1; + if (OCNO >= len) + goto slow; + + /* + * Quit sanity check -- it's hard to figure out exactly when + * we cross a screen boundary as we do in the cursor right + * movement. If cnt is so large that we're going to cross the + * boundary no matter what, stop now. + */ + if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt) + goto lscreen; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. + */ + for (cwtotal = 0; cnt--; cwtotal += cname[ch].len) + if ((ch = *(u_char *)p--) == '\t') + goto slow; + + /* + * Decrement the screen cursor by the total width of the + * characters minus 1. + */ + cwtotal -= 1; + + /* + * If we're moving left, and there's a wide character in the + * current position, go to the end of the character. + */ + if (cname[ch].len > 1) + cwtotal -= cname[ch].len - 1; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO < cwtotal) { +lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) { + for (smp = HMAP; smp <= TMAP; ++smp) + --smp->off; + goto paint; + } + goto slow; + } + SCNO -= cwtotal; + } else { + /* + * 4b: Cursor moved right. + * + * Point to the first character to the right. + */ + p += OCNO + 1; + cnt = CNO - OCNO; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. If we cross a + * screen boundary, we can quit. + */ + for (cwtotal = SCNO; cnt--;) { + if ((ch = *(u_char *)p++) == '\t') + goto slow; + if ((cwtotal += cname[ch].len) >= SCREEN_COLS(sp)) + break; + } + + /* + * Increment the screen cursor by the total width of the + * characters. + */ + SCNO = cwtotal; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO >= SCREEN_COLS(sp)) { + if (O_ISSET(sp, O_LEFTRIGHT)) { + SCNO -= SCREEN_COLS(sp); + for (smp = HMAP; smp <= TMAP; ++smp) + ++smp->off; + goto paint; + } + goto slow; + } + } + + /* + * 4c: Fast cursor update. + * + * Retrieve the current cursor position, and correct it + * for split screens. + */ +fast: getyx(stdscr, y, x); + y -= sp->woff; + goto number; + + /* + * 4d: Slow cursor update. + * + * Walk through the map and find the current line. If doing left-right + * scrolling and the cursor movement has changed the screen displayed, + * scroll the screen left or right, unless we're updating the info line + * in which case we just scroll that one line. Then update the screen + * lines for this file line until we have a new screen cursor position. + */ +slow: for (smp = HMAP; smp->lno != LNO; ++smp); + if (O_ISSET(sp, O_LEFTRIGHT)) { + cnt = svi_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp); + if (cnt != HMAP->off) { + if (ISINFOLINE(sp, smp)) + smp->off = cnt; + else + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + goto paint; + } + } + for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) { + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + if (y != -1) + break; + } + goto number; + + /* + * 5: Repaint the entire screen. + * + * Lost big, do what you have to do. We flush the cache as S_REDRAW + * gets set when the screen isn't worth fixing, and it's simpler to + * repaint. So, don't trust anything that we think we know about it. + */ +paint: for (smp = HMAP; smp <= TMAP; ++smp) + SMAP_FLUSH(smp); + for (smp = HMAP; smp <= TMAP; ++smp) + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + /* + * If it's a small screen and we're redrawing, clear the unused lines, + * ex may have overwritten them. + */ + if (F_ISSET(sp, S_REDRAW)) { + if (ISSMALLSCREEN(sp)) + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + F_CLR(sp, S_REDRAW); + } + + didpaint = 1; + + /* + * 6: Repaint the line numbers. + * + * If O_NUMBER is set and the S_RENUMBER bit is set, and we didn't + * repaint the screen, repaint all of the line numbers, they've + * changed. + */ +number: if (O_ISSET(sp, O_NUMBER) && F_ISSET(sp, S_RENUMBER) && !didpaint) { + if (svi_number(sp, ep)) + return (1); + F_CLR(sp, S_RENUMBER); + } + + /* + * 7: Refresh the screen. + * + * If the screen was corrupted, refresh it. + */ + if (F_ISSET(sp, S_REFRESH)) { + wrefresh(curscr); + F_CLR(sp, S_REFRESH); + } + + if (F_ISSET(sp, S_BELLSCHED)) + svi_bell(sp); + /* + * If the bottom line isn't in use by the colon command: + * + * Display any messages. Don't test S_UPDATE_MODE. The + * message printing routine set it to avoid anyone else + * destroying the message we're about to display. + * + * If the bottom line isn't in use by anyone, put out the + * standard status line. + */ + if (!F_ISSET(SVP(sp), SVI_INFOLINE)) + if (sp->msgq.lh_first != NULL && + !F_ISSET(sp->msgq.lh_first, M_EMPTY)) + svi_msgflush(sp); + else if (!F_ISSET(sp, S_UPDATE_MODE)) + svi_modeline(sp, ep); + + /* Update saved information. */ + OCNO = CNO; + OLNO = LNO; + + /* Place the cursor. */ + MOVE(sp, y, SCNO); + + /* Flush it all out. */ + refresh(); + + return (0); +} + +/* + * svi_msgflush -- + * Flush any accumulated messages. + */ +static int +svi_msgflush(sp) + SCR *sp; +{ + CH ikey; + CHAR_T ch; + CHNAME const *cname; + MSG *mp; + size_t chlen, len; + char *p; + +#define MCONTMSG " [More ...]" + + /* Display the messages. */ + cname = sp->gp->cname; + for (mp = sp->msgq.lh_first, p = NULL; + mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) { + p = mp->mbuf; + +lcont: /* Move to the message line and clear it. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* + * Turn on standout mode if requested, or, if we've split + * the screen and need a divider. + */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standout(); + + /* + * Print up to the "more" message. Avoid the last character + * in the last line, some hardware doesn't like it. + */ + if (svi_ncols(sp, p, mp->len, NULL) < sp->cols - 1) + len = sp->cols - 1; + else + len = (sp->cols - sizeof(MCONTMSG)) - 1; + for (;; ++p) { + if (!mp->len) + break; + ch = *(u_char *)p; + chlen = cname[ch].len; + if (chlen >= len) + break; + len -= chlen; + --mp->len; + ADDNSTR(cname[ch].name, chlen); + } + + /* + * If more, print continue message. If user key fails, + * keep showing the messages anyway. + */ + if (mp->len || (mp->q.le_next != NULL && + !F_ISSET(mp->q.le_next, M_EMPTY))) { + ADDNSTR(MCONTMSG, sizeof(MCONTMSG) - 1); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + break; + if (ikey.value == K_CR || + ikey.value == K_NL || ikey.ch == ' ') + break; + svi_bell(sp); + } + } + + /* Turn off standout mode. */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standend(); + + if (mp->len) + goto lcont; + + refresh(); + F_SET(mp, M_EMPTY); + } + return (0); +} + +#define RULERSIZE 15 +#define MODESIZE (RULERSIZE + 15) + +/* + * svi_modeline -- + * Update the mode line. + */ +static int +svi_modeline(sp, ep) + SCR *sp; + EXF *ep; +{ + char *s, buf[RULERSIZE]; + + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* Display a dividing line if not the bottom screen. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) + svi_divider(sp); + + /* Display the ruler. */ + if (O_ISSET(sp, O_RULER) && sp->cols > RULERSIZE + 2) { + MOVE(sp, INFOLINE(sp), sp->cols / 2 - RULERSIZE / 2); + clrtoeol(); + (void)snprintf(buf, + sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1); + ADDSTR(buf); + } + + /* Show the modified bit. */ + if (O_ISSET(sp, O_SHOWDIRTY) && + F_ISSET(ep, F_MODIFIED) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 9); + ADDSTR("*"); + } + + /* + * Show the mode. Leave the last character blank, in case it's a + * really dumb terminal with hardware scroll. Second, don't try + * to *paint* the last character, SunOS 4.1.1 and Ultrix 4.2 curses + * won't let you paint the last character in the screen. + */ + if (O_ISSET(sp, O_SHOWMODE) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 8); + s = F_ISSET(sp, S_INPUT) ? " Input" : "Command"; + ADDSTR(s); + } + + return (0); +} + +/* + * svi_divider -- + * Draw a dividing line between the screens. + */ +int +svi_divider(sp) + SCR *sp; +{ + size_t len; + +#define DIVIDESTR "+=+=+=+=+=+=+=+" + len = sizeof(DIVIDESTR) - 1 > sp->cols ? + sp->cols : sizeof(DIVIDESTR) - 1; + ADDNSTR(DIVIDESTR, len); + return (0); +} diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c new file mode 100644 index 000000000000..7daa972c8575 --- /dev/null +++ b/usr.bin/vi/svi/svi_relative.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_relative.c 8.7 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_column -- + * Return the logical column of the cursor. + */ +int +svi_column(sp, ep, cp) + SCR *sp; + EXF *ep; + size_t *cp; +{ + size_t col; + + col = SVP(sp)->sc_col; + if (O_ISSET(sp, O_NUMBER)) + col -= O_NUMBER_LENGTH; + *cp = col; + return (0); +} + +/* + * svi_relative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. If it's not easy, uses the underlying routine that + * really figures it out. It's broken into two parts because the + * svi_lrelative routine handles "logical" offsets, which nobody + * but the screen routines understand. + */ +size_t +svi_relative(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + size_t cno; + + /* First non-blank character. */ + if (sp->rcmflags == RCM_FNB) { + cno = 0; + (void)nonblank(sp, ep, lno, &cno); + return (cno); + } + + /* First character is easy, and common. */ + if (sp->rcmflags != RCM_LAST && sp->rcm == 0) + return (0); + + return (svi_lrelative(sp, ep, lno, 1)); +} + +/* + * svi_lrelative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. The offset is for the commands that move logical + * distances, i.e. if it's a logical scroll the closest physical + * distance is based on the logical line, not the physical line. + */ +size_t +svi_lrelative(sp, ep, lno, off) + SCR *sp; + EXF *ep; + recno_t lno; + size_t off; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (len == 0) + return (0); + + /* Last character is easy, and common. */ + if (sp->rcmflags == RCM_LAST) + return (len - 1); + + /* Discard logical lines. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, p = lp, llen = len; --off;) { + for (; len && scno < sp->cols; --len) + SCNO_INCREMENT; + if (len == 0) + return (llen - 1); + scno -= sp->cols; + } + + /* Step through the line until reach the right character. */ + while (len--) { + SCNO_INCREMENT; + if (scno >= sp->rcm) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == sp->rcm) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} + +/* + * svi_chposition -- + * Return the physical column from the line that will display a + * character closest to the specified column. + */ +size_t +svi_chposition(sp, ep, lno, cno) + SCR *sp; + EXF *ep; + recno_t lno; + size_t cno; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &llen)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (llen == 0) + return (0); + + /* Step through the line until reach the right character. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, len = llen, p = lp; len--;) { + SCNO_INCREMENT; + if (scno >= cno) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == cno) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c new file mode 100644 index 000000000000..16e2c36d8609 --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_screen.c 8.57 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static void d_to_h __P((SCR *, char *)); +static int svi_initscr_kluge __P((SCR *, struct termios *)); +static void svi_keypad __P((SCR *, int)); +static void svi_keypad_pc __P((int)); + +/* + * svi_screen_init -- + * Initialize a screen. + */ +int +svi_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = svi_bell; + sp->s_bg = svi_bg; + sp->s_busy = svi_busy; + sp->s_change = svi_change; + sp->s_chposition = svi_chposition; + sp->s_clear = svi_clear; + sp->s_column = svi_column; + sp->s_confirm = svi_confirm; + sp->s_down = svi_sm_down; + sp->s_edit = svi_screen_edit; + sp->s_end = svi_screen_end; + sp->s_ex_cmd = svi_ex_cmd; + sp->s_ex_run = svi_ex_run; + sp->s_ex_write = svi_ex_write; + sp->s_fg = svi_fg; + sp->s_fill = svi_sm_fill; + sp->s_get = svi_get; + sp->s_key_read = sex_key_read; + sp->s_optchange = svi_optchange; + sp->s_position = svi_sm_position; + sp->s_rabs = svi_rabs; + sp->s_refresh = svi_refresh; + sp->s_relative = svi_relative; + sp->s_rrel = svi_rrel; + sp->s_split = svi_split; + sp->s_suspend = svi_suspend; + sp->s_up = svi_sm_up; + + return (0); +} + +/* + * svi_screen_copy -- + * Copy to a new screen. + */ +int +svi_screen_copy(orig, sp) + SCR *orig, *sp; +{ + SVI_PRIVATE *osvi, *nsvi; + + /* Create the private screen structure. */ + CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE)); + sp->svi_private = nsvi; + +/* INITIALIZED AT SCREEN CREATE. */ + /* Lose svi_screens() cached information. */ + nsvi->ss_lno = OOBLNO; + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + } else { + osvi = SVP(orig); + nsvi->srows = osvi->srows; + if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + F_SET(nsvi, F_ISSET(osvi, SVI_NO_VBELL)); + } + return (0); +} + +/* + * svi_screen_end -- + * End a screen. + */ +int +svi_screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + + /* + * XXX + * If this is the last screen, end curses + * while we still have screen information. + */ + if (sp->q.cqe_prev == (void *)&sp->gp->dq && + sp->q.cqe_next == (void *)&sp->gp->dq && + sp->gp->hq.cqh_first == (void *)&sp->gp->hq && + svi_curses_end(sp)) + rval = 1; + + /* Free screen map. */ + if (HMAP != NULL) + FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP)); + + /* Free visual bell string. */ + if (SVP(sp)->VB != NULL) + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + + /* Free private memory. */ + FREE(SVP(sp), sizeof(SVI_PRIVATE)); + sp->svi_private = NULL; + + return (rval); +} + +/* + * We use a single curses "window" for each vi screen. The model would be + * simpler with two windows (one for the text, and one for the modeline) + * because scrolling the text window down would work correctly then, not + * affecting the mode line. As it is we have to play games to make it look + * right. The reason for this choice is that it would be difficult for + * curses to optimize the movement, i.e. detect that the downward scroll + * isn't going to change the modeline, set the scrolling region on the + * terminal and only scroll the first part of the text window. (Even if + * curses did detect it, the set-scrolling-region terminal commands can't + * be used by curses because it's indeterminate where the cursor ends up + * after they are sent.) + */ +/* + * svi_screen_edit -- + * Main vi curses screen loop. + */ +int +svi_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + int force; + + /* Get refresh to init curses. */ + F_SET(sp, S_RESIZE); + + for (;;) { + /* Reset the cursor. */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * Run vi. If vi fails, svi data structures may be + * corrupted, be extremely careful what you free up. + */ + if (vi(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); /* End the file. */ + (void)svi_curses_end(sp); /* End curses. */ + (void)screen_end(sp); /* End the screen. */ + return (1); + } + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force))/* End the file. */ + break; + (void)svi_join(sp, &tsp); /* Find a new screen. */ + if (tsp == NULL) + (void)svi_swap(sp, &tsp, NULL); + (void)screen_end(sp); /* End the screen. */ + if ((sp = tsp) == NULL) /* Switch. */ + return (0); + break; + case 0: /* Exit vi mode. */ + (void)svi_curses_end(sp); /* End curses. */ + d_to_h(sp, "Exit from vi"); + return (0); + case S_FSWITCH: /* File switch. */ + F_CLR(sp, S_FSWITCH); + F_SET(sp, S_REFORMAT); + break; + case S_SSWITCH: /* Screen switch. */ + F_CLR(sp, S_SSWITCH); + sp = sp->nextdisp; + break; + default: + abort(); + } + } + /* NOTREACHED */ +} + +/* + * svi_curses_init -- + * Initialize curses. + */ +int +svi_curses_init(sp) + SCR *sp; +{ + struct termios t; + int ixoff, ixon; + char *p, kbuf[2048]; + + /* + * Vi wants raw mode, excepting flow control characters. Both + * cbreak and raw modes have problems for us. In cbreak mode, + * we have to turn all the signals off explicitly. In raw mode, + * we have to turn flow control back on. Raw chosen for no strong + * reason. In both cases we have to periodically turn various + * signals on. + */ + if (tcgetattr(STDIN_FILENO, &t)) { + msgq(sp, M_SYSERR, "tcgetattr"); + return (1); + } + ixon = t.c_iflag & IXON; + ixoff = t.c_iflag & IXOFF; + + /* + * The initscr() in SunOS curses flushes the terminal driver queue. + * I have no idea if this stark raving insanity appears elsewhere, + * but since the SunOS curses is likely derived from the System III + * or System V versions, here's the workaround. + */ + if (svi_initscr_kluge(sp, &t)) + return (1); + + /* + * Start the screen. Initscr() doesn't provide useful error values + * or messages. Generally, either malloc failed or the terminal + * was unknown or lacked some necesssary feature. Try and guess so + * the user isn't even more pissed off because of the error message. + */ + errno = 0; + if (initscr() == NULL) { + if (errno) + msgq(sp, M_SYSERR, "initscr failed"); + else + msgq(sp, M_ERR, "Error: initscr failed."); + if ((p = getenv("TERM")) == NULL) + msgq(sp, M_ERR, + "Error: no terminal environment variable set."); + else if (tgetent(kbuf, p) != 1) + msgq(sp, M_ERR, +"Error: %s: unknown terminal type, or terminal lacking necessary features.", p); + else + msgq(sp, M_ERR, + "Error: %s: terminal type lacking necessary features.", p); + return (1); + } + + /* + * !!! + * If raw isn't turning off echo and newlines, something's wrong. + * However, just in case... + */ + noecho(); /* No character echo. */ + nonl(); /* No CR/NL translation. */ + raw(); /* No special characters. */ + idlok(stdscr, 1); /* Use hardware insert/delete line. */ + + /* + * Vi wants the cursor keys in application mode. The historic version + * of curses had no way to do this, and the newer versions (System V) + * only enable it through the wgetch() interface. Do it roughly, here. + */ + svi_keypad(sp, 1); + + /* + * XXX + * Major compatibility kluge. When we call the curses raw() routine, + * XON/XOFF flow control is turned off. Old terminals like to have + * it, so if it's originally set for the tty, we turn it back on. For + * some unknown reason, this causes System V curses to NOT correctly + * restore the terminal modes when SIGTSTP is received. + */ + if ((ixon || ixoff) && + !tcgetattr(STDIN_FILENO, &sp->gp->s5_curses_botch)) { + t = sp->gp->s5_curses_botch; + if (ixon) + t.c_iflag |= IXON; + if (ixoff) + t.c_iflag |= IXOFF; + F_SET(sp->gp, G_CURSES_S5CB); + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + } + + /* + * The first screen in the list gets it all. All other screens + * are hidden and lose their maps. + */ + d_to_h(sp, "Window resize"); + + /* Initialize terminal values. */ + SVP(sp)->srows = O_VAL(sp, O_LINES); + + /* + * Initialize screen values. + * + * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * Setup: + * t_minrows is the minimum rows to display + * t_maxrows is the maximum rows to display (rows - 1) + * t_rows is the rows currently being displayed + */ + sp->rows = SVP(sp)->srows; + sp->cols = O_VAL(sp, O_COLUMNS); + sp->woff = 0; + sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > sp->rows - 1) { + sp->t_minrows = sp->t_rows = sp->rows - 1; + msgq(sp, M_INFO, + "Windows option value is too large, max is %u", sp->t_rows); + } + sp->t_maxrows = sp->rows - 1; + + /* Create the screen map. */ + if ((HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + memset(HMAP, 0, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + F_SET(sp->gp, G_CURSES_INIT); /* Curses initialized. */ + F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */ + return (0); +} + +/* + * svi_rrel -- + * Change the relative size of the current screen. + */ +int +svi_rrel(sp, count) + SCR *sp; + long count; +{ + /* Can't grow beyond the size of the window. */ + if (count > O_VAL(sp, O_WINDOW)) + count = O_VAL(sp, O_WINDOW); + + sp->t_minrows = sp->t_rows = count; + if (sp->t_rows > sp->rows - 1) + sp->t_minrows = sp->t_rows = sp->rows - 1; + TMAP = HMAP + (sp->t_rows - 1); + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_curses_end -- + * Move to the bottom of the screen, end curses. + */ +int +svi_curses_end(sp) + SCR *sp; +{ + /* We get called before anything has been initialized. */ + if (!F_ISSET(sp->gp, G_CURSES_INIT)) + return (0); + F_CLR(sp->gp, G_CURSES_INIT); + + /* Move to the bottom of the screen. */ + if (move(INFOLINE(sp), 0) == OK) { + clrtoeol(); + refresh(); + } + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + F_CLR(sp->gp, G_CURSES_S5CB); + + svi_keypad(sp, 0); + endwin(); + return (0); +} + +/* + * svi_keypad -- + * Put the keypad/cursor arrows into or out of application mode. + */ +static void +svi_keypad(sp, on) + SCR *sp; + int on; +{ + char *sbp, *t, kbuf[2048], sbuf[128]; + + if (tgetent(kbuf, O_STR(sp, O_TERM)) != 1) + return; + sbp = sbuf; + if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL) + return; + (void)tputs(t, 0, svi_keypad_pc); +} + +/* + * svi_keypad_pc -- + * putchar routine for tputs(). + */ +static void +svi_keypad_pc(argch) + int argch; +{ + char ch; + + ch = argch; + (void)write(STDOUT_FILENO, &ch, sizeof(ch)); +} + +/* + * svi_initscr_kluge -- + * Read all of the waiting keys before calling initscr(). + */ +static int +svi_initscr_kluge(sp, tp) + SCR *sp; + struct termios *tp; +{ + struct termios rawt; + int rval; + + /* + * Turn off canonical input processing so that we get partial + * lines as well as complete ones. Also, set the MIN/TIME + * values -- System V and SMI systems overload VMIN and VTIME, + * such that VMIN is the same as the VEOF element, and VTIME is + * the same as the VEOL element. This means, that if VEOF was + * ^D, the default VMIN is 4. Majorly stupid. + */ + rawt = *tp; + rawt.c_cc[VMIN] = 1; + rawt.c_cc[VTIME] = 0; + rawt.c_lflag &= ~ICANON; + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, &rawt)) + return (1); + rval = term_key_queue(sp); + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, tp) || rval) + return (1); + return (0); +} + +/* + * d_to_h -- + * Move all but the current screen to the hidden queue. + */ +static void +d_to_h(sp, emsg) + SCR *sp; + char *emsg; +{ + SCR *tsp; + int hidden; + + for (hidden = 0; + (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) { + if (_HMAP(tsp) != NULL) + FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP)); + CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q); + } + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + if (hidden > 1) + msgq(sp, M_INFO, + "%s backgrounded %d screens; use :display to list the screens.", + emsg, hidden - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h new file mode 100644 index 000000000000..4bf754473df9 --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.h @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)svi_screen.h 8.30 (Berkeley) 12/22/93 + */ + +/* + * Structure for mapping lines to the screen. An SMAP is an array, with one + * structure element per screen line, which holds information describing the + * physical line which is displayed in the screen line. The first two fields + * (lno and off) are all that are necessary to describe a line. The rest of + * the information is useful to keep information from being re-calculated. + * + * Lno is the line number. Off is the screen offset into the line. For + * example, the pair 2:1 would be the first screen of line 2, and 2:2 would + * be the second. If doing left-right scrolling, all of the offsets will be + * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc. If doing the + * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1, + * etc. + * + * The SMAP is always as large as the physical screen, plus a slot for the + * info line, so that there is room to add any screen into another one at + * screen exit. + */ +typedef struct _smap { + recno_t lno; /* 1-N: Physical file line number. */ + size_t off; /* 1-N: Screen offset in the line. */ + + /* svi_line() cache information. */ + size_t c_sboff; /* 0-N: offset of first character byte. */ + size_t c_eboff; /* 0-N: offset of last character byte. */ + u_char c_scoff; /* 0-N: offset into the first character. */ + u_char c_eclen; /* 1-N: columns from the last character. */ + u_char c_ecsize; /* 1-N: size of the last character. */ +} SMAP; + + /* Macros to flush/test cached information. */ +#define SMAP_CACHE(smp) ((smp)->c_ecsize != 0) +#define SMAP_FLUSH(smp) ((smp)->c_ecsize = 0) + +typedef struct _svi_private { +/* INITIALIZED AT SCREEN CREATE. */ + SMAP *h_smap; /* First slot of the line map. */ + SMAP *t_smap; /* Last slot of the line map. */ + + size_t exlinecount; /* Ex overwrite count. */ + size_t extotalcount; /* Ex overwrite count. */ + size_t exlcontinue; /* Ex line continue value. */ + + /* svi_screens() cache information. */ + recno_t ss_lno; /* 1-N: Line number. */ + size_t ss_screens; /* Return value. */ + + recno_t olno; /* 1-N: old cursor file line. */ + size_t ocno; /* 0-N: old file cursor column. */ + size_t sc_col; /* 0-N: LOGICAL screen column. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + size_t srows; /* 1-N: Rows in the terminal/window. */ + + char *VB; /* Visual bell termcap string. */ + +#define SVI_CUR_INVALID 0x001 /* Cursor position is unknown. */ +#define SVI_DIVIDER 0x002 /* Screen divider is displayed. */ +#define SVI_INFOLINE 0x004 /* The infoline is being used by v_ntext(). */ +#define SVI_NO_VBELL 0x008 /* No visual bell available. */ +#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */ + u_int flags; +} SVI_PRIVATE; + +#define SVP(sp) ((SVI_PRIVATE *)((sp)->svi_private)) +#define HMAP (SVP(sp)->h_smap) +#define TMAP (SVP(sp)->t_smap) +#define _HMAP(sp) (SVP(sp)->h_smap) +#define _TMAP(sp) (SVP(sp)->t_smap) + +/* + * One extra slot is always allocated for the map so that we can use + * it to do vi :colon command input; see svi_get(). + */ +#define SIZE_HMAP(sp) (SVP(sp)->srows + 1) + +#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */ +#define O_NUMBER_LENGTH 8 + /* Columns on a screen. */ +#define SCREEN_COLS(sp) \ + ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols)) + +#define HALFSCREEN(sp) ((sp)->t_maxrows / 2) /* Half the screen. */ +#define HALFTEXT(sp) ((sp)->t_rows / 2) /* Half the text. */ + +#define INFOLINE(sp) ((sp)->t_maxrows) /* Info line test, offset. */ +#define ISINFOLINE(sp, smp) (((smp) - HMAP) == INFOLINE(sp)) + + /* Small screen test. */ +#define ISSMALLSCREEN(sp) ((sp)->t_minrows != (sp)->t_maxrows) + + /* Next tab offset. */ +#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP)) + + +#define SCNO_INCREMENT /* Step through line. */\ + scno += (ch = *(u_char *)p++) == '\t' && !listset ? \ + TAB_OFF(sp, scno) : cname[ch].len + +/* Move in a screen (absolute), and fail if it doesn't work. */ +#define MOVEA(sp, lno, cno) { \ + if (move(lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), abs.", \ + tail(__FILE__), __LINE__, lno, cno); \ + return (1); \ + } \ +} + +/* Move in a window, and fail if it doesn't work. */ +#define MOVE(sp, lno, cno) { \ + size_t __lno = (sp)->woff + (lno); \ + if (move(__lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), o(%u).", \ + tail(__FILE__), __LINE__, lno, cno, sp->woff); \ + return (1); \ + } \ +} + +/* Add a character. */ +#define ADDCH(ch) { \ + int __ch = (ch); \ + ADDNSTR(cname[__ch].name, cname[__ch].len); \ +} + +/* Add a string len bytes long. */ +#define ADDNSTR(str, len) { \ + if (addnstr(str, len) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Add a string. */ +#define ADDSTR(str) { \ + if (addstr(str) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Public routines. */ +void svi_bell __P((SCR *)); +int svi_bg __P((SCR *)); +int svi_busy __P((SCR *, char const *)); +int svi_change __P((SCR *, EXF *, recno_t, enum operation)); +size_t svi_chposition __P((SCR *, EXF *, recno_t, size_t)); +int svi_column __P((SCR *, EXF *, size_t *)); +enum confirm + svi_confirm __P((SCR *, EXF *, MARK *, MARK *)); +int svi_clear __P((SCR *)); +int svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *)); +int svi_ex_run __P((SCR *, EXF *, MARK *)); +int svi_ex_write __P((void *, const char *, int)); +int svi_fg __P((SCR *, CHAR_T *)); +enum input + svi_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +int svi_optchange __P((SCR *, int)); +int svi_rabs __P((SCR *, long)); +int svi_refresh __P((SCR *, EXF *)); +size_t svi_relative __P((SCR *, EXF *, recno_t)); +int svi_rrel __P((SCR *, long)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_edit __P((SCR *, EXF *)); +int svi_screen_end __P((SCR *)); +int svi_sm_down __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_sm_fill __P((SCR *, EXF *, recno_t, enum position)); +int svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position)); +int svi_sm_up __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_split __P((SCR *, ARGS *[])); +int svi_suspend __P((SCR *)); +int svi_swap __P((SCR *, SCR **, char *)); + +/* Private routines. */ +int svi_curses_end __P((SCR *)); +int svi_curses_init __P((SCR *)); +int svi_divider __P((SCR *)); +int svi_init __P((SCR *)); +int svi_join __P((SCR *, SCR **)); +int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *)); +size_t svi_lrelative __P((SCR *, EXF *, recno_t, size_t)); +size_t svi_ncols __P((SCR *, u_char *, size_t, size_t *)); +int svi_number __P((SCR *, EXF *)); +int svi_paint __P((SCR *, EXF *)); +size_t svi_screens __P((SCR *, EXF *, recno_t, size_t *)); +int svi_sm_1down __P((SCR *, EXF *)); +int svi_sm_1up __P((SCR *, EXF *)); +int svi_sm_cursor __P((SCR *, EXF *, SMAP **)); +int svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *)); +recno_t svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t)); +int svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *)); + +/* Private debugging routines. */ +#ifdef DEBUG +int svi_gdbrefresh __P((void)); +#endif diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c new file mode 100644 index 000000000000..5bcb6e220f3d --- /dev/null +++ b/usr.bin/vi/svi/svi_smap.c @@ -0,0 +1,1041 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_smap.c 8.29 (Berkeley) 11/30/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +static int svi_deleteln __P((SCR *, int)); +static int svi_insertln __P((SCR *, int)); +static int svi_sm_delete __P((SCR *, EXF *, recno_t)); +static int svi_sm_insert __P((SCR *, EXF *, recno_t)); +static int svi_sm_reset __P((SCR *, EXF *, recno_t)); + +/* + * svi_change -- + * Make a change to the screen. + */ +int +svi_change(sp, ep, lno, op) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; +{ + SMAP *p; + size_t oldy, oldx; + + /* Appending is the same as inserting, if the line is incremented. */ + if (op == LINE_APPEND) { + ++lno; + op = LINE_INSERT; + } + + /* Ignore the change if the line is after the map. */ + if (lno > TMAP->lno) + return (0); + + /* + * If the line is before the map, and it's a decrement, decrement + * the map. If it's an increment, increment the map. Otherwise, + * ignore it. + */ + if (lno < HMAP->lno) { + switch (op) { + case LINE_APPEND: + abort(); + /* NOTREACHED */ + case LINE_DELETE: + for (p = HMAP; p <= TMAP; ++p) + --p->lno; + if (sp->lno >= lno) + --sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + for (p = HMAP; p <= TMAP; ++p) + ++p->lno; + if (sp->lno >= lno) + ++sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + break; + } + return (0); + } + + F_SET(SVP(sp), SVI_SCREENDIRTY); + + /* Flush cached information from svi_screens(). */ + SVP(sp)->ss_lno = OOBLNO; + + /* Invalidate the cursor, if it's on this line. */ + if (sp->lno == lno) + F_SET(SVP(sp), SVI_CUR_INVALID); + + getyx(stdscr, oldy, oldx); + + switch (op) { + case LINE_DELETE: + if (svi_sm_delete(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + if (svi_sm_insert(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + if (svi_sm_reset(sp, ep, lno)) + return (1); + break; + default: + abort(); + } + + MOVEA(sp, oldy, oldx); + + return (0); +} + +/* + * svi_sm_fill -- + * Fill in the screen map, placing the specified line at the + * right position. There isn't any way to tell if an SMAP + * entry has been filled in, so this routine had better be + * called with P_FILL set before anything else is done. + * + * !!! + * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP + * slot is already filled in, P_BOTTOM means that the TMAP slot is + * already filled in, and we just finish up the job. + */ +int +svi_sm_fill(sp, ep, lno, pos) + SCR *sp; + EXF *ep; + recno_t lno; + enum position pos; +{ + SMAP *p, tmp; + + /* Flush all cached information from the SMAP. */ + for (p = HMAP; p <= TMAP; ++p) + SMAP_FLUSH(p); + + switch (pos) { + case P_FILL: + tmp.lno = 1; + tmp.off = 1; + + /* See if less than half a screen from the top. */ + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + lno = 1; + goto top; + } + + /* See if less than half a screen from the bottom. */ + if (file_lline(sp, ep, &tmp.lno)) + return (1); + tmp.off = svi_screens(sp, ep, tmp.lno, NULL); + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + TMAP->lno = tmp.lno; + TMAP->off = tmp.off; + goto bottom; + } + goto middle; + case P_TOP: + if (lno != OOBLNO) { +top: HMAP->lno = lno; + HMAP->off = 1; + } + /* If we fail, just punt. */ + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_MIDDLE: + /* If we fail, guess that the file is too small. */ +middle: p = HMAP + (TMAP - HMAP) / 2; + for (p->lno = lno, p->off = 1; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + + /* If we fail, just punt. */ + p = HMAP + (TMAP - HMAP) / 2; + for (; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_BOTTOM: + if (lno != OOBLNO) { + TMAP->lno = lno; + TMAP->off = svi_screens(sp, ep, lno, NULL); + } + /* If we fail, guess that the file is too small. */ +bottom: for (p = TMAP; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + break; + } + return (0); + + /* + * Try and put *something* on the screen. If this fails, + * we have a serious hard error. + */ +err: HMAP->lno = 1; + HMAP->off = 1; + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + return (1); + return (0); +} + +/* + * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the + * screen only contains one line, or, if the line is the entire screen, this + * gets fairly exciting. Skip the fun and simply return if there's only one + * line in the screen, or just call fill. Fill may not be entirely accurate, + * i.e. we may be painting the screen with something not even close to the + * cursor, but it's not like we're into serious performance issues here, and + * the refresh routine will fix it for us. + */ +#define TOO_WEIRD { \ + if (cnt_orig >= sp->t_rows) { \ + if (cnt_orig == 1) \ + return (0); \ + if (file_gline(sp, ep, lno, NULL) == NULL) \ + if (file_lline(sp, ep, &lno)) \ + return (1); \ + F_SET(sp, S_REDRAW); \ + return (svi_sm_fill(sp, ep, lno, P_TOP)); \ + } \ +} + +/* + * svi_sm_delete -- + * Delete a line out of the SMAP. + */ +static int +svi_sm_delete(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig; + + /* + * Find the line in the map, and count the number of screen lines + * which display any part of the deleted line. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 1, t = p + 1; + t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + + TOO_WEIRD; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, cnt_orig)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Decrement the line numbers for the rest of the map. */ + for (t = TMAP - cnt_orig; p <= t; ++p) + --p->lno; + + /* Display the new lines. */ + for (p = TMAP - cnt_orig;;) { + if (p < TMAP && svi_sm_next(sp, ep, p, p + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++p, NULL, NULL)) + return (1); + if (p == TMAP) + break; + } + return (0); +} + +/* + * svi_sm_insert -- + * Insert a line into the SMAP. + */ +static int +svi_sm_insert(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt; + + /* + * Find the line in the map, find out how many screen lines + * needed to display the line. + */ + for (p = HMAP; p->lno != lno; ++p); + cnt_orig = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (cnt_orig > cnt) + cnt_orig = cnt; + + /* Push down that many lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, cnt_orig)) + return (1); + + /* Shift the screen map down. */ + memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Increment the line numbers for the rest of the map. */ + for (t = p + cnt_orig; t <= TMAP; ++t) + ++t->lno; + + /* Fill in the SMAP for the new lines, and display. */ + for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + return (0); +} + +/* + * svi_sm_reset -- + * Reset a line in the SMAP. + */ +static int +svi_sm_reset(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt_new, cnt, diff; + + /* + * See if the number of on-screen rows taken up by the old display + * for the line is the same as the number needed for the new one. + * If so, repaint, otherwise do it the hard way. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + cnt_new = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + if (cnt_orig == cnt_new) { + do { + SMAP_FLUSH(p); + if (svi_line(sp, ep, p, NULL, NULL)) + return (1); + } while (++p < t); + return (0); + } + + if (cnt_orig < cnt_new) { + /* Get the difference. */ + diff = cnt_new - cnt_orig; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (diff > cnt) + diff = cnt; + + /* Push down the extra lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, diff)) + return (1); + + /* Shift the screen map down. */ + memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + } else { + /* Get the difference. */ + diff = cnt_orig - cnt_new; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, diff)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + + /* Display the new lines at the bottom of the screen. */ + for (t = TMAP - diff;;) { + if (t < TMAP && svi_sm_next(sp, ep, t, t + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++t, NULL, NULL)) + return (1); + if (t == TMAP) + break; + } + } + return (0); +} + +/* + * svi_sm_up -- + * Scroll the SMAP up count logical lines. + */ +int +svi_sm_up(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap, tmp; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* + * Check to see if movement is possible. Lots of checks... + * + * Find out if it's possible to move past the end of the map. If + * that's okay because we think that we can move the cursor down + * in the map, check to make sure that the map isn't mostly empty. + */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (tmp.lno > TMAP->lno && + !file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + if (!cursor_move || ignore_cursor || p == TMAP) { + v_eof(sp, ep, NULL); + return (1); + } + if (svi_sm_next(sp, ep, p, &tmp)) + return (1); + if (!file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + v_eof(sp, ep, NULL); + return (1); + } + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows; + --count, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (TMAP->lno != tmp.lno && + !file_gline(sp, ep, tmp.lno, NULL)) + break; + *++TMAP = tmp; + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* Decide what would show up on the screen. */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + + /* If the line doesn't exist, we're done. */ + if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL)) + break; + + /* Scroll the screen cursor up one logical line. */ + if (svi_sm_1up(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p > HMAP) + --p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* + * If we didn't move enough, head toward EOF. Check to make + * sure the lines actually, if the file is smaller than the + * screen they may not. + */ + for (; count; --count, ++p) + if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL)) + break; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_eof(sp, ep, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the top. */ + if (sp->lno < HMAP->lno) + p = HMAP; + } + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1up -- + * Scroll the SMAP up one. + */ +int +svi_sm_1up(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Delete the top line of the screen. Shift the screen map up. + * Display a new line at the bottom of the screen. + */ + MOVE(sp, 0, 0); + if (svi_deleteln(sp, 1)) + return (1); + + /* One-line screens can fail. */ + if (HMAP == TMAP) { + if (svi_sm_next(sp, ep, TMAP, TMAP)) + return (1); + } else { + memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_next(sp, ep, TMAP - 1, TMAP)) + return (1); + } + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_deleteln -- + * Delete a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_deleteln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + deleteln(); + MOVE(sp, INFOLINE(sp) - 1, 0); + insertln(); + MOVEA(sp, oldy, oldx); + } + return (0); +} + +/* + * svi_sm_down -- + * Scroll the SMAP down count logical lines. + */ +int +svi_sm_down(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* Check to see if movement is possible. */ + if (HMAP->lno == 1 && HMAP->off == 1 && + (!cursor_move || ignore_cursor || p == HMAP)) { + v_sof(sp, NULL); + return (1); + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows && + (HMAP->lno > 1 || HMAP->off > 1); + --count, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move) + ++p; + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* If the line doesn't exist, we're done. */ + if (HMAP->lno == 1 && HMAP->off == 1) + break; + + /* Scroll the screen and cursor down one logical line. */ + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p < TMAP) + ++p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* If we didn't move enough, move to SOF. */ + if (count) + p = HMAP; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_sof(sp, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the bottom. */ + if (sp->lno > TMAP->lno) + p = TMAP; + } + + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1down -- + * Scroll the SMAP down one. + */ +int +svi_sm_1down(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Clear the bottom line of the screen, insert a line at the top + * of the screen. Shift the screen map down, display a new line + * at the top of the screen. + */ + MOVE(sp, sp->t_rows, 0); + clrtoeol(); + MOVE(sp, 0, 0); + if (svi_insertln(sp, 1)) + return (1); + memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_prev(sp, ep, HMAP + 1, HMAP)) + return (1); + /* svi_sm_prev() flushed the cache. */ + if (svi_line(sp, ep, HMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_insertln -- + * Insert a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_insertln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + MOVE(sp, INFOLINE(sp) - 1, 0); + deleteln(); + MOVEA(sp, oldy, oldx); + insertln(); + } + return (0); +} + +/* + * svi_sm_next -- + * Fill in the next entry in the SMAP. + */ +int +svi_sm_next(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + size_t lcnt; + + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno + 1; + t->off = p->off; + } else { + lcnt = svi_screens(sp, ep, p->lno, NULL); + if (lcnt == p->off) { + t->lno = p->lno + 1; + t->off = 1; + } else { + t->lno = p->lno; + t->off = p->off + 1; + } + } + return (0); +} + +/* + * svi_sm_prev -- + * Fill in the previous entry in the SMAP. + */ +int +svi_sm_prev(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno - 1; + t->off = p->off; + } else if (p->off != 1) { + t->lno = p->lno; + t->off = p->off - 1; + } else { + t->lno = p->lno - 1; + t->off = svi_screens(sp, ep, t->lno, NULL); + } + return (t->lno == 0); +} + +/* + * svi_sm_cursor -- + * Return the SMAP entry referenced by the cursor. + */ +int +svi_sm_cursor(sp, ep, smp) + SCR *sp; + EXF *ep; + SMAP **smp; +{ + SMAP *p; + + /* See if the cursor is not in the map. */ + if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) { + *smp = NULL; + return (0); + } + + /* Find the first occurence of the line. */ + for (p = HMAP; p->lno != sp->lno; ++p); + + /* Fill in the map information until we find the right line. */ + for (; p <= TMAP; ++p) { + /* Short lines are common and easy to detect. */ + if (p != TMAP && (p + 1)->lno != p->lno) { + *smp = p; + return (0); + } + if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL)) + return (1); + if (p->c_eboff >= sp->cno) { + *smp = p; + return (0); + } + } + + /* It was past the end of the map after all. */ + *smp = NULL; + return (0); +} + +/* + * svi_sm_position -- + * Return the line/column of the top, middle or last line on the screen. + * (The vi H, M and L commands.) Here because only the screen routines + * know what's really out there. + */ +int +svi_sm_position(sp, ep, rp, cnt, pos) + SCR *sp; + EXF *ep; + MARK *rp; + u_long cnt; + enum position pos; +{ + SMAP *smp; + recno_t last; + + switch (pos) { + case P_TOP: + if (cnt > TMAP - HMAP) + goto err; + smp = HMAP + cnt; + break; + case P_MIDDLE: + if (cnt > (TMAP - HMAP) / 2) + goto err; + smp = (HMAP + (TMAP - HMAP) / 2) + cnt; + goto eof; + case P_BOTTOM: + if (cnt > TMAP - HMAP) { +err: msgq(sp, M_BERR, "Movement past the end-of-screen."); + return (1); + } + smp = TMAP - cnt; +eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) { + if (file_lline(sp, ep, &last)) + return (1); + for (; smp->lno > last && smp > HMAP; --smp); + } + break; + default: + abort(); + } + + if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL)) + return (1); + rp->lno = smp->lno; + rp->cno = smp->c_sboff; + + return (0); +} + +/* + * svi_sm_nlines -- + * Return the number of screen lines from an SMAP entry to the + * start of some file line, less than a maximum value. + */ +recno_t +svi_sm_nlines(sp, ep, from_sp, to_lno, max) + SCR *sp; + EXF *ep; + SMAP *from_sp; + recno_t to_lno; + size_t max; +{ + recno_t lno, lcnt; + + if (O_ISSET(sp, O_LEFTRIGHT)) + return (from_sp->lno > to_lno ? + from_sp->lno - to_lno : to_lno - from_sp->lno); + + if (from_sp->lno == to_lno) + return (from_sp->off - 1); + + if (from_sp->lno > to_lno) { + lcnt = from_sp->off - 1; /* Correct for off-by-one. */ + for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } else { + lno = from_sp->lno; + lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1; + for (; ++lno < to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } + return (lcnt); +} diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c new file mode 100644 index 000000000000..f6be03d38365 --- /dev/null +++ b/usr.bin/vi/svi/svi_split.c @@ -0,0 +1,581 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_split.c 8.29 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_split -- + * Split the screen. + */ +int +svi_split(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + MSG *mp, *next; + SCR *tsp, saved_sp; + SVI_PRIVATE saved_svp; + SMAP *smp; + size_t cnt, half; + int issmallscreen, splitup; + + /* Check to see if it's possible. */ + half = sp->rows / 2; + if (half < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen must be larger than %d to split", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Get a new screen. */ + if (screen_init(sp, &tsp, 0)) + return (1); + MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + if (_HMAP(tsp) == NULL) + return (1); + + /* + * We're about to modify the current screen. Save the contents + * in case something goes horribly, senselessly wrong. + */ + saved_sp = *sp; + saved_svp = *SVP(sp); + +/* INITIALIZED AT SCREEN CREATE. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * Set a flag so we know to fix the screen up later. + */ + issmallscreen = ISSMALLSCREEN(sp); + + /* + * Split the screen, and link the screens together. If the cursor + * is in the top half of the current screen, the new screen goes + * under the current screen. Else, it goes above the current screen. + * + * The columns in the screen don't change. + */ + tsp->cols = sp->cols; + + cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP; + if (cnt <= half) { /* Parent is top half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff + half; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->t_maxrows = sp->rows - 1; + + splitup = 0; + } else { /* Parent is bottom half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->woff += tsp->rows; + sp->t_maxrows = sp->rows - 1; + + /* Shift the parent's map down. */ + memmove(_HMAP(sp), + _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP)); + + splitup = 1; + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The child may have different screen options sizes than the + * parent, so use them. Make sure that the text counts aren't + * larger than the new screen sizes. + */ + if (issmallscreen) { + /* Fix the text line count for the parent. */ + if (splitup) + sp->t_rows -= tsp->rows; + + /* Fix the parent screen. */ + if (sp->t_rows > sp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > sp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + + /* Fix the child screen. */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->t_maxrows) + tsp->t_rows = tsp->t_maxrows; + if (tsp->t_minrows > tsp->t_maxrows) + tsp->t_minrows = tsp->t_maxrows; + + /* + * If we split up, i.e. the child is on top, lines that + * were painted in the parent may not be painted in the + * child. Clear any lines not being used in the child + * screen. + * + */ + if (splitup) + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } else { + sp->t_minrows = sp->t_rows = sp->rows - 1; + + /* + * The new screen may be a small screen, even though the + * parent was not. Don't complain if O_WINDOW is too large, + * we're splitting the screen so the screen is much smaller + * than normal. Clear any lines not being used in the child + * screen. + */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->rows - 1) + tsp->t_minrows = tsp->t_rows = tsp->rows - 1; + else + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } + + /* Adjust the ends of both maps. */ + _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1); + _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1); + + /* + * In any case, if the size of the scrolling region hasn't been + * modified by the user, reset it so it's reasonable for the split + * screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) { + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2; + } + + /* + * If files specified, build the file list, else, link to the + * current file. + */ + if (argv == NULL) { + if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL) + goto err; + } else + for (; (*argv)->len != 0; ++argv) + if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL) + goto err; + + /* Set up the argument and current FREF pointers. */ + if ((tsp->frp = file_first(tsp)) == NULL) { + msgq(sp, M_ERR, "No files in the file list."); + goto err; + } + + tsp->a_frp = tsp->frp; + + /* + * Copy the file state flags, start the file. Fill the child's + * screen map. If the file is unchanged, keep the screen and + * cursor the same. + */ + if (argv == NULL) { + tsp->ep = sp->ep; + ++sp->ep->refcnt; + + tsp->frp->flags = sp->frp->flags; + tsp->frp->lno = sp->lno; + tsp->frp->cno = sp->cno; + F_SET(tsp->frp, FR_CURSORSET); + + /* Copy the parent's map into the child's map. */ + memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP)); + } else { + if (file_init(tsp, tsp->frp, NULL, 0)) + goto err; + (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP); + } + + /* Everything's initialized, put the screen on the displayed queue.*/ + if (splitup) { + /* Link in before the parent. */ + CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q); + } else { + /* Link in after the parent. */ + CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q); + } + + /* Clear the current information lines in both screens. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + MOVE(tsp, INFOLINE(tsp), 0); + clrtoeol(); + + /* Redraw the status line for the parent screen. */ + (void)status(sp, sp->ep, sp->lno, 0); + + /* Save the parent screen's cursor information. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + /* Completely redraw the child screen. */ + F_SET(tsp, S_REDRAW); + + /* Switch screens. */ + sp->nextdisp = tsp; + F_SET(sp, S_SSWITCH); + return (0); + + /* Recover the original screen. */ +err: *sp = saved_sp; + *SVP(sp) = saved_svp; + + /* Copy any (probably error) messages in the new screen. */ + for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + free(mp->mbuf); + free(mp); + } + + /* Free the new screen. */ + free(_HMAP(tsp)); + free(SVP(tsp)); + FREE(tsp, sizeof(SCR)); + return (1); +} + +/* + * svi_bg -- + * Background the screen, and switch to the next one. + */ +int +svi_bg(csp) + SCR *csp; +{ + SCR *sp; + + /* Try and join with another screen. */ + if ((svi_join(csp, &sp))) + return (1); + if (sp == NULL) { + msgq(csp, M_ERR, + "You may not background your only displayed screen."); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + return (0); +} + +/* + * svi_join -- + * Join the screen into a related screen, if one exists, + * and return that screen. + */ +int +svi_join(csp, nsp) + SCR *csp, **nsp; +{ + SCR *sp; + size_t cnt; + + /* + * If a split screen, add space to parent/child. Make no effort + * to clean up the screen's values. If it's not exiting, we'll + * get it when the user asks to show it again. + */ + if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) { + if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) { + *nsp = NULL; + return (0); + } + sp->woff = csp->woff; + } + sp->rows += csp->rows; + if (ISSMALLSCREEN(sp)) { + sp->t_maxrows += csp->rows; + for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } else { + sp->t_maxrows += csp->rows; + sp->t_rows = sp->t_minrows = sp->t_maxrows; + TMAP = HMAP + (sp->t_rows - 1); + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + } + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + *nsp = sp; + return (0); +} + +/* + * svi_fg -- + * Background the current screen, and foreground a new one. + */ +int +svi_fg(csp, name) + SCR *csp; + CHAR_T *name; +{ + SCR *sp; + + if (svi_swap(csp, &sp, name)) + return (1); + if (sp == NULL) { + if (name == NULL) + msgq(csp, M_ERR, "There are no background screens."); + else + msgq(csp, M_ERR, + "There's no background screen editing a file named %s.", + name); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + return (0); +} + +/* + * svi_swap -- + * Swap the current screen with a hidden one. + */ +int +svi_swap(csp, nsp, name) + SCR *csp, **nsp; + char *name; +{ + SCR *sp; + int issmallscreen; + + /* Find the screen, or, if name is NULL, the first screen. */ + for (sp = csp->gp->hq.cqh_first; + sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next) + if (name == NULL || !strcmp(FILENAME(sp->frp), name)) + break; + if (sp == (void *)&csp->gp->hq) { + *nsp = NULL; + return (0); + } + *nsp = sp; + + /* Save the old screen's cursor information. */ + csp->frp->lno = csp->lno; + csp->frp->cno = csp->cno; + F_SET(csp->frp, FR_CURSORSET); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + /* Initialize terminal information. */ + SVP(sp)->srows = SVP(csp)->srows; + + issmallscreen = ISSMALLSCREEN(sp); + + /* Initialize screen information. */ + sp->rows = csp->rows; + sp->cols = csp->cols; + sp->woff = csp->woff; + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The new screens may have different screen options sizes than the + * old one, so use them. Make sure that text counts aren't larger + * than the new screen sizes. + */ + if (issmallscreen) { + sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > csp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > csp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + } else + sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1; + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + /* + * Don't change the screen's cursor information other than to + * note that the cursor is wrong. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * The HMAP may be NULL, if the screen got resized and + * a bunch of screens had to be hidden. + */ + if (HMAP == NULL) + MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + /* Fill the map. */ + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + + /* + * The new screen replaces the old screen in the parent/child list. + * We insert the new screen after the old one. If we're exiting, + * the exit will delete the old one, if we're foregrounding, the fg + * code will move the old one to the hidden queue. + */ + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q); + + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_rabs -- + * Change the absolute size of the current screen. + */ +int +svi_rabs(sp, count) + SCR *sp; + long count; +{ + SCR *g, *s; + + /* + * Figure out which screens will grow, which will shrink, and + * make sure it's possible. + */ + if (count == 0) + return (0); + if (count < 0) { + count = -count; + s = sp; + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + goto toosmall; + if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) { + if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq) + goto toobig; + g->woff -= count; + } else + s->woff += count; + } else { + g = sp; + if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq) + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + s = NULL; + else + s->woff += count; + else + s = NULL; + if (s == NULL) { + if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) { +toobig: msgq(sp, M_BERR, "The screen cannot %s.", + count < 0 ? "shrink" : "grow"); + return (1); + } + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) { +toosmall: msgq(sp, M_BERR, + "The screen can only shrink to %d rows.", + MINIMUM_SCREEN_ROWS); + return (1); + } + g->woff -= count; + } + } + + /* Update the screens. */ + g->rows += count; + g->t_rows += count; + if (g->t_minrows == g->t_maxrows) + g->t_minrows += count; + g->t_maxrows += count; + _TMAP(g) = _HMAP(g) + (g->t_rows - 1); + (void)status(g, g->ep, g->lno, 0); + F_SET(g, S_REDRAW); + + s->rows -= count; + s->t_rows -= count; + s->t_maxrows -= count; + if (s->t_minrows > s->t_maxrows) + s->t_minrows = s->t_maxrows; + _TMAP(s) = _HMAP(s) + (s->t_rows - 1); + (void)status(s, s->ep, s->lno, 0); + F_SET(s, S_REDRAW); + + return (0); +} diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c new file mode 100644 index 000000000000..0da531d8db26 --- /dev/null +++ b/usr.bin/vi/svi/svi_util.c @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_util.c 8.26 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" + +/* + * svi_screens -- + * Return the number of screens required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_screens(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + size_t cols, len, screens; + char *p; + + /* + * Check for single cached value. The cache is because, if + * the line is large, this routine gets called repeatedly. + * One other hack, lots of time the user is on column one, + * which is an easy one. + */ + if (cnop == NULL) { + if (SVP(sp)->ss_lno == lno) + return (SVP(sp)->ss_screens); + } else if (*cnop == 0) + return (1); + + /* Get a copy of the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0) + return (1); + + /* Figure out how many columns the line/column needs. */ + cols = svi_ncols(sp, p, len, cnop); + + /* Leading number if O_NUMBER option set. */ + if (O_ISSET(sp, O_NUMBER)) + cols += O_NUMBER_LENGTH; + + /* Trailing '$' if O_LIST option set. */ + if (O_ISSET(sp, O_LIST) && cnop == NULL) + cols += sp->gp->cname['$'].len; + + screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0)); + if (cnop == NULL) { + SVP(sp)->ss_lno = lno; + SVP(sp)->ss_screens = screens; + } + return (screens); +} + +/* + * svi_ncols -- + * Return the number of columns required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_ncols(sp, p, len, cnop) + SCR *sp; + u_char *p; + size_t len, *cnop; +{ + CHNAME const *cname; + size_t cno_cnt, scno; + int ch, listset; + + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + + if (cnop == NULL) + for (scno = 0; len; --len) + SCNO_INCREMENT; + else + for (cno_cnt = *cnop, scno = 0; len; --len) { + SCNO_INCREMENT; + if (cno_cnt == 0) + break; + --cno_cnt; + } + return (scno); +} + +/* + * bell_putchar -- + * Functional version of putchar, for tputs. + */ +static void +bell_putchar(ch) + int ch; +{ + (void)putchar(ch); +} + +/* + * vbell -- + * Set up the visual bell information. Broken out into a + * separate routine so don't allocate 4K every time we beep. + */ +static int +vbell(sp) + SCR *sp; +{ + size_t len; + char *s, *t, b1[2048], b2[2048]; + + /* Get the termcap information. */ + s = O_STR(sp, O_TERM); + if (tgetent(b1, s) != 1) { + msgq(sp, M_ERR, "No termcap entry for %s", s); + return (1); + } + + /* Get the visual bell string. */ + t = b2; + if (tgetstr("vb", &t) == NULL) { + msgq(sp, M_VINFO, + "No visual bell for %s terminal type", s); + return (1); + } + len = t - b2; + MALLOC_RET(sp, s, char *, len); + memmove(s, b2, len); + if (SVP(sp)->VB != NULL) + free(SVP(sp)->VB); + SVP(sp)->VB = t; + return (0); +} + +/* + * svi_bell -- + * Ring the bell. + */ +void +svi_bell(sp) + SCR *sp; +{ + if (O_ISSET(sp, O_FLASH) && !F_ISSET(SVP(sp), SVI_NO_VBELL)) + if (SVP(sp)->VB != NULL) { + (void)tputs(SVP(sp)->VB, 1, bell_putchar); + (void)fflush(stdout); + } else { + if (vbell(sp)) + F_SET(SVP(sp), SVI_NO_VBELL); + svi_bell(sp); + } + else + (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */ + F_CLR(sp, S_BELLSCHED); +} + +/* + * svi_optchange -- + * Screen specific "option changed" routine. + */ +int +svi_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_TERM: + /* Toss any saved visual bell information. */ + if (SVP(sp)->VB != NULL) { + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + SVP(sp)->VB = NULL; + } + F_CLR(SVP(sp), SVI_NO_VBELL); + F_SET(sp, S_RESIZE); + break; + case O_WINDOW: + if (svi_rrel(sp, O_VAL(sp, O_WINDOW))) + return (1); + break; + } + + (void)v_optchange(sp, opt); + (void)ex_optchange(sp, opt); + + return (0); +} + +/* + * svi_busy -- + * Put the cursor somewhere so the user will think we're busy. + */ +int +svi_busy(sp, msg) + SCR *sp; + char const *msg; +{ + MOVE(sp, INFOLINE(sp), 0); + if (msg) { + ADDSTR(msg); + clrtoeol(); + } + refresh(); + F_SET(SVP(sp), SVI_CUR_INVALID); + return (0); +} + +/* + * svi_clear -- + * Clear from the row down to the end of the screen. + */ +int +svi_clear(sp) + SCR *sp; +{ + size_t oldy, oldx, row; + + getyx(stdscr, oldy, oldx); + for (row = SVP(sp)->srows - 1; row >= oldy; --row) { + MOVEA(sp, row, 0); + clrtoeol(); + } + MOVEA(sp, oldy, oldx); + refresh(); + return (0); +} + +/* + * svi_suspend -- + * Suspend the svi screen; don't kill the process group, curses is + * expected to do that for us. + */ +int +svi_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + } + + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(getpid(), SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + + return (rval); +} + +/* + * svi_gdbrefresh -- + * Stub routine so can flush out screen changes using gdb. + */ +#ifdef DEBUG +int +svi_gdbrefresh() +{ + refresh(); + return (0); +} +#endif diff --git a/usr.bin/vi/term.c b/usr.bin/vi/term.c new file mode 100644 index 000000000000..d23fa80c4d45 --- /dev/null +++ b/usr.bin/vi/term.c @@ -0,0 +1,687 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)term.c 8.41 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "seq.h" + +static int keycmp __P((const void *, const void *)); + +/* + * If we're reading less than 20 characters, up the size of the tty buffer. + * This shouldn't ever happen, other than the first time through, but it's + * possible if a map is large enough. + */ +#define term_read_grow(sp, tty) \ + (tty)->len - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty) +static int __term_read_grow __P((SCR *, IBUF *)); + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. + */ +typedef struct _tklist { + char *ts; /* Key's termcap string. */ + char *output; /* Corresponding vi command. */ + char *name; /* Name. */ +} TKLIST; +static TKLIST const tklist[] = { + {"kA", "O", "insert line"}, + {"kD", "x", "delete character"}, + {"kd", "j", "cursor down"}, + {"kE", "D", "delete to eol"}, + {"kF", "\004", "scroll down"}, + {"kH", "$", "go to eol"}, + {"kh", "^", "go to sol"}, + {"kI", "i", "insert at cursor"}, + {"kL", "dd", "delete line"}, + {"kl", "h", "cursor left"}, + {"kN", "\006", "page down"}, + {"kP", "\002", "page up"}, + {"kR", "\025", "scroll up"}, + {"kS", "dG", "delete to end of screen"}, + {"kr", "l", "cursor right"}, + {"ku", "k", "cursor up"}, + {NULL}, +}; + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET. + */ +typedef struct _keylist { + u_char value; /* Special value. */ + CHAR_T ch; /* Key. */ +} KEYLIST; +static KEYLIST keylist[] = { + {K_CARAT, '^'}, + {K_CNTRLR, '\022'}, + {K_CNTRLT, '\024'}, + {K_CNTRLZ, '\032'}, + {K_COLON, ':'}, + {K_CR, '\r'}, + {K_ESCAPE, '\033'}, + {K_FORMFEED, '\f'}, + {K_NL, '\n'}, + {K_RIGHTBRACE, '}'}, + {K_RIGHTPAREN, ')'}, + {K_TAB, '\t'}, + {K_VEOF, '\004'}, + {K_VERASE, '\b'}, + {K_VINTR, '\003'}, + {K_VKILL, '\025'}, + {K_VLNEXT, '\026'}, + {K_VWERASE, '\027'}, + {K_ZERO, '0'}, +}; + +/* + * term_init -- + * Initialize the special key lookup table, and the special keys + * defined by the terminal's termcap entry. + */ +int +term_init(sp) + SCR *sp; +{ + extern CHNAME const asciiname[]; /* XXX */ + GS *gp; + KEYLIST *kp; + TKLIST const *tkp; + cc_t ch; + int cnt; + char *sbp, *t, buf[2 * 1024], sbuf[128]; + + /* + * XXX + * 8-bit, ASCII only, for now. Recompilation should get you + * any 8-bit character set, as long as nul isn't a character. + */ + gp = sp->gp; + gp->cname = asciiname; /* XXX */ + + /* Set keys found in the termios structure. */ +#define TERMSET(name, val) { \ + if ((ch = gp->original_termios.c_cc[name]) != _POSIX_VDISABLE) \ + for (kp = keylist;; ++kp) \ + if (kp->value == (val)) { \ + kp->ch = ch; \ + break; \ + } \ +} +/* + * VEOF, VERASE, VKILL are required by POSIX 1003.1-1990, + * VWERASE is a 4.4BSD extension. + */ +#ifdef VEOF + TERMSET(VEOF, K_VEOF); +#endif +#ifdef VERASE + TERMSET(VERASE, K_VERASE); +#endif +#ifdef VINTR + TERMSET(VINTR, K_VINTR); +#endif +#ifdef VKILL + TERMSET(VKILL, K_VKILL); +#endif +#ifdef VWERASE + TERMSET(VWERASE, K_VWERASE); +#endif + + /* Sort the special key list. */ + qsort(keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + + /* Initialize the fast lookup table. */ + CALLOC_RET(sp, + gp->special_key, u_char *, MAX_FAST_KEY + 1, sizeof(u_char)); + for (gp->max_special = 0, kp = keylist, + cnt = sizeof(keylist) / sizeof(keylist[0]); cnt--; ++kp) { + if (gp->max_special < kp->value) + gp->max_special = kp->value; + if (kp->ch <= MAX_FAST_KEY) + gp->special_key[kp->ch] = kp->value; + } + + /* Set key sequences found in the termcap entry. */ + switch (tgetent(buf, O_STR(sp, O_TERM))) { + case -1: + msgq(sp, M_ERR, + "tgetent: %s: %s.", O_STR(sp, O_TERM), strerror(errno)); + return (0); + case 0: + msgq(sp, M_ERR, + "%s: unknown terminal type.", O_STR(sp, O_TERM)); + return (0); + } + + for (tkp = tklist; tkp->name != NULL; ++tkp) { + sbp = sbuf; + if ((t = tgetstr(tkp->ts, &sbp)) == NULL) + continue; + if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), + tkp->output, strlen(tkp->output), SEQ_COMMAND, 0)) + return (1); + } + return (0); +} + +/* + * term_push -- + * Push keys onto the front of a buffer. + * + * There is a single input buffer in ex/vi. Characters are read onto the + * end of the buffer by the terminal input routines, and pushed onto the + * front of the buffer various other functions in ex/vi. Each key has an + * associated flag value, which indicates if it has already been quoted, + * if it is the result of a mapping or an abbreviation. + */ +int +term_push(sp, s, len, cmap, flags) + SCR *sp; + CHAR_T *s; /* Characters. */ + size_t len; /* Number of chars. */ + u_int cmap; /* Map count. */ + u_int flags; /* CH_* flags. */ +{ + IBUF *tty; + size_t nlen; + + /* If we have room, stuff the keys into the buffer. */ + tty = sp->gp->tty; + if (len <= tty->next || + (tty->ch != NULL && tty->cnt == 0 && len <= tty->len)) { + if (tty->cnt != 0) + tty->next -= len; + tty->cnt += len; + memmove(tty->ch + tty->next, s, len * sizeof(CHAR_T)); + memset(tty->chf + tty->next, flags, len); + memset(tty->cmap + tty->next, cmap, len); + return (0); + } + + /* Get enough space plus a little extra. */ + nlen = tty->cnt + len; + if (nlen > tty->len) { + size_t olen; + + nlen += 64; + olen = tty->len; + BINC_RET(sp, tty->ch, olen, nlen * sizeof(tty->ch[0])); + olen = tty->len; + BINC_RET(sp, tty->chf, olen, nlen * sizeof(tty->chf[0])); + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + } + + /* + * If there are currently characters in the queue, shift them up, + * leaving some extra room. + */ +#define TERM_PUSH_SHIFT 30 + if (tty->cnt) { + memmove(tty->ch + TERM_PUSH_SHIFT + len, + tty->ch + tty->next, tty->cnt * sizeof(tty->ch[0])); + memmove(tty->chf + TERM_PUSH_SHIFT + len, + tty->chf + tty->next, tty->cnt * sizeof(tty->chf[0])); + memmove(tty->cmap + TERM_PUSH_SHIFT + len, + tty->cmap + tty->next, tty->cnt * sizeof(tty->cmap[0])); + } + + /* Put the new characters into the queue. */ + tty->next = TERM_PUSH_SHIFT; + tty->cnt += len; + memmove(tty->ch + TERM_PUSH_SHIFT, s, len * sizeof(tty->ch[0])); + memset(tty->chf + TERM_PUSH_SHIFT, flags, len * sizeof(tty->chf[0])); + memset(tty->cmap + TERM_PUSH_SHIFT, cmap, len * sizeof(tty->cmap[0])); + return (0); +} + +/* + * Remove characters from the queue, simultaneously clearing the + * flag and map counts. + */ +#define QREM_HEAD(q, len) { \ + size_t __off = (q)->next; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ + else \ + (q)->next += len; \ +} +#define QREM_TAIL(q, len) { \ + size_t __off = (q)->next + (q)->cnt - 1; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ +} + +/* + * term_key -- + * Get the next key. + * + * !!! + * The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea + * of mapping keys is that one or more keystrokes act like a function key. + * What's going on is that vi is reading a number, and the character following + * the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the + * user is entering the z command, a valid command is "z40+", and we don't want + * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it + * into "z40xxx". However, if the user enters "35x", we want to put all of the + * characters through the mapping code. + * + * Historical practice is a bit muddled here. (Surprise!) It always permitted + * mapping digits as long as they weren't the first character of the map, e.g. + * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9 + * (the digit 0 was a special case as it doesn't indicate the start of a count) + * as the first character of the map, but then ignored those mappings. While + * it's probably stupid to map digits, vi isn't your mother. + * + * The way this works is that the TXT_MAPNODIGIT causes term_key to return the + * end-of-digit without "looking" at the next character, i.e. leaving it as the + * user entered it. Presumably, the next term_key call will tell us how the + * user wants it handled. + * + * There is one more complication. Users might map keys to digits, and, as + * it's described above, the commands "map g 1G|d2g" would return the keys + * "d2<end-of-digits>1G", when the user probably wanted "d21<end-of-digits>G". + * So, if a map starts off with a digit we continue as before, otherwise, we + * pretend that we haven't mapped the character and return <end-of-digits>. + * + * Now that that's out of the way, let's talk about Energizer Bunny macros. + * It's easy to create macros that expand to a loop, e.g. map x 3x. It's + * fairly easy to detect this example, because it's all internal to term_key. + * If we're expanding a macro and it gets big enough, at some point we can + * assume it's looping and kill it. The examples that are tough are the ones + * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion + * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then + * find the looping macro again. There is no way that we can detect this + * without doing a full parse of the command, because the character that might + * cause the loop (in this case 'x') may be a literal character, e.g. the map + * map x "ayy"xyy"byy is perfectly legal and won't cause a loop. + * + * Historic vi tried to detect looping macros by disallowing obvious cases in + * the map command, maps that that ended with the same letter as they started + * (which wrongly disallowed "map x 'x"), and detecting macros that expanded + * too many times before keys were returned to the command parser. It didn't + * get many (most?) of the tricky cases right, however, and it was certainly + * possible to create macros that ran forever. And, even if it did figure out + * what was going on, the user was usually tossed into ex mode. Finally, any + * changes made before vi realized that the macro was recursing were left in + * place. This implementation counts how many times each input character has + * been mapped. If it reaches some arbitrary value, we flush all mapped keys + * and return an error. + * + * XXX + * The final issue is recovery. It would be possible to undo all of the work + * that was done by the macro if we entered a record into the log so that we + * knew when the macro started, and, in fact, this might be worth doing at some + * point. Given that this might make the log grow unacceptably (consider that + * cursor keys are done with maps), for now we leave any changes made in place. + */ +enum input +term_key(sp, chp, flags) + SCR *sp; + CH *chp; + u_int flags; +{ + enum input rval; + struct timeval t, *tp; + CHAR_T ch; + GS *gp; + IBUF *tty; + SEQ *qp; + int cmap, ispartial, nr; + + gp = sp->gp; + tty = gp->tty; + + /* + * If the queue is empty, read more keys in. Since no timeout is + * requested, s_key_read will either return an error or will read + * some number of characters. + */ +loop: if (tty->cnt == 0) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + /* + * If there's something on the mode line that we wanted + * the user to see, they just entered a character so we + * can presume they saw it. + */ + if (F_ISSET(sp, S_UPDATE_MODE)) + F_CLR(sp, S_UPDATE_MODE); + } + + /* If the key is mappable and should be mapped, look it up. */ + if (!(tty->chf[tty->next] & CH_NOMAP) && + LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) { + /* Set up timeout value. */ + if (O_ISSET(sp, O_TIMEOUT)) { + tp = &t; + t.tv_sec = O_VAL(sp, O_KEYTIME) / 10; + t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L; + } else + tp = NULL; + + /* Get the next key. */ +newmap: ch = tty->ch[tty->next]; + if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch)) + goto nomap; + + /* Search the map. */ +remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt, + LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, + &ispartial); + + /* + * If get a partial match, read more characters and retry + * the map. If no characters read, return the characters + * unmapped. + */ + if (ispartial) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, tp)) + return (rval); + if (nr) + goto remap; + goto nomap; + } + + /* If no map, return the character. */ + if (qp == NULL) + goto nomap; + + /* + * If looking for the end of a digit string, and the first + * character of the map is it, pretend we haven't seen the + * character. + */ + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(qp->output[0])) + goto not_digit_ch; + + /* + * Only permit a character to be remapped a certain number + * of times before we figure that it's not going to finish. + */ + if ((cmap = tty->cmap[tty->next]) > MAX_MAP_COUNT) { + term_map_flush(sp, "Character remapped too many times"); + return (INP_ERR); + } + + /* Delete the mapped characters from the queue. */ + QREM_HEAD(tty, qp->ilen); + + /* If remapping characters, push the character on the queue. */ + if (O_ISSET(sp, O_REMAP)) { + if (term_push(sp, qp->output, qp->olen, ++cmap, 0)) + return (INP_ERR); + goto newmap; + } + + /* Else, push the characters on the queue and return one. */ + if (term_push(sp, qp->output, qp->olen, 0, CH_NOMAP)) + return (INP_ERR); + } + +nomap: ch = tty->ch[tty->next]; + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) { +not_digit_ch: chp->ch = NOT_DIGIT_CH; + chp->value = 0; + chp->flags = 0; + return (INP_OK); + } + + /* Fill in the return information. */ + chp->ch = ch; + chp->flags = tty->chf[tty->next]; + chp->value = term_key_val(sp, ch); + + /* Delete the character from the queue. */ + QREM_HEAD(tty, 1); + + /* + * O_BEAUTIFY eliminates all control characters except + * escape, form-feed, newline and tab. + */ + if (isprint(ch) || + !LF_ISSET(TXT_BEAUTIFY) || !O_ISSET(sp, O_BEAUTIFY) || + chp->value == K_ESCAPE || chp->value == K_FORMFEED || + chp->value == K_NL || chp->value == K_TAB) + return (INP_OK); + + goto loop; +} + +/* + * term_ab_flush -- + * Flush any abbreviated keys. + */ +void +term_ab_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !(tty->chf[tty->next] & CH_ABBREVIATED)) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->chf[tty->next] & CH_ABBREVIATED); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} +/* + * term_map_flush -- + * Flush any mapped keys. + */ +void +term_map_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !tty->cmap[tty->next]) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->cmap[tty->next]); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} + +/* + * term_user_key -- + * Get the next key, but require the user enter one. + */ +enum input +term_user_key(sp, chp) + SCR *sp; + CH *chp; +{ + enum input rval; + IBUF *tty; + int nr; + + /* + * Read any keys the user has waiting. Make the race + * condition as short as possible. + */ + if (rval = term_key_queue(sp)) + return (rval); + + /* Wait and read another key. */ + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + + /* Fill in the return information. */ + tty = sp->gp->tty; + chp->ch = tty->ch[tty->next + (tty->cnt - 1)]; + chp->flags = 0; + chp->value = term_key_val(sp, chp->ch); + + QREM_TAIL(tty, 1); + return (INP_OK); +} + +/* + * term_key_queue -- + * Read the keys off of the terminal queue until it's empty. + */ +int +term_key_queue(sp) + SCR *sp; +{ + enum input rval; + struct timeval t; + IBUF *tty; + int nr; + + t.tv_sec = 0; + t.tv_usec = 0; + for (tty = sp->gp->tty;;) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, &t)) + return (rval); + if (nr == 0) + break; + } + return (INP_OK); +} + +/* + * term_key_ch -- + * Fill in the key for a value. + */ +int +term_key_ch(sp, val, chp) + SCR *sp; + int val; + CHAR_T *chp; +{ + KEYLIST *kp; + + for (kp = keylist;; ++kp) + if (kp->value == val) { + *chp = kp->ch; + return (0); + } + /* NOTREACHED */ +} + +/* + * __term_key_val -- + * Fill in the value for a key. This routine is the backup + * for the term_key_val() macro. + */ +int +__term_key_val(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + KEYLIST k, *kp; + + k.ch = ch; + kp = bsearch(&k, keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + return (kp == NULL ? 0 : kp->value); +} + +/* + * __term_read_grow -- + * Grow the terminal queue. This routine is the backup for + * the term_read_grow() macro. + */ +static int +__term_read_grow(sp, tty) + SCR *sp; + IBUF *tty; +{ + size_t alen, len, nlen; + + nlen = tty->len + 64; + alen = tty->len - (tty->next + tty->cnt); + + len = tty->len; + BINC_RET(sp, tty->ch, len, nlen * sizeof(tty->ch[0])); + memset(tty->ch + tty->next + tty->cnt, 0, alen * sizeof(tty->ch[0])); + + len = tty->len; + BINC_RET(sp, tty->chf, len, nlen * sizeof(tty->chf[0])); + memset(tty->chf + tty->next + tty->cnt, 0, alen * sizeof(tty->chf[0])); + + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + memset(tty->cmap + + tty->next + tty->cnt, 0, alen * sizeof(tty->cmap[0])); + return (0); +} + +static int +keycmp(ap, bp) + const void *ap, *bp; +{ + return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch); +} diff --git a/usr.bin/vi/term.h b/usr.bin/vi/term.h new file mode 100644 index 000000000000..67f7f5d2822c --- /dev/null +++ b/usr.bin/vi/term.h @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)term.h 8.26 (Berkeley) 1/7/94 + */ + +/* Structure to return a character and associated information. */ +struct _ch { + CHAR_T ch; /* Character. */ + +#define K_CARAT 1 +#define K_CNTRLR 2 +#define K_CNTRLT 3 +#define K_CNTRLZ 4 +#define K_COLON 5 +#define K_CR 6 +#define K_ESCAPE 7 +#define K_FORMFEED 8 +#define K_NL 9 +#define K_RIGHTBRACE 10 +#define K_RIGHTPAREN 11 +#define K_TAB 12 +#define K_VEOF 13 +#define K_VERASE 14 +#define K_VINTR 15 +#define K_VKILL 16 +#define K_VLNEXT 17 +#define K_VWERASE 18 +#define K_ZERO 19 + u_char value; /* Special character flag values. */ + +#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */ +#define CH_NOMAP 0x02 /* Do not attempt to map the character. */ +#define CH_QUOTED 0x04 /* Character is already quoted. */ + u_char flags; +}; + +/* + * Structure for the key input buffer. + * + * MAX_MAP_COUNT was chosen based on the vi maze script, which remaps + * characters roughly 250 times. + */ +struct _ibuf { + CHAR_T *ch; /* Array of characters. */ + u_char *chf; /* Array of character flags (CH_*). */ +#define MAX_MAP_COUNT 270 /* Maximum times a character can remap. */ + u_char *cmap; /* Number of times character has been mapped. */ + + size_t cnt; /* Count of remaining characters. */ + size_t len; /* Array length. */ + size_t next; /* Offset of next array entry. */ +}; + /* Return if more keys in queue. */ +#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt) +#define MAPPED_KEYS_WAITING(sp) \ + (KEYS_WAITING(sp) && sp->gp->tty->cmap[sp->gp->tty->next]) + +/* + * Structure to name a character. Used both as an interface to the + * screen and to name objects named by characters in error messages. + */ +struct _chname { + char *name; /* Character name. */ + u_char len; /* Length of the character name. */ +}; + +/* + * Routines that return a key as a side-effect return: + * + * INP_OK Returning a character; must be 0. + * INP_EOF EOF. + * INP_ERR Error. + * + * The vi structure depends on the key routines being able to return INP_EOF + * multiple times without failing -- eventually enough things will end due to + * INP_EOF that vi will reach the command level for the screen, at which point + * the exit flags will be set and vi will exit. + */ +enum input { INP_OK=0, INP_EOF, INP_ERR }; + +/* + * Routines that return a confirmation return: + * + * CONF_NO User answered no. + * CONF_QUIT User answered quit, eof or an error. + * CONF_YES User answered yes. + */ +enum confirm { CONF_NO, CONF_QUIT, CONF_YES }; + +/* + * Ex/vi commands are generally separated by whitespace characters. We + * can't use the standard isspace(3) macro because it returns true for + * characters like ^K in the ASCII character set. The 4.4BSD isblank(3) + * macro does exactly what we want, but it's not portable yet. + * + * XXX + * Note side effect, ch is evaluated multiple times. + */ +#ifndef isblank +#define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +/* Various special characters, messages. */ +#define CURSOR_CH ' ' /* Cursor character. */ +#define END_CH '$' /* End of a range. */ +#define HEX_CH 'x' /* Leading hex number. */ +#define NOT_DIGIT_CH 'a' /* A non-isdigit() character. */ +#define NO_CH 'n' /* No. */ +#define QUIT_CH 'q' /* Quit. */ +#define YES_CH 'y' /* Yes. */ +#define CONFSTRING "confirm? [ynq]" +#define CONTMSG "Enter return to continue: " +#define CONTMSG_I "Enter return to continue [q to quit]: " + +/* Flags describing how input is handled. */ +#define TXT_AICHARS 0x000001 /* Leading autoindent chars. */ +#define TXT_ALTWERASE 0x000002 /* Option: altwerase. */ +#define TXT_APPENDEOL 0x000004 /* Appending after EOL. */ +#define TXT_AUTOINDENT 0x000008 /* Autoindent set this line. */ +#define TXT_BEAUTIFY 0x000010 /* Only printable characters. */ +#define TXT_BS 0x000020 /* Backspace returns the buffer. */ +#define TXT_CNTRLT 0x000040 /* Control-T is an indent special. */ +#define TXT_CR 0x000080 /* CR returns the buffer. */ +#define TXT_EMARK 0x000100 /* End of replacement mark. */ +#define TXT_ESCAPE 0x000200 /* Escape returns the buffer. */ +#define TXT_INFOLINE 0x000400 /* Editing the info line. */ +#define TXT_MAPCOMMAND 0x000800 /* Apply the command map. */ +#define TXT_MAPINPUT 0x001000 /* Apply the input map. */ +#define TXT_MAPNODIGIT 0x002000 /* Return to a digit. */ +#define TXT_NLECHO 0x004000 /* Echo the newline. */ +#define TXT_OVERWRITE 0x008000 /* Overwrite characters. */ +#define TXT_PROMPT 0x010000 /* Display a prompt. */ +#define TXT_RECORD 0x020000 /* Record for replay. */ +#define TXT_REPLACE 0x040000 /* Replace; don't delete overwrite. */ +#define TXT_REPLAY 0x080000 /* Replay the last input. */ +#define TXT_RESOLVE 0x100000 /* Resolve the text into the file. */ +#define TXT_SHOWMATCH 0x200000 /* Option: showmatch. */ +#define TXT_TTYWERASE 0x400000 /* Option: ttywerase. */ +#define TXT_WRAPMARGIN 0x800000 /* Option: wrapmargin. */ + +#define TXT_VALID_EX \ + (TXT_BEAUTIFY | TXT_CR | TXT_NLECHO | TXT_PROMPT) + +/* Support keyboard routines. */ +int __term_key_val __P((SCR *, ARG_CHAR_T)); +void term_ab_flush __P((SCR *, char *)); +int term_init __P((SCR *)); +enum input term_key __P((SCR *, CH *, u_int)); +int term_key_ch __P((SCR *, int, CHAR_T *)); +int term_key_queue __P((SCR *)); +void term_map_flush __P((SCR *, char *)); +int term_push __P((SCR *, CHAR_T *, size_t, u_int, u_int)); +enum input term_user_key __P((SCR *, CH *)); +int term_waiting __P((SCR *)); diff --git a/usr.bin/vi/timer.c b/usr.bin/vi/timer.c new file mode 100644 index 000000000000..e2bcfb1c8852 --- /dev/null +++ b/usr.bin/vi/timer.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)timer.c 8.7 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/time.h> + +#include "vi.h" + +static void busy_handler __P((int)); + +/* + * XXX + * There are two uses of timers in nvi. The first is to push the recovery + * information out to disk at periodic intervals. The second is to display + * a "busy" message if an operation takes too long. Rather than solve this + * in a general fashion, we depend on the fact that only a single screen in + * a window is active at a time, and that there are only two parts of the + * systems that use timers. + * + * It would be nice to reimplement this with multiple timers, a la POSIX + * 1003.1, but not many systems offer them yet. + */ + +/* + * busy_on -- + * Display a message if too much time passes. + */ +void +busy_on(sp, seconds, msg) + SCR *sp; + int seconds; + char const *msg; +{ + struct itimerval value; + struct sigaction act; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* Turn off the current timer, saving its current value. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &sp->time_value)) + return; + + /* + * Decrement the original timer by the number of seconds + * we're going to wait. + */ + if (sp->time_value.it_value.tv_sec > seconds) + sp->time_value.it_value.tv_sec -= seconds; + else + sp->time_value.it_value.tv_sec = 1; + + /* Reset the handler, saving its current value. */ + act.sa_handler = busy_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, &sp->time_handler); + + /* Reset the timer. */ + value.it_value.tv_sec = seconds; + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + + sp->time_msg = msg; + F_SET(sp, S_TIMER_SET); +} + +/* + * busy_off -- + * Reset the timer handlers. + */ +void +busy_off(sp) + SCR *sp; +{ + struct itimerval ovalue, value; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* If the timer flag isn't set, it must have fired. */ + if (!F_ISSET(sp, S_TIMER_SET)) + return; + + /* Ignore it if first on one of following system calls. */ + F_CLR(sp, S_TIMER_SET); + + /* Turn off the current timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &ovalue)) + return; + + /* If the timer wasn't running, we're done. */ + if (sp->time_handler.sa_handler == SIG_DFL) + return; + + /* + * Increment the old timer by the number of seconds + * remaining in the new one. + */ + sp->time_value.it_value.tv_sec += ovalue.it_value.tv_sec; + + /* Reset the handler to the original handler. */ + (void)sigaction(SIGALRM, &sp->time_handler, NULL); + + /* Reset the timer. */ + (void)setitimer(ITIMER_REAL, &sp->time_value, NULL); +} + +/* + * busy_handler -- + * Display a message when the timer goes off, and restore the + * timer to its original values. + */ +static void +busy_handler(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_TIMER_SET)) { + sp->s_busy(sp, sp->time_msg); + busy_off(sp); + } +} diff --git a/usr.bin/vi/trace.c b/usr.bin/vi/trace.c new file mode 100644 index 000000000000..f97e47bdc7db --- /dev/null +++ b/usr.bin/vi/trace.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trace.c 8.1 (Berkeley) 6/9/93 + */ + +#ifdef DEBUG +#include <sys/types.h> + +#include "vi.h" + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#ifdef __STDC__ +TRACE(SCR *sp, const char *fmt, ...) +#else +TRACE(sp, fmt, va_alist) + SCR *sp; + char *fmt; + va_dcl +#endif +{ + FILE *tfp; + va_list ap; + + if ((tfp = sp->gp->tracefp) == NULL) + return; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(tfp, fmt, ap); + va_end(ap); + + (void)fflush(tfp); +} +#endif diff --git a/usr.bin/vi/util.c b/usr.bin/vi/util.c new file mode 100644 index 000000000000..79a06516af48 --- /dev/null +++ b/usr.bin/vi/util.c @@ -0,0 +1,578 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)util.c 8.34 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "vi.h" + +/* + * msgq -- + * Display a message. + */ +void +#ifdef __STDC__ +msgq(SCR *sp, enum msgtype mt, const char *fmt, ...) +#else +msgq(sp, mt, fmt, va_alist) + SCR *sp; + enum msgtype mt; + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char msgbuf[1024]; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + /* + * It's possible to enter msg when there's no screen to hold + * the message. Always check sp before using it, and, if it's + * NULL, use __global_list. + */ + switch (mt) { + case M_BERR: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) { + F_SET(sp, S_BELLSCHED); + return; + } + mt = M_ERR; + break; + case M_VINFO: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) + return; + mt = M_INFO; + /* FALLTHROUGH */ + case M_INFO: + if (F_ISSET(sp, S_EXSILENT)) + return; + break; + case M_ERR: + case M_SYSERR: + break; + default: + abort(); + } + + /* Length is the min length of the message or the buffer. */ + if (mt == M_SYSERR) + if (sp->if_name != NULL) + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno, + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s%s%s.", + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else { + len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf), + "%s, %d: ", sp->if_name, sp->if_lno); + len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap); + } + + /* + * If len >= the size, some characters were discarded. + * Ignore trailing nul. + */ + if (len >= sizeof(msgbuf)) + len = sizeof(msgbuf) - 1; + + msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len); +} + +/* + * msg_app -- + * Append a message into the queue. This can fail, but there's + * nothing we can do if it does. + */ +void +msg_app(gp, sp, inv_video, p, len) + GS *gp; + SCR *sp; + int inv_video; + char *p; + size_t len; +{ + static int reenter; /* STATIC: Re-entrancy check. */ + MSG *mp, *nmp; + + /* + * It's possible to reenter msg when it allocates space. + * We're probably dead anyway, but no reason to drop core. + */ + if (reenter) + return; + reenter = 1; + + /* + * Find an empty structure, or allocate a new one. Use the + * screen structure if possible, otherwise the global one. + */ + if (sp != NULL) { + if ((mp = sp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&sp->msgq, mp, q); + goto store; + } + } else if ((mp = gp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&gp->msgq, mp, q); + goto store; + } + while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL) + mp = mp->q.le_next; + if (!F_ISSET(mp, M_EMPTY)) { + CALLOC(sp, nmp, MSG *, 1, sizeof(MSG)); + if (nmp == NULL) + goto ret; + LIST_INSERT_AFTER(mp, nmp, q); + mp = nmp; + } + + /* Get enough memory for the message. */ +store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len)) + goto ret; + + /* Store the message. */ + memmove(mp->mbuf, p, len); + mp->len = len; + mp->flags = inv_video ? M_INV_VIDEO : 0; + +ret: reenter = 0; +} + +/* + * msgrpt -- + * Report on the lines that changed. + * + * !!! + * Historic vi documentation (USD:15-8) claimed that "The editor will also + * always tell you when a change you make affects text which you cannot see." + * This isn't true -- edit a large file and do "100d|1". We don't implement + * this semantic as it would require that we track each line that changes + * during a command instead of just keeping count. + */ +int +msg_rpt(sp, is_message) + SCR *sp; + int is_message; +{ + static const char *const action[] = { + "added", "changed", "copied", "deleted", "joined", "moved", + "put", "left shifted", "right shifted", "yanked", NULL, + }; + recno_t total; + u_long rval; + int first, cnt; + size_t blen, len; + const char *const *ap; + char *bp, *p, number[40]; + + if (F_ISSET(sp, S_EXSILENT)) + return (0); + + if ((rval = O_VAL(sp, O_REPORT)) == 0) + goto norpt; + + GET_SPACE_RET(sp, bp, blen, 512); + p = bp; + + total = 0; + for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt) + if (sp->rptlines[cnt] != 0) { + total += sp->rptlines[cnt]; + len = snprintf(number, sizeof(number), + "%s%lu line%s %s", first ? "" : "; ", + sp->rptlines[cnt], + sp->rptlines[cnt] > 1 ? "s" : "", *ap); + memmove(p, number, len); + p += len; + first = 0; + } + + /* + * If nothing to report, return. Note that the number of lines + * must be > than the user's value, not >=. This is historic + * practice and means that users cannot report on single line + * changes. + */ + if (total > rval) { + *p = '\0'; + + if (is_message) + msgq(sp, M_INFO, "%s", bp); + else + ex_printf(EXCOOKIE, "%s\n", bp); + } + + FREE_SPACE(sp, bp, blen); + + /* Clear after each report. */ +norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines)); + return (0); +} + +/* + * binc -- + * Increase the size of a buffer. + */ +int +binc(sp, argp, bsizep, min) + SCR *sp; /* MAY BE NULL */ + void *argp; + size_t *bsizep, min; +{ + void *bpp; + size_t csize; + + /* If already larger than the minimum, just return. */ + csize = *bsizep; + if (min && csize >= min) + return (0); + + csize += MAX(min, 256); + bpp = *(char **)argp; + + /* For non-ANSI C realloc implementations. */ + if (bpp == NULL) + bpp = malloc(csize * sizeof(CHAR_T)); + else + bpp = realloc(bpp, csize * sizeof(CHAR_T)); + if (bpp == NULL) { + msgq(sp, M_SYSERR, NULL); + *bsizep = 0; + return (1); + } + *(char **)argp = bpp; + *bsizep = csize; + return (0); +} + +/* + * nonblank -- + * Set the column number of the first non-blank character + * including or after the starting column. On error, set + * the column to 0, it's safest. + */ +int +nonblank(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + char *p; + size_t cnt, len, off; + + /* Default. */ + off = *cnop; + *cnop = 0; + + /* Get the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + return (0); + GETLINE_ERR(sp, lno); + return (1); + } + + /* Set the offset. */ + if (len == 0 || off >= len) + return (0); + + for (cnt = off, p = &p[off], + len -= off; len && isblank(*p); ++cnt, ++p, --len); + + /* Set the return. */ + *cnop = len ? cnt : cnt - 1; + return (0); +} + +/* + * tail -- + * Return tail of a path. + */ +char * +tail(path) + char *path; +{ + char *p; + + if ((p = strrchr(path, '/')) == NULL) + return (path); + return (p + 1); +} + +/* + * set_window_size -- + * Set the window size, the row may be provided as an argument. + */ +int +set_window_size(sp, set_row, ign_env) + SCR *sp; + u_int set_row; + int ign_env; +{ + struct winsize win; + size_t col, row; + int user_set; + ARGS *argv[2], a, b; + char *s, buf[2048]; + + /* + * Get the screen rows and columns. If the values are wrong, it's + * not a big deal -- as soon as the user sets them explicitly the + * environment will be set and the screen package will use the new + * values. + * + * Try TIOCGWINSZ. + */ + row = col = 0; +#ifdef TIOCGWINSZ + if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { + row = win.ws_row; + col = win.ws_col; + } +#endif + + /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */ + if (row == 0 || col == 0) { + s = NULL; + if (F_ISSET(&sp->opts[O_TERM], OPT_SET)) + s = O_STR(sp, O_TERM); + else + s = getenv("TERM"); + if (s != NULL && tgetent(buf, s) == 1) { + if (row == 0) + row = tgetnum("li"); + if (col == 0) + col = tgetnum("co"); + } + } + /* If nothing else, well, it's probably a VT100. */ + if (row == 0) + row = 24; + if (col == 0) + col = 80; + + /* + * POSIX 1003.2 requires the environment to override, however, + * if we're here because of a signal, we don't want to use the + * old values. + */ + if (!ign_env) { + if ((s = getenv("LINES")) != NULL) + row = strtol(s, NULL, 10); + if ((s = getenv("COLUMNS")) != NULL) + col = strtol(s, NULL, 10); + } + + /* But, if we got an argument for the rows, use it. */ + if (set_row) + row = set_row; + + a.bp = buf; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b;; + + /* + * Tell the options code that the screen size has changed. + * Since the user didn't do the set, clear the set bits. + */ + user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "lines=%u", row); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_LINES], OPT_SET); + user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "columns=%u", col); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_COLUMNS], OPT_SET); + return (0); +} + +/* + * set_alt_name -- + * Set the alternate file name. + * + * Swap the alternate file name. It's a routine because I wanted some place + * to hang this comment. The alternate file name (normally referenced using + * the special character '#' during file expansion) is set by many + * operations. In the historic vi, the commands "ex", and "edit" obviously + * set the alternate file name because they switched the underlying file. + * Less obviously, the "read", "file", "write" and "wq" commands set it as + * well. In this implementation, some new commands have been added to the + * list. Where it gets interesting is that the alternate file name is set + * multiple times by some commands. If an edit attempt fails (for whatever + * reason, like the current file is modified but as yet unwritten), it is + * set to the file name that the user was unable to edit. If the edit + * succeeds, it is set to the last file name that was edited. Good fun. + * + * If the user edits a temporary file, there are time when there isn't an + * alternative file name. A name argument of NULL turns it off. + */ +void +set_alt_name(sp, name) + SCR *sp; + char *name; +{ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + if (name == NULL) + sp->alt_name = NULL; + else if ((sp->alt_name = strdup(name)) == NULL) + msgq(sp, M_SYSERR, NULL); +} + +/* + * baud_from_bval -- + * Return the baud rate using the standard defines. + */ +u_long +baud_from_bval(sp) + SCR *sp; +{ + speed_t v; + + switch (v = cfgetospeed(&sp->gp->original_termios)) { + case B50: + return (50); + case B75: + return (75); + case B110: + return (110); + case B134: + return (134); + case B150: + return (150); + case B200: + return (200); + case B300: + return (300); + case B600: + return (600); + case B1200: + return (1200); + case B1800: + return (1800); + case B2400: + return (2400); + case B4800: + return (4800); + case B0: /* Hangup -- ignore. */ + case B9600: + return (9600); + case B19200: + return (19200); + case B38400: + return (38400); + default: + /* + * EXTA and EXTB aren't required by POSIX 1003.1, and + * are almost certainly the same as some of the above + * values, so they can't be part of the case statement. + */ +#ifdef EXTA + if (v == EXTA) + return (19200); +#endif +#ifdef EXTB + if (v == EXTB) + return (38400); +#endif +#ifdef B57600 + if (v == B57600) + return (57600); +#endif +#ifdef B115200 + if (v == B115200) + return (115200); +#endif + msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v); + return (9600); + } +} + +/* + * v_strdup -- + * Strdup for wide character strings with an associated length. + */ +CHAR_T * +v_strdup(sp, str, len) + SCR *sp; + CHAR_T *str; + size_t len; +{ + CHAR_T *copy; + + MALLOC(sp, copy, CHAR_T *, len); + if (copy == NULL) + return (NULL); + memmove(copy, str, len * sizeof(CHAR_T)); + return (copy); +} diff --git a/usr.bin/vi/vi.1 b/usr.bin/vi/vi.1 new file mode 100644 index 000000000000..7c39b10c5ae2 --- /dev/null +++ b/usr.bin/vi/vi.1 @@ -0,0 +1,451 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)vi.1 8.3 (Berkeley) 3/19/94 +.\" +.Dd "March 19, 1994" +.Dt EX/VI 1 +.Os +.Sh NAME +.Nm ex, vi, view +.Nd text editors +.Sh SYNOPSIS +.Nm \&ex +.Op Fl eFlRsv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Nm \&vi +.Op Fl eFlRv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Nm view +.Op Fl eFlRv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Sh DESCRIPTION +.Nm \&Vi +is a screen oriented text editor. +.Nm \&Ex +is a line-oriented text editor. +.Nm \&Ex +and +.Nm \&vi +are different interfaces to the same program, +and it is possible to switch back and forth during an edit session. +.Nm View +is the equivalent of using the +.Fl R +(read-only) option of +.Nm \&vi . +.Pp +This manual page is the one provided with the +.Nm ex/vi +versions of the +.Nm ex/vi +text editors. +.Nm Ex/vi +are intended as bug-for-bug compatible replacements for the original +Fourth Berkeley Software Distribution (4BSD) +.Nm \&ex +and +.Nm \&vi +programs. +For the rest of this manual page, +.Nm ex/vi +is used only when it's necessary to distinguish it from the historic +implementations of +.Nm ex/vi . +.Pp +This manual page is intended for users already familiar with +.Nm ex/vi . +Anyone else should almost certainly read a good tutorial on the +editor before this manual page. +If you're in an unfamiliar environment, and you absolutely have to +get work done immediately, read the section near the end of this +manual page, entitled FAST STARTUP. +It's probably enough to get you going. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl c +Execute +.Ar cmd +immediately after starting the edit session. +Particularly useful for initial positioning in the file, however +.Ar cmd +is not limited to positioning commands. +This is the POSIX 1003.2 interface for the historic +.Dq "+cmd" +syntax. +.Nm Ex/vi +supports both the old and new syntax. +.It Fl e +Start editing in ex mode, as if the command name were +.Nm \&ex . +.It Fl F +Don't copy the entire file when first starting to edit. +(The default is to make a copy in case someone else modifies +the file during your edit session.) +.It Fl l +List the files that may be recovered using the +.Fl r +option of +.Nm \&vi . +This is the new interface for the historic syntax of the +.Fl r +option without a file argument. +.Nm Ex/vi +supports both the old and new syntax. +.It Fl R +Start editing in read-only mode, as if the command name was +.Nm view , +or the readonly option was set. +.It Fl r +Recover the specified file. +.It Fl s +Enter batch mode; applicable only to +.Nm \&ex +edit sessions. +Batch mode is useful when running +.Nm \&ex +scripts. +Prompts, informative messages and other user oriented message +are turned off, +and no startup files or environmental variables are read. +This is the POSIX 1003.2 interface for the historic +.Dq \&\- +argument. +.Nm \&Ex/vi +supports both the old and new syntax. +.It Fl t +Start editing at the specified tag. +(See +.Xr ctags 1 ). +.It Fl w +Set the initial window size to the specified number of lines. +.It Fl v +Start editing in vi mode, as if the command name was +.Nm \&vi +or +.Nm view . +.It Fl x +Reserved for X11 interfaces. +.Em "No X11 support is currently implemented." +.El +.Pp +.Nm Ex/vi +exit 0 on success, and greater than 0 if an error occurs. +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width XXXX -compact +.It Ev COLUMNS +The number of columns on the screen. +This value overrides any system or terminal specific values. +If the COLUMNS environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy columns +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev EXINIT +A list of +.Nm \&ex +startup commands. +.It Ev HOME +The user's home directory, used as the initial directory path +for the startup +.Pa $HOME/.exrc +file. +This value is also used as the default directory for the +.Nm \&vi +.Sy \&cd +command. +.It Ev LINES +The number of rows on the screen. +This value overrides any system or terminal specific values. +If the LINES environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy lines +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev SHELL +The user's shell of choice (see also the +.Sy shell +option). +.It Ev TERM +The user's terminal type. +The default is the type +.Dq unknown . +If the TERM environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy term +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev TMPDIR +The location used to stored temporary files (see also the +.Sy directory +option). +.El +.Sh SET OPTIONS +#include <set.opt.roff> +.Sh FAST STARTUP +This section will tell you the minimum amount that you need to +do simple editing tasks using +.Nm \&vi . +If you've never used any screen editor before, you're likely to have +problems even with this simple introduction. +In that case you should find someone that already knows +.Nm \&vi +and have them walk you through this section. +.Pp +.Nm \&Vi +is a screen editor. +This means that it takes up almost the entire screen, displaying part +of the file on each screen line, except for the last line of the screen. +The last line of the screen is used for you to give commands to +.Nm \&vi , +and for +.Nm \&vi +to give information to you. +.Pp +The other fact that you need to understand is that +.Nm \&vi +is a modeful editor, i.e. you are either entering text or you +are executing commands, and you have to be in the right mode +to do one or the other. +You will be in command mode when you first start editing a file. +There are commands that switch you into input mode. +There is only one key that takes you out of input mode, +and that is the <escape> key. +(Key names are written using less-than and greater-than signs, e.g. +<escape> means the +.Dq escape +key, usually labeled +.Dq esc +on your terminal's keyboard.) +If you're ever confused as to which mode you're in, +keep entering the <escape> key until +.Nm \&vi +beeps at you. +(Generally, +.Nm \&vi +will beep at you if you try and do something that's not allowed. +It will also display error messages.) +.Pp +To start editing a file, enter the command +.Dq Li "vi file_name<carriage-return>" . +The command you should enter as soon as you start editing is +.Dq Li ":set verbose showmode<carriage-return>" . +This will make the editor give you verbose error messages and display +the current mode at the bottom of the screen. +.Pp +The commands to move around the file are: +.Bl -tag -width XXXX -compact +.It Sy h +Move the cursor left one character. +.It Sy j +Move the cursor down one line. +.It Sy k +Move the cursor up one line. +.It Sy l +Move the cursor right one character. +.It Sy <cursor-arrows> +The cursor arrow keys should work, too. +.It Sy /text<carriage-return> +Search for the string +.Dq text +in the file, and move the cursor to its first character. +.El +.Pp +The commands to enter new text are: +.Bl -tag -width XXXX -compact +.It Sy a +Append new text, +.Em after +the cursor. +.It Sy i +Insert new text, +.Em before +the cursor. +.It Sy o +Open a new line below the line the cursor is on, and start +entering text. +.It Sy O +Open a new line above the line the cursor is on, and start +entering text. +.It Sy <escape> +Once you've entered input mode using the one of the +.Sy \&a , +.Sy \&i , +.Sy \&O , +or +.Sy \&o +commands, use +.Sy <escape> +to quit entering text and return to command mode. +.El +.Pp +The commands to copy text are: +.Bl -tag -width XXXX -compact +.It Sy yy +Copy the line the cursor is on. +.It Sy p +Append the copied line after the line the cursor is on. +.El +.Pp +The commands to delete text are: +.Bl -tag -width XXXX -compact +.It Sy dd +Delete the line the cursor is on. +.It Sy x +Delete the character the cursor is on. +.El +.Pp +The commands to write the file are: +.Bl -tag -width XXXX -compact +.It Sy :w<carriage-return> +Write the file back to the file with the name that you originally used +as an argument on the +.Nm \&vi +command line. +.It Sy :w file_name<carriage-return> +Write the file back to the file with the name +.Dq file_name . +.El +.Pp +The commands to quit editing and exit the editor are: +.Bl -tag -width XXXX -compact +.It Sy :q<carriage-return> +Quit editing and leave vi (if you've modified the file, but not +saved your changes, +.Nm \&vi +will refuse to quit). +.It Sy :q!<carriage-return> +Quit, discarding any modifications that you may have made. +.El +.Pp +One final caution. +Unusual characters can take up more than one column on the screen, +and long lines can take up more than a single screen line. +The above commands work on +.Dq physical +characters and lines, i.e. they affect the entire line no matter +how many screen lines it takes up and the entire character no matter +how many screen columns it takes up. +.Sh BUGS +See the file +.Pa vi/docs/bugs.current +for a list of the known bugs in this version. +.Sh FILES +.Bl -tag -width /var/tmp/vi.recover -compact +.It Pa /bin/sh +The default user shell. +.It Pa /etc/vi.exrc +System-wide vi startup file. +.It Pa /tmp +Temporary file directory. +.It Pa /var/tmp/vi.recover +Recovery file directory. +.It Pa $HOME/.exrc +user's home directory startup file. +.It Pa .exrc +local directory startup file. +.El +.Sh SEE ALSO +.Xr ctags 1 , +.Xr more 1 , +.Xr curses 3 , +.Xr dbopen 3 +.sp +The +.Dq "Vi Quick Reference" +card. +.sp +.Dq "An Introduction to Display Editing with Vi" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.sp +.Dq "Edit: A tutorial" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.sp +.Dq "\&Ex Reference Manual (Version 3.7)" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.Pp +.Nm Nroff/troff +source for the previous three documents are distributed with +.Nm ex/vi +in the +.Pa vi/docs/USD.doc +directory of the +.Nm ex/vi +source code. +.sp +The files +.Dq autowrite , +.Dq input , +.Dq quoting , +and +.Dq structures , +found in the +.Pa vi/docs/internals +directory of the +.Nm ex/vi +source code. +.Sh HISTORY +The +.Nm ex/vi +replacements for the +.Nm ex/vi +editor first appeared in 4.4BSD. +.Sh STANDARDS +.Nm \&Ex/vi +is close to IEEE Std1003.2 (``POSIX''). +That document differs from historical +.Nm ex/vi +practice in several places; there are changes to be made on both sides. diff --git a/usr.bin/vi/vi.h b/usr.bin/vi/vi.h new file mode 100644 index 000000000000..a979925c2b05 --- /dev/null +++ b/usr.bin/vi/vi.h @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vi.h 8.33 (Berkeley) 1/9/94 + */ + +/* System includes. */ +#include <queue.h> /* Required by screen.h. */ +#include <sys/time.h> /* Required by screen.h. */ + +#include <bitstring.h> /* Required by screen.h. */ +#include <limits.h> /* Required by screen.h. */ +#include <signal.h> /* Required by screen.h. */ +#include <stdio.h> /* Required by screen.h. */ +#include <termios.h> /* Required by gs.h. */ + +/* + * Required by screen.h. This is the first include that can pull + * in "compat.h". Should be after every other system include. + */ +#include <regex.h> + +/* + * Forward structure declarations. Not pretty, but the include files + * are far too interrelated for a clean solution. + */ +typedef struct _cb CB; +typedef struct _ch CH; +typedef struct _chname CHNAME; +typedef struct _excmdarg EXCMDARG; +typedef struct _exf EXF; +typedef struct _fref FREF; +typedef struct _gs GS; +typedef struct _ibuf IBUF; +typedef struct _mark MARK; +typedef struct _msg MSG; +typedef struct _option OPTION; +typedef struct _optlist OPTLIST; +typedef struct _scr SCR; +typedef struct _script SCRIPT; +typedef struct _seq SEQ; +typedef struct _tag TAG; +typedef struct _tagf TAGF; +typedef struct _text TEXT; + +/* + * Fundamental character types. + * + * CHAR_T An integral type that can hold any character. + * ARG_CHAR_T The type of a CHAR_T when passed as an argument using + * traditional promotion rules. It should also be able + * to be compared against any CHAR_T for equality without + * problems. + * MAX_CHAR_T The maximum value of any character. + * + * If no integral type can hold a character, don't even try the port. + */ +typedef u_char CHAR_T; +typedef u_int ARG_CHAR_T; +#define MAX_CHAR_T 0xff + +/* The maximum number of columns any character can take up on a screen. */ +#define MAX_CHARACTER_COLUMNS 4 + +/* + * Local includes. + */ +#include <db.h> /* Required by exf.h; includes compat.h. */ + +#include "search.h" /* Required by screen.h. */ +#include "args.h" /* Required by options.h. */ +#include "options.h" /* Required by screen.h. */ +#include "term.h" /* Required by screen.h. */ + +#include "msg.h" /* Required by gs.h. */ +#include "cut.h" /* Required by gs.h. */ +#include "gs.h" /* Required by screen.h. */ +#include "screen.h" /* Required by exf.h. */ +#include "mark.h" /* Required by exf.h. */ +#include "exf.h" +#include "log.h" +#include "mem.h" + +#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */ +#define EXCOOKIE sp +int ex_fflush __P((SCR *)); +int ex_printf __P((SCR *, const char *, ...)); +FILE *fwopen __P((SCR *, void *)); +#else +#define EXCOOKIE sp->stdfp +#define ex_fflush fflush +#define ex_printf fprintf +#endif + +/* Macros to set/clear/test flags. */ +#define F_SET(p, f) (p)->flags |= (f) +#define F_CLR(p, f) (p)->flags &= ~(f) +#define F_ISSET(p, f) ((p)->flags & (f)) + +#define LF_INIT(f) flags = (f) +#define LF_SET(f) flags |= (f) +#define LF_CLR(f) flags &= ~(f) +#define LF_ISSET(f) (flags & (f)) + +/* + * XXX + * MIN/MAX have traditionally been in <sys/param.h>. Don't + * try to get them from there, it's just not worth the effort. + */ +#ifndef MAX +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Function prototypes that don't seem to belong anywhere else. */ +u_long baud_from_bval __P((SCR *)); +char *charname __P((SCR *, ARG_CHAR_T)); +void busy_off __P((SCR *)); +void busy_on __P((SCR *, int, char const *)); +int nonblank __P((SCR *, EXF *, recno_t, size_t *)); +void set_alt_name __P((SCR *, char *)); +int set_window_size __P((SCR *, u_int, int)); +int status __P((SCR *, EXF *, recno_t, int)); +char *tail __P((char *)); +CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t)); + +#ifdef DEBUG +void TRACE __P((SCR *, const char *, ...)); +#endif + +/* Digraphs (not currently real). */ +int digraph __P((SCR *, int, int)); +int digraph_init __P((SCR *)); +void digraph_save __P((SCR *, int)); diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c new file mode 100644 index 000000000000..ec76a0668cf9 --- /dev/null +++ b/usr.bin/vi/xaw/xaw_screen.c @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)xaw_screen.c 8.4 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include "vi.h" + +/* + * xaw_init -- + * Athena widget screen initialization. + */ +int +xaw_screen_init(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "The Athena widget screen not yet implemented."); + return (1); +} + +/* + * xaw_screen_copy -- + * Copy to a new screen. + */ +int +xaw_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * xaw_screen_end -- + * End a screen. + */ +int +xaw_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * xaw -- + * Main vi Athena widget screen loop. + */ +int +xaw(sp, ep, spp) + SCR *sp, **spp; + EXF *ep; +{ + *spp = NULL; + return (0); +} diff --git a/usr.bin/vis/vis.1 b/usr.bin/vis/vis.1 index 7b8152847b21..457b0808f807 100644 --- a/usr.bin/vis/vis.1 +++ b/usr.bin/vis/vis.1 @@ -84,7 +84,7 @@ typically don't work with partial lines. Same as .Fl F . .It Fl l -Mark newlines with the visable sequence +Mark newlines with the visible sequence .Ql \e$ , followed by the newline. .It Fl n diff --git a/usr.bin/vmstat/names.c b/usr.bin/vmstat/names.c index 6f04979e815d..e022c385ff87 100644 --- a/usr.bin/vmstat/names.c +++ b/usr.bin/vmstat/names.c @@ -33,7 +33,7 @@ * @(#)names.c 5.2 (Berkeley) 6/4/91 */ -#if !defined(hp300) && !defined(tahoe) && !defined(vax) && !defined(__386BSD__) +#if !defined(hp300) && !defined(tahoe) && !defined(vax) && !defined(__FreeBSD__) char *defdrives[] = { 0 }; void read_names() @@ -41,7 +41,7 @@ void read_names() } #endif -#ifdef __386BSD__ +#ifdef __FreeBSD__ /* * 386BSD support added by Rodney W. Grimes, rgrimes@agora.rain.com 3/24/93 */ @@ -95,7 +95,7 @@ read_names() i++; } } -#endif /* __386BSD__ */ +#endif /* __FreeBSD__ */ #ifdef hp300 #include <hp300/dev/device.h> diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c index d65721f4de25..27862c68af1b 100644 --- a/usr.bin/vmstat/vmstat.c +++ b/usr.bin/vmstat/vmstat.c @@ -133,7 +133,7 @@ struct nlist nl[] = { #define X_UBDINIT (X_END+2) { "_ubdinit" }, #endif -#ifdef __386BSD__ +#ifdef __FreeBSD__ #define X_FREE (X_END+1) { "_vm_page_free_count" }, #define X_ACTIVE (X_END+2) @@ -146,7 +146,7 @@ struct nlist nl[] = { { "_page_size" }, #define X_ISA_BIO (X_END+6) { "_isa_devtab_bio" }, -#endif /* __386BSD__ */ +#endif /* __FreeBSD__ */ { "" }, }; @@ -160,7 +160,7 @@ struct vmmeter sum, osum; char *vmunix = _PATH_UNIX; char **dr_name; int *dr_select, dk_ndrive, ndrives; -#ifdef __386BSD__ +#ifdef __FreeBSD__ /* to make up for statistics that don't get updated */ int size, free_count, active_count, inactive, wired; #endif @@ -370,7 +370,7 @@ getdrivedata(argv) return(argv); } -#ifdef __386BSD__ +#ifdef __FreeBSD__ /* * Make up for the fact that under 0.1, VM doesn't update all of the * fields in the statistics structures. @@ -438,7 +438,7 @@ dovmstat(interval, reps) kread(X_SUM, &sum, sizeof(sum)); kread(X_TOTAL, &total, sizeof(total)); kread(X_VMSTAT, &vm_stat, sizeof(vm_stat)); -#ifdef __386BSD__ +#ifdef __FreeBSD__ fill_in_vm_stat (&vm_stat); #endif #ifdef notdef @@ -449,7 +449,7 @@ dovmstat(interval, reps) #define pgtok(a) ((a)*NBPG >> 10) #define rate(x) (((x) + halfuptime) / uptime) /* round */ (void)printf("%5ld %5ld ", -#ifdef __386BSD__ +#ifdef __FreeBSD__ pgtok(vm_stat.active_count), pgtok(vm_stat.free_count)); #else pgtok(total.t_avm), pgtok(total.t_free)); @@ -550,15 +550,16 @@ dotimes() } #endif +int pct(top, bot) - long top, bot; + u_long top, bot; { if (bot == 0) return(0); - return((top * 100) / bot); + return (int)((100.0 * top) / bot); } -#define PCT(top, bot) pct((long)(top), (long)(bot)) +#define PCT(top, bot) pct((u_long)(top), (u_long)(bot)) #if defined(tahoe) #include <machine/cpu.h> @@ -579,7 +580,7 @@ dosum() kread(X_SUM, &sum, sizeof(sum)); #ifdef NEWVM kread(X_VMSTAT, &vm_stat, sizeof(vm_stat)); -#ifdef __386BSD__ +#ifdef __FreeBSD__ fill_in_vm_stat(&vm_stat); #endif #else @@ -807,11 +808,11 @@ domem() kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); (void)printf("\nMemory statistics by type\n"); (void)printf( -" Type In Use MemUse HighUse Limit Requests TypeLimit KernLimit\n"); +" Type In Use MemUse HighUse Limit Requests TypeLimit KernLimit\n"); for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { if (ks->ks_calls == 0) continue; - (void)printf("%11s %7ld %7ldK %8ldK %5ldK %8ld %6u %9u\n", + (void)printf("%12s %7ld %7ldK %8ldK %5ldK %8ld %6u %9u\n", kmemnames[i] ? kmemnames[i] : "undefined", ks->ks_inuse, (ks->ks_memuse + 1023) / 1024, (ks->ks_maxused + 1023) / 1024, diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c index 44625b05568c..c24b88502b05 100644 --- a/usr.bin/w/w.c +++ b/usr.bin/w/w.c @@ -289,11 +289,15 @@ main(argc, argv) if (argwidth < 4) argwidth = 8; for (ep = ehead; ep != NULL; ep = ep->next) { - ep->args = strdup(kvm_getargs(ep->proc, kvm_getu(ep->proc))); - if (ep->args == NULL) { - error("out of memory"); - exit(1); + if(ep->proc != NULL) { + ep->args = strdup(kvm_getargs(ep->proc, kvm_getu(ep->proc))); + if (ep->args == NULL) { + error("out of memory"); + exit(1); + } } + + } /* sort by idle time */ if (sortidle && ehead != NULL) { @@ -326,7 +330,10 @@ main(argc, argv) printf(" - "); else prttime(ep->idle, " "); - printf("%.*s\n", argwidth, ep->args); + if(ep->args) + printf("%.*s\n", argwidth, ep->args); + else + printf("-\n"); } exit(0); } diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c index e7c1775c2cf7..503560c15215 100644 --- a/usr.bin/wall/wall.c +++ b/usr.bin/wall/wall.c @@ -156,7 +156,7 @@ makemsg(fname) } (void)fprintf(fp, "%79s\r\n", " "); - if (*fname && !(freopen(fname, "r", stdin))) { + if (fname && *fname && !(freopen(fname, "r", stdin))) { (void)fprintf(stderr, "wall: can't read %s.\n", fname); exit(1); } diff --git a/usr.bin/wc/wc.1 b/usr.bin/wc/wc.1 index 56c592aa829a..b36f8a5cb2f8 100644 --- a/usr.bin/wc/wc.1 +++ b/usr.bin/wc/wc.1 @@ -32,7 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)wc.1 6.4 (Berkeley) 6/27/91 +.\" from: @(#)wc.1 6.4 (Berkeley) 6/27/91 +.\" $Id: wc.1,v 1.2 1993/11/04 19:41:28 jtc Exp $ .\" .Dd June 27, 1991 .Dt WC 1 @@ -42,7 +43,8 @@ .Nd Word, line, and byte count. .Sh SYNOPSIS .Nm wc -.Op Fl clw +.Op Fl c | Fl m +.Op Fl lw .Op Ar file ... .Sh DESCRIPTION The @@ -66,6 +68,9 @@ is written to the standard output. .It Fl l The number of lines in each input file is written to the standard output. +.It Fl m +The number of characters in each input file +is written to the standard output. .It Fl w The number of words in each input file is written to the standard output. @@ -101,8 +106,6 @@ lines words bytes file_name .Pp The counts for lines, words and bytes are integers separated by spaces. -The ordering of the display of the number of lines, words, -and/or bytes is the order in which the options were specified. .Pp The .Nm wc @@ -110,5 +113,5 @@ utility exits 0 on success, and >0 if an error occurs. .Sh STANDARDS The .Nm wc -function conforms to -.St -p1003.2 . +utility conforms to +.St -p1003.2-92 . diff --git a/usr.bin/wc/wc.c b/usr.bin/wc/wc.c index 65df315f6d9e..1b9658e5a5ba 100644 --- a/usr.bin/wc/wc.c +++ b/usr.bin/wc/wc.c @@ -38,86 +38,88 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)wc.c 5.7 (Berkeley) 3/2/91"; +/*static char sccsid[] = "from: @(#)wc.c 5.7 (Berkeley) 3/2/91";*/ +static char rcsid[] = "$Id: wc.c,v 1.2.2.1 1994/03/07 01:39:22 rgrimes Exp $"; #endif /* not lint */ /* wc line, word and char count */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <errno.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/file.h> -#include <stdio.h> - -#define DEL 0177 /* del char */ -#define NL 012 /* newline char */ -#define SPACE 040 /* space char */ -#define TAB 011 /* tab char */ +#include <unistd.h> +#include <err.h> +static void print_counts(); +static void cnt(); static long tlinect, twordct, tcharct; static int doline, doword, dochar; +static int rval = 0; +int main(argc, argv) int argc; char **argv; { extern int optind; register int ch; - int total; + + setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "lwcm")) != -1) + switch((char)ch) { + case 'l': + doline = 1; + break; + case 'w': + doword = 1; + break; + case 'c': + case 'm': + dochar = 1; + break; + case '?': + default: + fprintf(stderr, "usage: wc [-c | -m] [-lw] [file ...]\n"); + exit(1); + } + argv += optind; + argc -= optind; /* * wc is unusual in that its flags are on by default, so, * if you don't get any arguments, you have to turn them * all on. */ - if (argc > 1 && argv[1][0] == '-' && argv[1][1]) { - while ((ch = getopt(argc, argv, "lwc")) != EOF) - switch((char)ch) { - case 'l': - doline = 1; - break; - case 'w': - doword = 1; - break; - case 'c': - dochar = 1; - break; - case '?': - default: - fputs("usage: wc [-lwc] [files]\n", stderr); - exit(1); - } - argv += optind; - argc -= optind; - } - else { - ++argv; - --argc; + if (!doline && !doword && !dochar) { doline = doword = dochar = 1; } - total = 0; if (!*argv) { cnt((char *)NULL); - putchar('\n'); - } - else do { - cnt(*argv); - printf(" %s\n", *argv); - ++total; - } while(*++argv); + } else { + int dototal = (argc > 1); + + do { + cnt(*argv); + } while(*++argv); - if (total > 1) { - if (doline) - printf(" %7ld", tlinect); - if (doword) - printf(" %7ld", twordct); - if (dochar) - printf(" %7ld", tcharct); - puts(" total"); + if (dototal) { + print_counts (tlinect, twordct, tcharct, "total"); + } } - exit(0); + + exit(rval); } + +static void cnt(file) char *file; { @@ -132,120 +134,126 @@ cnt(file) linect = wordct = charct = 0; if (file) { if ((fd = open(file, O_RDONLY, 0)) < 0) { - perror(file); - exit(1); + warn ("%s", file); + rval = 1; + return; } - if (!doword) { - /* - * line counting is split out because it's a lot - * faster to get lines than to get words, since - * the word count requires some logic. - */ - if (doline) { - while(len = read(fd, buf, MAXBSIZE)) { - if (len == -1) { - perror(file); - exit(1); - } - charct += len; - for (C = buf; len--; ++C) - if (*C == '\n') - ++linect; - } - tlinect += linect; - printf(" %7ld", linect); - if (dochar) { - tcharct += charct; - printf(" %7ld", charct); - } - close(fd); - return; + } else { + fd = STDIN_FILENO; + } + + if (!doword) { + /* + * line counting is split out because it's a lot + * faster to get lines than to get words, since + * the word count requires some logic. + */ + if (doline) { + while((len = read(fd, buf, MAXBSIZE)) > 0) { + charct += len; + for (C = buf; len--; ++C) + if (*C == '\n') + ++linect; + } + if (len == -1) { + warn ("%s", file); + rval = 1; } - /* - * if all we need is the number of characters and - * it's a directory or a regular or linked file, just - * stat the puppy. We avoid testing for it not being - * a special device in case someone adds a new type - * of inode. - */ - if (dochar) { - int ifmt; + } - if (fstat(fd, &sbuf)) { - perror(file); - exit(1); - } + /* + * if all we need is the number of characters and + * it's a directory or a regular or linked file, just + * stat the puppy. We avoid testing for it not being + * a special device in case someone adds a new type + * of inode. + */ + else if (dochar) { + int ifmt; + if (fstat(fd, &sbuf)) { + warn ("%s", file); + rval = 1; + } else { ifmt = sbuf.st_mode & S_IFMT; if (ifmt == S_IFREG || ifmt == S_IFLNK || ifmt == S_IFDIR) { - printf(" %7ld", sbuf.st_size); - tcharct += sbuf.st_size; - close(fd); - return; + charct = sbuf.st_size; + } else { + while((len = read(fd, buf, MAXBSIZE)) > 0) { + charct += len; + } + if (len == -1) { + warn ("%s", file); + rval = 1; + } } } } } else - fd = 0; - /* do it the hard way... */ - for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) { - if (len == -1) { - perror(file); - exit(1); - } - charct += len; - for (C = buf; len--; ++C) - switch(*C) { - case NL: - ++linect; - case TAB: - case SPACE: + { + /* do it the hard way... */ + gotsp = 1; + while ((len = read(fd, buf, MAXBSIZE)) > 0) { + charct += len; + for (C = buf; len--; ++C) { + if (isspace (*C)) { gotsp = 1; - continue; - default: -#ifdef notdef - /* - * This line of code implements the - * original V7 wc algorithm, i.e. - * a non-printing character doesn't - * toggle the "word" count, so that - * " ^D^F " counts as 6 spaces, - * while "foo^D^Fbar" counts as 8 - * characters. - * - * test order is important -- gotsp - * will normally be NO, so test it - * first - */ - if (gotsp && *C > SPACE && *C < DEL) { -#endif + if (*C == '\n') { + ++linect; + } + } else { /* - * This line implements the manual - * page, i.e. a word is a "maximal + * This line implements the POSIX + * spec, i.e. a word is a "maximal * string of characters delimited by - * spaces, tabs or newlines." Notice - * nothing was said about a character - * being printing or non-printing. + * whitespace." Notice nothing was + * said about a character being + * printing or non-printing. */ if (gotsp) { gotsp = 0; ++wordct; } + } } + } + if (len == -1) { + warn ("%s", file); + rval = 1; + } } - if (doline) { - tlinect += linect; - printf(" %7ld", linect); - } - if (doword) { - twordct += wordct; - printf(" %7ld", wordct); - } - if (dochar) { - tcharct += charct; - printf(" %7ld", charct); + + print_counts (linect, wordct, charct, file ? file : ""); + + /* don't bother checkint doline, doword, or dochar --- speeds + up the common case */ + tlinect += linect; + twordct += wordct; + tcharct += charct; + + if (close(fd)) { + warn ("%s", file); + rval = 1; } - close(fd); +} + + +void +print_counts (lines, words, chars, name) + long lines; + long words; + long chars; + char *name; +{ + + if (doline) + printf(" %7ld", lines); + if (doword) + printf(" %7ld", words); + if (dochar) + printf(" %7ld", chars); + + printf (" %s\n", name); } diff --git a/usr.bin/whereis/whereis.c b/usr.bin/whereis/whereis.c index 27137f6ce503..82c084462801 100644 --- a/usr.bin/whereis/whereis.c +++ b/usr.bin/whereis/whereis.c @@ -51,12 +51,13 @@ static char *bindirs[] = { "/sbin", "/usr/bin", "/usr/games", - "/usr/gnu", + "/usr/gnu/bin", "/usr/include", "/usr/libexec", "/usr/sbin", "/usr/share", "/usr/X386/bin", + "/usr/gnu/bin", "/usr/local/bin", "/etc", 0 @@ -95,6 +96,14 @@ static char *mandirs[] = { "/usr/local/man/man6", "/usr/local/man/man7", "/usr/local/man/man8", + "/usr/gnu/man/man1", + "/usr/gnu/man/man2", + "/usr/gnu/man/man3", + "/usr/gnu/man/man4", + "/usr/gnu/man/man5", + "/usr/gnu/man/man6", + "/usr/gnu/man/man7", + "/usr/gnu/man/man8", 0 }; static char *srcdirs[] = { @@ -102,7 +111,9 @@ static char *srcdirs[] = { "/usr/src/contrib", "/usr/src/etc", "/usr/src/games", - "/usr/src/gnu", + "/usr/src/gnu/lib", + "/usr/src/gnu/libexec", + "/usr/src/gnu/usr.bin", "/usr/src/include", "/usr/src/lib", "/usr/src/libexec", diff --git a/usr.bin/which/which.csh b/usr.bin/which/which.csh index 5295deb24578..9c74c3193c97 100644 --- a/usr.bin/which/which.csh +++ b/usr.bin/which/which.csh @@ -57,7 +57,7 @@ foreach arg ( $argv ) if ( -e $arg ) then echo $arg else - echo $arg not found + echo "$arg not found" > /dev/stderr endif continue else @@ -70,6 +70,6 @@ foreach arg ( $argv ) end endif if ( ! $?found ) then - echo no $arg in $path + echo "no $arg in $path" > /dev/stderr endif end diff --git a/usr.bin/window/Makefile b/usr.bin/window/Makefile index 4019676f1be5..872912bc9565 100644 --- a/usr.bin/window/Makefile +++ b/usr.bin/window/Makefile @@ -1,7 +1,6 @@ # @(#)Makefile 5.7 (Berkeley) 5/11/90 PROG= window -CFLAGS+=-R SRCS= char.c cmd.c cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \ context.c error.c lcmd.c lcmd1.c lcmd2.c main.c mloop.c parser1.c \ parser2.c parser3.c parser4.c parser5.c scanner.c startup.c string.c \ diff --git a/usr.bin/window/char.c b/usr.bin/window/char.c index df6e9914e9c8..886d5abe5daa 100644 --- a/usr.bin/window/char.c +++ b/usr.bin/window/char.c @@ -77,41 +77,41 @@ char _cmap[] = { _P|_U, _P|_U, _P|_U, _P|_U, _P|_U, _P|_U, _P|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U, - _C|_U, _C|_U, _C|_U, _C|_U + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U, + _P|_U, _P|_U, _P|_U, _P|_U }; char *_unctrl[] = { @@ -131,20 +131,20 @@ char *_unctrl[] = { "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "^?", - "\\200","\\201","\\202","\\203","\\204","\\205","\\206","\\207", - "\\210","\\211","\\212","\\213","\\214","\\215","\\216","\\217", - "\\220","\\221","\\222","\\223","\\224","\\225","\\226","\\227", - "\\230","\\231","\\232","\\233","\\234","\\235","\\236","\\237", - "\\240","\\241","\\242","\\243","\\244","\\245","\\246","\\247", - "\\250","\\251","\\252","\\253","\\254","\\255","\\256","\\257", - "\\260","\\261","\\262","\\263","\\264","\\265","\\266","\\267", - "\\270","\\271","\\272","\\273","\\274","\\275","\\276","\\277", - "\\300","\\301","\\302","\\303","\\304","\\305","\\306","\\307", - "\\310","\\311","\\312","\\313","\\314","\\315","\\316","\\317", - "\\320","\\321","\\322","\\323","\\324","\\325","\\326","\\327", - "\\330","\\331","\\332","\\333","\\334","\\335","\\336","\\337", - "\\340","\\341","\\342","\\343","\\344","\\345","\\346","\\347", - "\\350","\\351","\\352","\\353","\\354","\\355","\\356","\\357", - "\\360","\\361","\\362","\\363","\\364","\\365","\\366","\\367", - "\\370","\\371","\\372","\\373","\\374","\\375","\\376","\\377" + "\200","\201","\202","\203","\204","\205","\206","\207", + "\210","\211","\212","\213","\214","\215","\216","\217", + "\220","\221","\222","\223","\224","\225","\226","\227", + "\230","\231","\232","\233","\234","\235","\236","\237", + "\240","\241","\242","\243","\244","\245","\246","\247", + "\250","\251","\252","\253","\254","\255","\256","\257", + "\260","\261","\262","\263","\264","\265","\266","\267", + "\270","\271","\272","\273","\274","\275","\276","\277", + "\300","\301","\302","\303","\304","\305","\306","\307", + "\310","\311","\312","\313","\314","\315","\316","\317", + "\320","\321","\322","\323","\324","\325","\326","\327", + "\330","\331","\332","\333","\334","\335","\336","\337", + "\340","\341","\342","\343","\344","\345","\346","\347", + "\350","\351","\352","\353","\354","\355","\356","\357", + "\360","\361","\362","\363","\364","\365","\366","\367", + "\370","\371","\372","\373","\374","\375","\376","\377" }; diff --git a/usr.bin/window/cmd.c b/usr.bin/window/cmd.c index 63630e6c1322..efbfbc97bbac 100644 --- a/usr.bin/window/cmd.c +++ b/usr.bin/window/cmd.c @@ -43,7 +43,7 @@ static char sccsid[] = "@(#)cmd.c 3.40 (Berkeley) 6/6/90"; docmd() { - register char c; + register c; register struct ww *w; char out = 0; diff --git a/usr.bin/window/cmd1.c b/usr.bin/window/cmd1.c index 8be41db3bede..d9bc3aed48b1 100644 --- a/usr.bin/window/cmd1.c +++ b/usr.bin/window/cmd1.c @@ -115,7 +115,7 @@ int maxrow, maxcol; { static int scount; int count; - char c; + int c; int oldrow = *row, oldcol = *col; while ((c = wwgetc()) >= 0) { diff --git a/usr.bin/window/ww.h b/usr.bin/window/ww.h index 9acbc8e3b347..ca6b49533f77 100644 --- a/usr.bin/window/ww.h +++ b/usr.bin/window/ww.h @@ -256,8 +256,8 @@ char *wwib; /* input (keyboard) buffer */ char *wwibe; /* wwib + sizeof buffer */ char *wwibp; /* current read position in buffer */ char *wwibq; /* current write position in buffer */ -#define wwgetc() (wwibp < wwibq ? *wwibp++ & 0x7f : -1) -#define wwpeekc() (wwibp < wwibq ? *wwibp & 0x7f : -1) +#define wwgetc() (wwibp < wwibq ? *wwibp++ & 0xff : -1) +#define wwpeekc() (wwibp < wwibq ? *wwibp & 0xff : -1) #define wwungetc(c) (wwibp > wwib ? *--wwibp = (c) : -1) /* things for short circuiting wwiomux() */ diff --git a/usr.bin/window/wwgets.c b/usr.bin/window/wwgets.c index 111e52dba33c..b53bae177137 100644 --- a/usr.bin/window/wwgets.c +++ b/usr.bin/window/wwgets.c @@ -47,7 +47,7 @@ int n; register struct ww *w; { register char *p = buf; - register char c; + register c; char uc = w->ww_unctrl; static void rub(); diff --git a/usr.bin/window/wwinit.c b/usr.bin/window/wwinit.c index 033864c00328..609cdcee0c18 100644 --- a/usr.bin/window/wwinit.c +++ b/usr.bin/window/wwinit.c @@ -104,6 +104,8 @@ wwinit() wwnewtty.ww_termios.c_lflag = 0; for (i = 0; i < NCCS; i++) wwnewtty.ww_termios.c_cc[i] = _POSIX_VDISABLE; + wwnewtty.ww_termios.c_cc[VMIN] = 1; + wwnewtty.ww_termios.c_cc[VTIME] = 0; #endif wwnewtty.ww_fflags = wwoldtty.ww_fflags | FASYNC; if (wwsettty(0, &wwnewtty) < 0) @@ -180,6 +182,16 @@ wwinit() #endif wwbaud = 38400; break; +#ifdef B57600 + case B57600: + wwbaud = 57600; + break; +#endif +#ifdef B115200 + case B115200: + wwbaud = 115200; + break; +#endif } if (xxinit() < 0) diff --git a/usr.bin/window/wwinschar.c b/usr.bin/window/wwinschar.c index 8b32fc918949..9c8a2c6527b1 100644 --- a/usr.bin/window/wwinschar.c +++ b/usr.bin/window/wwinschar.c @@ -43,7 +43,7 @@ static char sccsid[] = "@(#)wwinschar.c 3.20 (Berkeley) 6/6/90"; wwinschar(w, row, col, c, m) register struct ww *w; -char c, m; +unsigned char c, m; { register i; int nvis; diff --git a/usr.bin/window/wwlabel.c b/usr.bin/window/wwlabel.c index bdff90d7aaf0..0b1998d03a76 100644 --- a/usr.bin/window/wwlabel.c +++ b/usr.bin/window/wwlabel.c @@ -60,7 +60,7 @@ char *l; register char *fmap; register char *smap; char touched; - char *p; + unsigned char *p; if (f->ww_fmap == 0) return; diff --git a/usr.bin/window/wwwrite.c b/usr.bin/window/wwwrite.c index 39269a05b780..3b8b6bb0a6dc 100644 --- a/usr.bin/window/wwwrite.c +++ b/usr.bin/window/wwwrite.c @@ -58,12 +58,12 @@ static char sccsid[] = "@(#)wwwrite.c 3.33 (Berkeley) 6/6/90"; */ wwwrite(w, p, n) register struct ww *w; -register char *p; +register unsigned char *p; int n; { char hascursor; - char *savep = p; - char *q = p + n; + unsigned char *savep = p; + unsigned char *q = p + n; char *r = 0; char *s; diff --git a/usr.bin/xargs/xargs.1 b/usr.bin/xargs/xargs.1 index 4b057a4ceab5..f953f9136ff2 100644 --- a/usr.bin/xargs/xargs.1 +++ b/usr.bin/xargs/xargs.1 @@ -158,7 +158,7 @@ The .Ar utility could not be found. .It 1 -Some other error occured. +Some other error occurred. .El .Sh "SEE ALSO" .Xr echo 1 , diff --git a/usr.bin/yacc/main.c b/usr.bin/yacc/main.c index f17e19b435cd..8105891d8aaf 100644 --- a/usr.bin/yacc/main.c +++ b/usr.bin/yacc/main.c @@ -71,7 +71,7 @@ int k; } -onintr() +void onintr() { done(1); } |
