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