| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: compile.c,v 1.42 2017/08/01 18:05:53 martijn Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*-  | 
    ||
4  | 
    * Copyright (c) 1992 Diomidis Spinellis.  | 
    ||
5  | 
    * Copyright (c) 1992, 1993  | 
    ||
6  | 
    * The Regents of the University of California. All rights reserved.  | 
    ||
7  | 
    *  | 
    ||
8  | 
    * This code is derived from software contributed to Berkeley by  | 
    ||
9  | 
    * Diomidis Spinellis of Imperial College, University of London.  | 
    ||
10  | 
    *  | 
    ||
11  | 
    * Redistribution and use in source and binary forms, with or without  | 
    ||
12  | 
    * modification, are permitted provided that the following conditions  | 
    ||
13  | 
    * are met:  | 
    ||
14  | 
    * 1. Redistributions of source code must retain the above copyright  | 
    ||
15  | 
    * notice, this list of conditions and the following disclaimer.  | 
    ||
16  | 
    * 2. Redistributions in binary form must reproduce the above copyright  | 
    ||
17  | 
    * notice, this list of conditions and the following disclaimer in the  | 
    ||
18  | 
    * documentation and/or other materials provided with the distribution.  | 
    ||
19  | 
    * 3. Neither the name of the University nor the names of its contributors  | 
    ||
20  | 
    * may be used to endorse or promote products derived from this software  | 
    ||
21  | 
    * without specific prior written permission.  | 
    ||
22  | 
    *  | 
    ||
23  | 
    * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  | 
    ||
24  | 
    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  | 
    ||
25  | 
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  | 
    ||
26  | 
    * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  | 
    ||
27  | 
    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  | 
    ||
28  | 
    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  | 
    ||
29  | 
    * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  | 
    ||
30  | 
    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  | 
    ||
31  | 
    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  | 
    ||
32  | 
    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  | 
    ||
33  | 
    * SUCH DAMAGE.  | 
    ||
34  | 
    */  | 
    ||
35  | 
    |||
36  | 
    #include <sys/types.h>  | 
    ||
37  | 
    #include <sys/stat.h>  | 
    ||
38  | 
    |||
39  | 
    #include <ctype.h>  | 
    ||
40  | 
    #include <errno.h>  | 
    ||
41  | 
    #include <fcntl.h>  | 
    ||
42  | 
    #include <limits.h>  | 
    ||
43  | 
    #include <regex.h>  | 
    ||
44  | 
    #include <stdio.h>  | 
    ||
45  | 
    #include <stdlib.h>  | 
    ||
46  | 
    #include <string.h>  | 
    ||
47  | 
    |||
48  | 
    #include "defs.h"  | 
    ||
49  | 
    #include "extern.h"  | 
    ||
50  | 
    |||
51  | 
    #define LHSZ 128  | 
    ||
52  | 
    #define LHMASK (LHSZ - 1)  | 
    ||
53  | 
    static struct labhash { | 
    ||
54  | 
    struct labhash *lh_next;  | 
    ||
55  | 
    u_int lh_hash;  | 
    ||
56  | 
    struct s_command *lh_cmd;  | 
    ||
57  | 
    int lh_ref;  | 
    ||
58  | 
    } *labels[LHSZ];  | 
    ||
59  | 
    |||
60  | 
    static char *compile_addr(char *, struct s_addr *);  | 
    ||
61  | 
    static char *compile_ccl(char **, char *);  | 
    ||
62  | 
    static char *compile_delimited(char *, char *, int);  | 
    ||
63  | 
    static char *compile_flags(char *, struct s_subst *);  | 
    ||
64  | 
    static char *compile_re(char *, regex_t **);  | 
    ||
65  | 
    static char *compile_subst(char *, struct s_subst *);  | 
    ||
66  | 
    static char *compile_text(void);  | 
    ||
67  | 
    static char *compile_tr(char *, char **);  | 
    ||
68  | 
    static struct s_command  | 
    ||
69  | 
    **compile_stream(struct s_command **);  | 
    ||
70  | 
    static char *duptoeol(char *, char *, char **);  | 
    ||
71  | 
    static void enterlabel(struct s_command *);  | 
    ||
72  | 
    static struct s_command  | 
    ||
73  | 
    *findlabel(char *);  | 
    ||
74  | 
    static void fixuplabel(struct s_command *, struct s_command *);  | 
    ||
75  | 
    static void uselabel(void);  | 
    ||
76  | 
    |||
77  | 
    /*  | 
    ||
78  | 
    * Command specification. This is used to drive the command parser.  | 
    ||
79  | 
    */  | 
    ||
80  | 
    struct s_format { | 
    ||
81  | 
    char code; /* Command code */  | 
    ||
82  | 
    int naddr; /* Number of address args */  | 
    ||
83  | 
    enum e_args args; /* Argument type */  | 
    ||
84  | 
    };  | 
    ||
85  | 
    |||
86  | 
    static struct s_format cmd_fmts[] = { | 
    ||
87  | 
    	{'{', 2, GROUP}, | 
    ||
88  | 
    	{'}', 0, ENDGROUP}, | 
    ||
89  | 
    	{'a', 1, TEXT}, | 
    ||
90  | 
    	{'b', 2, BRANCH}, | 
    ||
91  | 
    	{'c', 2, TEXT}, | 
    ||
92  | 
    	{'d', 2, EMPTY}, | 
    ||
93  | 
    	{'D', 2, EMPTY}, | 
    ||
94  | 
    	{'g', 2, EMPTY}, | 
    ||
95  | 
    	{'G', 2, EMPTY}, | 
    ||
96  | 
    	{'h', 2, EMPTY}, | 
    ||
97  | 
    	{'H', 2, EMPTY}, | 
    ||
98  | 
    	{'i', 1, TEXT}, | 
    ||
99  | 
    	{'l', 2, EMPTY}, | 
    ||
100  | 
    	{'n', 2, EMPTY}, | 
    ||
101  | 
    	{'N', 2, EMPTY}, | 
    ||
102  | 
    	{'p', 2, EMPTY}, | 
    ||
103  | 
    	{'P', 2, EMPTY}, | 
    ||
104  | 
    	{'q', 1, EMPTY}, | 
    ||
105  | 
    	{'r', 1, RFILE}, | 
    ||
106  | 
    	{'s', 2, SUBST}, | 
    ||
107  | 
    	{'t', 2, BRANCH}, | 
    ||
108  | 
    	{'w', 2, WFILE}, | 
    ||
109  | 
    	{'x', 2, EMPTY}, | 
    ||
110  | 
    	{'y', 2, TR}, | 
    ||
111  | 
    	{'!', 2, NONSEL}, | 
    ||
112  | 
    	{':', 0, LABEL}, | 
    ||
113  | 
    	{'#', 0, COMMENT}, | 
    ||
114  | 
    	{'=', 1, EMPTY}, | 
    ||
115  | 
    	{'\0', 0, COMMENT}, | 
    ||
116  | 
    };  | 
    ||
117  | 
    |||
118  | 
    /* The compiled program. */  | 
    ||
119  | 
    struct s_command *prog;  | 
    ||
120  | 
    |||
121  | 
    /*  | 
    ||
122  | 
    * Compile the program into prog.  | 
    ||
123  | 
    * Initialise appends.  | 
    ||
124  | 
    */  | 
    ||
125  | 
    void  | 
    ||
126  | 
    compile(void)  | 
    ||
127  | 
    { | 
    ||
128  | 
    18988  | 
    *compile_stream(&prog) = NULL;  | 
    |
129  | 
    9494  | 
    fixuplabel(prog, NULL);  | 
    |
130  | 
    9494  | 
    uselabel();  | 
    |
131  | 
    9494  | 
    appends = xreallocarray(NULL, appendnum, sizeof(struct s_appends));  | 
    |
132  | 
    9494  | 
    match = xreallocarray(NULL, maxnsub + 1, sizeof(regmatch_t));  | 
    |
133  | 
    9494  | 
    }  | 
    |
134  | 
    |||
135  | 
    #define EATSPACE() do {							\ | 
    ||
136  | 
    if (p) \  | 
    ||
137  | 
    while (isascii((unsigned char)*p) && \  | 
    ||
138  | 
    isspace((unsigned char)*p)) \  | 
    ||
139  | 
    p++; \  | 
    ||
140  | 
    } while (0)  | 
    ||
141  | 
    |||
142  | 
    static struct s_command **  | 
    ||
143  | 
    compile_stream(struct s_command **link)  | 
    ||
144  | 
    { | 
    ||
145  | 
    18988  | 
    char *p;  | 
    |
146  | 
    static char *lbuf; /* To avoid excessive malloc calls */  | 
    ||
147  | 
    static size_t bufsize;  | 
    ||
148  | 
    struct s_command *cmd, *cmd2, *stack;  | 
    ||
149  | 
    struct s_format *fp;  | 
    ||
150  | 
    int naddr; /* Number of addresses */  | 
    ||
151  | 
    |||
152  | 
    stack = 0;  | 
    ||
153  | 
    9494  | 
    	for (;;) { | 
    |
154  | 
    ✓✓ | 40502  | 
    		if ((p = cu_fgets(&lbuf, &bufsize)) == NULL) { | 
    
155  | 
    ✓✓ | 9333  | 
    if (stack != 0)  | 
    
156  | 
    error(COMPILE, "unexpected EOF (pending }'s)");  | 
    ||
157  | 
    9319  | 
    return (link);  | 
    |
158  | 
    }  | 
    ||
159  | 
    |||
160  | 
    ✓✗✓✗ ✓✓  | 
    113457  | 
    semicolon: EATSPACE();  | 
    
161  | 
    ✓✓✓✓ | 
    63746  | 
    if (*p == '#' || *p == '\0')  | 
    
162  | 
    continue;  | 
    ||
163  | 
    ✗✓ | 30486  | 
    		if (*p == ';') { | 
    
164  | 
    p++;  | 
    ||
165  | 
    goto semicolon;  | 
    ||
166  | 
    }  | 
    ||
167  | 
    30486  | 
    *link = cmd = xmalloc(sizeof(struct s_command));  | 
    |
168  | 
    30486  | 
    link = &cmd->next;  | 
    |
169  | 
    30486  | 
    cmd->nonsel = cmd->inrange = 0;  | 
    |
170  | 
    /* First parse the addresses */  | 
    ||
171  | 
    naddr = 0;  | 
    ||
172  | 
    |||
173  | 
    /* Valid characters to start an address */  | 
    ||
174  | 
    #define	addrchar(c)	(strchr("0123456789/\\$", (c))) | 
    ||
175  | 
    ✓✓ | 30486  | 
    		if (addrchar(*p)) { | 
    
176  | 
    naddr++;  | 
    ||
177  | 
    2775  | 
    cmd->a1 = xmalloc(sizeof(struct s_addr));  | 
    |
178  | 
    2775  | 
    p = compile_addr(p, cmd->a1);  | 
    |
179  | 
    ✓✗✓✗ ✓✓  | 
    9648  | 
    EATSPACE(); /* EXTENSION */  | 
    
180  | 
    ✓✓ | 2775  | 
    			if (*p == ',') { | 
    
181  | 
    243  | 
    p++;  | 
    |
182  | 
    ✓✗✓✗ ✗✓  | 
    729  | 
    EATSPACE(); /* EXTENSION */  | 
    
183  | 
    naddr++;  | 
    ||
184  | 
    243  | 
    cmd->a2 = xmalloc(sizeof(struct s_addr));  | 
    |
185  | 
    243  | 
    p = compile_addr(p, cmd->a2);  | 
    |
186  | 
    ✓✗✓✗ ✓✓  | 
    834  | 
    EATSPACE();  | 
    
187  | 
    			} else { | 
    ||
188  | 
    2532  | 
    cmd->a2 = 0;  | 
    |
189  | 
    }  | 
    ||
190  | 
    		} else { | 
    ||
191  | 
    27704  | 
    cmd->a1 = cmd->a2 = 0;  | 
    |
192  | 
    }  | 
    ||
193  | 
    |||
194  | 
    nonsel: /* Now parse the command */  | 
    ||
195  | 
    ✓✓ | 30803  | 
    if (!*p)  | 
    
196  | 
    error(COMPILE, "command expected");  | 
    ||
197  | 
    30782  | 
    cmd->code = *p;  | 
    |
198  | 
    ✓✓ | 1080010  | 
    for (fp = cmd_fmts; fp->code; fp++)  | 
    
199  | 
    ✓✓ | 539991  | 
    if (fp->code == *p)  | 
    
200  | 
    break;  | 
    ||
201  | 
    ✓✓ | 30782  | 
    if (!fp->code)  | 
    
202  | 
    error(COMPILE, "invalid command code %c", *p);  | 
    ||
203  | 
    ✗✓ | 30768  | 
    if (naddr > fp->naddr)  | 
    
204  | 
    error(COMPILE,  | 
    ||
205  | 
    "command %c expects up to %d address(es), found %d",  | 
    ||
206  | 
    *p, fp->naddr, naddr);  | 
    ||
207  | 
    ✓✓✓✓ ✓✓✓✓ ✓✓✓✓  | 
    62247  | 
    		switch (fp->args) { | 
    
208  | 
    case NONSEL: /* ! */  | 
    ||
209  | 
    324  | 
    p++;  | 
    |
210  | 
    ✓✗✓✗ ✗✓  | 
    972  | 
    EATSPACE();  | 
    
211  | 
    324  | 
    cmd->nonsel = 1;  | 
    |
212  | 
    324  | 
    goto nonsel;  | 
    |
213  | 
    		case GROUP:			/* { */ | 
    ||
214  | 
    1397  | 
    p++;  | 
    |
215  | 
    ✓✗✓✗ ✓✓  | 
    8142  | 
    EATSPACE();  | 
    
216  | 
    1397  | 
    cmd->next = stack;  | 
    |
217  | 
    stack = cmd;  | 
    ||
218  | 
    1397  | 
    link = &cmd->u.c;  | 
    |
219  | 
    ✓✓ | 1397  | 
    if (*p)  | 
    
220  | 
    goto semicolon;  | 
    ||
221  | 
    break;  | 
    ||
222  | 
    case ENDGROUP:  | 
    ||
223  | 
    /*  | 
    ||
224  | 
    * Short-circuit command processing, since end of  | 
    ||
225  | 
    * group is really just a noop.  | 
    ||
226  | 
    */  | 
    ||
227  | 
    1383  | 
    cmd->nonsel = 1;  | 
    |
228  | 
    ✗✓ | 1383  | 
    if (stack == 0)  | 
    
229  | 
    error(COMPILE, "unexpected }");  | 
    ||
230  | 
    cmd2 = stack;  | 
    ||
231  | 
    1383  | 
    stack = cmd2->next;  | 
    |
232  | 
    1383  | 
    cmd2->next = cmd;  | 
    |
233  | 
    /*FALLTHROUGH*/  | 
    ||
234  | 
    case EMPTY: /* d D g G h H l n N p P q x = \0 */  | 
    ||
235  | 
    3788  | 
    p++;  | 
    |
236  | 
    ✓✗✓✗ ✓✓  | 
    20388  | 
    EATSPACE();  | 
    
237  | 
    ✓✓ | 3788  | 
    			if (*p == ';') { | 
    
238  | 
    153  | 
    p++;  | 
    |
239  | 
    link = &cmd->next;  | 
    ||
240  | 
    153  | 
    goto semicolon;  | 
    |
241  | 
    }  | 
    ||
242  | 
    ✓✓ | 3635  | 
    if (*p)  | 
    
243  | 
    error(COMPILE,  | 
    ||
244  | 
    "extra characters at the end of %c command", cmd->code);  | 
    ||
245  | 
    break;  | 
    ||
246  | 
    case TEXT: /* a c i */  | 
    ||
247  | 
    156  | 
    p++;  | 
    |
248  | 
    ✓✗✓✗ ✓✓  | 
    510  | 
    EATSPACE();  | 
    
249  | 
    ✓✓ | 156  | 
    if (*p != '\\')  | 
    
250  | 
    error(COMPILE, "command %c expects \\ followed by"  | 
    ||
251  | 
    " text", cmd->code);  | 
    ||
252  | 
    149  | 
    p++;  | 
    |
253  | 
    ✓✗✓✗ ✓✓  | 
    894  | 
    EATSPACE();  | 
    
254  | 
    ✓✓ | 149  | 
    if (*p)  | 
    
255  | 
    error(COMPILE, "extra characters after \\ at the"  | 
    ||
256  | 
    " end of %c command", cmd->code);  | 
    ||
257  | 
    142  | 
    cmd->t = compile_text();  | 
    |
258  | 
    142  | 
    break;  | 
    |
259  | 
    case COMMENT: /* \0 # */  | 
    ||
260  | 
    break;  | 
    ||
261  | 
    case WFILE: /* w */  | 
    ||
262  | 
    367  | 
    p++;  | 
    |
263  | 
    ✓✗✓✗ ✓✓  | 
    2202  | 
    EATSPACE();  | 
    
264  | 
    ✗✓ | 367  | 
    if (*p == '\0')  | 
    
265  | 
    error(COMPILE, "filename expected");  | 
    ||
266  | 
    367  | 
    cmd->t = duptoeol(p, "w command", NULL);  | 
    |
267  | 
    ✗✓ | 367  | 
    			if (aflag) { | 
    
268  | 
    cmd->u.fd = -1;  | 
    ||
269  | 
    pledge_wpath = 1;  | 
    ||
270  | 
    }  | 
    ||
271  | 
    ✓✗ | 734  | 
    else if ((cmd->u.fd = open(p,  | 
    
272  | 
    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,  | 
    ||
273  | 
    367  | 
    DEFFILEMODE)) == -1)  | 
    |
274  | 
    error(FATAL, "%s: %s", p, strerror(errno));  | 
    ||
275  | 
    break;  | 
    ||
276  | 
    case RFILE: /* r */  | 
    ||
277  | 
    426  | 
    pledge_rpath = 1;  | 
    |
278  | 
    426  | 
    p++;  | 
    |
279  | 
    ✓✗✓✗ ✓✓  | 
    2556  | 
    EATSPACE();  | 
    
280  | 
    426  | 
    cmd->t = duptoeol(p, "read command", NULL);  | 
    |
281  | 
    426  | 
    break;  | 
    |
282  | 
    case BRANCH: /* b t */  | 
    ||
283  | 
    1244  | 
    p++;  | 
    |
284  | 
    ✓✗✓✗ ✓✓  | 
    7233  | 
    EATSPACE();  | 
    
285  | 
    ✓✓ | 1244  | 
    if (*p == '\0')  | 
    
286  | 
    169  | 
    cmd->t = NULL;  | 
    |
287  | 
    else  | 
    ||
288  | 
    1075  | 
    cmd->t = duptoeol(p, "branch", &p);  | 
    |
289  | 
    ✓✗ | 1244  | 
    			if (*p == ';') { | 
    
290  | 
    p++;  | 
    ||
291  | 
    goto semicolon;  | 
    ||
292  | 
    }  | 
    ||
293  | 
    break;  | 
    ||
294  | 
    case LABEL: /* : */  | 
    ||
295  | 
    305  | 
    p++;  | 
    |
296  | 
    ✓✗✓✗ ✓✓  | 
    1083  | 
    EATSPACE();  | 
    
297  | 
    305  | 
    cmd->t = duptoeol(p, "label", &p);  | 
    |
298  | 
    ✗✓ | 305  | 
    if (strlen(cmd->t) == 0)  | 
    
299  | 
    error(COMPILE, "empty label");  | 
    ||
300  | 
    305  | 
    enterlabel(cmd);  | 
    |
301  | 
    ✓✗ | 305  | 
    			if (*p == ';') { | 
    
302  | 
    p++;  | 
    ||
303  | 
    goto semicolon;  | 
    ||
304  | 
    }  | 
    ||
305  | 
    break;  | 
    ||
306  | 
    case SUBST: /* s */  | 
    ||
307  | 
    22469  | 
    p++;  | 
    |
308  | 
    ✓✗✗✓ | 
    44938  | 
    if (*p == '\0' || *p == '\\')  | 
    
309  | 
    error(COMPILE, "substitute pattern can not be"  | 
    ||
310  | 
    " delimited by newline or backslash");  | 
    ||
311  | 
    22469  | 
    cmd->u.s = xmalloc(sizeof(struct s_subst));  | 
    |
312  | 
    22469  | 
    p = compile_re(p, &cmd->u.s->re);  | 
    |
313  | 
    ✓✓ | 22469  | 
    if (p == NULL)  | 
    
314  | 
    error(COMPILE, "unterminated substitute pattern");  | 
    ||
315  | 
    22413  | 
    --p;  | 
    |
316  | 
    22413  | 
    p = compile_subst(p, cmd->u.s);  | 
    |
317  | 
    22413  | 
    p = compile_flags(p, cmd->u.s);  | 
    |
318  | 
    ✓✗✓✗ ✓✓  | 
    71439  | 
    EATSPACE();  | 
    
319  | 
    ✓✓ | 22413  | 
    			if (*p == ';') { | 
    
320  | 
    683  | 
    p++;  | 
    |
321  | 
    link = &cmd->next;  | 
    ||
322  | 
    683  | 
    goto semicolon;  | 
    |
323  | 
    }  | 
    ||
324  | 
    break;  | 
    ||
325  | 
    case TR: /* y */  | 
    ||
326  | 
    264  | 
    p++;  | 
    |
327  | 
    264  | 
    p = compile_tr(p, (char **)&cmd->u.y);  | 
    |
328  | 
    ✓✗✓✗ ✗✓  | 
    792  | 
    EATSPACE();  | 
    
329  | 
    ✓✓ | 264  | 
    			if (*p == ';') { | 
    
330  | 
    229  | 
    p++;  | 
    |
331  | 
    link = &cmd->next;  | 
    ||
332  | 
    229  | 
    goto semicolon;  | 
    |
333  | 
    }  | 
    ||
334  | 
    ✓✓ | 35  | 
    if (*p)  | 
    
335  | 
    error(COMPILE, "extra text at the end of a"  | 
    ||
336  | 
    " transform command");  | 
    ||
337  | 
    break;  | 
    ||
338  | 
    }  | 
    ||
339  | 
    }  | 
    ||
340  | 
    9319  | 
    }  | 
    |
341  | 
    |||
342  | 
    /*  | 
    ||
343  | 
    * Get a delimited string. P points to the delimeter of the string; d points  | 
    ||
344  | 
    * to a buffer area. Newline and delimiter escapes are processed; other  | 
    ||
345  | 
    * escapes are ignored.  | 
    ||
346  | 
    *  | 
    ||
347  | 
    * Returns a pointer to the first character after the final delimiter or NULL  | 
    ||
348  | 
    * in the case of a non-terminated string. The character array d is filled  | 
    ||
349  | 
    * with the processed string.  | 
    ||
350  | 
    */  | 
    ||
351  | 
    static char *  | 
    ||
352  | 
    compile_delimited(char *p, char *d, int is_tr)  | 
    ||
353  | 
    { | 
    ||
354  | 
    char c;  | 
    ||
355  | 
    |||
356  | 
    50676  | 
    c = *p++;  | 
    |
357  | 
    ✗✓ | 25338  | 
    if (c == '\0')  | 
    
358  | 
    return (NULL);  | 
    ||
359  | 
    ✗✓ | 25338  | 
    else if (c == '\\')  | 
    
360  | 
    error(COMPILE, "\\ can not be used as a string delimiter");  | 
    ||
361  | 
    ✗✓ | 25338  | 
    else if (c == '\n')  | 
    
362  | 
    error(COMPILE, "newline can not be used as a string delimiter");  | 
    ||
363  | 
    ✓✓ | 590397  | 
    	while (*p) { | 
    
364  | 
    ✓✓✓✓ | 
    318696  | 
    		if (*p == '[' && *p != c) { | 
    
365  | 
    ✓✗ | 7194  | 
    if ((d = compile_ccl(&p, d)) == NULL)  | 
    
366  | 
    error(COMPILE, "unbalanced brackets ([])");  | 
    ||
367  | 
    continue;  | 
    ||
368  | 
    ✓✓✓✓ | 
    313464  | 
    		} else if (*p == '\\' && p[1] == '[') { | 
    
369  | 
    44  | 
    *d++ = *p++;  | 
    |
370  | 
    ✓✓✓✓ | 
    313420  | 
    		} else if (*p == '\\' && p[1] == c) { | 
    
371  | 
    4150  | 
    p++;  | 
    |
372  | 
    ✓✓✓✓ | 
    309226  | 
    		} else if (*p == '\\' && p[1] == 'n') { | 
    
373  | 
    103  | 
    *d++ = '\n';  | 
    |
374  | 
    103  | 
    p += 2;  | 
    |
375  | 
    103  | 
    continue;  | 
    |
376  | 
    ✓✓✓✓ | 
    304870  | 
    		} else if (*p == '\\' && p[1] == '\\') { | 
    
377  | 
    ✓✓ | 9  | 
    if (is_tr)  | 
    
378  | 
    p++;  | 
    ||
379  | 
    else  | 
    ||
380  | 
    2  | 
    *d++ = *p++;  | 
    |
381  | 
    ✓✓ | 299981  | 
    		} else if (*p == c) { | 
    
382  | 
    25303  | 
    *d = '\0';  | 
    |
383  | 
    25303  | 
    return (p + 1);  | 
    |
384  | 
    }  | 
    ||
385  | 
    278881  | 
    *d++ = *p++;  | 
    |
386  | 
    }  | 
    ||
387  | 
    35  | 
    return (NULL);  | 
    |
388  | 
    25338  | 
    }  | 
    |
389  | 
    |||
390  | 
    |||
391  | 
    /* compile_ccl: expand a POSIX character class */  | 
    ||
392  | 
    static char *  | 
    ||
393  | 
    compile_ccl(char **sp, char *t)  | 
    ||
394  | 
    { | 
    ||
395  | 
    int c, d;  | 
    ||
396  | 
    14388  | 
    char *s = *sp;  | 
    |
397  | 
    |||
398  | 
    7194  | 
    *t++ = *s++;  | 
    |
399  | 
    ✓✓ | 7194  | 
    if (*s == '^')  | 
    
400  | 
    2943  | 
    *t++ = *s++;  | 
    |
401  | 
    ✗✓ | 7194  | 
    if (*s == ']')  | 
    
402  | 
    *t++ = *s++;  | 
    ||
403  | 
    ✓✗✓✓ | 
    157770  | 
    for (; *s && (*t = *s) != ']'; s++, t++)  | 
    
404  | 
    ✓✓✓✗ | 
    34075  | 
    		if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { | 
    
405  | 
    28  | 
    *++t = *++s, t++, s++;  | 
    |
406  | 
    ✓✓✗✓ | 
    420  | 
    for (c = *s; (*t = *s) != ']' || c != d; s++, t++)  | 
    
407  | 
    ✗✓ | 168  | 
    if ((c = *s) == '\0')  | 
    
408  | 
    return NULL;  | 
    ||
409  | 
    ✓✓✗✓ | 
    34158  | 
    		} else if (*s == '\\' && s[1] == 'n') { | 
    
410  | 
    *t = '\n';  | 
    ||
411  | 
    s++;  | 
    ||
412  | 
    }  | 
    ||
413  | 
    ✓✗ | 7194  | 
    	if (*s == ']') { | 
    
414  | 
    7194  | 
    *sp = ++s;  | 
    |
415  | 
    7194  | 
    return (++t);  | 
    |
416  | 
    	} else { | 
    ||
417  | 
    return (NULL);  | 
    ||
418  | 
    }  | 
    ||
419  | 
    7194  | 
    }  | 
    |
420  | 
    |||
421  | 
    /*  | 
    ||
422  | 
    * Get a regular expression. P points to the delimiter of the regular  | 
    ||
423  | 
    * expression; repp points to the address of a regexp pointer. Newline  | 
    ||
424  | 
    * and delimiter escapes are processed; other escapes are ignored.  | 
    ||
425  | 
    * Returns a pointer to the first character after the final delimiter  | 
    ||
426  | 
    * or NULL in the case of a non terminated regular expression. The regexp  | 
    ||
427  | 
    * pointer is set to the compiled regular expression.  | 
    ||
428  | 
    * Cflags are passed to regcomp.  | 
    ||
429  | 
    */  | 
    ||
430  | 
    static char *  | 
    ||
431  | 
    compile_re(char *p, regex_t **repp)  | 
    ||
432  | 
    { | 
    ||
433  | 
    int eval;  | 
    ||
434  | 
    char *re;  | 
    ||
435  | 
    |||
436  | 
    49522  | 
    re = xmalloc(strlen(p) + 1); /* strlen(re) <= strlen(p) */  | 
    |
437  | 
    24761  | 
    p = compile_delimited(p, re, 0);  | 
    |
438  | 
    ✓✓✓✓ | 
    49508  | 
    	if (p && strlen(re) == 0) { | 
    
439  | 
    361  | 
    *repp = NULL;  | 
    |
440  | 
    361  | 
    free(re);  | 
    |
441  | 
    361  | 
    return (p);  | 
    |
442  | 
    }  | 
    ||
443  | 
    24400  | 
    *repp = xmalloc(sizeof(regex_t));  | 
    |
444  | 
    ✓✓✗✓ | 
    48786  | 
    if (p && (eval = regcomp(*repp, re, Eflag ? REG_EXTENDED : 0)) != 0)  | 
    
445  | 
    error(COMPILE, "RE error: %s", strregerror(eval, *repp));  | 
    ||
446  | 
    ✓✓ | 24400  | 
    if (maxnsub < (*repp)->re_nsub)  | 
    
447  | 
    695  | 
    maxnsub = (*repp)->re_nsub;  | 
    |
448  | 
    24400  | 
    free(re);  | 
    |
449  | 
    24400  | 
    return (p);  | 
    |
450  | 
    24761  | 
    }  | 
    |
451  | 
    |||
452  | 
    /*  | 
    ||
453  | 
    * Compile the substitution string of a regular expression and set res to  | 
    ||
454  | 
    * point to a saved copy of it. Nsub is the number of parenthesized regular  | 
    ||
455  | 
    * expressions.  | 
    ||
456  | 
    */  | 
    ||
457  | 
    static char *  | 
    ||
458  | 
    compile_subst(char *p, struct s_subst *s)  | 
    ||
459  | 
    { | 
    ||
460  | 
    static char *lbuf;  | 
    ||
461  | 
    static size_t bufsize;  | 
    ||
462  | 
    int asize, ref, size;  | 
    ||
463  | 
    char c, *text, *op, *sp;  | 
    ||
464  | 
    int sawesc = 0;  | 
    ||
465  | 
    |||
466  | 
    44924  | 
    c = *p++; /* Terminator character */  | 
    |
467  | 
    ✗✓ | 22462  | 
    if (c == '\0')  | 
    
468  | 
    return (NULL);  | 
    ||
469  | 
    |||
470  | 
    22462  | 
    s->maxbref = 0;  | 
    |
471  | 
    22462  | 
    s->linenum = linenum;  | 
    |
472  | 
    text = NULL;  | 
    ||
473  | 
    asize = size = 0;  | 
    ||
474  | 
    22462  | 
    	do { | 
    |
475  | 
    22479  | 
    size_t len = ROUNDLEN(strlen(p) + 1);  | 
    |
476  | 
    ✓✓ | 22479  | 
    		if (asize - size < len) { | 
    
477  | 
    			do { | 
    ||
478  | 
    22472  | 
    asize += len;  | 
    |
479  | 
    ✗✓ | 22472  | 
    } while (asize - size < len);  | 
    
480  | 
    22472  | 
    text = xrealloc(text, asize);  | 
    |
481  | 
    22472  | 
    }  | 
    |
482  | 
    22479  | 
    op = sp = text + size;  | 
    |
483  | 
    ✓✓ | 652144  | 
    		for (; *p; p++) { | 
    
484  | 
    ✓✓ | 326041  | 
    			if (*p == '\\' || sawesc) { | 
    
485  | 
    /*  | 
    ||
486  | 
    * If this is a continuation from the last  | 
    ||
487  | 
    * buffer, we won't have a character to  | 
    ||
488  | 
    * skip over.  | 
    ||
489  | 
    */  | 
    ||
490  | 
    ✗✓ | 2446  | 
    if (sawesc)  | 
    
491  | 
    sawesc = 0;  | 
    ||
492  | 
    else  | 
    ||
493  | 
    2446  | 
    p++;  | 
    |
494  | 
    |||
495  | 
    ✗✓ | 2446  | 
    				if (*p == '\0') { | 
    
496  | 
    /*  | 
    ||
497  | 
    * This escaped character is continued  | 
    ||
498  | 
    * in the next part of the line. Note  | 
    ||
499  | 
    * this fact, then cause the loop to  | 
    ||
500  | 
    * exit w/ normal EOL case and reenter  | 
    ||
501  | 
    * above with the new buffer.  | 
    ||
502  | 
    */  | 
    ||
503  | 
    sawesc = 1;  | 
    ||
504  | 
    p--;  | 
    ||
505  | 
    continue;  | 
    ||
506  | 
    ✓✓ | 2446  | 
    				} else if (strchr("123456789", *p) != NULL) { | 
    
507  | 
    2202  | 
    *sp++ = '\\';  | 
    |
508  | 
    2202  | 
    ref = *p - '0';  | 
    |
509  | 
    ✓✓✗✓ | 
    3872  | 
    if (s->re != NULL &&  | 
    
510  | 
    1670  | 
    ref > s->re->re_nsub)  | 
    |
511  | 
    error(COMPILE,  | 
    ||
512  | 
    "\\%c not defined in the RE", *p);  | 
    ||
513  | 
    ✓✓ | 2202  | 
    if (s->maxbref < ref)  | 
    
514  | 
    1461  | 
    s->maxbref = ref;  | 
    |
515  | 
    ✓✓✓✓ | 
    481  | 
    } else if (*p == '&' || *p == '\\')  | 
    
516  | 
    57  | 
    *sp++ = '\\';  | 
    |
517  | 
    ✓✓ | 323595  | 
    			} else if (*p == c) { | 
    
518  | 
    22448  | 
    p++;  | 
    |
519  | 
    22448  | 
    *sp++ = '\0';  | 
    |
520  | 
    22448  | 
    size += sp - op;  | 
    |
521  | 
    22448  | 
    s->new = xrealloc(text, size);  | 
    |
522  | 
    22448  | 
    return (p);  | 
    |
523  | 
    ✗✓ | 301147  | 
    			} else if (*p == '\n') { | 
    
524  | 
    error(COMPILE,  | 
    ||
525  | 
    "unescaped newline inside substitute pattern");  | 
    ||
526  | 
    }  | 
    ||
527  | 
    303593  | 
    *sp++ = *p;  | 
    |
528  | 
    303593  | 
    }  | 
    |
529  | 
    31  | 
    size += sp - op;  | 
    |
530  | 
    ✓✓✓✓ | 
    62  | 
    } while ((p = cu_fgets(&lbuf, &bufsize)));  | 
    
531  | 
    error(COMPILE, "unterminated substitute in regular expression");  | 
    ||
532  | 
    22448  | 
    }  | 
    |
533  | 
    |||
534  | 
    /*  | 
    ||
535  | 
    * Compile the flags of the s command  | 
    ||
536  | 
    */  | 
    ||
537  | 
    static char *  | 
    ||
538  | 
    compile_flags(char *p, struct s_subst *s)  | 
    ||
539  | 
    { | 
    ||
540  | 
    int gn; /* True if we have seen g or n */  | 
    ||
541  | 
    long l;  | 
    ||
542  | 
    22448  | 
    char wfile[PATH_MAX], *q, *eq;  | 
    |
543  | 
    |||
544  | 
    22448  | 
    s->n = 1; /* Default */  | 
    |
545  | 
    22448  | 
    s->p = 0;  | 
    |
546  | 
    22448  | 
    s->wfile = NULL;  | 
    |
547  | 
    22448  | 
    s->wfd = -1;  | 
    |
548  | 
    35854  | 
    	for (gn = 0;;) { | 
    |
549  | 
    ✓✗✓✗ ✓✓  | 
    149058  | 
    EATSPACE(); /* EXTENSION */  | 
    
550  | 
    ✓✗✗✓ ✓✗✗✗ ✗✗✗✗ ✗✓✓✓  | 
    37492  | 
    		switch (*p) { | 
    
551  | 
    case 'g':  | 
    ||
552  | 
    ✓✓ | 13031  | 
    if (gn)  | 
    
553  | 
    error(COMPILE, "more than one number or 'g' in"  | 
    ||
554  | 
    " substitute flags");  | 
    ||
555  | 
    gn = 1;  | 
    ||
556  | 
    13024  | 
    s->n = 0;  | 
    |
557  | 
    13024  | 
    break;  | 
    |
558  | 
    case '\0':  | 
    ||
559  | 
    case '\n':  | 
    ||
560  | 
    case ';':  | 
    ||
561  | 
    21006  | 
    return (p);  | 
    |
562  | 
    case 'p':  | 
    ||
563  | 
    382  | 
    s->p = 1;  | 
    |
564  | 
    382  | 
    break;  | 
    |
565  | 
    case '1': case '2': case '3':  | 
    ||
566  | 
    case '4': case '5': case '6':  | 
    ||
567  | 
    case '7': case '8': case '9':  | 
    ||
568  | 
    ✓✓ | 1645  | 
    if (gn)  | 
    
569  | 
    error(COMPILE, "more than one number or 'g' in"  | 
    ||
570  | 
    " substitute flags");  | 
    ||
571  | 
    gn = 1;  | 
    ||
572  | 
    1638  | 
    l = strtol(p, &p, 10);  | 
    |
573  | 
    ✗✓ | 1638  | 
    if (l <= 0 || l >= INT_MAX)  | 
    
574  | 
    error(COMPILE,  | 
    ||
575  | 
    "number in substitute flags out of range");  | 
    ||
576  | 
    1638  | 
    s->n = (int)l;  | 
    |
577  | 
    1638  | 
    continue;  | 
    |
578  | 
    case 'w':  | 
    ||
579  | 
    1414  | 
    p++;  | 
    |
580  | 
    #ifdef HISTORIC_PRACTICE  | 
    ||
581  | 
    			if (*p != ' ') { | 
    ||
582  | 
    				warning("space missing before w wfile"); | 
    ||
583  | 
    return (p);  | 
    ||
584  | 
    }  | 
    ||
585  | 
    #endif  | 
    ||
586  | 
    ✓✗✓✗ ✓✓  | 
    8463  | 
    EATSPACE();  | 
    
587  | 
    1414  | 
    q = wfile;  | 
    |
588  | 
    1414  | 
    eq = wfile + sizeof(wfile) - 1;  | 
    |
589  | 
    ✓✓ | 45332  | 
    			while (*p) { | 
    
590  | 
    ✓✓ | 22652  | 
    if (*p == '\n')  | 
    
591  | 
    break;  | 
    ||
592  | 
    ✗✓ | 21252  | 
    if (q >= eq)  | 
    
593  | 
    error(COMPILE, "wfile too long");  | 
    ||
594  | 
    21252  | 
    *q++ = *p++;  | 
    |
595  | 
    }  | 
    ||
596  | 
    1414  | 
    *q = '\0';  | 
    |
597  | 
    ✓✓ | 1414  | 
    if (q == wfile)  | 
    
598  | 
    error(COMPILE, "no wfile specified");  | 
    ||
599  | 
    1407  | 
    s->wfile = strdup(wfile);  | 
    |
600  | 
    ✗✓ | 1407  | 
    if (aflag)  | 
    
601  | 
    pledge_wpath = 1;  | 
    ||
602  | 
    ✗✓ | 2814  | 
    else if ((s->wfd = open(wfile,  | 
    
603  | 
    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,  | 
    ||
604  | 
    1407  | 
    DEFFILEMODE)) == -1)  | 
    |
605  | 
    error(FATAL, "%s: %s", wfile, strerror(errno));  | 
    ||
606  | 
    1407  | 
    return (p);  | 
    |
607  | 
    default:  | 
    ||
608  | 
    error(COMPILE,  | 
    ||
609  | 
    "bad flag in substitute command: '%c'", *p);  | 
    ||
610  | 
    break;  | 
    ||
611  | 
    }  | 
    ||
612  | 
    13406  | 
    p++;  | 
    |
613  | 
    }  | 
    ||
614  | 
    22413  | 
    }  | 
    |
615  | 
    |||
616  | 
    /*  | 
    ||
617  | 
    * Compile a translation set of strings into a lookup table.  | 
    ||
618  | 
    */  | 
    ||
619  | 
    static char *  | 
    ||
620  | 
    compile_tr(char *p, char **transtab)  | 
    ||
621  | 
    { | 
    ||
622  | 
    int i;  | 
    ||
623  | 
    char *lt, *op, *np;  | 
    ||
624  | 
    char *old = NULL, *new = NULL;  | 
    ||
625  | 
    |||
626  | 
    ✓✗✗✓ | 
    876  | 
    if (*p == '\0' || *p == '\\')  | 
    
627  | 
    error(COMPILE,  | 
    ||
628  | 
    "transform pattern can not be delimited by newline or backslash");  | 
    ||
629  | 
    292  | 
    old = xmalloc(strlen(p) + 1);  | 
    |
630  | 
    292  | 
    p = compile_delimited(p, old, 1);  | 
    |
631  | 
    ✓✓ | 292  | 
    	if (p == NULL) { | 
    
632  | 
    error(COMPILE, "unterminated transform source string");  | 
    ||
633  | 
    goto bad;  | 
    ||
634  | 
    }  | 
    ||
635  | 
    285  | 
    new = xmalloc(strlen(p) + 1);  | 
    |
636  | 
    285  | 
    p = compile_delimited(--p, new, 1);  | 
    |
637  | 
    ✓✓ | 285  | 
    	if (p == NULL) { | 
    
638  | 
    error(COMPILE, "unterminated transform target string");  | 
    ||
639  | 
    goto bad;  | 
    ||
640  | 
    }  | 
    ||
641  | 
    ✓✗✓✗ ✗✓  | 
    813  | 
    EATSPACE();  | 
    
642  | 
    ✓✓ | 271  | 
    	if (strlen(new) != strlen(old)) { | 
    
643  | 
    error(COMPILE, "transform strings are not the same length");  | 
    ||
644  | 
    goto bad;  | 
    ||
645  | 
    }  | 
    ||
646  | 
    /* We assume characters are 8 bits */  | 
    ||
647  | 
    264  | 
    lt = xmalloc(UCHAR_MAX + 1);  | 
    |
648  | 
    ✓✓ | 135696  | 
    for (i = 0; i <= UCHAR_MAX; i++)  | 
    
649  | 
    67584  | 
    lt[i] = (char)i;  | 
    |
650  | 
    ✓✓ | 7664  | 
    for (op = old, np = new; *op; op++, np++)  | 
    
651  | 
    3568  | 
    lt[(u_char)*op] = *np;  | 
    |
652  | 
    264  | 
    *transtab = lt;  | 
    |
653  | 
    264  | 
    free(old);  | 
    |
654  | 
    264  | 
    free(new);  | 
    |
655  | 
    return (p);  | 
    ||
656  | 
    bad:  | 
    ||
657  | 
    free(old);  | 
    ||
658  | 
    free(new);  | 
    ||
659  | 
    return (NULL);  | 
    ||
660  | 
    264  | 
    }  | 
    |
661  | 
    |||
662  | 
    /*  | 
    ||
663  | 
    * Compile the text following an a, c, or i command.  | 
    ||
664  | 
    */  | 
    ||
665  | 
    static char *  | 
    ||
666  | 
    compile_text(void)  | 
    ||
667  | 
    { | 
    ||
668  | 
    int asize, esc_nl, size;  | 
    ||
669  | 
    284  | 
    char *lbuf, *text, *p, *op, *s;  | 
    |
670  | 
    142  | 
    size_t bufsize;  | 
    |
671  | 
    |||
672  | 
    142  | 
    lbuf = text = NULL;  | 
    |
673  | 
    asize = size = 0;  | 
    ||
674  | 
    ✓✗ | 298  | 
    	while ((p = cu_fgets(&lbuf, &bufsize))) { | 
    
675  | 
    156  | 
    size_t len = ROUNDLEN(strlen(p) + 1);  | 
    |
676  | 
    ✓✓ | 156  | 
    		if (asize - size < len) { | 
    
677  | 
    			do { | 
    ||
678  | 
    149  | 
    asize += len;  | 
    |
679  | 
    ✗✓ | 149  | 
    } while (asize - size < len);  | 
    
680  | 
    149  | 
    text = xrealloc(text, asize);  | 
    |
681  | 
    149  | 
    }  | 
    |
682  | 
    156  | 
    op = s = text + size;  | 
    |
683  | 
    ✓✓ | 6974  | 
    		for (esc_nl = 0; *p != '\0'; p++) { | 
    
684  | 
    ✓✓✓✗ ✓✓  | 
    3373  | 
    if (*p == '\\' && p[1] != '\0' && *++p == '\n')  | 
    
685  | 
    14  | 
    esc_nl = 1;  | 
    |
686  | 
    3331  | 
    *s++ = *p;  | 
    |
687  | 
    }  | 
    ||
688  | 
    156  | 
    size += s - op;  | 
    |
689  | 
    ✓✓ | 156  | 
    		if (!esc_nl) { | 
    
690  | 
    142  | 
    *s = '\0';  | 
    |
691  | 
    142  | 
    break;  | 
    |
692  | 
    }  | 
    ||
693  | 
    ✓✓ | 14  | 
    }  | 
    
694  | 
    142  | 
    free(lbuf);  | 
    |
695  | 
    142  | 
    text = xrealloc(text, size + 1);  | 
    |
696  | 
    142  | 
    text[size] = '\0';  | 
    |
697  | 
    142  | 
    return (text);  | 
    |
698  | 
    142  | 
    }  | 
    |
699  | 
    |||
700  | 
    /*  | 
    ||
701  | 
    * Get an address and return a pointer to the first character after  | 
    ||
702  | 
    * it. Fill the structure pointed to according to the address.  | 
    ||
703  | 
    */  | 
    ||
704  | 
    static char *  | 
    ||
705  | 
    compile_addr(char *p, struct s_addr *a)  | 
    ||
706  | 
    { | 
    ||
707  | 
    6057  | 
    char *end;  | 
    |
708  | 
    |||
709  | 
    ✓✓✓✗ ✗✗✗✗ ✗✗✗✗ ✓✗  | 
    3032  | 
    	switch (*p) { | 
    
710  | 
    case '\\': /* Context address */  | 
    ||
711  | 
    7  | 
    ++p;  | 
    |
712  | 
    /* FALLTHROUGH */  | 
    ||
713  | 
    case '/': /* Context address */  | 
    ||
714  | 
    2292  | 
    p = compile_re(p, &a->u.r);  | 
    |
715  | 
    ✓✓ | 2292  | 
    if (p == NULL)  | 
    
716  | 
    error(COMPILE, "unterminated regular expression");  | 
    ||
717  | 
    2285  | 
    a->type = AT_RE;  | 
    |
718  | 
    2285  | 
    return (p);  | 
    |
719  | 
    |||
720  | 
    case '$': /* Last line */  | 
    ||
721  | 
    94  | 
    a->type = AT_LAST;  | 
    |
722  | 
    94  | 
    return (p + 1);  | 
    |
723  | 
    /* Line number */  | 
    ||
724  | 
    case '0': case '1': case '2': case '3': case '4':  | 
    ||
725  | 
    case '5': case '6': case '7': case '8': case '9':  | 
    ||
726  | 
    639  | 
    a->type = AT_LINE;  | 
    |
727  | 
    639  | 
    a->u.l = strtoul(p, &end, 10);  | 
    |
728  | 
    639  | 
    return (end);  | 
    |
729  | 
    default:  | 
    ||
730  | 
    error(COMPILE, "expected context address");  | 
    ||
731  | 
    return (NULL);  | 
    ||
732  | 
    }  | 
    ||
733  | 
    3018  | 
    }  | 
    |
734  | 
    |||
735  | 
    /*  | 
    ||
736  | 
    * duptoeol --  | 
    ||
737  | 
    * Return a copy of all the characters up to \n or \0.  | 
    ||
738  | 
    */  | 
    ||
739  | 
    static char *  | 
    ||
740  | 
    duptoeol(char *s, char *ctype, char **semi)  | 
    ||
741  | 
    { | 
    ||
742  | 
    size_t len;  | 
    ||
743  | 
    int ws;  | 
    ||
744  | 
    char *start;  | 
    ||
745  | 
    |||
746  | 
    ws = 0;  | 
    ||
747  | 
    ✓✓ | 4346  | 
    	if (semi) { | 
    
748  | 
    ✓✓✓✓ ✓✗  | 
    17158  | 
    for (start = s; *s != '\0' && *s != '\n' && *s != ';'; ++s)  | 
    
749  | 
    3607  | 
    ws = isspace((unsigned char)*s);  | 
    |
750  | 
    	} else { | 
    ||
751  | 
    ✓✓✓✓ | 
    66703  | 
    for (start = s; *s != '\0' && *s != '\n'; ++s)  | 
    
752  | 
    16088  | 
    ws = isspace((unsigned char)*s);  | 
    |
753  | 
    793  | 
    *s = '\0';  | 
    |
754  | 
    }  | 
    ||
755  | 
    ✗✓ | 2173  | 
    if (ws)  | 
    
756  | 
    		warning("whitespace after %s", ctype); | 
    ||
757  | 
    2173  | 
    len = s - start + 1;  | 
    |
758  | 
    ✓✓ | 2173  | 
    if (semi)  | 
    
759  | 
    1380  | 
    *semi = s;  | 
    |
760  | 
    2173  | 
    s = xmalloc(len);  | 
    |
761  | 
    2173  | 
    strlcpy(s, start, len);  | 
    |
762  | 
    2173  | 
    return (s);  | 
    |
763  | 
    }  | 
    ||
764  | 
    |||
765  | 
    /*  | 
    ||
766  | 
    * Convert goto label names to addresses, and count a and r commands, in  | 
    ||
767  | 
    * the given subset of the script. Free the memory used by labels in b  | 
    ||
768  | 
    * and t commands (but not by :).  | 
    ||
769  | 
    *  | 
    ||
770  | 
    * TODO: Remove } nodes  | 
    ||
771  | 
    */  | 
    ||
772  | 
    static void  | 
    ||
773  | 
    fixuplabel(struct s_command *cp, struct s_command *end)  | 
    ||
774  | 
    { | 
    ||
775  | 
    |||
776  | 
    ✓✓ | 92728  | 
    for (; cp != end; cp = cp->next)  | 
    
777  | 
    ✗✓✗✓ ✓✓  | 
    33429  | 
    		switch (cp->code) { | 
    
778  | 
    case 'a':  | 
    ||
779  | 
    case 'r':  | 
    ||
780  | 
    491  | 
    appendnum++;  | 
    |
781  | 
    491  | 
    break;  | 
    |
782  | 
    case 'b':  | 
    ||
783  | 
    case 't':  | 
    ||
784  | 
    /* Resolve branch target. */  | 
    ||
785  | 
    ✓✓ | 1244  | 
    			if (cp->t == NULL) { | 
    
786  | 
    169  | 
    cp->u.c = NULL;  | 
    |
787  | 
    169  | 
    break;  | 
    |
788  | 
    }  | 
    ||
789  | 
    ✓✓ | 1075  | 
    if ((cp->u.c = findlabel(cp->t)) == NULL)  | 
    
790  | 
    error(COMPILE, "undefined label '%s'", cp->t);  | 
    ||
791  | 
    1068  | 
    free(cp->t);  | 
    |
792  | 
    1068  | 
    break;  | 
    |
793  | 
    		case '{': | 
    ||
794  | 
    /* Do interior commands. */  | 
    ||
795  | 
    1383  | 
    fixuplabel(cp->u.c, cp->next);  | 
    |
796  | 
    1383  | 
    break;  | 
    |
797  | 
    }  | 
    ||
798  | 
    10695  | 
    }  | 
    |
799  | 
    |||
800  | 
    /*  | 
    ||
801  | 
    * Associate the given command label for later lookup.  | 
    ||
802  | 
    */  | 
    ||
803  | 
    static void  | 
    ||
804  | 
    enterlabel(struct s_command *cp)  | 
    ||
805  | 
    { | 
    ||
806  | 
    struct labhash **lhp, *lh;  | 
    ||
807  | 
    u_char *p;  | 
    ||
808  | 
    u_int h, c;  | 
    ||
809  | 
    |||
810  | 
    ✓✓ | 2751  | 
    for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)  | 
    
811  | 
    918  | 
    h = (h << 5) + h + c;  | 
    |
812  | 
    305  | 
    lhp = &labels[h & LHMASK];  | 
    |
813  | 
    ✗✓ | 610  | 
    for (lh = *lhp; lh != NULL; lh = lh->lh_next)  | 
    
814  | 
    if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)  | 
    ||
815  | 
    error(COMPILE, "duplicate label '%s'", cp->t);  | 
    ||
816  | 
    305  | 
    lh = xmalloc(sizeof *lh);  | 
    |
817  | 
    305  | 
    lh->lh_next = *lhp;  | 
    |
818  | 
    305  | 
    lh->lh_hash = h;  | 
    |
819  | 
    305  | 
    lh->lh_cmd = cp;  | 
    |
820  | 
    305  | 
    lh->lh_ref = 0;  | 
    |
821  | 
    305  | 
    *lhp = lh;  | 
    |
822  | 
    305  | 
    }  | 
    |
823  | 
    |||
824  | 
    /*  | 
    ||
825  | 
    * Find the label contained in the command l in the command linked  | 
    ||
826  | 
    * list cp. L is excluded from the search. Return NULL if not found.  | 
    ||
827  | 
    */  | 
    ||
828  | 
    static struct s_command *  | 
    ||
829  | 
    findlabel(char *name)  | 
    ||
830  | 
    { | 
    ||
831  | 
    struct labhash *lh;  | 
    ||
832  | 
    u_char *p;  | 
    ||
833  | 
    u_int h, c;  | 
    ||
834  | 
    |||
835  | 
    ✓✓ | 8603  | 
    for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)  | 
    
836  | 
    2689  | 
    h = (h << 5) + h + c;  | 
    |
837  | 
    ✓✓ | 2150  | 
    	for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { | 
    
838  | 
    ✓✗✓✗ | 
    2136  | 
    		if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { | 
    
839  | 
    1068  | 
    lh->lh_ref = 1;  | 
    |
840  | 
    1068  | 
    return (lh->lh_cmd);  | 
    |
841  | 
    }  | 
    ||
842  | 
    }  | 
    ||
843  | 
    7  | 
    return (NULL);  | 
    |
844  | 
    1075  | 
    }  | 
    |
845  | 
    |||
846  | 
    /*  | 
    ||
847  | 
    * Warn about any unused labels. As a side effect, release the label hash  | 
    ||
848  | 
    * table space.  | 
    ||
849  | 
    */  | 
    ||
850  | 
    static void  | 
    ||
851  | 
    uselabel(void)  | 
    ||
852  | 
    { | 
    ||
853  | 
    struct labhash *lh, *next;  | 
    ||
854  | 
    int i;  | 
    ||
855  | 
    |||
856  | 
    ✓✓ | 2411808  | 
    	for (i = 0; i < LHSZ; i++) { | 
    
857  | 
    ✓✓ | 2384482  | 
    		for (lh = labels[i]; lh != NULL; lh = next) { | 
    
858  | 
    305  | 
    next = lh->lh_next;  | 
    |
859  | 
    ✗✓ | 305  | 
    if (!lh->lh_ref)  | 
    
860  | 
    				warning("unused label '%s'", | 
    ||
861  | 
    lh->lh_cmd->t);  | 
    ||
862  | 
    305  | 
    free(lh);  | 
    |
863  | 
    }  | 
    ||
864  | 
    }  | 
    ||
865  | 
    9312  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |