/*! * \file lib/raster/history.c * * \brief Raster Library - History management * * (C) 2001-2009 GRASS Development Team * * This program is free software under the GNU General Public License * (>=v2). Read the file COPYING that comes with GRASS for details. * * \author Original author CERL */ #include #include #include #include #include /*! * \brief Append a string to a History structure * * * \param hist pointer to History structure which holds history info * \param str string to append * * \return void */ void Rast_append_history(struct History *hist, const char *str) { hist->lines = G_realloc(hist->lines, (hist->nlines + 1) * sizeof(char *)); hist->lines[hist->nlines++] = G_store(str); } /*! * \brief Append a formatted string to a History structure * * * \param hist pointer to History structure which holds history info * \param fmt a string of format characters * \param ... the arguments associated with the format characters * * \return void */ void Rast_append_format_history(struct History *hist, const char *fmt, ...) { va_list ap; char *str; hist->lines = G_realloc(hist->lines, (hist->nlines + 1) * sizeof(char *)); va_start(ap, fmt); G_vasprintf(&str, fmt, ap); va_end(ap); hist->lines[hist->nlines++] = str; } int Rast__read_history(struct History *hist, FILE *fp) { int i; for (i = 0; i < HIST_NUM_FIELDS; i++) { char buf[4096]; if (!G_getl(buf, sizeof(buf), fp)) { fclose(fp); return -1; } G_ascii_check(buf); hist->fields[i] = G_store(buf); } hist->nlines = 0; for (;;) { char buf[4096]; if (!G_getl(buf, sizeof(buf), fp)) break; Rast_append_history(hist, buf); } fclose(fp); return 0; } /*! * \brief Read raster history file * * This routine reads the history file for the raster map name * in mapset into the hist structure. * * A diagnostic message is printed and -1 is returned if there is an * error reading the history file. Otherwise, 0 is returned. * * \param name map name * \param mapset mapset name * \param hist pointer to History structure which holds history info * * \return -1 on error * \return 0 on success */ int Rast_read_history(const char *name, const char *mapset, struct History *hist) { FILE *fp; G_zero(hist, sizeof(struct History)); fp = G_fopen_old("hist", name, mapset); if (!fp) { G_warning(_("Unable to get history information for <%s@%s>"), name, mapset); return -1; } if (Rast__read_history(hist, fp) == 0) return 0; G_warning(_("Unable to get history information for <%s@%s>"), name, mapset); return -1; } void Rast__write_history(struct History *hist, FILE *fp) { int i; for (i = 0; i < HIST_NUM_FIELDS; i++) fprintf(fp, "%s\n", hist->fields[i] ? hist->fields[i] : ""); for (i = 0; i < hist->nlines; i++) fprintf(fp, "%s\n", hist->lines[i]); fclose(fp); } /*! * \brief Write raster history file * * This routine writes the history file for the raster map * name in the current mapset from the hist structure. * * A diagnostic message is printed and -1 is returned if there is an * error writing the history file. Otherwise, 0 is returned. * * Note: The hist structure should first be initialized * using Rast_short_history(). * * \param name map name * \param[out] hist pointer to History structure which holds history info * * \return void */ void Rast_write_history(const char *name, struct History *hist) { FILE *fp = G_fopen_new("hist", name); if (!fp) G_fatal_error(_("Unable to write history information for <%s>"), name); Rast__write_history(hist, fp); } /*! * \brief Set the string of a specific history field * * * \param hist pointer to History structure which holds history info * \param field number of a specific history field, should be accessed with macros (HIST_MAPID, ...) * * \return string of the history field */ const char *Rast_get_history(struct History *hist, int field) { return hist->fields[field]; } /*! * \brief Set the string of a specific history field * * * \param hist pointer to History structure which holds history info * \param field number of a specific history field, should be accessed with macros (HIST_MAPID, ...) * \param str string of the history field * * \return void */ void Rast_set_history(struct History *hist, int field, const char *str) { if (hist->fields[field]) G_free(hist->fields[field]); hist->fields[field] = str ? G_store(str) : NULL; } void Rast_format_history(struct History *hist, int field, const char *fmt, ...) { va_list ap; if (hist->fields[field]) G_free(hist->fields[field]); va_start(ap, fmt); G_vasprintf(&hist->fields[field], fmt, ap); va_end(ap); } /*! * \brief Initialize history structure * * This routine initializes the hist structure, recording the * date, user, module name and the raster map name * structure. The type can be "raster", "reclass", "GDAL-link", * or "virtual". * * Note: This routine only initializes the data structure. It * does not write the history file. * * \param name map name * \param type map type * \param hist pointer to History structure which holds history info */ void Rast_short_history(const char *name, const char *type, struct History *hist) { G_zero(hist, sizeof(struct History)); Rast_set_history(hist, HIST_MAPID, G_date()); Rast_set_history(hist, HIST_TITLE, name); Rast_set_history(hist, HIST_MAPSET, G_mapset()); Rast_set_history(hist, HIST_CREATOR, G_whoami()); Rast_set_history(hist, HIST_MAPTYPE, type); Rast_format_history(hist, HIST_KEYWRD, _("generated by %s"), G_program_name()); Rast_set_history(hist, HIST_DATSRC_1, ""); Rast_set_history(hist, HIST_DATSRC_2, ""); hist->nlines = 0; } /*! * \brief Save command line to raster history structure * * This routine takes an existing (run Rast_short_history first() history * structure and adds the command line to the end of the comments * array, as cleaned & expanded by the parser. * * - First version had for loops of [i][j] character assignments and ending * nulls, but using the string libraries is cleaner and less bug prone. * - Second version had white space detection, intelligent wrapping, and * indentation of continued lines, but this proved a pain in the neck for * things like r.patch which can have long strings without any * parser-acceptable breaks. * - This is MK-III, simplified, but that's good: it's cut & paste-able. * * Note: use Rast_write_history() to write the structure. * * Sample Usage: * \code * struct History history; * Rast_short_history(rasterfile, "raster", &history); * Rast_command_history(&history); * Rast_write_history(rasterfile, &history); * \endcode * * \param hist pointer to History structure which holds history info * * \return 0 on success * \return 1 on failure (history file full, no change) * \return 2 on failure (history file full, added as much as we could) */ int Rast_command_history(struct History *hist) { char *cmdlin; int cmdlen; cmdlin = G_recreate_command(); cmdlen = strlen(cmdlin); if (hist->nlines > 0) /* add a blank line if preceding history exists */ Rast_append_history(hist, ""); if (cmdlen < 70) /* ie if it will fit on a single line */ Rast_append_history(hist, cmdlin); else { /* multi-line required */ int j; /* j is the current position in the command line string */ for (j = 0; cmdlen - j > 70; j += 68) { char buf[80]; memcpy(buf, &cmdlin[j], 68); buf[68] = '\\'; buf[69] = '\0'; Rast_append_history(hist, buf); } if (cmdlen - j > 0) /* ie anything left */ Rast_append_history(hist, &cmdlin[j]); } G_free(cmdlin); return 0; } void Rast_clear_history(struct History *hist) { int i; for (i = 0; i < hist->nlines; i++) G_free(hist->lines[i]); if (hist->lines) G_free(hist->lines); hist->lines = NULL; hist->nlines = 0; } void Rast_free_history(struct History *hist) { int i; for (i = 0; i < HIST_NUM_FIELDS; i++) if (hist->fields[i]) { G_free(hist->fields[i]); hist->fields[i] = NULL; } Rast_clear_history(hist); } int Rast_history_length(struct History *hist) { return hist->nlines; } const char *Rast_history_line(struct History *hist, int line) { if (line < 0 || line >= hist->nlines) return ""; return hist->lines[line]; }