perl 4.0 patch 31: patch #20, continued
[p5sagit/p5-mst-13.2.git] / form.c
1 /* $RCSfile: form.c,v $$Revision: 4.0.1.3 $$Date: 92/06/08 13:21:42 $
2  *
3  *    Copyright (c) 1991, Larry Wall
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  * $Log:        form.c,v $
9  * Revision 4.0.1.3  92/06/08  13:21:42  lwall
10  * patch20: removed implicit int declarations on funcions
11  * patch20: form feed for formats is now specifiable via $^L
12  * patch20: Perl now distinguishes overlapped copies from non-overlapped
13  * 
14  * Revision 4.0.1.2  91/11/05  17:18:43  lwall
15  * patch11: formats didn't fill their fields as well as they could
16  * patch11: ^ fields chopped hyphens on line break
17  * patch11: # fields could write outside allocated memory
18  * 
19  * Revision 4.0.1.1  91/06/07  11:07:59  lwall
20  * patch4: new copyright notice
21  * patch4: default top-of-form format is now FILEHANDLE_TOP
22  * 
23  * Revision 4.0  91/03/20  01:19:23  lwall
24  * 4.0 baseline.
25  * 
26  */
27
28 #include "EXTERN.h"
29 #include "perl.h"
30
31 /* Forms stuff */
32
33 static int countlines();
34
35 void
36 form_parseargs(fcmd)
37 register FCMD *fcmd;
38 {
39     register int i;
40     register ARG *arg;
41     register int items;
42     STR *str;
43     ARG *parselist();
44     line_t oldline = curcmd->c_line;
45     int oldsave = savestack->ary_fill;
46
47     str = fcmd->f_unparsed;
48     curcmd->c_line = fcmd->f_line;
49     fcmd->f_unparsed = Nullstr;
50     (void)savehptr(&curstash);
51     curstash = str->str_u.str_hash;
52     arg = parselist(str);
53     restorelist(oldsave);
54
55     items = arg->arg_len - 1;   /* ignore $$ on end */
56     for (i = 1; i <= items; i++) {
57         if (!fcmd || fcmd->f_type == F_NULL)
58             fatal("Too many field values");
59         dehoist(arg,i);
60         fcmd->f_expr = make_op(O_ITEM,1,
61           arg[i].arg_ptr.arg_arg,Nullarg,Nullarg);
62         if (fcmd->f_flags & FC_CHOP) {
63             if ((fcmd->f_expr[1].arg_type & A_MASK) == A_STAB)
64                 fcmd->f_expr[1].arg_type = A_LVAL;
65             else if ((fcmd->f_expr[1].arg_type & A_MASK) == A_EXPR)
66                 fcmd->f_expr[1].arg_type = A_LEXPR;
67             else
68                 fatal("^ field requires scalar lvalue");
69         }
70         fcmd = fcmd->f_next;
71     }
72     if (fcmd && fcmd->f_type)
73         fatal("Not enough field values");
74     curcmd->c_line = oldline;
75     Safefree(arg);
76     str_free(str);
77 }
78
79 int newsize;
80
81 #define CHKLEN(allow) \
82 newsize = (d - orec->o_str) + (allow); \
83 if (newsize >= curlen) { \
84     curlen = d - orec->o_str; \
85     GROWSTR(&orec->o_str,&orec->o_len,orec->o_len + (allow)); \
86     d = orec->o_str + curlen;   /* in case it moves */ \
87     curlen = orec->o_len - 2; \
88 }
89
90 void
91 format(orec,fcmd,sp)
92 register struct outrec *orec;
93 register FCMD *fcmd;
94 int sp;
95 {
96     register char *d = orec->o_str;
97     register char *s;
98     register int curlen = orec->o_len - 2;
99     register int size;
100     FCMD *nextfcmd;
101     FCMD *linebeg = fcmd;
102     char tmpchar;
103     char *t;
104     CMD mycmd;
105     STR *str;
106     char *chophere;
107
108     mycmd.c_type = C_NULL;
109     orec->o_lines = 0;
110     for (; fcmd; fcmd = nextfcmd) {
111         nextfcmd = fcmd->f_next;
112         CHKLEN(fcmd->f_presize);
113         /*SUPPRESS 560*/
114         if (s = fcmd->f_pre) {
115             while (*s) {
116                 if (*s == '\n') {
117                     while (d > orec->o_str && (d[-1] == ' ' || d[-1] == '\t'))
118                         d--;
119                     if (fcmd->f_flags & FC_NOBLANK) {
120                         if (d == orec->o_str || d[-1] == '\n') {
121                             orec->o_lines--;    /* don't print blank line */
122                             linebeg = fcmd->f_next;
123                             break;
124                         }
125                         else if (fcmd->f_flags & FC_REPEAT)
126                             nextfcmd = linebeg;
127                         else
128                             linebeg = fcmd->f_next;
129                     }
130                     else
131                         linebeg = fcmd->f_next;
132                 }
133                 *d++ = *s++;
134             }
135         }
136         if (fcmd->f_unparsed)
137             form_parseargs(fcmd);
138         switch (fcmd->f_type) {
139         case F_NULL:
140             orec->o_lines++;
141             break;
142         case F_LEFT:
143             (void)eval(fcmd->f_expr,G_SCALAR,sp);
144             str = stack->ary_array[sp+1];
145             s = str_get(str);
146             size = fcmd->f_size;
147             CHKLEN(size);
148             chophere = Nullch;
149             while (size && *s && *s != '\n') {
150                 if (*s == '\t')
151                     *s = ' ';
152                 size--;
153                 if (*s && index(chopset,(*d++ = *s++)))
154                     chophere = s;
155                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
156                     *s = ' ';
157             }
158             if (size || !*s)
159                 chophere = s;
160             else if (chophere && chophere < s && *s && index(chopset,*s))
161                 chophere = s;
162             if (fcmd->f_flags & FC_CHOP) {
163                 if (!chophere)
164                     chophere = s;
165                 size += (s - chophere);
166                 d -= (s - chophere);
167                 if (fcmd->f_flags & FC_MORE &&
168                   *chophere && strNE(chophere,"\n")) {
169                     while (size < 3) {
170                         d--;
171                         size++;
172                     }
173                     while (d[-1] == ' ' && size < fcmd->f_size) {
174                         d--;
175                         size++;
176                     }
177                     *d++ = '.';
178                     *d++ = '.';
179                     *d++ = '.';
180                     size -= 3;
181                 }
182                 while (*chophere && index(chopset,*chophere)
183                   && isSPACE(*chophere))
184                     chophere++;
185                 str_chop(str,chophere);
186             }
187             if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
188                 size = 0;                       /* no spaces before newline */
189             while (size) {
190                 size--;
191                 *d++ = ' ';
192             }
193             break;
194         case F_RIGHT:
195             (void)eval(fcmd->f_expr,G_SCALAR,sp);
196             str = stack->ary_array[sp+1];
197             t = s = str_get(str);
198             size = fcmd->f_size;
199             CHKLEN(size);
200             chophere = Nullch;
201             while (size && *s && *s != '\n') {
202                 if (*s == '\t')
203                     *s = ' ';
204                 size--;
205                 if (*s && index(chopset,*s++))
206                     chophere = s;
207                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
208                     *s = ' ';
209             }
210             if (size || !*s)
211                 chophere = s;
212             else if (chophere && chophere < s && *s && index(chopset,*s))
213                 chophere = s;
214             if (fcmd->f_flags & FC_CHOP) {
215                 if (!chophere)
216                     chophere = s;
217                 size += (s - chophere);
218                 s = chophere;
219                 while (*chophere && index(chopset,*chophere)
220                   && isSPACE(*chophere))
221                     chophere++;
222             }
223             tmpchar = *s;
224             *s = '\0';
225             while (size) {
226                 size--;
227                 *d++ = ' ';
228             }
229             size = s - t;
230             Copy(t,d,size,char);
231             d += size;
232             *s = tmpchar;
233             if (fcmd->f_flags & FC_CHOP)
234                 str_chop(str,chophere);
235             break;
236         case F_CENTER: {
237             int halfsize;
238
239             (void)eval(fcmd->f_expr,G_SCALAR,sp);
240             str = stack->ary_array[sp+1];
241             t = s = str_get(str);
242             size = fcmd->f_size;
243             CHKLEN(size);
244             chophere = Nullch;
245             while (size && *s && *s != '\n') {
246                 if (*s == '\t')
247                     *s = ' ';
248                 size--;
249                 if (*s && index(chopset,*s++))
250                     chophere = s;
251                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
252                     *s = ' ';
253             }
254             if (size || !*s)
255                 chophere = s;
256             else if (chophere && chophere < s && *s && index(chopset,*s))
257                 chophere = s;
258             if (fcmd->f_flags & FC_CHOP) {
259                 if (!chophere)
260                     chophere = s;
261                 size += (s - chophere);
262                 s = chophere;
263                 while (*chophere && index(chopset,*chophere)
264                   && isSPACE(*chophere))
265                     chophere++;
266             }
267             tmpchar = *s;
268             *s = '\0';
269             halfsize = size / 2;
270             while (size > halfsize) {
271                 size--;
272                 *d++ = ' ';
273             }
274             size = s - t;
275             Copy(t,d,size,char);
276             d += size;
277             *s = tmpchar;
278             if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
279                 size = 0;                       /* no spaces before newline */
280             else
281                 size = halfsize;
282             while (size) {
283                 size--;
284                 *d++ = ' ';
285             }
286             if (fcmd->f_flags & FC_CHOP)
287                 str_chop(str,chophere);
288             break;
289         }
290         case F_LINES:
291             (void)eval(fcmd->f_expr,G_SCALAR,sp);
292             str = stack->ary_array[sp+1];
293             s = str_get(str);
294             size = str_len(str);
295             CHKLEN(size+1);
296             orec->o_lines += countlines(s,size) - 1;
297             Copy(s,d,size,char);
298             d += size;
299             if (size && s[size-1] != '\n') {
300                 *d++ = '\n';
301                 orec->o_lines++;
302             }
303             linebeg = fcmd->f_next;
304             break;
305         case F_DECIMAL: {
306             double value;
307
308             (void)eval(fcmd->f_expr,G_SCALAR,sp);
309             str = stack->ary_array[sp+1];
310             size = fcmd->f_size;
311             CHKLEN(size+1);
312             /* If the field is marked with ^ and the value is undefined,
313                blank it out. */
314             if ((fcmd->f_flags & FC_CHOP) && !str->str_pok && !str->str_nok) {
315                 while (size) {
316                     size--;
317                     *d++ = ' ';
318                 }
319                 break;
320             }
321             value = str_gnum(str);
322             if (fcmd->f_flags & FC_DP) {
323                 sprintf(d, "%#*.*f", size, fcmd->f_decimals, value);
324             } else {
325                 sprintf(d, "%*.0f", size, value);
326             }
327             d += size;
328             break;
329         }
330         }
331     }
332     CHKLEN(1);
333     *d++ = '\0';
334 }
335
336 static int
337 countlines(s,size)
338 register char *s;
339 register int size;
340 {
341     register int count = 0;
342
343     while (size--) {
344         if (*s++ == '\n')
345             count++;
346     }
347     return count;
348 }
349
350 void
351 do_write(orec,stab,sp)
352 struct outrec *orec;
353 STAB *stab;
354 int sp;
355 {
356     register STIO *stio = stab_io(stab);
357     FILE *ofp = stio->ofp;
358
359 #ifdef DEBUGGING
360     if (debug & 256)
361         fprintf(stderr,"left=%ld, todo=%ld\n",
362           (long)stio->lines_left, (long)orec->o_lines);
363 #endif
364     if (stio->lines_left < orec->o_lines) {
365         if (!stio->top_stab) {
366             STAB *topstab;
367             char tmpbuf[256];
368
369             if (!stio->top_name) {
370                 if (!stio->fmt_name)
371                     stio->fmt_name = savestr(stab_name(stab));
372                 sprintf(tmpbuf, "%s_TOP", stio->fmt_name);
373                 topstab = stabent(tmpbuf,FALSE);
374                 if (topstab && stab_form(topstab))
375                     stio->top_name = savestr(tmpbuf);
376                 else
377                     stio->top_name = savestr("top");
378             }
379             topstab = stabent(stio->top_name,FALSE);
380             if (!topstab || !stab_form(topstab)) {
381                 stio->lines_left = 100000000;
382                 goto forget_top;
383             }
384             stio->top_stab = topstab;
385         }
386         if (stio->lines_left >= 0 && stio->page > 0)
387             fwrite(formfeed->str_ptr, formfeed->str_cur, 1, ofp);
388         stio->lines_left = stio->page_len;
389         stio->page++;
390         format(&toprec,stab_form(stio->top_stab),sp);
391         fputs(toprec.o_str,ofp);
392         stio->lines_left -= toprec.o_lines;
393     }
394   forget_top:
395     fputs(orec->o_str,ofp);
396     stio->lines_left -= orec->o_lines;
397 }