..
/
download
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <time.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include "s1kd_tools.h"
#define PROG_NAME "s1kd-upissue"
#define VERSION "5.0.1"
/* Message prefixes. */
#define ERR_PREFIX PROG_NAME ": ERROR: "
#define INF_PREFIX PROG_NAME ": INFO: "
/* Error messages. */
#define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n"
#define E_ICN_INWORK ERR_PREFIX "ICNs cannot have inwork issues.\n"
#define E_BAD_FILENAME ERR_PREFIX "Filename does not contain issue info and -N not specified.\n"
#define E_ISSUE_TOO_LARGE ERR_PREFIX "%s is at the max issue number.\n"
#define E_INWORK_TOO_LARGE ERR_PREFIX "%s is at the max inwork number.\n"
#define E_NON_XML_STDIN ERR_PREFIX "Cannot use -m, -N or read from stdin when file does not contain issue info metadata.\n"
#define E_FILE_EXISTS ERR_PREFIX "%s already exists. Use -f to overwrite.\n"
/* Info messages. */
#define I_UPISSUE INF_PREFIX "Upissued %s to %s\n"
/* Exit codes. */
#define EXIT_NO_FILE 1
#define EXIT_NO_OVERWRITE 2
#define EXIT_BAD_FILENAME 3
#define EXIT_BAD_DATE 4
#define EXIT_ICN_INWORK 5
#define EXIT_ISSUE_TOO_LARGE 6
/* Verbosity of the output. */
static enum verbosity { QUIET, NORMAL, VERBOSE } verbosity = NORMAL;
/* Show help/usage information. */
static void show_help(void)
{
puts("Usage: " PROG_NAME " [-045defHilmNQqRrsuvw^] [-1 <type>] [-2 <type>] [-c <reason>] [-I <date>] [-t <urt>] [-z <type>] [<file>...]");
putchar('\n');
puts("Options:");
puts(" -0, --unverified Set the quality assurance to unverified.");
puts(" -1, --first-ver <type> Set first verification type.");
puts(" -2, --second-ver <type> Set second verification type.");
puts(" -4, --remove-marks Remove change marks (but not RFUs).");
puts(" -5, --print Print filenames of upissued objects.");
puts(" -c, --reason <reason> Add an RFU to the upissued object.");
puts(" -d, --dry-run Do not create or modify any files.");
puts(" -e, --erase Remove old issue.");
puts(" -f, --overwrite Overwrite existing upissued object.");
puts(" -H, --highlight Highlight the last RFU.");
puts(" -h, -?, --help Show usage message.");
puts(" -I, --date <date> The issue date to use for the upissued objects.");
puts(" -i, --official Increase issue number instead of inwork.");
puts(" -l, --list Treat input as list of objects.");
puts(" -m, --modify Modify metadata without upissuing.");
puts(" -N, --omit-issue Omit issue/inwork numbers from filename.");
puts(" -Q, --keep-qa Keep quality assurance from old issue.");
puts(" -q, --quiet Quiet mode.");
puts(" -R, --keep-unassoc-marks Only delete change marks associated with an RFU.");
puts(" -r, --(keep|remove)-changes Keep RFUs and change marks from old issue. In -m mode, remove them.");
puts(" -s, --(keep|change)-date Do not change issue date. In -m mode, change issue date.");
puts(" -t, --type <urt> Set the updateReasonType of the last RFU.");
puts(" -u, --clean-rfus Remove unassociated RFUs.");
puts(" -w, --lock Make old and official issues read-only.");
puts(" -z, --issue-type <type> Set the issue type of the new issue.");
puts(" -^, --remove-deleted Remove \"delete\"d elements.");
puts(" --version Show version information");
LIBXML2_PARSE_LONGOPT_HELP
}
/* Show version information. */
static void show_version(void)
{
printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION);
printf("Using libxml %s\n", xmlParserVersion);
}
/* Remove change markup attributes from elements referencing old RFUs */
static void del_assoc_rfu_attrs(xmlNodePtr rfu, xmlXPathContextPtr ctx)
{
xmlXPathObjectPtr obj;
char *rfuid;
rfuid = (char *) xmlGetProp(rfu, BAD_CAST "id");
obj = xmlXPathEvalExpression(BAD_CAST "//*[@reasonForUpdateRefIds]", ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
char *ids, *id = NULL;
bool used = false;
ids = (char *) xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "reasonForUpdateRefIds");
while ((id = strtok(id ? NULL : ids, " "))) {
if (strcmp(id, rfuid) == 0) {
used = true;
break;
}
}
xmlFree(ids);
if (used) {
xmlUnsetProp(obj->nodesetval->nodeTab[i], BAD_CAST "changeType");
xmlUnsetProp(obj->nodesetval->nodeTab[i], BAD_CAST "changeMark");
xmlUnsetProp(obj->nodesetval->nodeTab[i], BAD_CAST "reasonForUpdateRefIds");
}
}
}
xmlXPathFreeObject(obj);
xmlFree(rfuid);
}
/* Determine if an RFU is ever referenced */
static bool rfu_used(xmlNodePtr rfu, xmlXPathContextPtr ctx)
{
xmlXPathObjectPtr obj;
bool ret = false;
char *rfuid;
rfuid = (char *) xmlGetProp(rfu, BAD_CAST "id");
obj = xmlXPathEvalExpression(BAD_CAST "//@reasonForUpdateRefIds", ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
char *ids, *id = NULL;
ids = (char *) xmlNodeGetContent(obj->nodesetval->nodeTab[i]);
while ((id = strtok(id ? NULL : ids, " "))) {
if (strcmp(id, rfuid) == 0) {
ret = true;
break;
}
}
xmlFree(ids);
if (ret) {
break;
}
}
}
xmlXPathFreeObject(obj);
xmlFree(rfuid);
return ret;
}
/* Remove RFUs which are never referenced */
static void rem_unassoc_rfus(xmlDocPtr doc)
{
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
ctx = xmlXPathNewContext(doc);
obj = xmlXPathEvalExpression(BAD_CAST "//reasonForUpdate", ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
if (!rfu_used(obj->nodesetval->nodeTab[i], ctx)) {
xmlUnlinkNode(obj->nodesetval->nodeTab[i]);
xmlFreeNode(obj->nodesetval->nodeTab[i]);
obj->nodesetval->nodeTab[i] = NULL;
}
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
}
/* Remove all change markup attributes */
static void del_rfu_attrs(xmlXPathContextPtr ctx, bool iss30)
{
xmlXPathObjectPtr obj;
const xmlChar *change, *mark, *rfc;
if (iss30) {
change = BAD_CAST "change";
mark = BAD_CAST "mark";
rfc = BAD_CAST "rfc";
obj = xmlXPathEvalExpression(BAD_CAST "//*[@change or @mark or @rfc or @level]", ctx);
} else {
change = BAD_CAST "changeType";
mark = BAD_CAST "changeMark";
rfc = BAD_CAST "reasonForUpdateRefIds";
obj = xmlXPathEvalExpression(BAD_CAST "//*[@changeType or @changeMark or @reasonForUpdateRefIds]", ctx);
}
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
xmlChar *type;
type = xmlGetProp(obj->nodesetval->nodeTab[i], change);
if (xmlStrcmp(type, BAD_CAST "delete") == 0) {
xmlUnlinkNode(obj->nodesetval->nodeTab[i]);
xmlFreeNode(obj->nodesetval->nodeTab[i]);
obj->nodesetval->nodeTab[i] = NULL;
} else {
xmlUnsetProp(obj->nodesetval->nodeTab[i], change);
xmlUnsetProp(obj->nodesetval->nodeTab[i], mark);
xmlUnsetProp(obj->nodesetval->nodeTab[i], rfc);
if (iss30) {
xmlUnsetProp(obj->nodesetval->nodeTab[i], BAD_CAST "level");
}
}
xmlFree(type);
}
}
xmlXPathFreeObject(obj);
}
/* Remove changeInline elements. */
static void del_change_inline(xmlNodePtr node, bool iss30)
{
bool remove;
xmlNodePtr cur;
/* Remove <change> (3.0-) or <changeInline> (4.0+) elements only if the
* change attributes have been removed (so that in -R mode,
* unassociated inline change elements will be kept). */
if (iss30) {
remove = xmlStrcmp(node->name, BAD_CAST "change") == 0 && !(xmlHasProp(node, BAD_CAST "mark") || xmlHasProp(node, BAD_CAST "change") || xmlHasProp(node, BAD_CAST "rfc"));
} else{
remove = xmlStrcmp(node->name, BAD_CAST "changeInline") == 0 && !(xmlHasProp(node, BAD_CAST "changeMark") || xmlHasProp(node, BAD_CAST "changeType") || xmlHasProp(node, BAD_CAST "reasonForUpdateRefIds"));
}
/* Apply to descendants first to remove nested changeInlines. */
cur = node->children;
while (cur) {
xmlNodePtr next = cur->next;
del_change_inline(cur, iss30);
cur = next;
}
/* Merge children into parent. */
if (remove) {
cur = node->last;
while (cur) {
xmlNodePtr prev = cur->prev;
xmlAddNextSibling(node, cur);
cur = prev;
}
xmlUnlinkNode(node);
xmlFreeNode(node);
}
}
/* Delete old RFUs */
static void del_rfus(xmlDocPtr doc, bool only_assoc, bool iss30)
{
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
ctx = xmlXPathNewContext(doc);
obj = xmlXPathEvalExpression(BAD_CAST "//reasonForUpdate", ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
if (only_assoc) {
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
del_assoc_rfu_attrs(obj->nodesetval->nodeTab[i], ctx);
xmlUnlinkNode(obj->nodesetval->nodeTab[i]);
xmlFreeNode(obj->nodesetval->nodeTab[i]);
obj->nodesetval->nodeTab[i] = NULL;
}
} else {
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
xmlUnlinkNode(obj->nodesetval->nodeTab[i]);
xmlFreeNode(obj->nodesetval->nodeTab[i]);
obj->nodesetval->nodeTab[i] = NULL;
}
del_rfu_attrs(ctx, iss30);
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
del_change_inline(xmlDocGetRootElement(doc), iss30);
}
static void del_marks(xmlDocPtr doc, bool iss30)
{
xmlXPathContextPtr ctx;
ctx = xmlXPathNewContext(doc);
del_rfu_attrs(ctx, iss30);
xmlXPathFreeContext(ctx);
del_change_inline(xmlDocGetRootElement(doc), iss30);
}
static void set_unverified(xmlDocPtr doc, bool iss30)
{
xmlNodePtr qa, cur;
qa = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//qa" : "//qualityAssurance"));
if (!qa)
return;
cur = qa->children;
while (cur) {
xmlNodePtr next = cur->next;
xmlUnlinkNode(cur);
xmlFreeNode(cur);
cur = next;
}
xmlNewChild(qa, NULL, BAD_CAST (iss30 ? "unverif" : "unverified"), NULL);
}
static void set_qa(xmlDocPtr doc, char *firstver, char *secondver, bool iss30)
{
xmlNodePtr qa, unverif, ver1, ver2;
if (!(firstver || secondver))
return;
qa = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//qa" : "//qualityAssurance"));
if (!qa)
return;
unverif = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//unverif" : "//unverified"));
if (unverif) {
xmlUnlinkNode(unverif);
xmlFreeNode(unverif);
}
ver1 = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//firstver" : "//firstVerification"));
ver2 = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//secver" : "//secondVerification"));
if (firstver) {
if (!secondver) {
xmlNodePtr ver2;
ver2 = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? "//secver" : "//secondVerification"));
if (ver2) {
xmlUnlinkNode(ver2);
xmlFreeNode(ver2);
}
}
if (ver1) {
xmlUnlinkNode(ver1);
xmlFreeNode(ver1);
}
ver1 = xmlNewChild(qa, NULL, BAD_CAST (iss30 ? "firstver" : "firstVerification"), NULL);
xmlSetProp(ver1, BAD_CAST (iss30 ? "type" : "verificationType"), BAD_CAST firstver);
}
if (secondver) {
if (!ver1) {
ver1 = xmlNewChild(qa, NULL, BAD_CAST (iss30 ? "firstver" : "firstVerification"), NULL);
xmlSetProp(ver1, BAD_CAST (iss30 ? "type" : "verificationType"), BAD_CAST secondver);
}
if (ver2) {
xmlUnlinkNode(ver2);
xmlFreeNode(ver2);
}
ver2 = xmlNewChild(qa, NULL, BAD_CAST (iss30 ? "secver" : "secondVerification"), NULL);
xmlSetProp(ver2, BAD_CAST (iss30 ? "type" : "verificationType"), BAD_CAST secondver);
}
}
/* Add RFUs to the upissued object. */
#define ISS_30_RFU_PATH "(//qa|//sbc|//fic|//idstatus//ein|//skill|//rfu)[last()]"
#define ISS_4X_RFU_PATH "(//qualityAssurance|//systemBreakdownCode|//functionalItemCode|//identAndStatusSection//functionalItemRef|//skillLevel|//reasonForUpdate)[last()]"
static void add_rfus(xmlDocPtr doc, xmlNodePtr rfus, bool iss30)
{
xmlNodePtr node, cur, next;
node = xpath_first_node(doc, NULL, BAD_CAST (iss30 ? ISS_30_RFU_PATH : ISS_4X_RFU_PATH));
if (!node) {
return;
}
next = xmlNextElementSibling(node);
while (next && (
xmlStrcmp(next->name, BAD_CAST "rfu") == 0 ||
xmlStrcmp(next->name, BAD_CAST "reasonForUpdate") == 0)) {
node = next;
next = xmlNextElementSibling(node);
}
for (cur = rfus->last; cur; cur = cur->prev) {
xmlNodePtr rfu;
rfu = xmlCopyNode(cur, 1);
if (iss30) {
xmlNodeSetName(rfu, BAD_CAST "rfu");
xmlUnsetProp(rfu, BAD_CAST "updateReasonType");
xmlUnsetProp(rfu, BAD_CAST "updateHighlight");
} else {
xmlChar *content;
content = xmlNodeGetContent(rfu);
xmlNodeSetContent(rfu, NULL);
xmlNewChild(rfu, NULL, BAD_CAST "simplePara", content);
xmlFree(content);
}
xmlAddNextSibling(node, rfu);
}
}
static void set_iss_date(xmlDocPtr dmdoc, const char *issdate)
{
char year_s[5], month_s[3], day_s[3];
xmlNodePtr issueDate;
issueDate = xpath_first_node(dmdoc, NULL, BAD_CAST "//issueDate|//issdate");
if (issdate) {
sscanf(issdate, "%4s-%2s-%2s", year_s, month_s, day_s);
} else {
time_t now;
struct tm *local;
unsigned short year, month, day;
time(&now);
local = localtime(&now);
year = local->tm_year + 1900;
month = local->tm_mon + 1;
day = local->tm_mday;
if (snprintf(year_s, 5, "%u", year) < 0 ||
snprintf(month_s, 3, "%.2u", month) < 0 ||
snprintf(day_s, 3, "%.2u", day) < 0)
exit(EXIT_BAD_DATE);
}
xmlSetProp(issueDate, (xmlChar *) "year", (xmlChar *) year_s);
xmlSetProp(issueDate, (xmlChar *) "month", (xmlChar *) month_s);
xmlSetProp(issueDate, (xmlChar *) "day", (xmlChar *) day_s);
}
static void set_status(xmlDocPtr dmdoc, const char *status, bool iss30, xmlNodePtr issueInfo)
{
xmlNodePtr dmStatus;
if (iss30) {
xmlSetProp(issueInfo, BAD_CAST "type", BAD_CAST status);
} else {
if ((dmStatus = xpath_first_node(dmdoc, NULL, BAD_CAST "//dmStatus|//pmStatus"))) {
xmlSetProp(dmStatus, BAD_CAST "issueType", BAD_CAST status);
}
}
}
/* Upissue options */
static bool print_fnames = false;
static bool newissue = false;
static bool overwrite = false;
static char *status = NULL;
static bool no_issue = false;
static bool keep_rfus = false;
static bool set_date = true;
static bool only_assoc_rfus = false;
static bool reset_qa = true;
static bool dry_run = false;
static char *firstver = NULL;
static char *secondver = NULL;
static xmlNodePtr rfus = NULL;
static bool lock = false;
static bool remdel= false;
static bool remold = false;
static bool only_mod = false;
static bool clean_rfus = false;
static char *issdate = NULL;
static bool remove_marks = false;
static bool set_unverif = false;
static void upissue(const char *path)
{
char *issueNumber = NULL;
char *inWork = NULL;
int issueNumber_int = 0;
int inWork_int = 0;
char dmfile[PATH_MAX], cpfile[PATH_MAX];
xmlDocPtr dmdoc;
xmlNodePtr issueInfo;
xmlChar *issno_name, *inwork_name;
bool iss30 = false;
int up_issueNumber_int = 0;
int up_inWork_int = 0;
char upissued_issueNumber[32];
char upissued_inWork[32];
char *p, *i = NULL;
strcpy(dmfile, path);
if (access(dmfile, F_OK) == -1 && strcmp(dmfile, "-") != 0) {
if (verbosity >= NORMAL) {
fprintf(stderr, ERR_PREFIX "Could not read file %s.\n", dmfile);
}
exit(EXIT_NO_FILE);
}
dmdoc = read_xml_doc(dmfile);
if (dmdoc) {
issueInfo = xpath_first_node(dmdoc, NULL, BAD_CAST "//issueInfo|//issno");
} else {
issueInfo = NULL;
}
if (!issueInfo && no_issue) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_NON_XML_STDIN);
}
exit(EXIT_NO_OVERWRITE);
}
/* Apply modifications without actually upissuing. */
if (only_mod) {
iss30 = xmlStrcmp(issueInfo->name, BAD_CAST "issueInfo") != 0;
if (clean_rfus && !iss30) {
rem_unassoc_rfus(dmdoc);
}
if (remdel) {
rem_delete_elems(dmdoc);
}
if (set_unverif) {
set_unverified(dmdoc, iss30);
}
add_rfus(dmdoc, rfus, iss30);
set_qa(dmdoc, firstver, secondver, iss30);
if (status) {
set_status(dmdoc, status, iss30, issueInfo);
}
/* The following options have the opposite effect in -m mode:
* -s sets the date
* -r removes RFUs
*/
if (!set_date) {
set_iss_date(dmdoc, issdate);
}
if (keep_rfus) {
del_rfus(dmdoc, only_assoc_rfus, iss30);
} else if (remove_marks) {
del_marks(dmdoc, iss30);
}
if (overwrite) {
save_xml_doc(dmdoc, dmfile);
} else {
save_xml_doc(dmdoc, "-");
}
xmlFreeDoc(dmdoc);
return;
}
p = strchr(dmfile, '_');
/* Issue info from XML */
if (issueInfo) {
i = p + 1;
iss30 = strcmp((char *) issueInfo->name, "issueInfo") != 0;
if (iss30) {
issno_name = BAD_CAST "issno";
inwork_name = BAD_CAST "inwork";
} else {
issno_name = BAD_CAST "issueNumber";
inwork_name = BAD_CAST "inWork";
}
issueNumber = (char *) xmlGetProp(issueInfo, issno_name);
inWork = (char *) xmlGetProp(issueInfo, inwork_name);
if (!inWork) {
inWork = strdup("00");
}
/* Get issue/inwork from filename only */
} else if (p) {
i = p + 1;
if (i > dmfile + strlen(dmfile) - 6) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_BAD_FILENAME);
}
exit(EXIT_BAD_FILENAME);
}
issueNumber = calloc(4, 1);
inWork = calloc(3, 1);
strncpy(issueNumber, i, 3);
strncpy(inWork, i + 4, 2);
/* Get issue from ICN */
} else if ((p = strchr(dmfile, '-'))) {
int n, c = 0;
int l;
if (!newissue) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_ICN_INWORK);
}
exit(EXIT_ICN_INWORK);
}
l = strlen(dmfile);
/* Get second-to-last '-' */
for (n = l; n >= 0; --n) {
if (dmfile[n] == '-') {
if (c == 1) {
break;
} else {
++c;
}
}
}
i = dmfile + n + 1;
if (n == -1 || i > dmfile + l - 6) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_BAD_FILENAME);
}
exit(EXIT_BAD_FILENAME);
}
issueNumber = calloc(4, 1);
strncpy(issueNumber, i, 3);
} else {
if (verbosity >= NORMAL) {
fprintf(stderr, E_BAD_FILENAME);
}
exit(EXIT_BAD_FILENAME);
}
if ((issueNumber_int = atoi(issueNumber)) >= 999) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_ISSUE_TOO_LARGE, dmfile);
}
exit(EXIT_ISSUE_TOO_LARGE);
}
if (inWork) {
if ((inWork_int = atoi(inWork)) >= 99) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_INWORK_TOO_LARGE, dmfile);
}
exit(EXIT_ISSUE_TOO_LARGE);
}
}
if (newissue) {
up_issueNumber_int = issueNumber_int + 1;
if (inWork) {
up_inWork_int = 0;
}
} else {
up_issueNumber_int = issueNumber_int;
if (inWork) {
up_inWork_int = inWork_int + 1;
}
}
snprintf(upissued_issueNumber, 32, "%.3d", up_issueNumber_int);
snprintf(upissued_inWork, 32, "%.2d", up_inWork_int);
if (issueInfo) {
xmlSetProp(issueInfo, issno_name, BAD_CAST upissued_issueNumber);
xmlSetProp(issueInfo, inwork_name, BAD_CAST upissued_inWork);
/* Optionally cleanup unused RFUs */
if (clean_rfus && !iss30) {
rem_unassoc_rfus(dmdoc);
}
/* When upissuing an official module to first inwork issue... */
if (strcmp(inWork, "00") == 0) {
/* Delete RFUs */
if (!keep_rfus) {
del_rfus(dmdoc, only_assoc_rfus, iss30);
}
/* Set unverified */
if (reset_qa) {
set_unverified(dmdoc, iss30);
}
/* Or, perform certain actions any time. */
} else {
if (remdel) {
rem_delete_elems(dmdoc);
}
if (set_unverif) {
set_unverified(dmdoc, iss30);
}
}
if (set_date) {
set_iss_date(dmdoc, issdate);
}
set_qa(dmdoc, firstver, secondver, iss30);
add_rfus(dmdoc, rfus, iss30);
/* If an issue type is specified, use that. */
if (status) {
set_status(dmdoc, status, iss30, issueInfo);
/* Otherwise:
* - if the object is official, default to "status"
* - if the object is not official, keep the previous issue type. */
} else if (inWork_int == 0) {
set_status(dmdoc, "status", iss30, issueInfo);
}
if (remove_marks) {
del_marks(dmdoc, iss30);
}
}
xmlFree(issueNumber);
xmlFree(inWork);
if (!dmdoc) { /* Preserve non-XML filename for copying */
strcpy(cpfile, dmfile);
}
if (!no_issue) {
if (!dry_run) {
if (remold && dmdoc) { /* Delete previous issue (XML file) */
remove(dmfile);
} else if (lock) { /* Remove write permission from previous issue. */
mkreadonly(dmfile);
}
}
if (p) {
memcpy(i, upissued_issueNumber, 3);
if (inWork) {
memcpy(i + 4, upissued_inWork, 2);
}
}
}
if (!dry_run) {
if (!overwrite && access(dmfile, F_OK) != -1) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_FILE_EXISTS, dmfile);
}
exit(EXIT_NO_OVERWRITE);
}
if (dmdoc) {
save_xml_doc(dmdoc, dmfile);
} else { /* Copy non-XML file */
copy(cpfile, dmfile);
if (remold) { /* Delete previous issue (non-XML file) */
remove(cpfile);
}
}
/* Lock official issues. */
if (lock && newissue) {
mkreadonly(dmfile);
}
}
if (verbosity >= VERBOSE) {
fprintf(stderr, I_UPISSUE, path, dmfile);
}
if (print_fnames) {
puts(dmfile);
}
if (dmdoc) {
xmlFreeDoc(dmdoc);
}
}
static void upissue_list(const char *path)
{
FILE *f;
char line[PATH_MAX];
if (path) {
if (!(f = fopen(path, "r"))) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_BAD_LIST, path);
}
return;
}
} else {
f = stdin;
}
while (fgets(line, PATH_MAX, f)) {
strtok(line, "\t\r\n");
upissue(line);
}
if (path) {
fclose(f);
}
}
int main(int argc, char **argv)
{
int i;
bool islist = false;
const char *sopts = "ivsNfrRI:Qq01:2:45delc:t:Hwmuz:^h?";
struct option lopts[] = {
{"version" , no_argument , 0, 0},
{"help" , no_argument , 0, 'h'},
{"unverified" , no_argument , 0, '0'},
{"first-ver" , required_argument, 0, '1'},
{"second-ver" , required_argument, 0, '2'},
{"remove-marks" , no_argument , 0, '4'},
{"print" , no_argument , 0, '5'},
{"reason" , required_argument, 0, 'c'},
{"dry-run" , no_argument , 0, 'd'},
{"erase" , no_argument , 0, 'e'},
{"overwrite" , no_argument , 0, 'f'},
{"keep-date" , no_argument , 0, 's'},
{"change-date" , no_argument , 0, 's'},
{"official" , no_argument , 0, 'i'},
{"list" , no_argument , 0, 'l'},
{"modify" , no_argument , 0, 'm'},
{"omit-issue" , no_argument , 0, 'N'},
{"keep-qa" , no_argument , 0, 'Q'},
{"quiet" , no_argument , 0, 'q'},
{"keep-unassoc-marks", no_argument , 0, 'R'},
{"keep-changes" , no_argument , 0, 'r'},
{"remove-changes" , no_argument , 0, 'r'},
{"date" , required_argument, 0, 'I'},
{"type" , required_argument, 0, 't'},
{"clean-rfus" , no_argument , 0, 'u'},
{"highlight" , no_argument , 0, 'H'},
{"verbose" , no_argument , 0, 'v'},
{"lock" , no_argument , 0, 'w'},
{"issue-type" , required_argument, 0, 'z'},
{"remove-deleted" , no_argument , 0, '^'},
LIBXML2_PARSE_LONGOPT_DEFS
{0, 0, 0, 0}
};
int loptind = 0;
rfus = xmlNewNode(NULL, BAD_CAST "rfus");
while ((i = getopt_long(argc, argv, sopts, lopts, &loptind)) != -1) {
switch (i) {
case 0:
if (strcmp(lopts[loptind].name, "version") == 0) {
show_version();
return 0;
}
LIBXML2_PARSE_LONGOPT_HANDLE(lopts, loptind, optarg)
break;
case '0':
set_unverif = true;
break;
case '1':
firstver = strdup(optarg);
break;
case '2':
secondver = strdup(optarg);
break;
case '4':
remove_marks = true;
break;
case '5':
print_fnames = true;
break;
case 'c':
xmlNewChild(rfus, NULL, BAD_CAST "reasonForUpdate", BAD_CAST optarg);
break;
case 'd':
dry_run = true;
break;
case 'e':
remold = true;
break;
case 'f':
overwrite = true;
break;
case 'I':
issdate = strdup(optarg);
break;
case 'i':
newissue = true;
break;
case 'l':
islist = true;
break;
case 'm':
only_mod = true;
no_issue = true;
break;
case 'N':
no_issue = true;
overwrite = true;
break;
case 'Q':
reset_qa = false;
break;
case 'q':
--verbosity;
break;
case 'R':
only_assoc_rfus = true;
break;
case 'r':
keep_rfus = true;
break;
case 's':
set_date = false;
break;
case 't':
xmlSetProp(rfus->last, BAD_CAST "updateReasonType", BAD_CAST optarg);
break;
case 'u':
clean_rfus = true;
break;
case 'H':
xmlSetProp(rfus->last, BAD_CAST "updateHighlight", BAD_CAST "1");
break;
case 'v':
++verbosity;
break;
case 'w':
lock = true;
break;
case 'z':
status = strdup(optarg);
if (!(strcmp(status, "changed") == 0 || strcmp(status, "rinstate-changed") == 0)) {
remove_marks = true;
}
break;
case '^':
remdel = true;
break;
case 'h':
case '?':
show_help();
return 0;
}
}
if (optind < argc) {
for (i = optind; i < argc; i++) {
if (islist) {
upissue_list(argv[i]);
} else {
upissue(argv[i]);
}
}
} else if (islist) {
upissue_list(NULL);
} else {
no_issue = true;
overwrite = true;
upissue("-");
}
free(firstver);
free(secondver);
free(status);
free(issdate);
xmlFreeNode(rfus);
xmlCleanupParser();
return 0;
}
gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/s1kd-upissue/s1kd-upissue.c