perl 4.0 patch 36: (combined patch)
[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     int blank = TRUE;
108
109     mycmd.c_type = C_NULL;
110     orec->o_lines = 0;
111     for (; fcmd; fcmd = nextfcmd) {
112         nextfcmd = fcmd->f_next;
113         CHKLEN(fcmd->f_presize);
114         /*SUPPRESS 560*/
115         if (s = fcmd->f_pre) {
116             while (*s) {
117                 if (*s == '\n') {
118                     t = orec->o_str;
119                     if (blank && (fcmd->f_flags & FC_REPEAT)) {
120                         while (d > t && (d[-1] != '\n'))
121                             d--;
122                     }
123                     else {
124                         while (d > t && (d[-1] == ' ' || d[-1] == '\t'))
125                             d--;
126                     }
127                     if (fcmd->f_flags & FC_NOBLANK) {
128                         if (blank || d == orec->o_str || d[-1] == '\n') {
129                             orec->o_lines--;    /* don't print blank line */
130                             linebeg = fcmd->f_next;
131                             break;
132                         }
133                         else if (fcmd->f_flags & FC_REPEAT)
134                             nextfcmd = linebeg;
135                         else
136                             linebeg = fcmd->f_next;
137                     }
138                     else
139                         linebeg = fcmd->f_next;
140                     blank = TRUE;
141                 }
142                 *d++ = *s++;
143             }
144         }
145         if (fcmd->f_unparsed)
146             form_parseargs(fcmd);
147         switch (fcmd->f_type) {
148         case F_NULL:
149             orec->o_lines++;
150             break;
151         case F_LEFT:
152             (void)eval(fcmd->f_expr,G_SCALAR,sp);
153             str = stack->ary_array[sp+1];
154             s = str_get(str);
155             size = fcmd->f_size;
156             CHKLEN(size);
157             chophere = Nullch;
158             while (size && *s && *s != '\n') {
159                 if (*s == '\t')
160                     *s = ' ';
161                 else if (*s != ' ')
162                     blank = FALSE;
163                 size--;
164                 if (*s && index(chopset,(*d++ = *s++)))
165                     chophere = s;
166                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
167                     *s = ' ';
168             }
169             if (size || !*s)
170                 chophere = s;
171             else if (chophere && chophere < s && *s && index(chopset,*s))
172                 chophere = s;
173             if (fcmd->f_flags & FC_CHOP) {
174                 if (!chophere)
175                     chophere = s;
176                 size += (s - chophere);
177                 d -= (s - chophere);
178                 if (fcmd->f_flags & FC_MORE &&
179                   *chophere && strNE(chophere,"\n")) {
180                     while (size < 3) {
181                         d--;
182                         size++;
183                     }
184                     while (d[-1] == ' ' && size < fcmd->f_size) {
185                         d--;
186                         size++;
187                     }
188                     *d++ = '.';
189                     *d++ = '.';
190                     *d++ = '.';
191                     size -= 3;
192                 }
193                 while (*chophere && index(chopset,*chophere)
194                   && isSPACE(*chophere))
195                     chophere++;
196                 str_chop(str,chophere);
197             }
198             if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
199                 size = 0;                       /* no spaces before newline */
200             while (size) {
201                 size--;
202                 *d++ = ' ';
203             }
204             break;
205         case F_RIGHT:
206             (void)eval(fcmd->f_expr,G_SCALAR,sp);
207             str = stack->ary_array[sp+1];
208             t = s = str_get(str);
209             size = fcmd->f_size;
210             CHKLEN(size);
211             chophere = Nullch;
212             while (size && *s && *s != '\n') {
213                 if (*s == '\t')
214                     *s = ' ';
215                 else if (*s != ' ')
216                     blank = FALSE;
217                 size--;
218                 if (*s && index(chopset,*s++))
219                     chophere = s;
220                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
221                     *s = ' ';
222             }
223             if (size || !*s)
224                 chophere = s;
225             else if (chophere && chophere < s && *s && index(chopset,*s))
226                 chophere = s;
227             if (fcmd->f_flags & FC_CHOP) {
228                 if (!chophere)
229                     chophere = s;
230                 size += (s - chophere);
231                 s = chophere;
232                 while (*chophere && index(chopset,*chophere)
233                   && isSPACE(*chophere))
234                     chophere++;
235             }
236             tmpchar = *s;
237             *s = '\0';
238             while (size) {
239                 size--;
240                 *d++ = ' ';
241             }
242             size = s - t;
243             Copy(t,d,size,char);
244             d += size;
245             *s = tmpchar;
246             if (fcmd->f_flags & FC_CHOP)
247                 str_chop(str,chophere);
248             break;
249         case F_CENTER: {
250             int halfsize;
251
252             (void)eval(fcmd->f_expr,G_SCALAR,sp);
253             str = stack->ary_array[sp+1];
254             t = s = str_get(str);
255             size = fcmd->f_size;
256             CHKLEN(size);
257             chophere = Nullch;
258             while (size && *s && *s != '\n') {
259                 if (*s == '\t')
260                     *s = ' ';
261                 else if (*s != ' ')
262                     blank = FALSE;
263                 size--;
264                 if (*s && index(chopset,*s++))
265                     chophere = s;
266                 if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
267                     *s = ' ';
268             }
269             if (size || !*s)
270                 chophere = s;
271             else if (chophere && chophere < s && *s && index(chopset,*s))
272                 chophere = s;
273             if (fcmd->f_flags & FC_CHOP) {
274                 if (!chophere)
275                     chophere = s;
276                 size += (s - chophere);
277                 s = chophere;
278                 while (*chophere && index(chopset,*chophere)
279                   && isSPACE(*chophere))
280                     chophere++;
281             }
282             tmpchar = *s;
283             *s = '\0';
284             halfsize = size / 2;
285             while (size > halfsize) {
286                 size--;
287                 *d++ = ' ';
288             }
289             size = s - t;
290             Copy(t,d,size,char);
291             d += size;
292             *s = tmpchar;
293             if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
294                 size = 0;                       /* no spaces before newline */
295             else
296                 size = halfsize;
297             while (size) {
298                 size--;
299                 *d++ = ' ';
300             }
301             if (fcmd->f_flags & FC_CHOP)
302                 str_chop(str,chophere);
303             break;
304         }
305         case F_LINES:
306             (void)eval(fcmd->f_expr,G_SCALAR,sp);
307             str = stack->ary_array[sp+1];
308             s = str_get(str);
309             size = str_len(str);
310             CHKLEN(size+1);
311             orec->o_lines += countlines(s,size) - 1;
312             Copy(s,d,size,char);
313             d += size;
314             if (size && s[size-1] != '\n') {
315                 *d++ = '\n';
316                 orec->o_lines++;
317             }
318             linebeg = fcmd->f_next;
319             break;
320         case F_DECIMAL: {
321             double value;
322
323             (void)eval(fcmd->f_expr,G_SCALAR,sp);
324             str = stack->ary_array[sp+1];
325             size = fcmd->f_size;
326             CHKLEN(size+1);
327             /* If the field is marked with ^ and the value is undefined,
328                blank it out. */
329             if ((fcmd->f_flags & FC_CHOP) && !str->str_pok && !str->str_nok) {
330                 while (size) {
331                     size--;
332                     *d++ = ' ';
333                 }
334                 break;
335             }
336             blank = FALSE;
337             value = str_gnum(str);
338             if (fcmd->f_flags & FC_DP) {
339                 sprintf(d, "%#*.*f", size, fcmd->f_decimals, value);
340             } else {
341                 sprintf(d, "%*.0f", size, value);
342             }
343             d += size;
344             break;
345         }
346         }
347     }
348     CHKLEN(1);
349     *d++ = '\0';
350 }
351
352 static int
353 countlines(s,size)
354 register char *s;
355 register int size;
356 {
357     register int count = 0;
358
359     while (size--) {
360         if (*s++ == '\n')
361             count++;
362     }
363     return count;
364 }
365
366 void
367 do_write(orec,stab,sp)
368 struct outrec *orec;
369 STAB *stab;
370 int sp;
371 {
372     register STIO *stio = stab_io(stab);
373     FILE *ofp = stio->ofp;
374
375 #ifdef DEBUGGING
376     if (debug & 256)
377         fprintf(stderr,"left=%ld, todo=%ld\n",
378           (long)stio->lines_left, (long)orec->o_lines);
379 #endif
380     if (stio->lines_left < orec->o_lines) {
381         if (!stio->top_stab) {
382             STAB *topstab;
383             char tmpbuf[256];
384
385             if (!stio->top_name) {
386                 if (!stio->fmt_name)
387                     stio->fmt_name = savestr(stab_name(stab));
388                 sprintf(tmpbuf, "%s_TOP", stio->fmt_name);
389                 topstab = stabent(tmpbuf,FALSE);
390                 if (topstab && stab_form(topstab))
391                     stio->top_name = savestr(tmpbuf);
392                 else
393                     stio->top_name = savestr("top");
394             }
395             topstab = stabent(stio->top_name,FALSE);
396             if (!topstab || !stab_form(topstab)) {
397                 stio->lines_left = 100000000;
398                 goto forget_top;
399             }
400             stio->top_stab = topstab;
401         }
402         if (stio->lines_left >= 0 && stio->page > 0)
403             fwrite(formfeed->str_ptr, formfeed->str_cur, 1, ofp);
404         stio->lines_left = stio->page_len;
405         stio->page++;
406         format(&toprec,stab_form(stio->top_stab),sp);
407         fputs(toprec.o_str,ofp);
408         stio->lines_left -= toprec.o_lines;
409     }
410   forget_top:
411     fputs(orec->o_str,ofp);
412     stio->lines_left -= orec->o_lines;
413 }