/***************************************************************************** Read a single log file. *****************************************************************************/ void file_read(int fd, short int flnm, char *fnam, FILE *strm, const geoid_height_t *gdhtp, const status_t* status, const cmdlnopts_t *cmdopt) { logfile_t lgfl; #if !defined(FILENAME_DATE_PTR) date_time_t dt; #endif gps_fix_t *gfxp = NULL; float *gcrp = NULL; char *pstr = ""; int fn; if (cmdopt->vflg) printf("Requesting metadata for file %4d\n", flnm); /* Read metadata for file number flnm */ if (get_file_info(fd, flnm, &lgfl) < 0) { fprintf(stderr,"rtkgps: Error reading information for file %d [%s]\n", flnm, gcstrerror(rcerrno)); free(fnam); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(5); } /* If memory is allocated for the file name, the output path is a destination directory. Construct a standard file path with the directory as a base, and open the file for writing */ if (fnam != NULL) { #if !defined(FILENAME_DATE_PTR) if (get_file_start_time(fd, &lgfl, &dt) != 1) { fprintf(stderr,"rtkgps: Error reading initial time for file %d [%s]\n", flnm, gcstrerror(rcerrno)); free(fnam); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(5); } #endif /* Add filename postfix to indicate file is not complete (data still being captured). */ if (flnm == status->nfile-1) pstr = "_part"; /* Construct file name */ #if defined(FILENAME_DATE_PTR) sprintf(fnam, "%s/%8.8s_%06x%s.%s", cmdopt->dsts, lgfl.date, lgfl.memp, pstr, (cmdopt->nflg)?"rngl":"nmea"); #else sprintf(fnam, "%s/%8.8sT%6.6sZ%s.%s", cmdopt->dsts, lgfl.date, dt.time, pstr, (cmdopt->nflg)?"rngl":"nmea"); #endif /* Skip existing files if requested */ if (cmdopt->uflg && is_nzsregfile(fnam) && flnm != status->nfile-1) { if (cmdopt->vflg) printf("Skipping download for file %4d\n", flnm); return; } /* Create backup of output file if it already exists */ if (file_backup(fnam) != 0) { fprintf(stderr,"rtkgps: Error creating backup of file %s\n", fnam); free(fnam); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(3); } /* Attempt to open file */ if ((strm = fopen(fnam, "w")) == NULL) { fprintf(stderr,"rtkgps: Error opening output file %s [%s]\n", fnam, gcstrerror(rcerrno)); free(fnam); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(3); } } /* Allocate memory for the log file data */ if ((gfxp = malloc(lgfl.nfix*sizeof(gps_fix_t))) == NULL) { fprintf(stderr,"rtkgps: Error allocating memory\n"); free(fnam); if (fnam != NULL) fclose(strm); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(2); } #ifdef GEOIDCOR /* If necessary, allocate memory for geoid correction values */ if (lgfl.fxtyp > 0 && gdhtp->filep != NULL) { if ((gcrp = malloc(lgfl.nfix*sizeof(float))) == NULL) { fprintf(stderr,"rtkgps: Error allocating memory\n"); free(fnam); if (fnam != NULL) fclose(strm); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(2); } } #endif if (cmdopt->vflg) printf("Requesting content of file %4d\n", flnm); /* Read the log file data */ fn = get_file_data(fd, &lgfl, gfxp); if (fn < 0) { fprintf(stderr,"rtkgps: Error reading file %d [%s]\n", flnm, gcstrerror(rcerrno)); /* Remove empty file so that it isn't skipped (with -u flag) on read restart */ if (fnam != NULL) remove(fnam); free(gcrp); free(gfxp); free(fnam); if (fnam != NULL) fclose(strm); outlog_enable(fd, status->gpsms, cmdopt); coms_close(fd, cmdopt); exit(5); } #ifdef GEOIDCOR if (gcrp != NULL) { const double dgrd = 360.0/(2*M_PI); unsigned short n; if (cmdopt->vflg) printf("Computing geoid altitude corrections\n"); for (n = 0; n < lgfl.nfix; n++) { gcrp[n] = geoid_calc_correction(gdhtp, dgrd*gfxp[n].lat, dgrd*gfxp[n].lng); } } #endif /* Print the log file data to the output stream */ if (cmdopt->nflg) { if (fnam != NULL) print_hdr_native(strm); print_log_native(strm, &lgfl, gfxp, gcrp); } else { if (fnam != NULL) print_hdr_nmea(strm, cmdopt->btas); print_log_nmea(strm, &lgfl, gfxp, gcrp); } /* Free memory for geoid correction values */ free(gcrp); /* Free memory for the log file data */ free(gfxp); /* If memory is allocated for the file name, the output path is a directory and the output stream was opened in this function, so the stream should be closed here */ if (fnam != NULL) fclose(strm); }
/***************************************************************************** Perform rtkgps read command. *****************************************************************************/ void cmd_read(cmdlnopts_t *cmdopt) { int fd; status_t status; geoid_height_t gdht = {0,0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,NULL,NULL}; FILE *strm = NULL; char *fnam = NULL; short int n; #ifdef GEOIDCOR /* Set up geoid correction data structure */ if (geoid_calc_open(GGRDPATH, &gdht) == -1) { fprintf(stderr, "rtkgps: Warning: could not access geoid correction " "data\n"); } #endif /* Open communication with logger */ fd = coms_open(cmdopt); /* Set up progress bar if requested */ if (cmdopt->pflg) { #ifdef HAVE_TIOCGWINSZ struct winsize ws; if (ioctl(1, TIOCGWINSZ, (void *)&ws) == 0) prgbrfp = 1; else if (ioctl(2, TIOCGWINSZ, (void *)&ws) == 0) prgbrfp = 2; #else prgbrfp = 1; #endif if (prgbrfp) gdpfp = get_data_progress; } if (cmdopt->vflg) printf("Requesting logger status information\n"); /* Read logger status */ status_read(fd, &status, cmdopt); /* Handle unspecified ends of file number range */ if (cmdopt->fnmn == -1) cmdopt->fnmn = 0; if (cmdopt->fnmx == -1) cmdopt->fnmx = status.nfile-1; /* Check for minimum file number out of range */ if (cmdopt->fnmn >= status.nfile) { fprintf(stderr,"rtkgps: Requested file number(s) all invalid\n"); coms_close(fd, cmdopt); exit(1); } /* Check for maximum file number out of range */ if (cmdopt->fnmx >= status.nfile) { cmdopt->fnmx = status.nfile-1; fprintf(stderr, "rtkgps: Warning: reduced maximum requested file " "number to valid range\n"); } outlog_disable(fd, cmdopt); /* The complicated handling of output files, split between this function and file_read, is due to the following output policy: • If no output file is specified, selected logfiles are written to stdout • If the specified output file is a regular file, all selected logfiles are written to that file • If the specified output file is a directory, each selected logfile is written to a unique file within that directory */ if (cmdopt->dsts == NULL) strm = stdout; /* If output flag not specified, output stream is stdout */ else { /* If output flag is specified, determine whether it is a directory */ if (is_directory(cmdopt->dsts)) { /* If specified output path is a directory, allocate memory for constructing the full output filename */ if ((fnam = malloc(strlen(cmdopt->dsts) + 32)) == NULL) { fprintf(stderr,"rtkgps: Error allocating memory\n"); outlog_enable(fd, status.gpsms, cmdopt); coms_close(fd, cmdopt); exit(2); } } else { /* If specified output path is not a directory, open the file for writing after creating a backup if the file already exists. */ if (file_backup(cmdopt->dsts) != 0) { fprintf(stderr,"rtkgps: Error creating backup of file %s\n", cmdopt->dsts); outlog_enable(fd, status.gpsms, cmdopt); coms_close(fd, cmdopt); exit(3); } if ((strm = fopen(cmdopt->dsts, "w")) == NULL) { fprintf(stderr,"rtkgps: Error opening output file %s [%s]\n", cmdopt->dsts, gcstrerror(rcerrno)); outlog_enable(fd, status.gpsms, cmdopt); coms_close(fd, cmdopt); exit(3); } } } if (strm != NULL) { /* Write output file header */ if (cmdopt->nflg) print_hdr_native(strm); else print_hdr_nmea(strm, cmdopt->btas); } /* Reset warning function message records */ warning(NULL, 0, NULL); /* Read requested range of log files */ for (n = cmdopt->fnmn; n <= cmdopt->fnmx; n++) { char nstr[8]; /* If progress bar requested and verbose output not enabled, set up prefix displaying file number */ if (cmdopt->pflg && !cmdopt->vflg) { sprintf(nstr, "%4d ", n); text_progress_bar(0.0, nstr); } file_read(fd, n, fnam, strm, &gdht, &status, cmdopt); /* Reset warning function message records */ warning(NULL, 0, NULL); } /* Free memory allocated for file name */ free(fnam); outlog_enable(fd, status.gpsms, cmdopt); /* Close ommunication with logger */ coms_close(fd, cmdopt); #ifdef GEOIDCOR /* Destroy geoid correction data structure */ geoid_calc_close(&gdht); #endif }
/* * file_write -- * Write the file to disk. Historic vi had fairly convoluted * semantics for whether or not writes would happen. That's * why all the flags. * * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int)); */ int file_write(SCR *sp, MARK *fm, MARK *tm, char *name, int flags) { enum { NEWFILE, OLDFILE } mtype; struct stat sb; EXF *ep; FILE *fp; FREF *frp; MARK from, to; size_t len; u_long nlno, nch; int fd, nf, noname, oflags, rval; char *p, *s, *t, buf[MAXPATHLEN + 64]; const char *msgstr; ep = sp->ep; frp = sp->frp; /* * Writing '%', or naming the current file explicitly, has the * same semantics as writing without a name. */ if (name == NULL || !strcmp(name, frp->name)) { noname = 1; name = frp->name; } else noname = 0; /* Can't write files marked read-only, unless forced. */ if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) { msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? "244|Read-only file, not written; use ! to override" : "245|Read-only file, not written"); return (1); } /* If not forced, not appending, and "writeany" not set ... */ if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { /* Don't overwrite anything but the original file. */ if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) && !stat(name, &sb)) { msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ? "246|%s exists, not written; use ! to override" : "247|%s exists, not written"); return (1); } /* * Don't write part of any existing file. Only test for the * original file, the previous test catches anything else. */ if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) { msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? "248|Partial file, not written; use ! to override" : "249|Partial file, not written"); return (1); } } /* * Figure out if the file already exists -- if it doesn't, we display * the "new file" message. The stat might not be necessary, but we * just repeat it because it's easier than hacking the previous tests. * The information is only used for the user message and modification * time test, so we can ignore the obvious race condition. * * One final test. If we're not forcing or appending the current file, * and we have a saved modification time, object if the file changed * since we last edited or wrote it, and make them force it. */ if (stat(name, &sb)) mtype = NEWFILE; else { if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) && ((F_ISSET(ep, F_DEVSET) && (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) || sb.st_mtime != ep->mtime)) { msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ? "250|%s: file modified more recently than this copy; use ! to override" : "251|%s: file modified more recently than this copy"); return (1); } mtype = OLDFILE; } /* Set flags to create, write, and either append or truncate. */ oflags = O_CREAT | O_WRONLY | (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC); /* Backup the file if requested. */ if (!opts_empty(sp, O_BACKUP, 1) && file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE)) return (1); /* Open the file. */ SIGBLOCK; if ((fd = open(name, oflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) { msgq_str(sp, M_SYSERR, name, "%s"); SIGUNBLOCK; return (1); } SIGUNBLOCK; /* Try and get a lock. */ if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL) msgq_str(sp, M_ERR, name, "252|%s: write lock was unavailable"); #if __linux__ /* * XXX * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set). * This bug is fixed in libc 4.6.x. * * This code works around this problem for libc 4.5.x users. * Note that this code is harmless if you're using libc 4.6.x. */ if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) { msgq(sp, M_SYSERR, name); return (1); } #endif /* * Use stdio for buffering. * * XXX * SVR4.2 requires the fdopen mode exactly match the original open * mode, i.e. you have to open with "a" if appending. */ if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) { msgq_str(sp, M_SYSERR, name, "%s"); (void)close(fd); return (1); } /* Build fake addresses, if necessary. */ if (fm == NULL) { from.lno = 1; from.cno = 0; fm = &from; if (db_last(sp, &to.lno)) return (1); to.cno = 0; tm = &to; } rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0); /* * Save the new last modification time -- even if the write fails * we re-init the time. That way the user can clean up the disk * and rewrite without having to force it. */ if (noname) { if (stat(name, &sb)) time(&ep->mtime); else { F_SET(ep, F_DEVSET); ep->mdev = sb.st_dev; ep->minode = sb.st_ino; ep->mtime = sb.st_mtime; } } /* * If the write failed, complain loudly. ex_writefp() has already * complained about the actual error, reinforce it if data was lost. */ if (rval) { if (!LF_ISSET(FS_APPEND)) msgq_str(sp, M_ERR, name, "254|%s: WARNING: FILE TRUNCATED"); return (1); } /* * Once we've actually written the file, it doesn't matter that the * file name was changed -- if it was, we've already whacked it. */ F_CLR(frp, FR_NAMECHANGE); /* * If wrote the entire file, and it wasn't by appending it to a file, * clear the modified bit. If the file was written to the original * file name and the file is a temporary, set the "no exit" bit. This * permits the user to write the file and use it in the context of the * filesystem, but still keeps them from discarding their changes by * exiting. */ if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) { F_CLR(ep, F_MODIFIED); if (F_ISSET(frp, FR_TMPFILE)) { if (noname) F_SET(frp, FR_TMPEXIT); else F_CLR(frp, FR_TMPEXIT); } } p = msg_print(sp, name, &nf); switch (mtype) { case NEWFILE: msgstr = msg_cat(sp, "256|%s: new file: %lu lines, %lu characters", NULL); len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); break; case OLDFILE: msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ? "315|%s: appended: %lu lines, %lu characters" : "257|%s: %lu lines, %lu characters", NULL); len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); break; default: abort(); } /* * There's a nasty problem with long path names. Cscope and tags files * can result in long paths and vi will request a continuation key from * the user. Unfortunately, the user has typed ahead, and chaos will * result. If we assume that the characters in the filenames only take * a single screen column each, we can trim the filename. */ s = buf; if (len >= sp->cols) { for (s = buf, t = buf + strlen(p); s < t && (*s != '/' || len >= sp->cols - 3); ++s, --len); if (s == t) s = buf; else { *--s = '.'; /* Leading ellipses. */ *--s = '.'; *--s = '.'; } } msgq(sp, M_INFO, s); if (nf) FREE_SPACE(sp, p, 0); return (0); }