1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rodney Ruddock of the University of Guelph. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char sccsid[] = "@(#)g.c 5.2 (Berkeley) 01/23/93"; 13 #endif /* not lint */ 14 15 #include <sys/types.h> 16 17 #include <db.h> 18 #include <regex.h> 19 #include <setjmp.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "ed.h" 27 #include "extern.h" 28 29 static int find_line __P((LINE *)); 30 static void w_cmd_l_file __P((FILE *, FILE *, int *)); 31 32 /* 33 * Find a line that we noted matched the RE earlier in the current 34 * buffer (it may have disappeared because of the commands in the 35 * command list). 36 */ 37 int 38 find_line(dot) 39 LINE *dot; 40 { 41 LINE *l_cl; 42 43 l_cl = top; 44 for (;;) { 45 if (l_cl == dot) 46 return (1); 47 if (l_cl == bottom) 48 return (0); 49 l_cl = l_cl->below; 50 } 51 } 52 53 /* 54 * Write the command line to a STDIO tmp file. See g() below. 55 * This allows us to use cmd_loop to run the command list because 56 * we "trick" cmd_loop into reading a STDIO file instead of stdin. 57 */ 58 void 59 w_cmd_l_file(fp, inputt, errnum) 60 FILE *fp, *inputt; 61 int *errnum; 62 { 63 int l_esc = 0, l_cnt = 0; 64 65 for (;;) { 66 ss = getc(inputt); 67 if (ss == '\\') { 68 l_esc = 1; 69 ss = getc(inputt); 70 } 71 if (((ss == '\n') && (l_esc == 0)) || (ss == EOF)) { 72 /* if no command list default command list to 'p' */ 73 if (l_cnt == 0) 74 fputc('p', fp); 75 break; 76 } 77 l_esc = 0; 78 fputc(ss, fp); 79 l_cnt++; 80 } 81 if (ss == EOF) 82 clearerr(inputt); 83 } 84 85 /* 86 * The global function. All global commands (g, G, v, and V) are handled 87 * in here. The lines to be affected by the command list are 1st noted 88 * and then the command list is invoked for each line matching the RE. 89 * Note the trick of how the command list is executed. Saves a lot of 90 * code (and allows for \n's in substitutions). 91 */ 92 void 93 g(inputt, errnum) 94 FILE *inputt; 95 int *errnum; 96 { 97 struct g_list *l_Head, *l_Foot, *l_gut, *l_old; 98 static char *l_template_g; 99 char *l_patt; 100 static int l_template_flag = 0; 101 int l_re_success, l_flag_v = 0, l_err; 102 FILE *l_fp; 103 #ifdef POSIX 104 LINE *l_posix_cur; 105 #endif 106 107 if (start_default && End_default) { 108 start = top; 109 End = bottom; 110 } else 111 if (start_default) 112 start = End; 113 if (start == NULL) { 114 strcpy(help_msg, "bad address"); 115 *errnum = -1; 116 return; 117 } 118 if (sigint_flag) 119 SIGINT_ACTION; 120 121 if (l_template_flag == 0) { 122 l_template_flag = 1; 123 l_template_g = calloc(FILENAME_LEN, sizeof(char)); 124 if (l_template_g == NULL) { 125 *errnum = -1; 126 strcpy(help_msg, "out of memory error"); 127 return; 128 } 129 } 130 /* set up the STDIO command list file */ 131 bcopy("/tmp/_4.4bsd_ed_g_XXXXXX\0", l_template_g, 24); 132 mktemp(l_template_g); 133 134 l_Head = l_Foot = l_gut = l_old = NULL; 135 136 if ((ss == 'v') || (ss == 'V')) 137 l_flag_v = 1; 138 139 if ((ss == 'G') || (ss == 'V')) { 140 /* 141 * If it's an interactive global command we use stdin, not a 142 * file. 143 */ 144 GV_flag = 1; 145 l_fp = stdin; 146 } else { 147 if ((l_fp = fopen(l_template_g, "w+")) == NULL) { 148 perror("ed: file I/O error"); 149 exit(1); 150 } 151 } 152 153 ss = getc(inputt); 154 155 /* Get the RE for the global command. */ 156 l_patt = get_pattern(ss, inputt, errnum, 0); 157 if (sigint_flag) 158 SIGINT_ACTION; 159 /* Instead of: if ((*errnum == -1) && (ss == '\n'))... */ 160 if (*errnum < -1) 161 return; 162 *errnum = 0; 163 if ((l_patt[1] == '\0') && (RE_flag == 0)) { 164 *errnum = -1; 165 ungetc(ss, inputt); 166 return; 167 } else 168 if (l_patt[1] || (RE_patt == NULL)) { 169 free(RE_patt); 170 RE_patt = l_patt; 171 } 172 RE_sol = (RE_patt[1] == '^') ? 1 : 0; 173 if ((RE_patt[1]) && 174 (regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) { 175 regerror(l_err, &RE_comp, help_msg, 128); 176 *errnum = -1; 177 RE_flag = 0; 178 ungetc(ss, inputt); 179 return; 180 } 181 RE_flag = 1; 182 if (sigint_flag) 183 SIGINT_ACTION; 184 #ifdef POSIX 185 l_posix_cur = current; 186 #endif 187 current = start; 188 189 for (;;) { 190 /* 191 * Find the lines in the buffer that the global command wants 192 * to work with. 193 */ 194 if (sigint_flag) 195 goto point; 196 get_line(current->handle, current->len); 197 l_re_success = 198 regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0); 199 /* l_re_success=0 => success */ 200 if ((l_re_success != 0 && l_flag_v == 1) || 201 (l_re_success == 0 && l_flag_v == 0)) { 202 if (l_Head == NULL) { 203 l_gut = malloc(sizeof(struct g_list)); 204 if (l_gut == NULL) { 205 *errnum = -1; 206 strcpy(help_msg, "out of memory error"); 207 #ifdef POSIX 208 current = l_posix_cur; 209 #endif 210 return; 211 } 212 (l_gut->next) = NULL; 213 (l_gut->cell) = current; 214 l_Foot = l_Head = l_gut; 215 } else { 216 (l_gut->next) = malloc(sizeof(struct g_list)); 217 if ((l_gut->next) == NULL) { 218 *errnum = -1; 219 strcpy(help_msg, "out of memory error"); 220 goto clean; 221 } 222 l_gut = l_gut->next; 223 (l_gut->cell) = current; 224 (l_gut->next) = NULL; 225 l_Foot = l_gut; 226 } 227 } 228 if (End == current) 229 break; 230 current = current->below; 231 } 232 233 if (l_Head == NULL) { 234 strcpy(help_msg, "no matches found"); 235 *errnum = -1; 236 #ifdef POSIX 237 current = l_posix_cur; 238 #endif 239 return; 240 } 241 /* if non-interactive, get the command list */ 242 if (GV_flag == 0) 243 w_cmd_l_file(l_fp, inputt, errnum); 244 l_gut = l_Head; 245 246 if (g_flag == 0) 247 u_clr_stk(); 248 249 for (;;) { 250 /* 251 * Execute the command list on the lines that still exist that 252 * we indicated earlier that global wants to work with. 253 */ 254 if (sigint_flag) 255 goto point; 256 if (GV_flag == 0) 257 fseek(l_fp, (off_t)0, 0); 258 if (find_line(l_gut->cell)) { 259 current = (l_gut->cell); 260 get_line(current->handle, current->len); 261 if (GV_flag == 1) 262 printf("%s\n", text); 263 g_flag++; 264 explain_flag--; 265 cmd_loop(l_fp, errnum); 266 explain_flag++; 267 g_flag--; 268 if ((GV_flag == 1) && (*errnum < 0)) { 269 ungetc('\n', l_fp); 270 break; 271 } 272 *errnum = 0; 273 if (l_gut == l_Foot) 274 break; 275 l_gut = l_gut->next; 276 } 277 } 278 279 point: 280 if (GV_flag == 0) { 281 fclose(l_fp); 282 unlink(l_template_g); 283 } 284 GV_flag = 0; 285 clean: 286 /* clean up */ 287 l_gut = l_Head; 288 while (1) { 289 if (l_gut == NULL) 290 break; 291 l_old = l_gut; 292 l_gut = l_gut->next; 293 free(l_old); 294 } 295 #ifdef POSIX 296 current = l_posix_cur; 297 #endif 298 } 299