diff --git a/tables/c_imt.c b/tables/c_imt.c index 1e6c832d0..37a15d593 100644 --- a/tables/c_imt.c +++ b/tables/c_imt.c @@ -1,23 +1,33 @@ +#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) +# include +# define WXYZ_ZYXW_Unix +#endif # include # include # include # include # include "ctables.h" -/* Image file name template descriptor. - NOTE: This version handles only explicit file names, no wildcards. +# define DELTA_MAXFILES 100 + +/* Expand a file name template. More than one file name may be given, separated by a blank or comma - (or both). If a file name includes an expression in brackets at the - end of the name, only the portion before the '[' will be used when - checking whether the file exists. + (or both). For Unix-like systems, the file names may include wildcard + characters. If a file name includes an expression in brackets at the + end of the name (e.g. [sci,1], for specifying a FITS extension), that + expression will be stripped off before expanding wildcards, and then + it will be appended back on to each matching file name. example: - r_imt = c_imtopen (rawlist); - n_raw = c_imtlen (r_imt); - j = c_imtgetim (r_imt, rawfile, STIS_LINE); - c_imtrew (r_imt); - c_imtclose (r_imt); + IRAFPointer r_imt; + int n_raw, nchar; + + r_imt = c_imtopen(rawlist); + n_raw = c_imtlen(r_imt); + nchar = c_imtgetim(r_imt, rawfile, STIS_LINE); + c_imtrew(r_imt); + c_imtclose(r_imt); */ typedef struct { @@ -28,9 +38,11 @@ typedef struct { int current_index; /* index of current file in 'files' */ } ImtDescr; -static void findFiles (ImtDescr *imt_descr); +static int findFiles(ImtDescr *imt_descr); +static int add_filename(ImtDescr *imt_descr, char *filename); +static int more_names(ImtDescr *imt_descr, int new_maxfiles); -IRAFPointer c_imtopen (char *pattern) { +IRAFPointer c_imtopen(char *pattern) { /* Create a file name template object. argument: @@ -42,22 +54,36 @@ function value o: file name template descriptor ImtDescr *imt_descr; IRAFPointer imt; + int status; - imt_descr = (ImtDescr *)calloc (1, sizeof(ImtDescr)); - imt_descr->pattern = (char *)calloc (strlen(pattern)+1, sizeof(char)); - strcpy (imt_descr->pattern, pattern); + imt_descr = (ImtDescr *)calloc(1, sizeof(ImtDescr)); + if (imt_descr == NULL) { + return NULL; + } + imt_descr->pattern = (char *)calloc(strlen(pattern)+1, sizeof(char)); + if (imt_descr->pattern == NULL) { + free(imt_descr); + return NULL; + } + imt_descr->files = NULL; + imt_descr->nfiles = 0; + imt_descr->alloc_nfiles = 0; + imt_descr->current_index = 0; - /* allocate and populate the list of file names */ - findFiles (imt_descr); + strcpy(imt_descr->pattern, pattern); - imt_descr->current_index = 0; + /* Populate the list of file names. */ + status = findFiles(imt_descr); + if (status != 0) { + return NULL; + } imt = (void *)imt_descr; return imt; } -int c_imtlen (IRAFPointer imt) { +int c_imtlen(IRAFPointer imt) { /* Return the number of file names that match the template. argument: @@ -78,7 +104,7 @@ function value o: number of file names in the list return nfiles; } -void c_imtrew (IRAFPointer imt) { +void c_imtrew(IRAFPointer imt) { /* "Rewind" the list, i.e. reset the file name index to 0. argument: @@ -91,7 +117,7 @@ IRAFPointer imt i: file name template descriptor imt_descr->current_index = 0; } -int c_imtgetim (IRAFPointer imt, char *outstr, int maxch) { +int c_imtgetim(IRAFPointer imt, char *outstr, int maxch) { /* Get the next file name in the list. arguments: @@ -120,22 +146,22 @@ function value o: length of the file name, or 0 if there are no i = imt_descr->current_index; - if (strlen (imt_descr->files[i]) <= maxch) { - strcpy (outstr, imt_descr->files[i]); + if (strlen(imt_descr->files[i]) <= maxch) { + strcpy(outstr, imt_descr->files[i]); } else { - setError (ERR_STRING_TOO_LONG, + setError(ERR_STRING_TOO_LONG, "c_imtgetim: file name is too long"); - strncpy (outstr, imt_descr->files[i], maxch); + strncpy(outstr, imt_descr->files[i], maxch); outstr[maxch] = '\0'; } /* point to the next name in the list */ imt_descr->current_index += 1; - return (strlen (imt_descr->files[i])); + return strlen(imt_descr->files[i]); } -void c_imtclose (IRAFPointer imt) { +void c_imtclose(IRAFPointer imt) { /* Deallocate memory for the imt descriptor. argument: @@ -147,34 +173,41 @@ IRAFPointer imt i: file name template descriptor if (imt != NULL) { imt_descr = (ImtDescr *)imt; - free (imt_descr->pattern); + free(imt_descr->pattern); for (i = 0; i < imt_descr->nfiles; i++) - free (imt_descr->files[i]); - free (imt_descr->files); - free (imt_descr); + free(imt_descr->files[i]); + free(imt_descr->files); + free(imt_descr); } } -static void findFiles (ImtDescr *imt_descr) { +static int findFiles(ImtDescr *imt_descr) { /* Allocate and populate the list of file names. */ - char *filename; /* a name from the list */ +#ifdef WXYZ_ZYXW_Unix + glob_t globbuf; +#endif + char *fullname; /* name with environment variable resolved */ + char *filename; /* a file name from the pattern */ + char *ext_spec; /* expression in brackets at end of name */ + int len_ext_spec; /* length of expression with brackets */ + int len_fname; /* length of a file name */ + int max_strlen; /* maximum length of filename pattern */ + int flags; /* for the glob function */ int nfiles; /* allocated size of file list */ - int n; /* number of files that exist */ int i, j; - int done, done_2, done_3; + int k; + int brackets; /* incr. with '[', decremented with ']' */ + int done, done_2, done_3; /* boolean */ int status = 0; - /* Count the number of names in a comma- or blank-separated list. - This can give one more than the actual number of names, e.g. if - the string begins with a blank or ends with a blank, or if no name - is given, but the extra (blank) name will be rejected later. - Also, we want alloc_nfiles to be at least one because we'll use - this number for the length when allocating the 'files' array. + /* Count the number of names in a comma- or blank-separated list, + and get the length of the longest file name in the list. */ nfiles = 0; + max_strlen = 0; i = 0; done = 0; while (!done) { @@ -184,13 +217,19 @@ static void findFiles (ImtDescr *imt_descr) { done = 1; } done = 0; + brackets = 0; + j = 0; while (!done) { if (imt_descr->pattern[i] == '\0') { nfiles++; + max_strlen = (i - j > max_strlen) ? (i - j) : max_strlen; + j = i; done = 1; - } else if (imt_descr->pattern[i] == ',' || - imt_descr->pattern[i] == ' ') { + } else if ((imt_descr->pattern[i] == ',' || + imt_descr->pattern[i] == ' ') && !brackets) { nfiles++; + max_strlen = (i - j > max_strlen) ? (i - j) : max_strlen; + j = i; i++; /* check for and skip over repeated commas or blanks */ done_2 = 0; @@ -203,17 +242,30 @@ static void findFiles (ImtDescr *imt_descr) { } } } else { + if (imt_descr->pattern[i] == '[') + brackets++; + else if (imt_descr->pattern[i] == ']') + brackets--; i++; } } - imt_descr->alloc_nfiles = nfiles; - - filename = (char *)calloc (SZ_FNAME+1, sizeof(char)); + if (brackets != 0) { + printf("Warning Unmatched bracket in pattern '%s'\n", + imt_descr->pattern); + } + if (max_strlen < 1) + max_strlen = SZ_FNAME; - imt_descr->files = (char **)calloc (imt_descr->alloc_nfiles, - sizeof(char *)); + filename = (char *)calloc(max_strlen+1, sizeof(char)); - imt_descr->nfiles = 0; /* set in loop */ + /* Allocate space for nfiles files. This may be increased later, + if one or more names in the list include wildcard characters. + */ + status = more_names(imt_descr, nfiles); + if (status != 0) { + setError(status, "c_imtopen: out of memory"); + return status; + } /* preserve i as an index in pattern through all the following loops */ i = 0; @@ -226,7 +278,7 @@ static void findFiles (ImtDescr *imt_descr) { done = 1; } - n = 0; + brackets = 0; done = 0; while (!done) { @@ -234,8 +286,9 @@ static void findFiles (ImtDescr *imt_descr) { done_2 = 0; while (!done_2) { if (imt_descr->pattern[i] == '\0' || - imt_descr->pattern[i] == ',' || - imt_descr->pattern[i] == ' ') { + ((imt_descr->pattern[i] == ',' || + imt_descr->pattern[i] == ' ') && + !brackets)) { done_2 = 1; filename[j] = '\0'; if (imt_descr->pattern[i] != '\0') { @@ -254,6 +307,10 @@ static void findFiles (ImtDescr *imt_descr) { } } else { filename[j] = imt_descr->pattern[i]; + if (imt_descr->pattern[i] == '[') + brackets++; + else if (imt_descr->pattern[i] == ']') + brackets--; i++; j++; } @@ -261,22 +318,172 @@ static void findFiles (ImtDescr *imt_descr) { if (imt_descr->pattern[i] == '\0') done = 1; - fullname = (char *)calloc (SZ_FNAME+1, sizeof(char)); - status = c_vfn2osfn (filename, fullname); + fullname = (char *)calloc(SZ_FNAME+1, sizeof(char)); + status = c_vfn2osfn(filename, fullname); if (status != 0) { - setError (status, "c_imtopen: error from c_vfn2osfn"); - free (fullname); - return; + setError(status, "c_imtopen: error from c_vfn2osfn"); + free(fullname); + return status; } +#ifdef WXYZ_ZYXW_Unix + /* If fullname ends with an expression in brackets (e.g. an + extension name or number), strip it off before expanding + wildcards, then append it to each file name. + */ + len_fname = strlen(fullname); + ext_spec = NULL; + if (len_fname > 2 && fullname[len_fname-1] == ']') { + for (k = len_fname - 1; k > 0; k--) { + if (fullname[k] == '[') { + len_ext_spec = len_fname - k; + ext_spec = malloc((len_ext_spec + 10) * sizeof(char)); + if (ext_spec == NULL) { + setError(status, "c_imtopen: out of memory"); + return 1; + } + strcpy(ext_spec, fullname+k); + fullname[k] = '\0'; /* chop off "[whatever]" */ + break; + } + } + } + globbuf.gl_offs = 0; + /* GLOB_MARK --> Append a slash to each path which corresponds + to a directory. + GLOB_NOCHECK --> If no pattern matches, return the original + pattern. This is needed to allow specifying output files, + which should not exist yet. + */ + flags = GLOB_MARK | GLOB_NOCHECK; + status = glob(fullname, flags, NULL, &globbuf); + if (status != 0) { + char msg[SZ_FNAME]; + sprintf(msg, "c_imtopen: error %d from glob", status); + setError(status, msg); + free(fullname); + free(filename); + if (ext_spec != NULL) + free(ext_spec); + return status; + } + if (ext_spec == NULL) { + for (k = 0; k < globbuf.gl_pathc; k++) { + status = add_filename(imt_descr, globbuf.gl_pathv[k]); + if (status != 0) { + setError(status, "c_imtopen: out of memory"); + return status; + } + } + } else { + /* re-use fullname */ + for (k = 0; k < globbuf.gl_pathc; k++) { + strcpy(fullname, globbuf.gl_pathv[k]); + strcat(fullname, ext_spec); + status = add_filename(imt_descr, fullname); + if (status != 0) { + setError(status, "c_imtopen: out of memory"); + return status; + } + } + free(ext_spec); + } + globfree(&globbuf); +#else + /* if a file name was specified, add it to the list */ if (fullname[0] != '\0' && fullname[0] != ' ') { - imt_descr->files[n] = (char *)calloc (SZ_FNAME+1, sizeof(char)); - strcpy (imt_descr->files[n], fullname); - n++; - imt_descr->nfiles = n; + status = add_filename(imt_descr, fullname); } - free (fullname); +#endif + + free(fullname); } - free (filename); + free(filename); + + return status; +} + +static int add_filename(ImtDescr *imt_descr, char *filename) { + +/* Add one file name to the list in imt_descr. Memory will be reallocated + if necessary to allow more room for file names. + +arguments: +ImtDescr *imt_descr i: Pointer to an ImtDescr struct. +char *filename i: Pointer to a string containing a file name. +function value o: status; 0 is OK, 1 means out of memory. +*/ + + int len; /* length of the file name */ + int n; /* number of file names in imt */ + int status = 0; + + if (imt_descr->nfiles >= imt_descr->alloc_nfiles) { + status = more_names(imt_descr, -1); + if (status != 0) { + return status; + } + } + + len = strlen(filename) + 1; + n = imt_descr->nfiles + 1; + imt_descr->files[n-1] = malloc(len * sizeof(char)); + if (imt_descr->files[n-1] == NULL) { + status = 1; + } else { + strcpy(imt_descr->files[n-1], filename); + } + imt_descr->nfiles = n; + + return status; +} + +static int more_names(ImtDescr *imt_descr, int new_maxfiles) { + +/* Allocate or reallocate the `files` member of imt_descr. + If new_maxfiles is less than or equal to zero, it will be set to the + current maximum number of files plus an increment that is defined in + this file as a macro. If the new maximum number of files is less than + or equal to the current maximum, this function will return without + changing anything; otherwise, the `files` member of imt_descr will be + reallocated to increase its length. + +arguments: +ImtDescr *imt_descr i: Pointer to an ImtDescr struct. Memory may be + allocated or reallocated, and the alloc_nfiles + member may be updated. +int new_maxfiles i: The new maximum number of files; the `files` array + of pointers will be reallocated (if necessary) to + be this length. If new_maxfiles is negative or + zero, the current array length will be increased + by a default amount. +function value o: status; 0 is OK, 1 means out of memory. +*/ + + void *x; + int i; + int status = 0; + + if (new_maxfiles <= 0) { + new_maxfiles = imt_descr->alloc_nfiles + DELTA_MAXFILES; + } + if (new_maxfiles > imt_descr->alloc_nfiles) { + x = realloc(imt_descr->files, new_maxfiles * sizeof(char *)); + + if (x == NULL) { + status = 1; + } else { + imt_descr->files = x; + for (i = imt_descr->alloc_nfiles; i < new_maxfiles; i++) { + imt_descr->files[i] = NULL; + } + imt_descr->alloc_nfiles = new_maxfiles; + status = 0; + } + } else { + status = 0; /* don't need to reallocate */ + } + + return status; }