1 /*=========================================================================
2 
3   Program: GDCM (Grassroots DICOM). A DICOM library
4 
5   Copyright (c) 2006-2011 Mathieu Malaterre
6   All rights reserved.
7   See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 /*
15  * Simple command line tool to echo/store/find/move DICOM using
16  * DICOM Query/Retrieve
17  * This is largely inspired by other tool available from other toolkit, namely:
18  * echoscu (DCMTK)
19  * findscu (DCMTK)
20  * movescu (DCMTK)
21  * storescu (DCMTK)
22  */
23 
24 #include "gdcmCompositeNetworkFunctions.h"
25 
26 #include <iostream>
27 #include <fstream>
28 #include <stdlib.h>
29 #include <getopt.h>
30 #include "gdcmVersion.h"
31 #include "gdcmGlobal.h"
32 #include "gdcmSystem.h"
33 #include "gdcmDirectory.h"
34 #include "gdcmDataSet.h"
35 #include "gdcmFileMetaInformation.h"
36 #include "gdcmUIDGenerator.h"
37 
38 #include "gdcmBaseRootQuery.h"
39 #include "gdcmQueryFactory.h"
40 #include "gdcmPrinter.h"
41 
42 
PrintVersion()43 static void PrintVersion()
44 {
45   std::cout << "gdcmscu: gdcm " << gdcm::Version::GetVersion() << " ";
46   const char date[] = "$Date$";
47   std::cout << date << std::endl;
48 }
49 
PrintHelp()50 static void PrintHelp()
51 {
52   PrintVersion();
53   std::cout << "Usage: gdcmscu [OPTION]...[OPERATION]...HOSTNAME...[PORT]..." << std::endl;
54   std::cout << "Execute a DICOM Q/R operation to HOSTNAME, using port PORT (104 when not specified)\n";
55   std::cout << "Options:" << std::endl;
56   std::cout << "  -H --hostname       Hostname." << std::endl;
57   std::cout << "  -p --port           Port number." << std::endl;
58   std::cout << "     --aetitle        Set Calling AE Title." << std::endl;
59   std::cout << "     --call           Set Called AE Title." << std::endl;
60   std::cout << "Mode Options:" << std::endl;
61   std::cout << "     --echo           C-ECHO (default when none)." << std::endl;
62   std::cout << "     --store          C-STORE." << std::endl;
63   std::cout << "     --find           C-FIND." << std::endl;
64   std::cout << "     --move           C-MOVE." << std::endl;
65   std::cout << "     --get            C-GET." << std::endl;
66   std::cout << "C-STORE Options:" << std::endl;
67   std::cout << "  -i --input          DICOM filename" << std::endl;
68   std::cout << "  -r --recursive      recursively process (sub-)directories." << std::endl;
69   std::cout << "     --store-query    Store constructed query in file." << std::endl;
70   std::cout << "C-FIND Options:" << std::endl;
71   std::cout << "     --worklist       C-FIND Worklist Model." << std::endl;
72   std::cout << "     --patientroot    C-FIND Patient Root Model." << std::endl;
73   std::cout << "     --studyroot      C-FIND Study Root Model." << std::endl;
74   std::cout << "     --patient        C-FIND Query on Patient Info (cannot be used with --studyroot)" << std::endl;
75   std::cout << "     --study          C-FIND Query on Study Info." << std::endl;
76   std::cout << "     --series         C-FIND Query on Series Info." << std::endl;
77   std::cout << "     --image          C-FIND Query on Image Info." << std::endl;
78   //std::cout << "     --psonly         C-FIND Patient/Study Only Model." << std::endl;
79   std::cout << "     --key            0123,4567=VALUE for specifying search criteria (wildcard allowed)." << std::endl;
80   std::cout << "                      With --key, leave blank (ie, --key 10,10="") to retrieve values" << std::endl;
81   std::cout << "C-MOVE Options:" << std::endl;
82   std::cout << "  -o --output         DICOM output directory." << std::endl;
83   std::cout << "     --port-scp       Port used for incoming association." << std::endl;
84   std::cout << "     --key            0123,4567=VALUE for specifying search criteria (wildcard not allowed)." << std::endl;
85   std::cout << "  Note that C-MOVE supports the same queries as C-FIND, but no wildcards are allowed." << std::endl;
86   std::cout << "C-GET Options:" << std::endl;
87   std::cout << "General Options:" << std::endl;
88   std::cout << "     --root-uid               Root UID." << std::endl;
89   std::cout << "  -V --verbose   more verbose (warning+error)." << std::endl;
90   std::cout << "  -W --warning   print warning info." << std::endl;
91   std::cout << "  -D --debug     print debug info." << std::endl;
92   std::cout << "  -E --error     print error info." << std::endl;
93   std::cout << "  -h --help      print help." << std::endl;
94   std::cout << "     --queryhelp print query help." << std::endl;
95   std::cout << "  -v --version   print version." << std::endl;
96   std::cout << "  -L --log-file  set log file (instead of cout)." << std::endl;
97 
98   try
99     {
100     std::locale l("");
101     std::string loc = l.name();
102     std::cout << std::endl;
103     std::cout << "Local Name: " << loc << std::endl;
104     }
105   catch( const std::exception& e)
106     {
107     std::cerr << e.what() << std::endl;
108     }
109   std::cout << "Local Character Set: " << gdcm::System::GetLocaleCharset() << std::endl;
110   std::vector<gdcm::ECharSet> charsettype;
111   charsettype.push_back( gdcm::QueryFactory::GetCharacterFromCurrentLocale() );
112   gdcm::DataElement de = gdcm::QueryFactory::ProduceCharacterSetDataElement(charsettype);
113   const gdcm::ByteValue *bv = de.GetByteValue();
114   std::string s( bv->GetPointer(), bv->GetLength() );
115   std::cout << "DICOM Character Set: [" << s << "]" << std::endl;
116 }
117 
PrintQueryHelp(int inFindPatientRoot)118 static void PrintQueryHelp(int inFindPatientRoot)
119 {
120   gdcm::BaseRootQuery* theBase;
121   if (inFindPatientRoot)
122   {
123     std::cout << "To find the help for a study-level query, type" <<std::endl;
124     std::cout << " --queryhelp --studyroot" << std::endl;
125     theBase = gdcm::QueryFactory::ProduceQuery(gdcm::ePatientRootType, gdcm::eFind, gdcm::ePatient);
126     theBase->WriteHelpFile(std::cout);
127     delete theBase;
128   }
129   else
130   {
131     std::cout << "To find the help for a patient-level query, type" <<std::endl;
132     std::cout << " --queryhelp --patientroot" << std::endl;
133     std::cout << "These are the study level, study root queries: " << std::endl;
134     theBase = gdcm::QueryFactory::ProduceQuery(gdcm::eStudyRootType, gdcm::eFind, gdcm::eStudy);
135     theBase->WriteHelpFile(std::cout);
136     delete theBase;
137   }
138 }
139 
main(int argc,char * argv[])140 int main(int argc, char *argv[])
141 {
142   int c;
143   //int digit_optind = 0;
144 
145   std::string shostname;
146   std::string callingaetitle = "GDCMSCU";
147   std::string callaetitle = "ANY-SCP";
148   int port = 104; // default
149   int portscp = 0;
150   int outputopt = 0;
151   int portscpnum = 0;
152   gdcm::Directory::FilenamesType filenames;
153   std::string outputdir;
154   int storequery = 0;
155   int verbose = 0;
156   int warning = 0;
157   int debug = 0;
158   int error = 0;
159   int help = 0;
160   int queryhelp = 0;
161   int version = 0;
162   int echomode = 0;
163   int storemode = 0;
164   int findmode = 0;
165   int movemode = 0;
166   int getmode = 0;
167   int findworklist = 0;
168   int findpatientroot = 0;
169   int findstudyroot = 0;
170   int patientquery = 0;
171   int studyquery = 0;
172   int seriesquery = 0;
173   int imagequery = 0;
174   int findpsonly = 0;
175   std::string queryfile;
176   std::string root;
177   int rootuid = 0;
178   int recursive = 0;
179   int logfile = 0;
180   std::string logfilename;
181   gdcm::Tag tag;
182   std::vector< std::pair<gdcm::Tag, std::string> > keys;
183 
184   while (true) {
185     //int this_option_optind = optind ? optind : 1;
186     int option_index = 0;
187     /*
188      struct option {
189      const char *name;
190      int has_arg;
191      int *flag;
192      int val;
193      };
194      */
195     static struct option long_options[] = {
196       {"verbose", 0, &verbose, 1},
197       {"warning", 0, &warning, 1},
198       {"debug", 0, &debug, 1},
199       {"error", 0, &error, 1},
200       {"help", 0, &help, 1},
201       {"version", 0, &version, 1},
202       {"hostname", 1, nullptr, 0},     // -h
203       {"aetitle", 1, nullptr, 0},     //
204       {"call", 1, nullptr, 0},     //
205       {"port", 0, &port, 1}, // -p
206       {"input", 1, nullptr, 0}, // dcmfile-in
207       {"echo", 0, &echomode, 1}, // --echo
208       {"store", 0, &storemode, 1}, // --store
209       {"find", 0, &findmode, 1}, // --find
210       {"move", 0, &movemode, 1}, // --move
211       {"key", 1, nullptr, 0}, // (15) --key
212       {"worklist", 0, &findworklist, 1}, // --worklist
213       {"patientroot", 0, &findpatientroot, 1}, // --patientroot
214       {"studyroot", 0, &findstudyroot, 1}, // --studyroot
215       {"psonly", 0, &findpsonly, 1}, // --psonly
216       {"port-scp", 1, &portscp, 1}, // (20) --port-scp
217       {"output", 1, &outputopt, 1}, // --output
218       {"recursive", 0, &recursive, 1},
219       {"store-query", 1, &storequery, 1},
220       {"queryhelp", 0, &queryhelp, 1},
221       {"patient", 0, &patientquery, 1}, // --patient
222       {"study", 0, &studyquery, 1}, // --study
223       {"series", 0, &seriesquery, 1}, // --series
224       {"image", 0, &imagequery, 1}, // --image
225       {"log-file", 1, &logfile, 1}, // --log-file
226       {"get", 0, &getmode, 1}, // --get
227       {nullptr, 0, nullptr, 0} // required
228     };
229     static const char short_options[] = "i:H:p:L:VWDEhvk:o:r";
230     c = getopt_long (argc, argv, short_options,
231                      long_options, &option_index);
232     if (c == -1)
233     {
234       break;
235     }
236 
237     switch (c)
238     {
239       case 0:
240       case '-':
241       {
242         const char *s = long_options[option_index].name; (void)s;
243         //printf ("option %s", s);
244         if (optarg)
245         {
246           if( option_index == 0 ) /* input */
247           {
248             assert( strcmp(s, "input") == 0 );
249             filenames.push_back( optarg );
250           }
251           else if( option_index == 7 ) /* calling aetitle */
252           {
253             assert( strcmp(s, "aetitle") == 0 );
254             //assert( callingaetitle.empty() );
255             callingaetitle = optarg;
256           }
257           else if( option_index == 8 ) /* called aetitle */
258           {
259             assert( strcmp(s, "call") == 0 );
260             //assert( callaetitle.empty() );
261             callaetitle = optarg;
262           }
263           else if( option_index == 15 ) /* key */
264           {
265             assert( strcmp(s, "key") == 0 );
266             if( !tag.ReadFromCommaSeparatedString(optarg) )
267             {
268               std::cerr << "Could not read Tag: " << optarg << std::endl;
269               return 1;
270             }
271             std::stringstream ss;
272             ss.str( optarg );
273             uint16_t dummy;
274             char cdummy; // comma
275             ss >> std::hex >> dummy;
276             assert( tag.GetGroup() == dummy );
277             ss >> cdummy;
278             assert( cdummy == ',' );
279             ss >> std::hex >> dummy;
280             assert( tag.GetElement() == dummy );
281             ss >> cdummy;
282             assert( cdummy == ',' || cdummy == '=' );
283             std::string str;
284             //ss >> str;
285             std::getline(ss, str); // do not skip whitespace
286             if( str.size() % 2 == 1 ) str += " ";
287             keys.emplace_back(tag, str );
288           }
289           else if( option_index == 20 ) /* port-scp */
290           {
291             assert( strcmp(s, "port-scp") == 0 );
292             portscpnum = atoi(optarg);
293           }
294           else if( option_index == 21 ) /* output */
295           {
296             assert( strcmp(s, "output") == 0 );
297             outputdir = optarg;
298           }
299           else if( option_index == 23 ) /* store-query */
300           {
301             assert( strcmp(s, "store-query") == 0 );
302             queryfile = optarg;
303           }
304           else if( option_index == 29 ) /* log-file */
305           {
306             assert( strcmp(s, "log-file") == 0 );
307             logfilename = optarg;
308           }
309           else
310           {
311             // If you reach here someone mess-up the index and the argument in
312             // the getopt table
313             assert( 0 );
314           }
315           //printf (" with arg %s", optarg);
316         }
317         //printf ("\n");
318       }
319         break;
320 
321       case 'k':
322       {
323         if( !tag.ReadFromCommaSeparatedString(optarg) )
324         {
325           std::cerr << "Could not read Tag: " << optarg << std::endl;
326           return 1;
327         }
328         std::stringstream ss;
329         ss.str( optarg );
330         uint16_t dummy;
331         char cdummy; // comma
332         ss >> std::hex >> dummy;
333         assert( tag.GetGroup() == dummy );
334         ss >> cdummy;
335         assert( cdummy == ',' );
336         ss >> std::hex >> dummy;
337         assert( tag.GetElement() == dummy );
338         ss >> cdummy;
339         assert( cdummy == ',' || cdummy == '=' );
340         std::string str;
341         std::getline(ss, str); // do not skip whitespace
342         keys.emplace_back(tag, str );
343       }
344         break;
345 
346       case 'i':
347         //printf ("option i with value '%s'\n", optarg);
348         filenames.push_back( optarg );
349         break;
350 
351       case 'r':
352         recursive = 1;
353         break;
354 
355       case 'o':
356         assert( outputdir.empty() );
357         outputdir = optarg;
358         break;
359 
360       case 'H':
361         shostname = optarg;
362         break;
363 
364       case 'p':
365         port = atoi( optarg );
366         break;
367 
368       case 'L':
369         logfile = 1;
370         logfilename = optarg;
371         break;
372 
373       case 'V':
374         verbose = 1;
375         break;
376 
377       case 'W':
378         warning = 1;
379         break;
380 
381       case 'D':
382         debug = 1;
383         break;
384 
385       case 'E':
386         error = 1;
387         break;
388 
389       case 'h':
390         help = 1;
391         break;
392 
393       case 'q':
394         queryhelp = 1;
395         break;
396 
397       case 'v':
398         version = 1;
399         break;
400 
401       case '?':
402         break;
403 
404       default:
405         printf ("?? getopt returned character code 0%o ??\n", c);
406     }
407   }
408 
409   if (optind < argc)
410     {
411     int v = argc - optind;
412     // hostname port filename
413     if( v == 1 )
414       {
415       shostname = argv[optind++];
416       }
417     else if( v == 2 )
418       {
419       shostname = argv[optind++];
420       port = atoi( argv[optind++] );
421       }
422     else if( v >= 3 )
423       {
424       shostname = argv[optind++];
425       port = atoi( argv[optind++] );
426       std::vector<std::string> files;
427       while (optind < argc)
428         {
429         files.emplace_back(argv[optind++] );
430         }
431       filenames = files;
432       }
433     else
434       {
435       return 1;
436       }
437     assert( optind == argc );
438     }
439 
440   if( version )
441   {
442     PrintVersion();
443     return 0;
444   }
445 
446   if( help )
447     {
448     PrintHelp();
449     return 0;
450     }
451   if(queryhelp)
452     {
453     PrintQueryHelp(findpatientroot);
454     return 0;
455     }
456   const bool theDebug = debug != 0;
457   const bool theWarning = warning != 0;
458   const bool theError = error != 0;
459   const bool theVerbose = verbose != 0;
460   const bool theRecursive = recursive != 0;
461   // Debug is a little too verbose
462   gdcm::Trace::SetDebug( theDebug );
463   gdcm::Trace::SetWarning( theWarning );
464   gdcm::Trace::SetError( theError );
465   // when verbose is true, make sure warning+error are turned on:
466   if( verbose )
467     {
468     gdcm::Trace::SetWarning( theVerbose );
469     gdcm::Trace::SetError( theVerbose);
470     }
471   if( logfile )
472     {
473     gdcm::Trace::SetStreamToFile( logfilename.c_str() );
474     }
475   gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( callaetitle.c_str() );
476   if( !rootuid )
477     {
478     // only read the env var if no explicit cmd line option
479     // maybe there is an env var defined... let's check
480     const char *rootuid_env = getenv("GDCM_ROOT_UID");
481     if( rootuid_env )
482       {
483       rootuid = 1;
484       root = rootuid_env;
485       }
486     }
487   if( rootuid )
488     {
489     // root is set either by the cmd line option or the env var
490     if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
491       {
492       std::cerr << "specified Root UID is not valid: " << root << std::endl;
493       return 1;
494       }
495     gdcm::UIDGenerator::SetRoot( root.c_str() );
496     }
497 
498   if( shostname.empty() )
499     {
500     //std::cerr << "Hostname missing" << std::endl;
501     PrintHelp(); // needed to display help message when no arg
502     return 1;
503     }
504   if( port == 0 )
505     {
506     std::cerr << "Problem with port number" << std::endl;
507     return 1;
508     }
509   // checkout outputdir opt:
510   if( outputopt )
511     {
512     if( !gdcm::System::FileIsDirectory( outputdir.c_str()) )
513       {
514       if( !gdcm::System::MakeDirectory( outputdir.c_str() ) )
515         {
516         std::cerr << "Sorry: " << outputdir << " is not a valid directory.";
517         std::cerr << std::endl;
518         std::cerr << "and I could not create it.";
519         std::cerr << std::endl;
520         return 1;
521         }
522       }
523     }
524 
525   const char *hostname = shostname.c_str();
526   std::string mode = "echo";
527   if ( echomode )
528     {
529     mode = "echo";
530     }
531   else if ( storemode )
532     {
533     mode = "store";
534     }
535   else if ( findmode )
536     {
537     mode = "find";
538     }
539   else if ( movemode )
540     {
541     mode = "move";
542     }
543   else if ( getmode )
544     {
545     mode = "get";
546     }
547   else if ( findworklist )
548     {
549     mode = "worklist";
550     }
551 
552   //this class contains the networking calls
553 
554   if ( mode == "server" ) // C-STORE SCP
555     {
556     // MM: Do not expose that to user for now (2010/10/11).
557     //CStoreServer( port );
558     return 1;
559     }
560   else if ( mode == "echo" ) // C-ECHO SCU
561     {
562     // ./bin/gdcmscu mi2b2.slicer.org 11112  --aetitle ACME1 --call MI2B2
563     // ./bin/gdcmscu --echo mi2b2.slicer.org 11112  --aetitle ACME1 --call MI2B2
564     bool didItWork = gdcm::CompositeNetworkFunctions::CEcho( hostname, (uint16_t)port,
565       callingaetitle.c_str(), callaetitle.c_str() );
566     gdcmDebugMacro( (didItWork ? "Echo succeeded." : "Echo failed.") );
567     return didItWork ? 0 : 1;
568     }
569   else if ( mode == "move" ) // C-FIND SCU
570     {
571     // ./bin/gdcmscu --move --patient dhcp-67-183 5678 move
572     // ./bin/gdcmscu --move --patient mi2b2.slicer.org 11112 move
573     gdcm::ERootType theRoot = gdcm::eStudyRootType;
574     if (findpatientroot)
575       theRoot = gdcm::ePatientRootType;
576     gdcm::EQueryLevel theLevel = gdcm::eStudy;
577     if (patientquery)
578       theLevel = gdcm::ePatient;
579     if (seriesquery)
580       theLevel = gdcm::eSeries;
581     if (imagequery)
582       theLevel = gdcm::eImage;
583 
584     gdcm::SmartPointer<gdcm::BaseRootQuery> theQuery =
585       gdcm::CompositeNetworkFunctions::ConstructQuery(theRoot, theLevel ,keys, gdcm::eMove );
586 
587     if (findstudyroot == 0 && findpatientroot == 0)
588       {
589       if (gdcm::Trace::GetErrorFlag())
590         {
591         std::cerr << "Need to explicitly choose query retrieve level, --patientroot or --studyroot" << std::endl;
592         }
593       std::cerr << "Move failed." << std::endl;
594       return 1;
595       }
596 
597     if( !portscp )
598       {
599       std::cerr << "Need to set explicitly port number for SCP association"
600         " --port-scp" << std::endl;
601       //std::cerr << "Move failed." << std::endl;
602       return 1;
603       }
604 
605     if( storequery )
606       {
607       if (!theQuery->WriteQuery(queryfile))
608         {
609         std::cerr << "Could not write out query to: " << queryfile << std::endl;
610         std::cerr << "Move failed." << std::endl;
611         return 1;
612         }
613       }
614 
615     if (!theQuery->ValidateQuery(false))
616       {
617       std::cerr << "You have not constructed a valid find query."
618         " Please try again." << std::endl;
619       return 1;
620       }
621 
622     //!!! added the boolean to 'interleave writing', which basically writes
623     //each file out as it comes across, rather than all at once at the end.
624     //Turn off the boolean to have it written all at once at the end.
625     bool didItWork = gdcm::CompositeNetworkFunctions::CMove( hostname, (uint16_t)port,
626       theQuery, (uint16_t)portscpnum,
627       callingaetitle.c_str(), callaetitle.c_str(), outputdir.c_str() );
628     gdcmDebugMacro( (didItWork ? "Move succeeded." : "Move failed.") );
629     return didItWork ? 0 : 1;
630     }
631   else if ( mode == "worklist" )
632     {
633     gdcm::ERootType theRoot =  gdcm::ePatientRootType;
634     gdcm::EQueryLevel theLevel = gdcm::eSeries;
635     gdcm::SmartPointer<gdcm::BaseRootQuery> theQuery = gdcm::CompositeNetworkFunctions::ConstructQuery( theRoot, theLevel ,keys, gdcm::eWLMFind );
636     if (!theQuery->ValidateQuery(false))
637       {
638       //issue
639       }
640     std::vector<gdcm::DataSet> theDataSet;
641     if( !gdcm::CompositeNetworkFunctions::CFind(hostname, (uint16_t)port, theQuery, theDataSet,
642         callingaetitle.c_str(), callaetitle.c_str()) )
643       {
644       }
645     gdcm::Printer p;
646     std::ostream &os = gdcm::Trace::GetStream();
647     for( std::vector<gdcm::DataSet>::iterator itor
648       = theDataSet.begin(); itor != theDataSet.end(); itor++)
649       {
650       os << "WL Find Response: " << (itor - theDataSet.begin() + 1) << std::endl;
651       p.PrintDataSet( *itor, os );
652       os << std::endl;
653       }
654 
655     if( gdcm::Trace::GetWarningFlag() ) // == verbose flag
656       {
657       os << "WL Find was successful." << std::endl;
658       }
659     }
660   else if ( mode == "find" ) // C-FIND SCU
661     {
662     // Construct C-FIND DataSet:
663     // ./bin/gdcmscu --find --patient dhcp-67-183 5678
664     // ./bin/gdcmscu --find --patient mi2b2.slicer.org 11112  --aetitle ACME1 --call MI2B2
665     // findscu -aec MI2B2 -P -k 0010,0010=F* mi2b2.slicer.org 11112 patqry.dcm
666 
667     // PATIENT query:
668     // ./bin/gdcmscu --find --patient mi2b2.slicer.org 11112  --aetitle ACME1 --call MI2B2 --key 10,10="F*" -V
669     gdcm::ERootType theRoot = gdcm::eStudyRootType;
670     if (findpatientroot)
671       theRoot = gdcm::ePatientRootType;
672     gdcm::EQueryLevel theLevel = gdcm::eStudy;
673     if (patientquery)
674       theLevel = gdcm::ePatient;
675     if (seriesquery)
676       theLevel = gdcm::eSeries;
677     if (imagequery)
678       theLevel = gdcm::eImage;
679 
680     gdcm::SmartPointer<gdcm::BaseRootQuery> theQuery =
681       gdcm::CompositeNetworkFunctions::ConstructQuery(theRoot, theLevel ,keys);
682 
683     if (findstudyroot == 0 && findpatientroot == 0)
684       {
685       if (gdcm::Trace::GetErrorFlag())
686         {
687         std::cerr << "Need to explicitly choose query retrieve level, --patientroot or --studyroot" << std::endl;
688         }
689       std::cerr << "Find failed." << std::endl;
690       return 1;
691       }
692     if (!theQuery)
693       {
694       std::cerr << "Query construction failed." <<std::endl;
695       return 1;
696       }
697 
698     if( storequery )
699       {
700       if (!theQuery->WriteQuery(queryfile))
701         {
702         std::cerr << "Could not write out query to: " << queryfile << std::endl;
703         return 1;
704         }
705       }
706 
707     //doing a non-strict query, the second parameter there.
708     //look at the base query comments
709     if (!theQuery->ValidateQuery(false))
710       {
711       std::cerr << "You have not constructed a valid find query."
712         " Please try again." << std::endl;
713       return 1;
714       }
715     //the value in that tag corresponds to the query type
716     std::vector<gdcm::DataSet> theDataSet;
717     if( !gdcm::CompositeNetworkFunctions::CFind(hostname, (uint16_t)port, theQuery, theDataSet,
718         callingaetitle.c_str(), callaetitle.c_str()) )
719       {
720       gdcmDebugMacro( "Problem in CFind." );
721       return 1;
722       }
723 
724     gdcm::Printer p;
725     std::ostream &os = gdcm::Trace::GetStream();
726     for( std::vector<gdcm::DataSet>::iterator itor
727       = theDataSet.begin(); itor != theDataSet.end(); itor++)
728       {
729       os << "Find Response: " << (itor - theDataSet.begin() + 1) << std::endl;
730       p.PrintDataSet( *itor, os );
731       os << std::endl;
732       }
733 
734     if( gdcm::Trace::GetWarningFlag() ) // == verbose flag
735       {
736       os << "Find was successful." << std::endl;
737       }
738     return 0;
739     }
740   else if ( mode == "store" ) // C-STORE SCU
741     {
742     // mode == directory
743     gdcm::Directory::FilenamesType thefiles;
744     for( gdcm::Directory::FilenamesType::const_iterator file = filenames.begin();
745       file != filenames.end(); ++file )
746       {
747       if( gdcm::System::FileIsDirectory(file->c_str()) )
748         {
749         gdcm::Directory::FilenamesType files;
750         gdcm::Directory dir;
751         dir.Load(*file, theRecursive);
752         files = dir.GetFilenames();
753         thefiles.insert(thefiles.end(), files.begin(), files.end());
754         }
755       else
756         {
757         // This is a file simply add it
758         thefiles.push_back(*file);
759         }
760       }
761     bool didItWork =
762       gdcm::CompositeNetworkFunctions::CStore(hostname, (uint16_t)port, thefiles,
763         callingaetitle.c_str(), callaetitle.c_str());
764 
765     gdcmDebugMacro( (didItWork ? "Store was successful." : "Store failed.") );
766     return didItWork ? 0 : 1;
767     }
768   else if ( mode == "get" ) // C-GET SCU
769     {
770     return 1;
771     }
772   else
773     {
774     assert( 0 );
775     return 1;
776     }
777 
778   return 0;
779 }
780