char * parse_name (char const *s, int strip_leading, char const **endp) { char *ret; while (ISSPACE ((unsigned char) *s)) s++; if (*s == '"') ret = parse_c_string (s, endp); else { char const *t; for (t = s; *t && ! ISSPACE ((unsigned char) *t); t++) /* do nothing*/ ; ret = savebuf (s, t - s + 1); ret[t - s] = 0; if (endp) *endp = t; } if (! strip_leading_slashes (ret, strip_leading)) { free (ret); ret = NULL; } return ret; }
/*---------------------------------------------------------------*/ int main(int argc, char **argv) { SDCMFILEINFO **sdfi_list; SDCMFILEINFO *sdfi=NULL; int nlist; int NRuns; int nthfile; char *fname, *psname, *protoname; int PrevRunNo; Progname = argv[0] ; argc --; argv++; ErrorInit(NULL, NULL, NULL) ; DiagInit(NULL, NULL, NULL) ; if (argc == 0) usage_exit(); cmdline = argv2cmdline(argc,argv); parse_commandline(argc, argv); check_options(); uname(&uts); getcwd(cwd,2000); fprintf(stdout,"%s\n",vcid); fprintf(stdout,"cwd %s\n",cwd); fprintf(stdout,"cmdline %s\n",cmdline); fprintf(stdout,"sysname %s\n",uts.sysname); fprintf(stdout,"hostname %s\n",uts.nodename); fprintf(stdout,"machine %s\n",uts.machine); fprintf(stdout,"user %s\n",VERuser()); strip_leading_slashes(sdicomdir); /* open the output file, or set output stream to stdout */ if (outfile != NULL) { outstream = fopen(outfile,"w"); if (outstream == NULL) { fprintf(stderr,"ERROR: could not open %s for writing\n",outfile); exit(1); } } else outstream = stdout; /* Get all the Siemens DICOM files from this directory */ fprintf(stderr,"INFO: scanning path to Siemens DICOM DIR:\n %s\n", sdicomdir); sdfi_list = ScanSiemensDCMDir(sdicomdir, &nlist); if (sdfi_list == NULL) { fprintf(stderr,"ERROR: scanning directory %s\n",sdicomdir); exit(1); } printf("INFO: found %d Siemens files\n",nlist); printf("%s\n",sdfi_list[0]->NumarisVer); tmpstring = sdcmExtractNumarisVer(sdfi_list[0]->NumarisVer, &Maj, &Min, &MinMin); if (tmpstring == NULL) free(tmpstring); else { if ((Min == 1 && MinMin <= 6) && Maj < 4) { // This should only be run for pretty old data. I've lost // track as to which versions should do this. With Maj<4, // I'm pretty sure that this section of code will never // be run. It might need to be run with version 4VA16 // and earlier. Note: this same code is in DICOMRead.c printf("Computing TR with number of slices\n"); TRSlice = 1; } } /* Sort the files by Series, Slice Position, and Image Number */ printf("Sorting\n"); fflush(stdout); fflush(stderr); SortSDCMFileInfo(sdfi_list,nlist); /* Assign run numbers to each file (count number of runs)*/ if (sortbyrun) { fprintf(stderr,"Assigning Run Numbers\n"); fflush(stderr); NRuns = sdfiAssignRunNo2(sdfi_list, nlist); if (NRuns == 0) { fprintf(stderr,"ERROR: sorting runs\n"); fflush(stderr); exit(1); } } PrevRunNo = -1; for (nthfile = 0; nthfile < nlist; nthfile++) { sdfi = sdfi_list[nthfile]; if (summarize && PrevRunNo == sdfi->RunNo) continue; PrevRunNo = sdfi->RunNo; sdfi->RepetitionTime /= 1000.0; // Old version of Siemens software had reptime as time between // slices. New has reptime as time between volumes. // Code (above) looks at version and does the right thing. if (sdfi->IsMosaic && TRSlice) sdfi->RepetitionTime *= sdfi->VolDim[2]; fname = fio_basename(sdfi->FileName,NULL); psname = strip_white_space(sdfi->PulseSequence); protoname = strip_white_space(sdfi->ProtocolName); fprintf(outstream, "%4d %s %3d %d %4d %d %3d %3d %3d %3d %8.4f %8.4f %s\n", nthfile+1, fname, sdfi->SeriesNo, sdfi->ErrorFlag, sdfi->ImageNo, sdfi->IsMosaic, sdfi->VolDim[0], sdfi->VolDim[1], sdfi->VolDim[2], sdfi->NFrames, sdfi->RepetitionTime, sdfi->EchoTime, protoname); fflush(outstream); free(fname); free(psname); free(protoname); } while (nlist--) { // free strings free(sdfi_list[nlist]->FileName); free(sdfi_list[nlist]->StudyDate); free(sdfi_list[nlist]->StudyTime); free(sdfi_list[nlist]->PatientName); free(sdfi_list[nlist]->SeriesTime); free(sdfi_list[nlist]->AcquisitionTime); free(sdfi_list[nlist]->ScannerModel); free(sdfi_list[nlist]->NumarisVer); free(sdfi_list[nlist]->PulseSequence); free(sdfi_list[nlist]->ProtocolName); free(sdfi_list[nlist]->PhEncDir); // free(sdfi_list[nlist]); } free(sdfi_list); if (outfile != NULL) fclose(outstream); fflush(stdout); fflush(stderr); exit(0); return(0); }
void fetchname (char const *at, int strip_leading, char **pname, char **ptimestr, struct timespec *pstamp) { char *name; const char *t; char *timestr = NULL; struct timespec stamp; stamp.tv_sec = -1; stamp.tv_nsec = 0; while (ISSPACE ((unsigned char) *at)) at++; if (debug & 128) say ("fetchname %s %d\n", at, strip_leading); if (*at == '"') { name = parse_c_string (at, &t); if (! name) { if (debug & 128) say ("ignoring malformed filename %s\n", quotearg (at)); return; } } else { for (t = at; *t; t++) { if (ISSPACE ((unsigned char) *t)) { /* Allow file names with internal spaces, but only if a tab separates the file name from the date. */ char const *u = t; while (*u != '\t' && ISSPACE ((unsigned char) u[1])) u++; if (*u != '\t' && (strchr (u + 1, pstamp ? '\t' : '\n'))) continue; break; } } name = savebuf (at, t - at + 1); name[t - at] = 0; } /* If the name is "/dev/null", ignore the name and mark the file as being nonexistent. The name "/dev/null" appears in patches regardless of how NULL_DEVICE is spelled. */ if (strcmp (name, "/dev/null") == 0) { free (name); if (pstamp) { pstamp->tv_sec = 0; pstamp->tv_nsec = 0; } return; } /* Ignore the name if it doesn't have enough slashes to strip off. */ if (! strip_leading_slashes (name, strip_leading)) { free (name); return; } if (ptimestr) { char const *u = t + strlen (t); if (u != t && *(u-1) == '\n') u--; if (u != t && *(u-1) == '\r') u--; timestr = savebuf (t, u - t + 1); timestr[u - t] = 0; } if (*t != '\n') { if (! pstamp) { free (name); free (timestr); return; } if (set_time | set_utc) get_date (&stamp, t, &initial_time); else { /* The head says the file is nonexistent if the timestamp is the epoch; but the listed time is local time, not UTC, and POSIX.1 allows local time offset anywhere in the range -25:00 < offset < +26:00. Match any time in that range. */ const struct timespec lower = { -25L * 60 * 60 }, upper = { 26L * 60 * 60 }; if (get_date (&stamp, t, &initial_time) && timespec_cmp (stamp, lower) > 0 && timespec_cmp (stamp, upper) < 0) { stamp.tv_sec = 0; stamp.tv_nsec = 0; } } } free (*pname); *pname = name; if (ptimestr) { free (*ptimestr); *ptimestr = timestr; } if (pstamp) *pstamp = stamp; }
/*- * The logic here for -C <dir> attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) sprintf(bsdtar->pending_chdir, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static const char* strip_leading_slashes(const char *p) { /* Remove leading "/../", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && ( p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ } return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); } if (archive_entry_hardlink(entry)) { r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } } if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { const char *linkname = archive_entry_hardlink(entry); name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (linkname != NULL) { linkname = strip_components(linkname, bsdtar->strip_components); if (linkname == NULL) return (1); archive_entry_copy_hardlink(entry, linkname); } } /* By default, don't write or restore absolute pathnames. */ if (!bsdtar->option_absolute_paths) { const char *rp, *p = name; int slashonly = 1; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; slashonly = 0; } do { rp = p; /* Remove leading drive letter from archives created * on Windows. */ if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; slashonly = 0; } p = strip_leading_slashes(p); } while (rp != p); if (p != name && !bsdtar->warned_lead_slash) { /* Generate a warning the first time this happens. */ if (slashonly) lafe_warnc(0, "Removing leading '%c' from member names", name[0]); else lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } /* Special case: Stripping everything yields ".". */ if (*p == '\0') name = "."; else name = p; p = archive_entry_hardlink(entry); if (p != NULL) { rp = strip_leading_slashes(p); if (rp == '\0') return (1); if (rp != p) { char *linkname = strdup(rp); archive_entry_copy_hardlink(entry, linkname); free(linkname); } } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Safely replace name in archive_entry. */ if (name != archive_entry_pathname(entry)) { char *q = strdup(name); archive_entry_copy_pathname(entry, q); free(q); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); }