1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME       : project_file.c
3 @DESCRIPTION: Code to do manipulate the project files (files containing
4               info on what to do with files for each project).
5 @METHOD     :
6 @GLOBALS    :
7 @CALLS      :
8 @CREATED    : February 14, 1995 (Peter Neelin)
9 @MODIFIED   :
10  * $Log: project_file.c,v $
11  * Revision 6.8  2001-04-21 13:29:06  neelin
12  * Added PROJECT_CAPTURE file type and support for .default project file.
13  *
14  * Revision 6.7  2001/04/09 23:02:50  neelin
15  * Modified copyright notice, removing permission statement since copying,
16  * etc. is probably not permitted by our non-disclosure agreement with
17  * Philips.
18  *
19  * Revision 6.6  2001/02/19 22:03:13  neelin
20  * Port to linux.
21  *
22  * Revision 6.5  2000/06/14 18:24:08  neelin
23  * Added UseSafeOrientations keyword to project files to allow forcing of
24  * direction cosines to standard (safe) ones, and modified convert_to_dicom
25  * so that this is no longer the default behaviour.
26  *
27  * Revision 6.4  2000/02/21 23:48:14  neelin
28  * More changes to improve dicom conformance for MNH PACS system.
29  * Allow UID prefix to be defined in project file. Define SOP instance UID in
30  * addition to study and series instance UIDs and frame-of-reference UID and
31  * make sure that these are all the constant for the same image data.
32  * Set series number from acquisition number.
33  * Set study description from part of comment field.
34  *
35  * Revision 6.3  2000/01/31 13:57:38  neelin
36  * Added keyword to project file to allow definition of the local AEtitle.
37  * A simple syntax allows insertion of the host name into the AEtitle.
38  *
39  * Revision 6.2  1999/10/29 17:52:04  neelin
40  * Fixed Log keyword
41  *
42  * Revision 6.1  1997/09/12 23:13:28  neelin
43  * Added ability to convert gyrocom images to dicom images.
44  *
45  * Revision 6.0  1997/09/12  13:23:50  neelin
46  * Release of minc version 0.6
47  *
48  * Revision 5.1  1997/09/11  13:09:40  neelin
49  * Added more complicated syntax for project files so that different things
50  * can be done to the data. The old syntax is still supported.
51  *
52  * Revision 5.0  1997/08/21  13:24:50  neelin
53  * Release of minc version 0.5
54  *
55  * Revision 4.0  1997/05/07  20:01:07  neelin
56  * Release of minc version 0.4
57  *
58  * Revision 3.0  1995/05/15  19:31:44  neelin
59  * Release of minc version 0.3
60  *
61  * Revision 1.1  1995/02/14  18:12:26  neelin
62  * Initial revision
63  *
64 @COPYRIGHT  :
65               Copyright 1995 Peter Neelin, McConnell Brain Imaging Centre,
66               Montreal Neurological Institute, McGill University.
67 ---------------------------------------------------------------------------- */
68 
69 #include <sys/types.h>
70 #include <unistd.h>
71 #include <dirent.h>
72 #include <ctype.h>
73 #include <string.h>
74 #include <gcomserver.h>
75 
76 /* Macros */
77 #define STREQ(s1, s2) (strcmp(s1, s2) == 0)
78 #define STRCOPY(s1, s2, maxlen) \
79 { (void) strncpy(s1, s2, (maxlen)-1); s1[(maxlen)-1] = '\0'; }
80 
81 /* ----------------------------- MNI Header -----------------------------------
82 @NAME       : read_project_file
83 @INPUT      : project_name - name to use for project file
84 @OUTPUT     : project_info - information about the project returned in
85                  a user-allocated structure. If NULL, then no data is
86                  returned.
87 @RETURNS    : TRUE if an error occurs, FALSE otherwise.
88 @DESCRIPTION: Routine to read in information for a given project.
89 @METHOD     :
90 @GLOBALS    :
91 @CALLS      :
92 @CREATED    : February 14, 1995 (Peter Neelin)
93 @MODIFIED   : September 9, 1997 (P.N.)
94 ---------------------------------------------------------------------------- */
read_project_file(char * project_name,Project_File_Info * project_info)95 public int read_project_file(char *project_name,
96                              Project_File_Info *project_info)
97 {
98    Project_File_Info temp_project_info;
99    char project_string[256];
100    char output_default_file[256];
101    int ichar, ochar;
102    int length;
103    FILE *fp;
104    char string[512];
105    int project_name_given;
106    char *keyword, *value, *ptr, *error;
107    int oldformat;
108    int iline;
109 
110    /* Check that the user actually wants return values */
111    if (project_info == NULL) {
112       project_info = &temp_project_info;
113    }
114 
115    /* Set some default values */
116    project_info->type = PROJECT_DIRECTORY;
117    project_info->info.directory.file_prefix[0] = '\0';
118    project_info->info.directory.command_line[0] = '\0';
119    project_info->info.directory.output_uid = INT_MIN;
120    project_info->info.directory.output_gid = INT_MIN;
121 
122    /* Copy the project name, removing spaces */
123    if (project_name != NULL)
124       length = strlen(project_name);
125    else
126       length = 0;
127    for (ichar=0, ochar=0;
128         (ichar < length) && (ochar < sizeof(project_string)-1);
129         ichar++) {
130       if (isprint((int) project_name[ichar]) &&
131           !isspace((int) project_name[ichar])) {
132          project_string[ochar] = (char) toupper((int) project_name[ichar]);
133          ochar++;
134       }
135    }
136    project_string[ochar] = '\0';
137 
138    /* Get the host name if there is no project string */
139    project_name_given = (strlen(project_string) > (size_t) 0);
140    if (!project_name_given)
141       (void) gethostname(project_string, sizeof(project_string) - 1);
142    (void) sprintf(output_default_file, "%s/%s%s",
143                   OUTPUT_DEFAULT_FILE_DIR, OUTPUT_DEFAULT_FILE_PREFIX,
144                   project_string);
145 
146    /* Open and read the defaults file. If it is not there and no project
147       name was given, then try the ".default" extension. Return TRUE (error)
148       only if a project name was given */
149    if ((fp=fopen(output_default_file, "r")) == NULL) {
150       if (project_name_given) {
151          return TRUE;
152       }
153       else {
154          (void) sprintf(output_default_file, "%s/%s%s",
155                         OUTPUT_DEFAULT_FILE_DIR, OUTPUT_DEFAULT_FILE_PREFIX,
156                         OUTPUT_DEFAULT_FILE_SUFFIX);
157          if ((fp=fopen(output_default_file, "r")) == NULL) {
158             return FALSE;
159          }
160       }
161    }
162 
163    /* Loop over lines of the file */
164    iline = 0;
165    project_info->type = PROJECT_UNKNOWN;
166    while (fgets(string, (int) sizeof(string), fp) != NULL) {
167 
168       /* Remove the newline, or read in the rest of the line
169          if it is too long */
170       if (string[strlen(string) - 1] == '\n') {
171          string[strlen(string) - 1] = '\0';
172       }
173       else {
174          while ((getc(fp) != (int) '\n') && !feof(fp)) {}
175       }
176 
177       /* Ignore comments and blank lines (don't even count them as lines) */
178       if (string[0] == '#') continue;
179       for (ptr=string; (*ptr != '\0') && isspace((int) *ptr); ptr++) {}
180       if (*ptr == '\0') continue;
181 
182       /* If this is the first line, figure out whether we have an old
183          or new format file (look for the "=") */
184       ptr = strchr(string, (int) '=');
185       if (iline == 0) {
186          oldformat = (ptr == NULL);
187       }
188 
189       /* Read old format file */
190       if (oldformat) {
191          switch (iline) {
192          case 0:
193             project_info->type = PROJECT_DIRECTORY;
194             if (sscanf(string, "%s %d %d",
195                        project_info->info.directory.file_prefix,
196                        &project_info->info.directory.output_uid,
197                        &project_info->info.directory.output_gid) != 3) {
198                (void) fclose(fp);
199                return TRUE;
200             }
201             break;
202          case 1:
203             (void) strcpy(project_info->info.directory.command_line, string);
204             break;
205          default:
206             (void) fprintf(stderr,
207                "Old format project file (%s) contains extra lines\n",
208                            project_string);
209             (void) fclose(fp);
210             return TRUE;
211          }
212       }
213 
214       /* Read new format file */
215       else {                /* New format */
216 
217          /* Check for missing '=' */
218          if (ptr == NULL) {
219             (void) fprintf(stderr, "Project file error (%s): syntax error\n",
220                            project_string);
221             (void) fclose(fp);
222             return TRUE;
223          }
224 
225          /* Get keyword and value (remove leading and trailing whitespace) */
226          for (keyword=string; isspace((int) *keyword); keyword++) {}
227          if (*keyword == '=') keyword--;
228          for (value = ptr+1; isspace((int) *value); value++) {}
229          for (ptr--; (ptr > keyword) && isspace((int) *ptr); ptr--) {}
230          ptr++;
231          *ptr = '\0';
232          for (ptr=&value[strlen(value)-1];
233               (ptr > value) && isspace((int) *ptr); ptr--) {}
234          ptr++;
235          *ptr = '\0';
236 
237          /* Downcase the keyword */
238          for (ichar=0; (size_t) ichar < strlen(keyword); ichar++) {
239             keyword[ichar] = tolower((int) keyword[ichar]);
240          }
241 
242          /* Set up error string */
243          error = NULL;
244 
245          /* Look for the project type on the first line */
246          if (iline == 0) {
247             for (ichar=0; (size_t) ichar < strlen(value); ichar++) {
248                value[ichar] = tolower((int) value[ichar]);
249             }
250             if (STREQ(keyword, "type")) {
251                if (STREQ(value, "directory")) {
252                   project_info->type = PROJECT_DIRECTORY;
253                }
254                else if (STREQ(value, "dicom")) {
255                   project_info->type = PROJECT_DICOM;
256                   project_info->info.dicom.hostname[0] = '\0';
257                   project_info->info.dicom.port[0] = '\0';
258                   project_info->info.dicom.AEtitle[0] = '\0';
259                   project_info->info.dicom.LocalAEtitle[0] = '\0';
260                   project_info->info.dicom.UIDprefix[0] = '\0';
261                   project_info->info.dicom.use_safe_orientations = FALSE;
262                   project_info->info.dicom.afpin = NULL;
263                   project_info->info.dicom.afpout = NULL;
264                }
265                else if (STREQ(value, "capture")) {
266                   project_info->type = PROJECT_CAPTURE;
267                }
268                else {
269                   error = "Unknown project type";
270                }
271             }
272             else {
273                error = "Type must be given on first line";
274             }
275          }
276 
277          /* Get other keywords */
278          else {                           /* Subsequent lines */
279 
280             /* Check that project type is defined */
281             if (project_info->type == PROJECT_UNKNOWN) {
282                error = "Type must be given on first line";
283             }
284 
285             /* Keywords for directory type */
286             else if ((project_info->type == PROJECT_DIRECTORY) ||
287                      (project_info->type == PROJECT_CAPTURE)) {
288                if (STREQ("prefix", keyword)) {
289                   STRCOPY(project_info->info.directory.file_prefix,
290                           value, LONG_LINE);
291                }
292                else if (STREQ("uid", keyword)) {
293                   project_info->info.directory.output_uid = atoi(value);
294                }
295                else if (STREQ("gid", keyword)) {
296                   project_info->info.directory.output_gid = atoi(value);
297                }
298                else if (STREQ("command", keyword)) {
299                   STRCOPY(project_info->info.directory.command_line,
300                           value, LONG_LINE);
301                }
302                else {
303                   error = "Unrecognized keyword for given type";
304                }
305             }
306 
307             /* Keywords for dicom type */
308             else if (project_info->type == PROJECT_DICOM) {
309                if (STREQ("host", keyword)) {
310                   STRCOPY(project_info->info.dicom.hostname,
311                           value, SHORT_LINE);
312                }
313                else if (STREQ("port", keyword)) {
314                   STRCOPY(project_info->info.dicom.port,
315                           value, SHORT_LINE);
316                }
317                else if (STREQ("aetitle", keyword)) {
318                   STRCOPY(project_info->info.dicom.AEtitle,
319                           value, SHORT_LINE);
320                }
321                else if (STREQ("localaetitle", keyword)) {
322                   STRCOPY(project_info->info.dicom.LocalAEtitle,
323                           value, SHORT_LINE);
324                }
325                else if (STREQ("uidprefix", keyword)) {
326                   STRCOPY(project_info->info.dicom.UIDprefix,
327                           value, SHORT_LINE);
328                }
329                else if (STREQ("usesafeorientations", keyword)) {
330                   for (ichar=0; (size_t) ichar < strlen(value); ichar++) {
331                      value[ichar] = tolower((int) value[ichar]);
332                   }
333                   if (STREQ(value, "true") || STREQ(value, "yes")) {
334                      project_info->info.dicom.use_safe_orientations =
335                         TRUE;
336                   }
337                   else if (STREQ(value, "false") || STREQ(value, "no")) {
338                      project_info->info.dicom.use_safe_orientations =
339                         FALSE;
340                   }
341                   else {
342                      error = "Unrecognized boolean value";
343                   }
344                }
345                else {
346                   error = "Unrecognized keyword for given type";
347                }
348             }
349 
350          }          /* Subsequent lines */
351 
352          /* Check for error */
353          if (error != NULL) {
354             (void) fprintf(stderr, "Project file error (%s), keyword %s: %s\n",
355                            project_string, keyword, error);
356             (void) fclose(fp);
357             return TRUE;
358          }
359 
360       }             /* New format */
361 
362       /* Increment the line counter */
363       iline++;
364    }
365 
366    /* Close the file */
367    (void) fclose(fp);
368 
369    /* Check that something was found */
370    if (project_info->type == PROJECT_UNKNOWN) {
371       return TRUE;
372    }
373 
374    /* Check that minimal set of keywords was found */
375    if (project_info->type == PROJECT_DICOM) {
376       if ((project_info->info.dicom.hostname[0] == '\0') ||
377           (project_info->info.dicom.port[0] == '\0') ||
378           (project_info->info.dicom.AEtitle[0] == '\0')) {
379          (void) fprintf(stderr, "Project file (%s) missing options\n",
380                         project_string);
381          return TRUE;
382       }
383    }
384 
385    return FALSE;
386 }
387 
388 /* ----------------------------- MNI Header -----------------------------------
389 @NAME       : get_project_option_string
390 @INPUT      : (none)
391 @OUTPUT     : project_option_string - string containing list of options
392                  for project name
393               maxlen_project_option - maximum length for the string (including
394                  '\0' at end)
395 @RETURNS    : (nothing)
396 @DESCRIPTION: Routine to get a list of possibilities for the project name
397               (looking for appropriately named files).
398 @METHOD     :
399 @GLOBALS    :
400 @CALLS      :
401 @CREATED    : February 14, 1995 (Peter Neelin)
402 @MODIFIED   :
403 ---------------------------------------------------------------------------- */
get_project_option_string(char * project_option_string,int maxlen_project_option)404 public void get_project_option_string(char *project_option_string,
405                                       int maxlen_project_option)
406 {
407    DIR *dirp;
408    struct dirent *dp;
409    int length;
410    char *name, *filler;
411    int compare_length;
412 
413    /* Set up the string */
414    if (maxlen_project_option > 0) {
415       project_option_string[0] = '\0';
416       length = 1;
417    }
418 
419    /* Open directory */
420    if ((dirp = opendir(OUTPUT_DEFAULT_FILE_DIR)) == NULL)
421       return;
422 
423    /* Loop through directory entries */
424    compare_length = strlen(OUTPUT_DEFAULT_FILE_PREFIX);
425    while ((dp = readdir(dirp)) != NULL) {
426 
427       /* Check for an entry with the right prefix */
428       if (strncmp(OUTPUT_DEFAULT_FILE_PREFIX,
429                   dp->d_name, compare_length) == 0) {
430 
431          /* Check for an uppercase letter */
432          if ((strlen(dp->d_name) > (size_t) compare_length) &&
433              (isupper(dp->d_name[compare_length]))) {
434             name = &dp->d_name[compare_length];
435 
436             /* Check that we can read the project file */
437             if (!read_project_file(name, NULL)) {
438                if (length > 1)
439                   filler = ", ";
440                else
441                   filler = "";
442                if ((strlen(name) + length + strlen(filler))
443                    < (size_t) maxlen_project_option) {
444                   (void) strcat(strcat(project_option_string, filler), name);
445                   length += strlen(filler) + strlen(name);
446                }
447             }        /* We can read the project file */
448 
449          }        /* Found uppercase letter */
450 
451       }        /* Found file matching prefix */
452 
453    }        /* Loop over files */
454 }
455