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