/* * Process the commands arguments */ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) { /* handle (s)ubstitution command */ if (sed_cmd->cmd == 's') cmdstr += parse_subst_cmd(sed_cmd, cmdstr); /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ else if (strchr("aic", sed_cmd->cmd)) { if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') bb_error_msg_and_die ("only a beginning address can be specified for edit commands"); while(isspace(*cmdstr)) cmdstr++; sed_cmd->string = bb_xstrdup(cmdstr); parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0); cmdstr += strlen(cmdstr); /* handle file cmds: (r)ead */ } else if(strchr("rw", sed_cmd->cmd)) { if (sed_cmd->end_line || sed_cmd->end_match) bb_error_msg_and_die("Command only uses one address"); cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string); if(sed_cmd->cmd=='w') sed_cmd->file=bb_xfopen(sed_cmd->string,"w"); /* handle branch commands */ } else if (strchr(":bt", sed_cmd->cmd)) { int length; while(isspace(*cmdstr)) cmdstr++; length = strcspn(cmdstr, semicolon_whitespace); if (length) { sed_cmd->string = strndup(cmdstr, length); cmdstr += length; } } /* translation command */ else if (sed_cmd->cmd == 'y') { char *match, *replace; int i=cmdstr[0]; cmdstr+=parse_regex_delim(cmdstr, &match, &replace)+1; /* \n already parsed, but \delimiter needs unescaping. */ parse_escapes(match,match,strlen(match),i,i); parse_escapes(replace,replace,strlen(replace),i,i); sed_cmd->string = xcalloc(1, (strlen(match) + 1) * 2); for (i = 0; match[i] && replace[i]; i++) { sed_cmd->string[i * 2] = match[i]; sed_cmd->string[(i * 2) + 1] = replace[i]; } free(match); free(replace); } /* if it wasnt a single-letter command that takes no arguments * then it must be an invalid command. */ else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { bb_error_msg_and_die("Unsupported command %c", sed_cmd->cmd); } /* give back whatever's left over */ return (cmdstr); }
static char *parse_cmd_str(struct sed_cmd * const sed_cmd, const char *const cmdstr) { int idx = 0; /* parse the command * format is: [addr][,addr]cmd * |----||-----||-| * part1 part2 part3 */ /* first part (if present) is an address: either a number or a /regex/ */ if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); /* second part (if present) will begin with a comma */ if (cmdstr[idx] == ',') { idx++; idx += get_address(sed_cmd, &cmdstr[idx], &sed_cmd->end_line, &sed_cmd->end_match); } /* last part (mandatory) will be a command */ if (cmdstr[idx] == '\0') error_msg_and_die("missing command"); sed_cmd->cmd = cmdstr[idx]; /* if it was a single-letter command that takes no arguments (such as 'p' * or 'd') all we need to do is increment the index past that command */ if (strchr("pd", cmdstr[idx])) { idx++; } /* handle (s)ubstitution command */ else if (sed_cmd->cmd == 's') { idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]); } /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ else if (strchr("aic", sed_cmd->cmd)) { if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') error_msg_and_die("only a beginning address can be specified for edit commands"); idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); } /* handle file cmds: (r)ead */ else if (sed_cmd->cmd == 'r') { if (sed_cmd->end_line || sed_cmd->end_match) error_msg_and_die("Command only uses one address"); idx += parse_file_cmd(sed_cmd, &cmdstr[idx]); } else { error_msg_and_die("invalid command"); } /* give back whatever's left over */ return (char *)&cmdstr[idx]; }
/* * Process the commands arguments */ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) { static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}"; enum { IDX_s = 0, IDX_a, IDX_i, IDX_c, IDX_r, IDX_w, IDX_colon, IDX_b, IDX_t, IDX_T, IDX_y, IDX_d, IDX_D, IDX_g, IDX_G, IDX_h, IDX_H, IDX_l, IDX_n, IDX_N, IDX_p, IDX_P, IDX_q, IDX_x, IDX_equal, IDX_lbrace, IDX_rbrace, IDX_nul }; unsigned idx; BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul); idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; /* handle (s)ubstitution command */ if (idx == IDX_s) { cmdstr += parse_subst_cmd(sed_cmd, cmdstr); } /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ else if (idx <= IDX_c) { /* a,i,c */ unsigned len; if (idx < IDX_c) { /* a,i */ if (sed_cmd->end_line || sed_cmd->end_match) bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd); } for (;;) { if (*cmdstr == '\n' || *cmdstr == '\\') { cmdstr++; break; } if (!isspace(*cmdstr)) break; cmdstr++; } len = strlen(cmdstr); sed_cmd->string = copy_parsing_escapes(cmdstr, len); cmdstr += len; /* "\anychar" -> "anychar" */ parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0'); } /* handle file cmds: (r)ead */ else if (idx <= IDX_w) { /* r,w */ if (idx < IDX_w) { /* r */ if (sed_cmd->end_line || sed_cmd->end_match) bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd); } cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); if (sed_cmd->cmd == 'w') { sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); sed_cmd->sw_last_char = '\n'; } } /* handle branch commands */ else if (idx <= IDX_T) { /* :,b,t,T */ int length; cmdstr = skip_whitespace(cmdstr); length = strcspn(cmdstr, semicolon_whitespace); if (length) { sed_cmd->string = xstrndup(cmdstr, length); cmdstr += length; } } /* translation command */ else if (idx == IDX_y) { char *match, *replace; int i = cmdstr[0]; cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1; /* \n already parsed, but \delimiter needs unescaping. */ parse_escapes(match, match, -1, i, i); parse_escapes(replace, replace, -1, i, i); sed_cmd->string = xzalloc((strlen(match) + 1) * 2); for (i = 0; match[i] && replace[i]; i++) { sed_cmd->string[i*2] = match[i]; sed_cmd->string[i*2+1] = replace[i]; } free(match); free(replace); } /* if it wasn't a single-letter command that takes no arguments * then it must be an invalid command. */ else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */ bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); } /* give back whatever's left over */ return cmdstr; }
static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) { int cflags = G.regex_type; char *match; int idx; /* * A substitution command should look something like this: * s/match/replace/ #giIpw * || | ||| * mandatory optional */ idx = parse_regex_delim(substr, &match, &sed_cmd->string); /* determine the number of back references in the match string */ /* Note: we compute this here rather than in the do_subst_command() * function to save processor time, at the expense of a little more memory * (4 bits) per sed_cmd */ /* process the flags */ sed_cmd->which_match = 1; dbg("s flags:'%s'", substr + idx + 1); while (substr[++idx]) { dbg("s flag:'%c'", substr[idx]); /* Parse match number */ if (isdigit(substr[idx])) { if (match[0] != '^') { /* Match 0 treated as all, multiple matches we take the last one. */ const char *pos = substr + idx; /* FIXME: error check? */ sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10); idx = pos - substr - 1; } continue; } /* Skip spaces */ if (isspace(substr[idx])) continue; switch (substr[idx]) { /* Replace all occurrences */ case 'g': if (match[0] != '^') sed_cmd->which_match = 0; break; /* Print pattern space */ case 'p': sed_cmd->sub_p = 1; break; /* Write to file */ case 'w': { char *fname; idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); sed_cmd->sw_file = xfopen_for_write(fname); sed_cmd->sw_last_char = '\n'; free(fname); break; } /* Ignore case (gnu extension) */ case 'i': case 'I': cflags |= REG_ICASE; break; /* Comment */ case '#': // while (substr[++idx]) continue; idx += strlen(substr + idx); // same /* Fall through */ /* End of command */ case ';': case '}': goto out; default: dbg("s bad flags:'%s'", substr + idx); bb_error_msg_and_die("bad option in substitution expression"); } } out: /* compile the match string into a regex */ if (*match != '\0') { /* If match is empty, we use last regex used at runtime */ sed_cmd->sub_match = xzalloc(sizeof(regex_t)); dbg("xregcomp('%s',%x)", match, cflags); xregcomp(sed_cmd->sub_match, match, cflags); dbg("regcomp ok"); } free(match); return idx; }
static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr) { int cflags = 0; char *match; int idx = 0; /* * A substitution command should look something like this: * s/match/replace/ #gIpw * || | ||| * mandatory optional */ idx = parse_regex_delim(substr, &match, &sed_cmd->string); /* determine the number of back references in the match string */ /* Note: we compute this here rather than in the do_subst_command() * function to save processor time, at the expense of a little more memory * (4 bits) per sed_cmd */ /* process the flags */ sed_cmd->which_match=1; while (substr[++idx]) { /* Parse match number */ if(isdigit(substr[idx])) { if(match[0]!='^') { /* Match 0 treated as all, multiple matches we take the last one. */ char *pos=substr+idx; sed_cmd->which_match=(unsigned short)strtol(substr+idx,&pos,10); idx=pos-substr; } continue; } /* Skip spaces */ if(isspace(substr[idx])) continue; switch (substr[idx]) { /* Replace all occurrences */ case 'g': if (match[0] != '^') sed_cmd->which_match = 0; break; /* Print pattern space */ case 'p': sed_cmd->sub_p = 1; break; case 'w': { char *temp; idx+=parse_file_cmd(sed_cmd,substr+idx,&temp); break; } /* Ignore case (gnu exension) */ case 'I': cflags |= REG_ICASE; break; case ';': case '}': goto out; default: bb_error_msg_and_die("bad option in substitution expression"); } } out: /* compile the match string into a regex */ if (*match != '\0') { /* If match is empty, we use last regex used at runtime */ sed_cmd->sub_match = (regex_t *) xmalloc(sizeof(regex_t)); xregcomp(sed_cmd->sub_match, match, cflags); } free(match); return idx; }