1 /*
2 *
3 * Copyright (C) 2003-2019, OFFIS e.V.
4 * All rights reserved. See COPYRIGHT file for details.
5 *
6 * This software and supporting documentation were developed by
7 *
8 * OFFIS e.V.
9 * R&D Division Health
10 * Escherweg 2
11 * D-26121 Oldenburg, Germany
12 *
13 *
14 * Module: dcmdata
15 *
16 * Author: Michael Onken
17 *
18 * Purpose: Class for modifying DICOM files from commandline
19 *
20 */
21
22 #include "dcmtk/config/osconfig.h" // make sure OS specific configuration is included first
23
24 #include "mdfconen.h"
25 #include "mdfdsman.h"
26 #include "dcmtk/ofstd/ofstd.h"
27 #include "dcmtk/ofstd/ofconapp.h"
28 #include "dcmtk/dcmdata/dctk.h"
29 #include "dcmtk/dcmdata/dcistrmz.h" /* for dcmZlibExpectRFC1950Encoding */
30
31 #define SHORTCOL 4
32 #define LONGCOL 21
33
34 #ifdef WITH_ZLIB
35 BEGIN_EXTERN_C
36 #include <zlib.h>
37 END_EXTERN_C
38 #endif
39
40 static OFLogger dcmodifyLogger = OFLog::getLogger("dcmtk.apps.dcmodify");
41
MdfJob(const MdfJob & other)42 MdfJob::MdfJob(const MdfJob& other)
43 : option(other.option), path(other.path), value(other.value)
44 {
45 }
46
operator ==(const MdfJob & j) const47 OFBool MdfJob::operator==(const MdfJob &j) const
48 {
49 return (option == j.option) && (path == j.path) && (value == j.value);
50 }
51
operator =(const MdfJob & j)52 MdfJob &MdfJob::operator=(const MdfJob &j)
53 {
54 option = j.option;
55 path = j.path;
56 value = j.value;
57 return *this;
58 }
59
60
MdfConsoleEngine(int argc,char * argv[],const char * application_name)61 MdfConsoleEngine::MdfConsoleEngine(int argc, char *argv[],
62 const char *application_name)
63 : app(NULL), cmd(NULL), ds_man(NULL), ignore_errors_option(OFFalse),
64 update_metaheader_uids_option(OFTrue), no_backup_option(OFFalse),
65 read_mode_option(ERM_autoDetect), input_xfer_option(EXS_Unknown),
66 output_dataset_option(OFFalse), output_xfer_option(EXS_Unknown),
67 glenc_option(EGL_recalcGL), enctype_option(EET_ExplicitLength),
68 padenc_option(EPD_withoutPadding), filepad_option(0),
69 itempad_option(0), ignore_missing_tags_option(OFFalse),
70 no_reservation_checks(OFFalse), ignore_un_modifies(OFFalse),
71 create_if_necessary(OFFalse), was_created(OFFalse), jobs(NULL), files(NULL)
72 {
73 char rcsid[200];
74 // print application header
75 sprintf(rcsid, "$dcmtk: %s v%s %s $", application_name, OFFIS_DCMTK_VERSION, OFFIS_DCMTK_RELEASEDATE);
76
77 // the next lines describe commandline arguments/options
78 app = new OFConsoleApplication(application_name, "Modify DICOM files", rcsid);
79 cmd = new OFCommandLine();
80
81 cmd->setOptionColumns(LONGCOL, SHORTCOL);
82 cmd->setParamColumn(LONGCOL + SHORTCOL + 4);
83
84 cmd->addParam("dcmfile-in", "DICOM input filename to be modified", OFCmdParam::PM_MultiMandatory);
85
86 // add options to commandline application
87 cmd->addGroup("general options:", LONGCOL, SHORTCOL + 2);
88 cmd->addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
89 cmd->addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive);
90 OFLog::addOptions(*cmd);
91 cmd->addGroup("input options:");
92 cmd->addSubGroup("input file format:");
93 cmd->addOption("--read-file", "+f", "read file format or data set (default)");
94 cmd->addOption("--read-file-only", "+fo", "read file format only");
95 cmd->addOption("--read-dataset", "-f", "read data set without file meta information");
96 cmd->addOption("--create-file", "+fc", "create file format if file does not exist");
97 cmd->addSubGroup("input transfer syntax:");
98 cmd->addOption("--read-xfer-auto", "-t=", "use TS recognition (default)");
99 cmd->addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header");
100 cmd->addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS");
101 cmd->addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS");
102 cmd->addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS");
103 cmd->addSubGroup("parsing of odd-length attributes:");
104 cmd->addOption("--accept-odd-length", "+ao", "accept odd length attributes (default)");
105 cmd->addOption("--assume-even-length", "+ae", "assume real length is one byte larger");
106 cmd->addSubGroup("automatic data correction:");
107 cmd->addOption("--enable-correction", "+dc", "enable automatic data correction (default)");
108 cmd->addOption("--disable-correction", "-dc", "disable automatic data correction");
109 #ifdef WITH_ZLIB
110 cmd->addSubGroup("bitstream format of deflated input:");
111 cmd->addOption("--bitstream-deflated", "+bd", "expect deflated bitstream (default)");
112 cmd->addOption("--bitstream-zlib", "+bz", "expect deflated zlib bitstream");
113 #endif
114
115 cmd->addGroup("processing options:");
116 cmd->addSubGroup("backup input files:");
117 cmd->addOption("--backup", "backup files before modifying (default)");
118 cmd->addOption("--no-backup", "-nb", "don't backup files (DANGEROUS)");
119 cmd->addSubGroup("insert mode:");
120 cmd->addOption("--insert", "-i", 1, "\"[t]ag-path=[v]alue\"",
121 "insert (or overwrite) path at position t\nwith value v", OFCommandLine::AF_NoWarning);
122 cmd->addOption("--insert-from-file", "-if", 1, "\"[t]ag-path=[f]ilename\"",
123 "insert (or overwrite) path at position t\nwith value from file f", OFCommandLine::AF_NoWarning);
124 cmd->addOption("--no-reserv-check", "-nrc", "do not check private reservations\nwhen inserting private tags");
125 cmd->addSubGroup("modify mode:");
126 cmd->addOption("--modify", "-m", 1, "\"[t]ag-path=[v]alue\"",
127 "modify tag at position t to value v", OFCommandLine::AF_NoWarning);
128 cmd->addOption("--modify-from-file", "-mf", 1, "\"[t]ag-path=[f]ilename\"",
129 "modify tag at position t to value from file f", OFCommandLine::AF_NoWarning);
130 cmd->addOption("--modify-all", "-ma", 1, "\"[t]ag=[v]alue\"",
131 "modify ALL matching tags t in file to value v", OFCommandLine::AF_NoWarning);
132 cmd->addSubGroup("erase mode:");
133 cmd->addOption("--erase", "-e", 1, "\"[t]ag-path\"",
134 "erase tag/item at position t", OFCommandLine::AF_NoWarning);
135 cmd->addOption("--erase-all", "-ea", 1, "\"[t]ag\"",
136 "erase ALL matching tags t in file", OFCommandLine::AF_NoWarning);
137 cmd->addOption("--erase-private", "-ep", "erase ALL private data from file", OFCommandLine::AF_NoWarning);
138 cmd->addSubGroup("unique identifier:");
139 cmd->addOption("--gen-stud-uid", "-gst", "generate new Study Instance UID", OFCommandLine::AF_NoWarning);
140 cmd->addOption("--gen-ser-uid", "-gse", "generate new Series Instance UID", OFCommandLine::AF_NoWarning);
141 cmd->addOption("--gen-inst-uid", "-gin", "generate new SOP Instance UID", OFCommandLine::AF_NoWarning);
142 cmd->addOption("--no-meta-uid", "-nmu", "do not update metaheader UIDs if related\nUIDs in the dataset are modified");
143 cmd->addSubGroup("error handling:");
144 cmd->addOption("--ignore-errors", "-ie", "continue with file, if modify error occurs");
145 cmd->addOption("--ignore-missing-tags", "-imt", "treat 'tag not found' as success\nwhen modifying or erasing in datasets");
146 cmd->addOption("--ignore-un-values", "-iun", "do not try writing any values to elements\nhaving a VR of UN");
147 cmd->addGroup("output options:");
148 cmd->addSubGroup("output file format:");
149 cmd->addOption("--write-file", "+F", "write file format (default)");
150 cmd->addOption("--write-dataset", "-F", "write data set without file meta information");
151 cmd->addSubGroup("output transfer syntax:");
152 cmd->addOption("--write-xfer-same", "+t=", "write with same TS as input (default)");
153 cmd->addOption("--write-xfer-little", "+te", "write with explicit VR little endian TS");
154 cmd->addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS");
155 cmd->addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS");
156 cmd->addSubGroup("post-1993 value representations:");
157 cmd->addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)");
158 cmd->addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB");
159 cmd->addSubGroup("group length encoding:");
160 cmd->addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)");
161 cmd->addOption("--group-length-create", "+g", "always write with group length elements");
162 cmd->addOption("--group-length-remove", "-g", "always write without group length elements");
163 cmd->addSubGroup("length encoding in sequences and items:");
164 cmd->addOption("--length-explicit", "+le", "write with explicit lengths (default)");
165 cmd->addOption("--length-undefined", "-le", "write with undefined lengths");
166 cmd->addSubGroup("data set trailing padding (not with --write-dataset):");
167 cmd->addOption("--padding-retain", "-p=", "do not change padding\n(default if not --write-dataset)");
168 cmd->addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)");
169 cmd->addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer",
170 "align file on multiple of f bytes\nand items on multiple of i bytes");
171
172 // evaluate commandline
173 prepareCmdLineArgs(argc, argv, application_name);
174 if (app->parseCommandLine(*cmd, argc, argv))
175 {
176 /* print help text and exit */
177 if (cmd->getArgCount() == 0)
178 app->printUsage();
179
180 /* check exclusive options first */
181 if (cmd->hasExclusiveOption())
182 {
183 if (cmd->findOption("--version"))
184 {
185 app->printHeader(OFTrue /*print host identifier*/);
186 ofConsole.lockCout() << OFendl << "External libraries used:";
187 #ifdef WITH_ZLIB
188 ofConsole.getCout() << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl;
189 #else
190 ofConsole.getCout() << " none" << OFendl;
191 #endif
192 ofConsole.unlockCout();
193 delete app;
194 delete cmd;
195 exit(0);
196 }
197 }
198
199 // iterate the files (parameters) and save them in list
200 files = new OFList<OFString>;
201 OFString current_file;
202 for (int i = 1; i <= cmd->getParamCount(); i++)
203 {
204 cmd->getParam(i,current_file);
205 files->push_back(current_file);
206 }
207 // if no files are given: return with error message
208 if (files->empty())
209 {
210 OFLOG_ERROR(dcmodifyLogger, "no dicom files given!");
211 delete app;
212 delete cmd;
213 exit(1);
214 }
215
216 // make sure data dictionary is loaded
217 if (!dcmDataDict.isDictionaryLoaded())
218 OFLOG_WARN(dcmodifyLogger, "no data dictionary loaded, "
219 << "check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE);
220 }
221
222 /* print resource identifier */
223 OFLOG_DEBUG(dcmodifyLogger, rcsid << OFendl);
224 }
225
226
parseNonJobOptions()227 void MdfConsoleEngine::parseNonJobOptions()
228 {
229 // catch "general" options
230 OFLog::configureFromCommandLine(*cmd, *app);
231
232 // input options
233 cmd->beginOptionBlock();
234 if (cmd->findOption("--read-file"))
235 read_mode_option = ERM_autoDetect;
236 if (cmd->findOption("--read-file-only"))
237 read_mode_option = ERM_fileOnly;
238 if (cmd->findOption("--read-dataset"))
239 read_mode_option = ERM_dataset;
240 cmd->endOptionBlock();
241
242 if (cmd->findOption("--create-file"))
243 create_if_necessary = OFTrue;
244
245 cmd->beginOptionBlock();
246 if (cmd->findOption("--read-xfer-auto"))
247 input_xfer_option = EXS_Unknown;
248 if (cmd->findOption("--read-xfer-detect"))
249 dcmAutoDetectDatasetXfer.set(OFTrue);
250 if (cmd->findOption("--read-xfer-little"))
251 {
252 app->checkDependence("--read-xfer-little", "--read-dataset", read_mode_option == ERM_dataset);
253 input_xfer_option = EXS_LittleEndianExplicit;
254 }
255 if (cmd->findOption("--read-xfer-big"))
256 {
257 app->checkDependence("--read-xfer-big", "--read-dataset", read_mode_option == ERM_dataset);
258 input_xfer_option = EXS_BigEndianExplicit;
259 }
260 if (cmd->findOption("--read-xfer-implicit"))
261 {
262 app->checkDependence("--read-xfer-implicit", "--read-dataset", read_mode_option == ERM_dataset);
263 input_xfer_option = EXS_LittleEndianImplicit;
264 }
265 cmd->endOptionBlock();
266
267 cmd->beginOptionBlock();
268 if (cmd->findOption("--accept-odd-length"))
269 dcmAcceptOddAttributeLength.set(OFTrue);
270 if (cmd->findOption("--assume-even-length"))
271 dcmAcceptOddAttributeLength.set(OFFalse);
272 cmd->endOptionBlock();
273
274 cmd->beginOptionBlock();
275 if (cmd->findOption("--enable-correction"))
276 dcmEnableAutomaticInputDataCorrection.set(OFTrue);
277 if (cmd->findOption("--disable-correction"))
278 dcmEnableAutomaticInputDataCorrection.set(OFFalse);
279 cmd->endOptionBlock();
280
281 #ifdef WITH_ZLIB
282 cmd->beginOptionBlock();
283 if (cmd->findOption("--bitstream-deflated"))
284 dcmZlibExpectRFC1950Encoding.set(OFFalse);
285 if (cmd->findOption("--bitstream-zlib"))
286 dcmZlibExpectRFC1950Encoding.set(OFTrue);
287 cmd->endOptionBlock();
288 #endif
289
290 // processing options
291 cmd->beginOptionBlock();
292 if (cmd->findOption("--backup"))
293 no_backup_option = OFFalse;
294 if (cmd->findOption("--no-backup"))
295 no_backup_option = OFTrue;
296 cmd->endOptionBlock();
297
298 if (cmd->findOption("--no-reserv-check"))
299 no_reservation_checks = OFTrue;
300
301 if (cmd->findOption("--no-meta-uid"))
302 update_metaheader_uids_option = OFFalse;
303
304 if (cmd->findOption("--ignore-errors"))
305 ignore_errors_option = OFTrue;
306 if (cmd->findOption("--ignore-missing-tags"))
307 ignore_missing_tags_option = OFTrue;
308 if (cmd->findOption("--ignore-un-values"))
309 ignore_un_modifies = OFTrue;
310
311 // output options
312 cmd->beginOptionBlock();
313 if (cmd->findOption("--write-file"))
314 output_dataset_option = OFFalse;
315 if (cmd->findOption("--write-dataset"))
316 {
317 output_dataset_option = OFTrue;
318 app->checkConflict("--write-dataset", "--create-file", create_if_necessary);
319 }
320 cmd->endOptionBlock();
321
322 cmd->beginOptionBlock();
323 if (cmd->findOption("--write-xfer-same"))
324 output_xfer_option = EXS_Unknown;
325 if (cmd->findOption("--write-xfer-little"))
326 output_xfer_option = EXS_LittleEndianExplicit;
327 if (cmd->findOption("--write-xfer-big"))
328 output_xfer_option = EXS_BigEndianExplicit;
329 if (cmd->findOption("--write-xfer-implicit"))
330 output_xfer_option = EXS_LittleEndianImplicit;
331 cmd->endOptionBlock();
332
333 cmd->beginOptionBlock();
334 if (cmd->findOption("--enable-new-vr"))
335 dcmEnableGenerationOfNewVRs();
336 if (cmd->findOption("--disable-new-vr"))
337 dcmDisableGenerationOfNewVRs();
338 cmd->endOptionBlock();
339
340 cmd->beginOptionBlock();
341 if (cmd->findOption("--group-length-recalc"))
342 glenc_option = EGL_recalcGL;
343 if (cmd->findOption("--group-length-create"))
344 glenc_option = EGL_withGL;
345 if (cmd->findOption("--group-length-remove"))
346 glenc_option = EGL_withoutGL;
347 cmd->endOptionBlock();
348
349 cmd->beginOptionBlock();
350 if (cmd->findOption("--length-explicit"))
351 enctype_option = EET_ExplicitLength;
352 if (cmd->findOption("--length-undefined"))
353 enctype_option = EET_UndefinedLength;
354 cmd->endOptionBlock();
355
356 cmd->beginOptionBlock();
357 if (cmd->findOption("--padding-retain"))
358 {
359 app->checkConflict("--padding-retain", "--write-dataset", output_dataset_option);
360 padenc_option = EPD_noChange;
361 }
362 if (cmd->findOption("--padding-off"))
363 padenc_option = EPD_withoutPadding;
364 if (cmd->findOption("--padding-create"))
365 {
366 app->checkConflict("--padding-create", "--write-dataset", output_dataset_option);
367 app->checkValue(cmd->getValueAndCheckMin(filepad_option, 0));
368 app->checkValue(cmd->getValueAndCheckMin(itempad_option, 0));
369 padenc_option = EPD_withPadding;
370 }
371 cmd->endOptionBlock();
372 }
373
374
parseCommandLine()375 void MdfConsoleEngine::parseCommandLine()
376 {
377 jobs = new OFList<MdfJob>;
378 OFString option_string;
379 // check all options, that don't belong to a specific job
380 parseNonJobOptions();
381
382 cmd->gotoFirstOption();
383 // iterate over commandline arguments from first to last
384 do {
385 if (cmd->getCurrentOption(option_string))
386 {
387 MdfJob aJob;
388 OFString option_value, tag_path, tag_value;
389 if (option_string == "--insert")
390 aJob.option = "i";
391 else if (option_string == "--insert-from-file")
392 aJob.option = "if";
393 else if (option_string == "--modify")
394 aJob.option = "m";
395 else if (option_string == "--modify-from-file")
396 aJob.option = "mf";
397 else if (option_string == "--modify-all")
398 aJob.option = "ma";
399 else if (option_string == "--erase")
400 aJob.option = "e";
401 else if (option_string == "--erase-all")
402 aJob.option = "ea";
403 else if (option_string == "--erase-private")
404 aJob.option = "ep";
405 else if (option_string == "--gen-stud-uid")
406 aJob.option = "gst";
407 else if (option_string == "--gen-ser-uid")
408 aJob.option = "gse";
409 else if (option_string == "--gen-inst-uid")
410 aJob.option = "gin";
411 // else this is a non job option, e.g. -v, -d, -f, ...
412 else
413 continue;
414 // get any parameters if job expects some
415 if (jobOptionExpectsParameters(aJob.option))
416 {
417 cmd->getValue(option_value);
418 splitPathAndValue(option_value, tag_path, tag_value);
419 aJob.path = tag_path;
420 aJob.value = tag_value;
421 }
422 // finally, and schedule job
423 jobs->push_back(aJob);
424 }
425 } while (cmd->gotoNextOption());
426 }
427
428
jobOptionExpectsParameters(const OFString & job)429 OFBool MdfConsoleEngine::jobOptionExpectsParameters(const OFString &job)
430 {
431 return (job != "ep") && (job != "gst") && (job != "gse") && (job != "gin");
432 }
433
434
splitPathAndValue(const OFString & whole,OFString & path,OFString & value)435 void MdfConsoleEngine::splitPathAndValue(const OFString &whole,
436 OFString &path,
437 OFString &value)
438 {
439 size_t pos = whole.find("=");
440 if (pos != OFString_npos)
441 {
442 path = whole.substr(0, pos);
443 value = whole.substr(pos + 1, value.length() - 1);
444 }
445 else path = whole;
446 }
447
448
executeJob(const MdfJob & job,const char * filename)449 int MdfConsoleEngine::executeJob(const MdfJob &job,
450 const char *filename)
451 {
452 OFCondition result;
453 int count = 0;
454 int error_count = 0;
455 OFLOG_INFO(dcmodifyLogger, "Executing (option|path|value): "
456 << job.option << "|" << job.path << "|" << job.value);
457 // start modify operation based on job option
458 if (job.option=="i")
459 result = ds_man->modifyOrInsertPath(job.path, job.value, OFFalse, update_metaheader_uids_option, ignore_missing_tags_option, no_reservation_checks);
460 else if (job.option == "if")
461 result = ds_man->modifyOrInsertFromFile(job.path, job.value /*filename*/, OFFalse, update_metaheader_uids_option, ignore_missing_tags_option, no_reservation_checks);
462 else if (job.option == "m")
463 result = ds_man->modifyOrInsertPath(job.path, job.value, OFTrue, update_metaheader_uids_option, ignore_missing_tags_option, no_reservation_checks);
464 else if (job.option == "mf")
465 result = ds_man->modifyOrInsertFromFile(job.path, job.value /*filename*/, OFTrue, update_metaheader_uids_option, ignore_missing_tags_option, no_reservation_checks);
466 else if (job.option == "ma")
467 result = ds_man->modifyAllTags(job.path, job.value, update_metaheader_uids_option, count, ignore_missing_tags_option);
468 else if (job.option == "e")
469 result = ds_man->deleteTag(job.path, OFFalse, ignore_missing_tags_option);
470 else if (job.option == "ea")
471 result = ds_man->deleteTag(job.path, OFTrue, ignore_missing_tags_option);
472 else if (job.option == "ep")
473 result = ds_man->deletePrivateData();
474 else if (job.option == "gst")
475 result = ds_man->generateAndInsertUID(DCM_StudyInstanceUID);
476 else if (job.option == "gse")
477 result = ds_man->generateAndInsertUID(DCM_SeriesInstanceUID);
478 else if (job.option == "gin")
479 result = ds_man->generateAndInsertUID(DCM_SOPInstanceUID);
480 // no valid job option found:
481 else
482 {
483 error_count++;
484 OFLOG_ERROR(dcmodifyLogger, "no valid option: " << job.option);
485 }
486 // if modify operation failed
487 if (result.bad() && error_count == 0)
488 {
489 if (filename != NULL)
490 OFLOG_ERROR(dcmodifyLogger, "modifying tag in file " << OFString(filename) << ": " << result.text());
491 else
492 OFLOG_ERROR(dcmodifyLogger, "modifying tag: " << result.text());
493 error_count++;
494 }
495 return error_count;
496 }
497
498
startProvidingService()499 int MdfConsoleEngine::startProvidingService()
500 {
501 OFCondition result;
502 const char *filename;
503 // return value of this function
504 int errors = 0;
505 // parse command line into file and job list
506 parseCommandLine();
507 // iterators for job and file loops
508 OFListIterator(MdfJob) job_it;
509 OFListIterator(MdfJob) job_last = jobs->end();;
510 OFListIterator(OFString) file_it = files->begin();
511 OFListIterator(OFString) file_last = files->end();;
512 // outer loop: iterate over all files
513 while (file_it != file_last)
514 {
515 filename = (*file_it).c_str();
516 result = loadFile(filename);
517
518 // if file could be loaded:
519 if (result.good())
520 {
521 // for each file, set job iterator back to first entry
522 job_it = jobs->begin();
523 // inner loop: iterate over jobs, execute all jobs for current file
524 while (job_it != job_last)
525 {
526 errors += executeJob(*job_it, filename);
527 job_it++;
528 }
529 // if there were no errors or user wants to override them, save:
530 if (errors == 0 || ignore_errors_option)
531 {
532 if (was_created && (output_xfer_option == EXS_Unknown))
533 {
534 output_xfer_option = EXS_LittleEndianExplicit;
535 }
536 result = ds_man->saveFile(filename, output_xfer_option,
537 enctype_option, glenc_option,
538 padenc_option, filepad_option,
539 itempad_option, output_dataset_option);
540 if (result.bad())
541 {
542 OFLOG_ERROR(dcmodifyLogger, "couldn't save file: " << result.text());
543 errors++;
544 if (!no_backup_option && !was_created)
545 {
546 result = restoreFile(filename);
547 if (result.bad())
548 {
549 OFLOG_ERROR(dcmodifyLogger, "couldn't restore file: " << result.text());
550 errors++;
551 }
552 }
553 }
554 }
555 // errors occurred and user doesn't want to ignore them:
556 else if (!no_backup_option && !was_created)
557 {
558 result = restoreFile(filename);
559 if (result.bad())
560 {
561 OFLOG_ERROR(dcmodifyLogger, "couldn't restore file!");
562 errors++;
563 }
564 }
565 }
566 // if loading fails:
567 else
568 {
569 errors++;
570 OFLOG_ERROR(dcmodifyLogger, "unable to load file " << filename <<": " << result.text());
571 }
572 file_it++;
573 // output separator line if required
574 if ((file_it != file_last) || (errors > 0))
575 OFLOG_INFO(dcmodifyLogger, "------------------------------------");
576 }
577 return errors;
578 }
579
580
loadFile(const char * filename)581 OFCondition MdfConsoleEngine::loadFile(const char *filename)
582 {
583 OFCondition result;
584 // free memory
585 delete ds_man;
586 ds_man = new MdfDatasetManager();
587 ds_man->setModifyUNValues(!ignore_un_modifies);
588 OFLOG_INFO(dcmodifyLogger, "Processing file: " << filename);
589 // load file into dataset manager
590 was_created = !OFStandard::fileExists(filename);
591 result = ds_man->loadFile(filename, read_mode_option, input_xfer_option, create_if_necessary);
592 if (result.good() && !no_backup_option && !was_created)
593 result = backupFile(filename);
594 return result;
595 }
596
597
backupFile(const char * filename)598 OFCondition MdfConsoleEngine::backupFile(const char *filename)
599 {
600 int result;
601 OFString backup = filename;
602 backup += ".bak";
603 OFLOG_INFO(dcmodifyLogger, "Creating backup of input file: " << backup);
604 // delete backup file, if it already exists
605 if (OFStandard::fileExists(backup.c_str()))
606 {
607 result = remove(backup.c_str());
608 if (result != 0)
609 {
610 OFLOG_ERROR(dcmodifyLogger, "couldn't delete previous backup file, unable to backup!");
611 return EC_IllegalCall;
612 }
613 }
614 // if backup file could be removed, backup original file
615 result = rename(filename, backup.c_str());
616 // set return value
617 if (result != 0)
618 {
619 OFLOG_ERROR(dcmodifyLogger, "unable to backup, no write permission?");
620 return EC_IllegalCall;
621 }
622
623 return EC_Normal;
624 }
625
626
restoreFile(const char * filename)627 OFCondition MdfConsoleEngine::restoreFile(const char *filename)
628 {
629 int result;
630 OFString backup = filename;
631 backup += ".bak";
632 OFLOG_INFO(dcmodifyLogger, "Restoring original file from backup");
633 // delete the (original) file that dcmodify couldn't modify
634 if (OFStandard::fileExists(filename))
635 {
636 result = remove(filename);
637 if (result != 0)
638 {
639 OFLOG_ERROR(dcmodifyLogger, "unable to delete original file for restoring backup!");
640 return EC_IllegalCall;
641 }
642 }
643 // and rename backup file back to original filename
644 result = rename(backup.c_str(), filename);
645 // error renaming backup file
646 if (result != 0)
647 {
648 OFLOG_ERROR(dcmodifyLogger, "unable to rename backup file to original filename!");
649 return EC_IllegalCall;
650 }
651 // you only get to this point, if restoring was completely successful
652 return EC_Normal;
653 }
654
655
~MdfConsoleEngine()656 MdfConsoleEngine::~MdfConsoleEngine()
657 {
658 delete app;
659 delete cmd;
660 delete files;
661 delete jobs;
662 delete ds_man;
663 }
664