X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fascii.c;h=29d7b76b2940d76dbc0af108827922785f8e71ce;hb=HEAD;hp=0a26c75ab3fee34aad00628e834bce282452c002;hpb=baf657198fcf0cabe289246dc07a82da67c86f5b;p=pspp-builds.git diff --git a/src/output/ascii.c b/src/output/ascii.c index 0a26c75a..29d7b76b 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -19,10 +19,11 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -33,6 +34,7 @@ #include "error.h" #include "minmax.h" #include "output.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -40,13 +42,17 @@ /* ASCII driver options: (defaults listed first) output-file="pspp.list" + append=no|yes If output-file exists, append to it? + chart-files="pspp-#.png" Name used for charts. + chart-type=png Format of charts (use "none" to disable). + paginate=on|off Formfeeds are desired? tab-width=8 Width of a tab; 0 to not use tabs. headers=on|off Put headers at top of page? emphasis=bold|underline|none Style to use for emphasis. - length=66 - width=130 + length=66|auto + width=79|auto squeeze=off|on Squeeze multiple newlines into exactly one. top-margin=2 @@ -96,12 +102,17 @@ struct ascii_driver_ext struct pool *pool; /* User parameters. */ + bool append; /* Append if output-file already exists? */ bool headers; /* Print headers at top of page? */ bool paginate; /* Insert formfeeds? */ bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */ enum emphasis_style emphasis; /* How to emphasize text. */ int tab_width; /* Width of a tab; 0 not to use tabs. */ + const char *chart_type; /* Type of charts to output; NULL for none. */ + const char *chart_file_name; /* Name of files used for charts. */ + bool auto_width; /* Use viewwidth as page width? */ + bool auto_length; /* Use viewlength as page width? */ int page_length; /* Page length before subtracting margins. */ int top_margin; /* Top margin in lines. */ int bottom_margin; /* Bottom margin in lines. */ @@ -112,13 +123,16 @@ struct ascii_driver_ext /* Internal state. */ char *file_name; /* Output file name. */ FILE *file; /* Output file. */ + bool reported_error; /* Reported file open error? */ int page_number; /* Current page number. */ struct line *lines; /* Page content. */ int line_cap; /* Number of lines allocated. */ + int chart_cnt; /* Number of charts so far. */ }; static void ascii_flush (struct outp_driver *); static int get_default_box_char (size_t idx); +static bool update_page_size (struct outp_driver *, bool issue_error); static bool handle_option (struct outp_driver *this, const char *key, const struct string *val); @@ -136,11 +150,16 @@ ascii_open_driver (struct outp_driver *this, struct substring options) this->horiz_line_width[i] = this->vert_line_width[i] = i != OUTP_L_NONE; this->ext = x = pool_create_container (struct ascii_driver_ext, pool); + x->append = false; x->headers = true; x->paginate = true; x->squeeze_blank_lines = false; x->emphasis = EMPH_BOLD; x->tab_width = 8; + x->chart_file_name = pool_strdup (x->pool, "pspp-#.png"); + x->chart_type = pool_strdup (x->pool, "png"); + x->auto_width = false; + x->auto_length = false; x->page_length = 66; x->top_margin = 2; x->bottom_margin = 2; @@ -149,26 +168,17 @@ ascii_open_driver (struct outp_driver *this, struct substring options) x->init = NULL; x->file_name = pool_strdup (x->pool, "pspp.list"); x->file = NULL; + x->reported_error = false; x->page_number = 0; x->lines = NULL; x->line_cap = 0; + x->chart_cnt = 0; if (!outp_parse_options (options, handle_option, this)) goto error; - this->length = x->page_length - x->top_margin - x->bottom_margin - 1; - if (x->headers) - this->length -= 3; - - if (this->width < 59 || this->length < 15) - { - error (0, 0, - _("ascii: page excluding margins and headers " - "must be at least 59 characters wide by 15 lines long, but as " - "configured is only %d characters by %d lines"), - this->width, this->length); - return false; - } + if (!update_page_size (this, true)) + goto error; for (i = 0; i < LNS_COUNT; i++) if (x->box[i] == NULL) @@ -218,6 +228,43 @@ get_default_box_char (size_t idx) } } +/* Re-calculates the page width and length based on settings, + margins, and, if "auto" is set, the size of the user's + terminal window or GUI output window. */ +static bool +update_page_size (struct outp_driver *this, bool issue_error) +{ + struct ascii_driver_ext *x = this->ext; + int margins = x->top_margin + x->bottom_margin + 1 + (x->headers ? 3 : 0); + + if (x->auto_width) + this->width = settings_get_viewwidth (); + if (x->auto_length) + x->page_length = settings_get_viewlength (); + + this->length = x->page_length - margins; + + if (this->width < 59 || this->length < 15) + { + if (issue_error) + error (0, 0, + _("ascii: page excluding margins and headers " + "must be at least 59 characters wide by 15 lines long, but " + "as configured is only %d characters by %d lines"), + this->width, this->length); + if (this->width < 59) + this->width = 59; + if (this->length < 15) + { + this->length = 15; + x->page_length = this->length + margins; + } + return false; + } + + return true; +} + static bool ascii_close_driver (struct outp_driver *this) { @@ -236,8 +283,7 @@ enum boolean_arg, emphasis_arg, nonneg_int_arg, - pos_int_arg, - output_file_arg, + page_size_arg, string_arg }; @@ -246,19 +292,21 @@ static const struct outp_option option_tab[] = {"headers", boolean_arg, 0}, {"paginate", boolean_arg, 1}, {"squeeze", boolean_arg, 2}, + {"append", boolean_arg, 3}, {"emphasis", emphasis_arg, 0}, - {"output-file", output_file_arg, 0}, - - {"length", pos_int_arg, 0}, - {"width", pos_int_arg, 1}, + {"length", page_size_arg, 0}, + {"width", page_size_arg, 1}, {"top-margin", nonneg_int_arg, 0}, {"bottom-margin", nonneg_int_arg, 1}, {"tab-width", nonneg_int_arg, 2}, - {"init", string_arg, 0}, + {"output-file", string_arg, 0}, + {"chart-files", string_arg, 1}, + {"chart-type", string_arg, 2}, + {"init", string_arg, 3}, {NULL, 0, 0}, }; @@ -295,33 +343,51 @@ handle_option (struct outp_driver *this, const char *key, case -1: error (0, 0, _("ascii: unknown parameter `%s'"), key); break; - case output_file_arg: - x->file_name = pool_strdup (x->pool, value); - break; - case pos_int_arg: + case page_size_arg: { char *tail; int arg; - errno = 0; - arg = strtol (value, &tail, 0); - if (arg < 1 || errno == ERANGE || *tail) - { - error (0, 0, _("ascii: positive integer required as `%s' value"), - key); - break; - } - switch (subcat) - { - case 0: - x->page_length = arg; - break; - case 1: - this->width = arg; - break; - default: - NOT_REACHED (); - } + if (ss_equals_case (ds_ss (val), ss_cstr ("auto"))) + { + if (!(this->device & OUTP_DEV_SCREEN)) + { + /* We only let `screen' devices have `auto' + length or width because output to such devices + is flushed before each new command. Resizing + a device in the middle of output seems like a + bad idea. */ + error (0, 0, _("ascii: only screen devices may have `auto' " + "length or width")); + } + else if (subcat == 0) + x->auto_length = true; + else + x->auto_width = true; + } + else + { + errno = 0; + arg = strtol (value, &tail, 0); + if (arg < 1 || errno == ERANGE || *tail) + { + error (0, 0, _("ascii: positive integer required as " + "`%s' value"), + key); + break; + } + switch (subcat) + { + case 0: + x->page_length = arg; + break; + case 1: + this->width = arg; + break; + default: + NOT_REACHED (); + } + } } break; case emphasis_arg: @@ -391,14 +457,36 @@ handle_option (struct outp_driver *this, const char *key, case 2: x->squeeze_blank_lines = setting; break; + case 3: + x->append = setting; + break; default: NOT_REACHED (); } } break; case string_arg: - free (x->init); - x->init = pool_strdup (x->pool, value); + switch (subcat) + { + case 0: + x->file_name = pool_strdup (x->pool, value); + break; + case 1: + if (ds_find_char (val, '#') != SIZE_MAX) + x->chart_file_name = pool_strdup (x->pool, value); + else + error (0, 0, _("`chart-files' value must contain `#'")); + break; + case 2: + if (value[0] != '\0') + x->chart_type = pool_strdup (x->pool, value); + else + x->chart_type = NULL; + break; + case 3: + x->init = pool_strdup (x->pool, value); + break; + } break; default: NOT_REACHED (); @@ -413,19 +501,30 @@ ascii_open_page (struct outp_driver *this) struct ascii_driver_ext *x = this->ext; int i; + update_page_size (this, false); + if (x->file == NULL) { - x->file = fn_open (x->file_name, "w"); - if (x->file == NULL) + x->file = fn_open (x->file_name, x->append ? "a" : "w"); + if (x->file != NULL) { - error (0, errno, _("ascii: opening output file \"%s\""), - x->file_name); - return; + pool_attach_file (x->pool, x->file); + if (x->init != NULL) + fputs (x->init, x->file); + } + else + { + /* Report the error to the user and complete + initialization. If we do not finish initialization, + then calls to other driver functions will segfault + later. It would be better to simply drop the driver + entirely, but we do not have a convenient mechanism + for this (yet). */ + if (!x->reported_error) + error (0, errno, _("ascii: opening output file \"%s\""), + x->file_name); + x->reported_error = true; } - pool_attach_file (x->pool, x->file); - - if (x->init != NULL) - fputs (x->init, x->file); } x->page_number++; @@ -504,6 +603,15 @@ ascii_line (struct outp_driver *this, } } +static void +ascii_submit (struct outp_driver *this UNUSED, struct som_entity *s) +{ + extern struct som_table_class tab_table_class; + + assert (s->class == &tab_table_class); + assert (s->type == SOM_CHART); +} + static void text_draw (struct outp_driver *this, enum outp_font font, @@ -543,9 +651,10 @@ text_draw (struct outp_driver *this, ext->lines[y].chars[x++] = *string++ | attr; } -/* Divides the text T->S into lines of width T->H. Sets T->V to the - number of lines necessary. Actually draws the text if DRAW is - true. */ +/* Divides the text T->S into lines of width T->H. Sets *WIDTH + to the maximum width of a line and *HEIGHT to the number of + lines, if those arguments are non-null. Actually draws the + text if DRAW is true. */ static void delineate (struct outp_driver *this, const struct outp_text *text, bool draw, int *width, int *height) @@ -623,7 +732,6 @@ ascii_text_draw (struct outp_driver *this, const struct outp_text *t) assert (this->page_open); delineate (this, t, true, NULL, NULL); } - /* ascii_close_page () and support routines. */ @@ -747,7 +855,6 @@ static void ascii_flush (struct outp_driver *this) { struct ascii_driver_ext *x = this->ext; - if (x->file != NULL) { if (fn_close (x->file_name, x->file) != 0) @@ -759,16 +866,55 @@ ascii_flush (struct outp_driver *this) } static void -ascii_chart_initialise (struct outp_driver *d UNUSED, struct chart *ch) +ascii_chart_initialise (struct outp_driver *this, struct chart *ch) { - error (0, 0, _("ascii: charts are unsupported by this driver")); - ch->lp = 0; + struct ascii_driver_ext *x = this->ext; + struct outp_text t; + char *text; + + if (x->chart_type == NULL) + return; + + /* Initialize chart. */ + chart_init_separate (ch, x->chart_type, x->chart_file_name, ++x->chart_cnt); + if (ch->file_name == NULL) + return; + + /* Mention chart in output. + First advance current position. */ + if (!this->page_open) + outp_open_page (this); + else + { + this->cp_y++; + if (this->cp_y >= this->length) + { + outp_close_page (this); + outp_open_page (this); + } + } + + /* Then write the text. */ + text = xasprintf ("See %s for a chart.", ch->file_name); + t.font = OUTP_FIXED; + t.justification = OUTP_LEFT; + t.string = ss_cstr (text); + t.h = this->width; + t.v = 1; + t.x = 0; + t.y = this->cp_y; + ascii_text_draw (this, &t); + this->cp_y++; + + free (text); } static void -ascii_chart_finalise (struct outp_driver *d UNUSED, struct chart *ch UNUSED) +ascii_chart_finalise (struct outp_driver *this, struct chart *ch) { - + struct ascii_driver_ext *x = this->ext; + if (x->chart_type != NULL) + chart_finalise_separate (ch); } const struct outp_class ascii_class = @@ -783,7 +929,7 @@ const struct outp_class ascii_class = ascii_close_page, ascii_flush, - NULL, + ascii_submit, ascii_line, ascii_text_metrics,