Wed Aug 22 20:08:54 2018 UTC ()
Fix (hopefully) the problem reported on current-users by Patrick Welche.
we had incorrect usage of setstackmark()/popstackmark()

There was an ancient idiom (imported from CSRG in 1993) where code
can do:
	setstackmark(&smark); loop until whatever condition {
		/* do lots of code */ popstackmark(&smark);
	} popstackmark(&smark);

The 1st (inner) popstackmark() resets the stack, conserving memory,
The 2nd one is needed just in case the "whatever condition" was never
true, and the first one was never executed.

This is (was) safe as all popstackmark() did was reset the stack.
That could be done over and over again with no harm.

That is, until 2000 when a fix from FreeBSD for another problem was
imported.  That connected all the stack marks as a list (so they can be
located).  That caused the problem, as the idiom was not changed, now
there is this list of marks, and popstackmark() was removing an entry.

It rarely (never?) caused any problems as the idiom was rarely used
(the shell used to do loops like above, mostly, without the inner
popstackmark()).  Further, the stack mark list is only ever used when
a memory block is realloc'd.

That is, until last weekend - with the recent set of changes.

Part of that copied code from FreeBSD introduced the idiom above
into more functions - functions used much more, and with a greater
possibility of stack marks being set on blocks that are realloc'd
and so cause the problem.   In the FreeBSD code, they changed the idiom,
and always do a setstackmark() immediately after the inner popstackmark().
But not for reasons related to a list of stack marks, as in the
intervening period, FreeBSD deleted that, but for another reason.

We do not have their issue, and I did not believe that their
updated idiom was needed (I did some analysis of exactly this issue -
just missed the important part!), and just continued using the old one.
Hence Patrick's core dump....

The solution used here is to split popstackmark() into 2 halves,
popstackmark() continues to do what it has (recently) done,
but is now implemented as a call of (a new func) rststackmark()
which does all the original work of popstackmark - but not removing
the entry from the stack mark list (which remains in popstackmark()).
Then in the idiom above, the inner popstackmark() turns into a call of
rststackmark() so the stack is reset, but the stack mark list is
unchanged.  Tail recursion elimination makes this essentially free.


(kre)
diff -r1.159 -r1.160 src/bin/sh/eval.c
diff -r1.75 -r1.76 src/bin/sh/main.c
diff -r1.31 -r1.32 src/bin/sh/memalloc.c
diff -r1.17 -r1.18 src/bin/sh/memalloc.h

cvs diff -r1.159 -r1.160 src/bin/sh/eval.c (expand / switch to unified diff)

--- src/bin/sh/eval.c 2018/08/19 23:50:27 1.159
+++ src/bin/sh/eval.c 2018/08/22 20:08:54 1.160
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: eval.c,v 1.159 2018/08/19 23:50:27 kre Exp $ */ 1/* $NetBSD: eval.c,v 1.160 2018/08/22 20:08:54 kre Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1993 4 * Copyright (c) 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist. 8 * Kenneth Almquist.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -27,27 +27,27 @@ @@ -27,27 +27,27 @@
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35#include <sys/cdefs.h> 35#include <sys/cdefs.h>
36#ifndef lint 36#ifndef lint
37#if 0 37#if 0
38static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 38static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
39#else 39#else
40__RCSID("$NetBSD: eval.c,v 1.159 2018/08/19 23:50:27 kre Exp $"); 40__RCSID("$NetBSD: eval.c,v 1.160 2018/08/22 20:08:54 kre Exp $");
41#endif 41#endif
42#endif /* not lint */ 42#endif /* not lint */
43 43
44#include <stdbool.h> 44#include <stdbool.h>
45#include <stdlib.h> 45#include <stdlib.h>
46#include <signal.h> 46#include <signal.h>
47#include <stdio.h> 47#include <stdio.h>
48#include <string.h> 48#include <string.h>
49#include <errno.h> 49#include <errno.h>
50#include <limits.h> 50#include <limits.h>
51#include <unistd.h> 51#include <unistd.h>
52#include <sys/fcntl.h> 52#include <sys/fcntl.h>
53#include <sys/stat.h> 53#include <sys/stat.h>
@@ -224,27 +224,27 @@ evalstring(char *s, int flag) @@ -224,27 +224,27 @@ evalstring(char *s, int flag)
224 setstackmark(&smark); 224 setstackmark(&smark);
225 setinputstring(s, 1, line_number); 225 setinputstring(s, 1, line_number);
226 226
227 any = 0; /* to determine if exitstatus will have been set */ 227 any = 0; /* to determine if exitstatus will have been set */
228 while ((n = parsecmd(0)) != NEOF) { 228 while ((n = parsecmd(0)) != NEOF) {
229 XTRACE(DBG_EVAL, ("evalstring: "), showtree(n)); 229 XTRACE(DBG_EVAL, ("evalstring: "), showtree(n));
230 if (n && nflag == 0) { 230 if (n && nflag == 0) {
231 if (last && at_eof()) 231 if (last && at_eof())
232 evaltree(n, flag | EV_EXIT); 232 evaltree(n, flag | EV_EXIT);
233 else 233 else
234 evaltree(n, flag); 234 evaltree(n, flag);
235 any = 1; 235 any = 1;
236 } 236 }
237 popstackmark(&smark); 237 rststackmark(&smark);
238 } 238 }
239 popfile(); 239 popfile();
240 popstackmark(&smark); 240 popstackmark(&smark);
241 if (!any) 241 if (!any)
242 exitstatus = 0; 242 exitstatus = 0;
243 if (last) 243 if (last)
244 exraise(EXEXIT); 244 exraise(EXEXIT);
245} 245}
246 246
247 247
248 248
249/* 249/*
250 * Evaluate a parse tree. The value is left in the global variable 250 * Evaluate a parse tree. The value is left in the global variable
@@ -352,27 +352,27 @@ evaltree(union node *n, int flags) @@ -352,27 +352,27 @@ evaltree(union node *n, int flags)
352 do_etest = !(flags & EV_TESTED); 352 do_etest = !(flags & EV_TESTED);
353 break; 353 break;
354 default: 354 default:
355#ifdef NODETYPENAME 355#ifdef NODETYPENAME
356 out1fmt("Node type = %d(%s)\n", 356 out1fmt("Node type = %d(%s)\n",
357 n->type, NODETYPENAME(n->type)); 357 n->type, NODETYPENAME(n->type));
358#else 358#else
359 out1fmt("Node type = %d\n", n->type); 359 out1fmt("Node type = %d\n", n->type);
360#endif 360#endif
361 flushout(&output); 361 flushout(&output);
362 break; 362 break;
363 } 363 }
364 n = next; 364 n = next;
365 popstackmark(&smark); 365 rststackmark(&smark);
366 } while(n != NULL); 366 } while(n != NULL);
367 out1: 367 out1:
368 popstackmark(&smark); 368 popstackmark(&smark);
369 out2: 369 out2:
370 if (pendingsigs) 370 if (pendingsigs)
371 dotrap(); 371 dotrap();
372 if (eflag && exitstatus != 0 && do_etest) 372 if (eflag && exitstatus != 0 && do_etest)
373 exitshell(exitstatus); 373 exitshell(exitstatus);
374 if (flags & EV_EXIT) 374 if (flags & EV_EXIT)
375 exraise(EXEXIT); 375 exraise(EXEXIT);
376} 376}
377 377
378 378

cvs diff -r1.75 -r1.76 src/bin/sh/main.c (expand / switch to unified diff)

--- src/bin/sh/main.c 2018/08/19 23:50:27 1.75
+++ src/bin/sh/main.c 2018/08/22 20:08:54 1.76
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: main.c,v 1.75 2018/08/19 23:50:27 kre Exp $ */ 1/* $NetBSD: main.c,v 1.76 2018/08/22 20:08:54 kre Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1991, 1993 4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist. 8 * Kenneth Almquist.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -32,27 +32,27 @@ @@ -32,27 +32,27 @@
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35#include <sys/cdefs.h> 35#include <sys/cdefs.h>
36#ifndef lint 36#ifndef lint
37__COPYRIGHT("@(#) Copyright (c) 1991, 1993\ 37__COPYRIGHT("@(#) Copyright (c) 1991, 1993\
38 The Regents of the University of California. All rights reserved."); 38 The Regents of the University of California. All rights reserved.");
39#endif /* not lint */ 39#endif /* not lint */
40 40
41#ifndef lint 41#ifndef lint
42#if 0 42#if 0
43static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; 43static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95";
44#else 44#else
45__RCSID("$NetBSD: main.c,v 1.75 2018/08/19 23:50:27 kre Exp $"); 45__RCSID("$NetBSD: main.c,v 1.76 2018/08/22 20:08:54 kre Exp $");
46#endif 46#endif
47#endif /* not lint */ 47#endif /* not lint */
48 48
49#include <errno.h> 49#include <errno.h>
50#include <stdio.h> 50#include <stdio.h>
51#include <signal.h> 51#include <signal.h>
52#include <sys/stat.h> 52#include <sys/stat.h>
53#include <unistd.h> 53#include <unistd.h>
54#include <stdlib.h> 54#include <stdlib.h>
55#include <locale.h> 55#include <locale.h>
56#include <fcntl.h> 56#include <fcntl.h>
57 57
58 58
@@ -284,28 +284,27 @@ cmdloop(int top) @@ -284,28 +284,27 @@ cmdloop(int top)
284 if (nflag) 284 if (nflag)
285 break; 285 break;
286 if (!stoppedjobs()) { 286 if (!stoppedjobs()) {
287 if (!iflag || !Iflag) 287 if (!iflag || !Iflag)
288 break; 288 break;
289 out2str("\nUse \"exit\" to leave shell.\n"); 289 out2str("\nUse \"exit\" to leave shell.\n");
290 } 290 }
291 numeof++; 291 numeof++;
292 } else if (n != NULL && nflag == 0) { 292 } else if (n != NULL && nflag == 0) {
293 job_warning = (job_warning == 2) ? 1 : 0; 293 job_warning = (job_warning == 2) ? 1 : 0;
294 numeof = 0; 294 numeof = 0;
295 evaltree(n, 0); 295 evaltree(n, 0);
296 } 296 }
297 popstackmark(&smark); 297 rststackmark(&smark);
298 setstackmark(&smark); 
299 298
300 /* 299 /*
301 * Any SKIP* can occur here! SKIP(FUNC|BREAK|CONT) occur when 300 * Any SKIP* can occur here! SKIP(FUNC|BREAK|CONT) occur when
302 * a dotcmd is in a loop or a function body and appropriate 301 * a dotcmd is in a loop or a function body and appropriate
303 * built-ins occurs in file scope in the sourced file. Values 302 * built-ins occurs in file scope in the sourced file. Values
304 * other than SKIPFILE are reset by the appropriate eval*() 303 * other than SKIPFILE are reset by the appropriate eval*()
305 * that contained the dotcmd() call. 304 * that contained the dotcmd() call.
306 */ 305 */
307 skip = current_skipstate(); 306 skip = current_skipstate();
308 if (skip != SKIPNONE) { 307 if (skip != SKIPNONE) {
309 if (skip == SKIPFILE) 308 if (skip == SKIPFILE)
310 stop_skipping(); 309 stop_skipping();
311 break; 310 break;

cvs diff -r1.31 -r1.32 src/bin/sh/memalloc.c (expand / switch to unified diff)

--- src/bin/sh/memalloc.c 2018/07/22 20:37:57 1.31
+++ src/bin/sh/memalloc.c 2018/08/22 20:08:54 1.32
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: memalloc.c,v 1.31 2018/07/22 20:37:57 kre Exp $ */ 1/* $NetBSD: memalloc.c,v 1.32 2018/08/22 20:08:54 kre Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1991, 1993 4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist. 8 * Kenneth Almquist.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -27,27 +27,27 @@ @@ -27,27 +27,27 @@
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35#include <sys/cdefs.h> 35#include <sys/cdefs.h>
36#ifndef lint 36#ifndef lint
37#if 0 37#if 0
38static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; 38static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95";
39#else 39#else
40__RCSID("$NetBSD: memalloc.c,v 1.31 2018/07/22 20:37:57 kre Exp $"); 40__RCSID("$NetBSD: memalloc.c,v 1.32 2018/08/22 20:08:54 kre Exp $");
41#endif 41#endif
42#endif /* not lint */ 42#endif /* not lint */
43 43
44#include <stdlib.h> 44#include <stdlib.h>
45#include <unistd.h> 45#include <unistd.h>
46 46
47#include "shell.h" 47#include "shell.h"
48#include "output.h" 48#include "output.h"
49#include "memalloc.h" 49#include "memalloc.h"
50#include "error.h" 50#include "error.h"
51#include "machdep.h" 51#include "machdep.h"
52#include "mystring.h" 52#include "mystring.h"
53 53
@@ -150,47 +150,55 @@ stalloc(int nbytes) @@ -150,47 +150,55 @@ stalloc(int nbytes)
150 150
151void 151void
152stunalloc(pointer p) 152stunalloc(pointer p)
153{ 153{
154 if (p == NULL) { /*DEBUG */ 154 if (p == NULL) { /*DEBUG */
155 write(2, "stunalloc\n", 10); 155 write(2, "stunalloc\n", 10);
156 abort(); 156 abort();
157 } 157 }
158 stacknleft += stacknxt - (char *)p; 158 stacknleft += stacknxt - (char *)p;
159 stacknxt = p; 159 stacknxt = p;
160} 160}
161 161
162 162
163 163/* save the current status of the sh stack */
164void 164void
165setstackmark(struct stackmark *mark) 165setstackmark(struct stackmark *mark)
166{ 166{
167 mark->stackp = stackp; 167 mark->stackp = stackp;
168 mark->stacknxt = stacknxt; 168 mark->stacknxt = stacknxt;
169 mark->stacknleft = stacknleft; 169 mark->stacknleft = stacknleft;
170 mark->sstrnleft = sstrnleft; 170 mark->sstrnleft = sstrnleft;
171 mark->marknext = markp; 171 mark->marknext = markp;
172 markp = mark; 172 markp = mark;
173} 173}
174 174
175 175/* reset the stack mark, and remove it from the list of marks */
176void 176void
177popstackmark(struct stackmark *mark) 177popstackmark(struct stackmark *mark)
178{ 178{
 179 markp = mark->marknext; /* delete mark from the list */
 180 rststackmark(mark); /* and reset stack */
 181}
 182
 183/* reset the shell stack to its state recorded in the stack mark */
 184void
 185rststackmark(struct stackmark *mark)
 186{
179 struct stack_block *sp; 187 struct stack_block *sp;
180 188
181 INTOFF; 189 INTOFF;
182 markp = mark->marknext; 
183 while (stackp != mark->stackp) { 190 while (stackp != mark->stackp) {
 191 /* delete any recently allocated mem blocks */
184 sp = stackp; 192 sp = stackp;
185 stackp = sp->prev; 193 stackp = sp->prev;
186 ckfree(sp); 194 ckfree(sp);
187 } 195 }
188 stacknxt = mark->stacknxt; 196 stacknxt = mark->stacknxt;
189 stacknleft = mark->stacknleft; 197 stacknleft = mark->stacknleft;
190 sstrnleft = mark->sstrnleft; 198 sstrnleft = mark->sstrnleft;
191 INTON; 199 INTON;
192} 200}
193 201
194 202
195/* 203/*
196 * When the parser reads in a string, it wants to stick the string on the 204 * When the parser reads in a string, it wants to stick the string on the

cvs diff -r1.17 -r1.18 src/bin/sh/memalloc.h (expand / switch to unified diff)

--- src/bin/sh/memalloc.h 2017/06/17 07:22:12 1.17
+++ src/bin/sh/memalloc.h 2018/08/22 20:08:54 1.18
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: memalloc.h,v 1.17 2017/06/17 07:22:12 kre Exp $ */ 1/* $NetBSD: memalloc.h,v 1.18 2018/08/22 20:08:54 kre Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1991, 1993 4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist. 8 * Kenneth Almquist.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -45,26 +45,27 @@ struct stackmark { @@ -45,26 +45,27 @@ struct stackmark {
45 45
46extern char *stacknxt; 46extern char *stacknxt;
47extern int stacknleft; 47extern int stacknleft;
48extern int sstrnleft; 48extern int sstrnleft;
49extern int herefd; 49extern int herefd;
50 50
51pointer ckmalloc(size_t); 51pointer ckmalloc(size_t);
52pointer ckrealloc(pointer, int); 52pointer ckrealloc(pointer, int);
53char *savestr(const char *); 53char *savestr(const char *);
54pointer stalloc(int); 54pointer stalloc(int);
55void stunalloc(pointer); 55void stunalloc(pointer);
56void setstackmark(struct stackmark *); 56void setstackmark(struct stackmark *);
57void popstackmark(struct stackmark *); 57void popstackmark(struct stackmark *);
 58void rststackmark(struct stackmark *);
58void growstackblock(void); 59void growstackblock(void);
59void grabstackblock(int); 60void grabstackblock(int);
60char *growstackstr(void); 61char *growstackstr(void);
61char *makestrspace(void); 62char *makestrspace(void);
62void ungrabstackstr(char *, char *); 63void ungrabstackstr(char *, char *);
63 64
64 65
65 66
66#define stackblock() stacknxt 67#define stackblock() stacknxt
67#define stackblocksize() stacknleft 68#define stackblocksize() stacknleft
68#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() 69#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
69#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) 70#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
70#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); } 71#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); }