BSD 4_4 release
[unix-history] / usr / src / contrib / ed / g.c
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rodney Ruddock of the University of Guelph.
*
* 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[] = "@(#)g.c 8.1 (Berkeley) 5/31/93";
#endif /* not lint */
#include <sys/types.h>
#include <limits.h>
#include <regex.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef DBI
#include <db.h>
#endif
#include "ed.h"
#include "extern.h"
static int find_line __P((LINE *));
static void w_cmd_l_file __P((FILE *, FILE *, int *));
/*
* Find a line that we noted matched the RE earlier in the current
* buffer (it may have disappeared because of the commands in the
* command list).
*/
static int
find_line(dot)
LINE *dot;
{
LINE *l_cl;
l_cl = top;
for (;;) {
if (l_cl == dot)
return (1);
if (l_cl == bottom)
return (0);
l_cl = l_cl->below;
}
}
/*
* Write the command line to a STDIO tmp file. See g() below.
* This allows us to use cmd_loop to run the command list because
* we "trick" cmd_loop into reading a STDIO file instead of stdin.
*/
static void
w_cmd_l_file(fp, inputt, errnum)
FILE *fp, *inputt;
int *errnum;
{
int sl=0, jmp_flag, l_cnt=0;
if (jmp_flag = setjmp(ctrl_position3))
return;
for (;;) {
sigspecial3 = 1;
ss = getc(inputt);
sigspecial3 = 0;
skip1:
if (ss == EOF)
goto skip2;
else if (ss == '\n') {
if (sl != '\\') {
skip2:
if (l_cnt == 0)
fputc('p', fp);
else
fputc(sl, fp);
break;
}
}
else if ((ss == '\\') && (sl == '\\')) {
sigspecial3 = 1;
sl = getc(inputt);
sigspecial3 = 0;
if (sl == '\\') {
sigspecial3 = 1;
sl = getc(inputt);
sigspecial3 = 0;
if (sl == EOF)
goto skip2;
if (sl == '\n') {
fputc('\\', fp);
ss = sl;
}
else {
fputc('\\', fp);
fputc('\\', fp);
ss = sl;
sl = '\\';
goto skip1;
}
}
else {
fputc('\\', fp);
fputc('\\', fp);
if ((sl == '\n') || (sl == EOF))
goto skip2;
else
ss = sl;
}
}
else if (l_cnt)
fputc(sl, fp);
sl = ss;
l_cnt++;
}
fputc('\n', fp);
if (ss == EOF)
clearerr(inputt);
}
/*
* The global function. All global commands (g, G, v, and V) are handled
* in here. The lines to be affected by the command list are 1st noted
* and then the command list is invoked for each line matching the RE.
* Note the trick of how the command list is executed. Saves a lot of
* code (and allows for \n's in substitutions).
*/
void
g(inputt, errnum)
FILE *inputt;
int *errnum;
{
static char *l_template_g;
char *l_patt;
static int l_template_flag = 0;
int l_re_success, l_flag_v = 0, l_err, l_num;
register l_gut_cnt, a;
register LINE **l_gut=gut;
FILE *l_fp;
#ifdef POSIX
LINE *l_posix_cur;
#endif
if (Start_default && End_default) {
Start = top;
End = bottom;
} else
if (Start_default)
Start = End;
if (Start == NULL) {
strcpy(help_msg, "buffer empty");
*errnum = -1;
return;
}
if (l_template_flag == 0) {
sigspecial++;
l_template_flag = 1;
l_template_g = calloc(FILENAME_LEN, sizeof(char));
sigspecial--;
if (sigint_flag && (!sigspecial))
SIGINT_ACTION;
if (l_template_g == NULL) {
*errnum = -1;
strcpy(help_msg, "out of memory error");
return;
}
}
/* set up the STDIO command list file */
memmove(l_template_g, "/tmp/_4.4bsd_ed_g_XXXXXX\0", 24);
mktemp(l_template_g);
if ((ss == 'v') || (ss == 'V'))
l_flag_v = 1;
if ((ss == 'G') || (ss == 'V')) {
/*
* If it's an interactive global command we use stdin, not a
* file.
*/
GV_flag = 1;
l_fp = stdin;
} else {
sigspecial++;
if ((l_fp = fopen(l_template_g, "w+")) == NULL) {
perror("ed: file I/O error, save buffer in ed.hup");
do_hup(); /* does not return */
}
sigspecial--;
if (sigint_flag && (!sigspecial))
goto point;
}
ss = getc(inputt);
/* Get the RE for the global command. */
l_patt = get_pattern(ss, inputt, errnum, 0);
/* Instead of: if ((*errnum == -1) && (ss == '\n'))... */
if (*errnum < -1)
return;
*errnum = 0;
if ((l_patt[1] == '\0') && (RE_flag == 0)) {
*errnum = -1;
ungetc(ss, inputt);
return;
} else
if (l_patt[1] || (RE_patt == NULL)) {
sigspecial++;
free(RE_patt);
RE_patt = l_patt;
sigspecial--;
if (sigint_flag && (!sigspecial))
goto point;
}
RE_sol = (RE_patt[1] == '^') ? 1 : 0;
if ((RE_patt[1]) &&
(regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) {
regerror(l_err, &RE_comp, help_msg, 128);
*errnum = -1;
RE_flag = 0;
ungetc(ss, inputt);
return;
}
RE_flag = 1;
if (GV_flag)
ss = getc(inputt);
#ifdef POSIX
l_posix_cur = current;
#endif
current = Start;
sigspecial++;
if ((l_num = line_number(bottom)) > gut_num) {
sigspecial++;
gut_num = l_num + 512;
free(l_gut);
gut = l_gut = malloc(sizeof(LINE **) * gut_num);
sigspecial--;
if (l_gut == NULL) {
*errnum = -1;
strcpy(help_msg, "out of memory error");
#ifdef POSIX
current = l_posix_cur;
#endif
ungetc('\n', inputt);
return;
}
}
l_gut_cnt = 0;
for (;;) {
/*
* Find the lines in the buffer that the global command wants
* to work with.
*/
get_line(current->handle, current->len);
if (sigint_flag && (!sigspecial))
goto point;
l_re_success =
regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
/* l_re_success=0 => success */
if ( (l_re_success == 0 && l_flag_v == 0) ||
(l_re_success && l_flag_v)) {
l_gut[l_gut_cnt++] = current;
}
if (End == current)
break;
current = current->below;
}
sigspecial--;
if (sigint_flag && (!sigspecial))
goto point;
if (l_gut_cnt == 0) {
strcpy(help_msg, "no matches found");
#ifdef POSIX
current = l_posix_cur;
#endif
return;
}
/* if non-interactive, get the command list */
if (GV_flag == 0) {
sigspecial++;
w_cmd_l_file(l_fp, inputt, errnum);
sigspecial--;
if (sigint_flag)
goto point;
}
if (g_flag == 0)
u_clr_stk();
sigspecial++;
for (a=0; a<l_gut_cnt; a++) {
/*
* Execute the command list on the lines that still exist that
* we indicated earlier that global wants to work with.
*/
if (sigint_flag)
goto point;
if (GV_flag == 0)
fseek(l_fp, (off_t)0, 0);
if (find_line(l_gut[a])) {
current = (l_gut[a]);
get_line(current->handle, current->len);
if (sigint_flag)
goto point;
if (GV_flag == 1)
printf("%s\n", text);
g_flag++;
explain_flag--;
sigspecial--;
cmd_loop(l_fp, errnum);
sigspecial++;
explain_flag++;
g_flag--;
if ((GV_flag == 1) && (*errnum < 0)) {
ungetc('\n', l_fp);
break;
}
*errnum = 0;
}
}
point:
if (GV_flag == 0) {
fclose(l_fp);
unlink(l_template_g);
}
else
ungetc('\n', inputt);
GV_flag = 0;
#ifdef POSIX
current = l_posix_cur;
#endif
sigspecial--;
if (sigint_flag)
SIGINT_ACTION;
}