/* Function to edit the values of a CVL format picture headers using a normal text editor (i.e. the value of the EDITOR or VISUAL environment variables). The contents of the picture header are printed in a temporary text file which the user then edits. After making any changes to the values, the user exits the editor saving the new version (^X-^F) (or aborting with ^C) and a new CVL format picture file is created with the new header in file by the same name. A backup copy of the old picture file can be saved. Syntax: edhead [-b] pict-file ... The -b option specifies that a backup copy of the image should be put in a file of the same name with ".BAK" appended to it. If no changes are made, then no backup is created. The user can change the comments in the picture header or the the length of comments field. If the two */ #include #include #include #include #include #include #define CLOSE_ALL() { int i; for (i = 3; i < 20; close(i++)); } main (argc, argv) int argc; char *argv[]; {register int index, i, j, new_comment_length; int input=0, output=1, rename (), backup, changed, old_comment_length, x; #define MAX_BUFFER_SIZE 110 #define FILENAME_SIZE 1024 #define COMMENTS_SIZE 1024 char backup_filename[FILENAME_SIZE], temp_filename[MAX_BUFFER_SIZE], command[MAX_BUFFER_SIZE], *getenv (), *comments, new_comments[COMMENTS_SIZE], *calloc (), *c, buffer[FILENAME_SIZE]; FILE *temp_file, *fopen (); struct cvl_p oldarea, temparea; /* Check that one or the other editor environment variables is set */ if (!getenv ("EDITOR") && !getenv ("VISUAL")) {fprintf (stderr, "You must set the environment variable EDITOR or VISUAL to\n"); fprintf (stderr, "the name of the text editor that you use (see 'setenv')\n"); exit (-1); } /* Scan arguments for the -b option */ backup=0; for (i=1; i < argc;) if (*argv[i] == '-') {switch (*(argv[i]+1)) {case 'b': case 'B': backup = 1; break; default: fprintf (stderr, "%s is not a valid option\n"); argc = 1; goto usage; } /* remove option from command arguments */ for (j=i+1; j < argc; j++) argv[j-1] = argv[j]; argc--; } else i++; /* Check to make sure filename arguments are present */ usage: if (argc < 2) {fprintf (stderr, "Usage: %s [-b] ...\n", argv[0]); fprintf (stderr, "Do NOT redirect standard input or output\n"); exit(-2); } /* Loop through filenames in command line, index points to filename */ index = 0; while (++index < argc) {if ((input = open (argv[index], 0)) == -1) {fprintf (stderr, "Can't open '%s' for input\n", argv[index]); exit(-1); } /* Read old header and comments */ ihead (input, &oldarea); if ((comments = calloc (oldarea.cv_lcom + 1, sizeof *comments)) == NULL) {fprintf (stderr, "Can't allocate %d byte(s) for comment buffer\n", oldarea.cv_lcom + 1); exit(-1); } else if (oldarea.cv_lcom && /* Read comments */ oldarea.cv_lcom != read (input, comments, oldarea.cv_lcom)) {fprintf (stderr, "Can't read comments from input file\n"); exit(-1); } else comments[oldarea.cv_lcom] = '\0'; /* Terminate comment string */ old_comment_length = oldarea.cv_lcom; /* Create a temporary file filenname and the command to edit it. */ sprintf (temp_filename, "/tmp/edhead%d.tmp", getpid ()); if (!(c = getenv ("EDITOR"))) c = getenv ("VISUAL"); sprintf (command, "%s %s", c, temp_filename); /* Perform the following loop until the user is satisfied with his changes: write the text file for the header let the user edit it if comments don't agree with length of comments then ask user which to use or if he wants to reedit if so, loop again */ do { /* Create a temporary file with an ASCII representation of the header contents and the comments. If the user specified multiple files on the command line, then tell him/her which file this is. */ if ((temp_file = fopen (temp_filename, "w")) == NULL) {fprintf (stderr, "Can't open temporary file (%s)\n", temp_filename); exit (-1); } /* Print filename for multiple file commands */ if (argc > 2) fprintf (temp_file, "\t\tFile: %s\n", argv[index]); phead (&oldarea, temp_file); for (i=0; i < oldarea.cv_lcom; i++) fprintf (temp_file, "%c", comments[i]); fclose (temp_file); /* Edit the temporary file */ do_command (command); /* Reopen the temporary file to read the changes */ if ((temp_file = fopen (temp_filename, "r")) == NULL) {fprintf (stderr, "Can't open edited header file '%s'\n", temp_filename); dispose_of (temp_file, temp_filename); exit (-1); } /* Read changes to temporary file */ if (argc > 2) {sprintf (buffer, "\\t\\tFile: %s\\n", argv[index]); fscanf (temp_file, buffer); } changed = rhead (&oldarea, temp_file); /* Read comments remaining in the temporary file and count the length */ for (new_comment_length=0; new_comment_length < COMMENTS_SIZE-1 && (new_comments[new_comment_length] = getc(temp_file)) != EOF; new_comment_length++); if (new_comment_length == COMMENTS_SIZE - 1 && getc(temp_file) != EOF) {fprintf (stderr, "Comments are too long (length > %d)\n", COMMENTS_SIZE - 1); dispose_of (temp_file, temp_filename); exit (-1); } new_comments[new_comment_length] = '\0'; dispose_of (temp_file, temp_filename); /* If length of comments is different then value specified in the header, then ask if the user wants to use the length of the text from the edited file as the lenghth of comments */ if (oldarea.cv_lcom != new_comment_length) {fprintf (stderr, "Length of comments in edited file is %d,\n", new_comment_length); fprintf (stderr, "but header comment field says length is %d\n", oldarea.cv_lcom); fprintf (stderr, "Update the header comment field to %d? (y, n, or e for reedit) ", new_comment_length); for (*c = getc (stdin), getc (stdin); *c != 'y' && *c != 'Y' && *c != 'n' && *c != 'N' && *c != 'e' && *c != 'E'; *c = getc (stdin), getc (stdin)) fprintf (stderr, "'%c' is an invalid response\n", *c); if (*c == 'y' || *c == 'Y') oldarea.cv_lcom = new_comment_length; } } while (oldarea.cv_lcom != new_comment_length && (*c == 'e' || *c == 'E')); fprintf (stderr, "Header has %schanged\n", changed || new_comment_length != old_comment_length ? "" : "not "); fprintf (stderr, "Comments have %schanged\n", strcmp (new_comments, comments) ? "" : "not "); /* If there were changes, we must write out the new file */ if (changed || new_comment_length != old_comment_length || strcmp (new_comments, comments)) { /* If the backup option wasn't specified, inquire as to whether or not the user wants a backup */ if (!backup) {fprintf (stderr, "Do you want to keep the old version? (y or n) "); while ((*c = getc (stdin)) != 'y' && *c != 'Y' && *c != 'n' && *c != 'N') {fprintf (stderr, "'%c' is an invalid response\n", *c); getc (stdin); /* extra carriage return */ } getc (stdin); /* extra carriage return */ fprintf (stderr, "\n"); backup = *c == 'y' || *c == 'Y'; } /* If a backup is called for or the size of the file changes then rename the old file to its current name + .BAK */ if (backup || strlen (comments) != strlen (new_comments)) {strncpy (backup_filename, argv[index], FILENAME_SIZE); strncat (backup_filename, ".BAK", FILENAME_SIZE - strlen(backup_filename)); close (input); if (rename (argv[index], backup_filename)) {fprintf (stderr, "Error renaming file from '%s' to '%s'\n", argv[index], backup_filename); exit(-1); } if (backup) fprintf (stderr, "Old version is stored in %s\n", backup_filename); if ((input = open (backup_filename, 0)) == -1) {fprintf (stderr, "Error reopening %s as input\n", backup_filename); exit(-1); } else if ((output = creat (argv[index], 0664)) == -1) {fprintf (stderr, "Can't reopen '%s' for output after renaming\n", argv[index]); exit(-1); } /* Write out the new header and comments */ ohead (output, &oldarea); write (output, new_comments, new_comment_length); /* Copy the input file contents (after the header and comments) to the output file and close both files*/ ihead (input, &temparea); read (input, comments, temparea.cv_lcom); while (i = read (input, new_comments, COMMENTS_SIZE)) write (output, new_comments, i); close (output); close (input); /* remove backup file if not wanted */ if (!backup && (i = unlink (backup_filename))) fprintf (stderr, "Error deleting %s = %d\n", backup_filename, i); } /* if there are changes but the file size remains the same and no backup is to be saved, then we can edit in place. We close the input file and reopen it in read-write mode, and write the new header and comments into it. */ else {close (input); if ((input = open (argv[index], 2)) == -1) {fprintf (stderr, "Can't open %s in read-write mode\n", argv[index]); exit(-1); } ohead (input, &oldarea); write (input, new_comments, new_comment_length); close (input); } } } exit (0); } /* Close and remove a file */ dispose_of (f, filename) FILE *f; char *filename; {int i; fclose (f); if (i = unlink(filename)) fprintf (stderr, "Error deleting %s = %d\n", filename, i); } do_command (com) /* execute a command as a sub-process */ char *com; {int sig, status; char *shell; if (!(shell = getenv ("SHELL"))) shell = "/bin/csh"; if (fork() == 0) { CLOSE_ALL(); for (sig = 0; sig <= NSIG; sig++) signal(sig, SIG_DFL); execl(shell, "csh", "-c", com, 0); perror("Can't exec c-shell"); exit(EX_OSFILE); } while (wait(&status) < 0); if (status) fprintf(stderr, "\n Sub-shell exited with return status: %d\n", status); } /* Print ASCII representaion of header into file */ phead (HD, outfile) struct cvl_p *HD; FILE *outfile; {fprintf (outfile, "long cv_tag;\t %o\t/* 044520 'PI' Identifier for pictures */\n", (HD->cv_tag) & 0177777); fprintf (outfile, " \t %o\t/* 0152103 'CT'+0100000 includes parity bit */\n", (HD->cv_tag>>16) & 0177777); fprintf (outfile, "short cv_lcom;\t %d\t/* length of comments (default 0) */\n", HD->cv_lcom); fprintf (outfile, "short cv_type;\t %d\t/* type of pixel encoding (default 0) */\n", HD->cv_type); fprintf (outfile, "short cv_dims;\t %d\t/* dimensions (default 2) */\n", HD->cv_dims); fprintf (outfile, "short cv_hpls;\t %d\t/* hyperplanes (default 1) */\n", HD->cv_hpls); fprintf (outfile, "short cv_plns;\t %d\t/* planes (default 1) */\n", HD->cv_plns); fprintf (outfile, "short cv_rows;\t %d\t/* rows */\n", HD->cv_rows); fprintf (outfile, "short cv_cols;\t %d\t/* columns */\n", HD->cv_cols); fprintf (outfile, "short cv_bnds;\t %d\t/* bands (default 1) */\n", HD->cv_bnds); fprintf (outfile, "short cv_bp;\t %d\t/* bits per pixel (default 8) */\n", HD->cv_bp); fprintf (outfile, "\n"); fprintf (outfile, "short cv_ebb;\t %d\t/* exponent bits per band (0 or 7) */\n", HD->cv_ebb); fprintf (outfile, "short cv_sbb;\t %d\t/* significant bits per band (no sign bit)*/\n", HD->cv_sbb); fprintf (outfile, "short cv_bb;\t %d\t/* bits per band (often redundant) */\n", HD->cv_bb); fprintf (outfile, "short cv_sbp;\t %d\t/* significant bits per pixel (often 8 or 6)*/\n", HD->cv_sbp); fprintf (outfile, "short cv_ebp;\t %d\t/* exponent bits per pixel (0 or 7) */\n", HD->cv_ebp); fprintf (outfile, "Comment field:\n"); } rhead (HD, infile) struct cvl_p *HD; FILE *infile; {int i, changed=0; fscanf (infile, "long cv_tag;\t %o\t/* 044520 'PI' Identifier for pictures */\n", &i); fscanf (infile, " \t %o\t/* 0152103 'CT'+0100000 includes parity bit */\n", &i); fscanf (infile, "short cv_lcom;\t %d\t/* length of comments (default 0) */\n", &i); HD->cv_lcom = i; fscanf (infile, "short cv_type;\t %d\t/* type of pixel encoding (default 0) */\n", &i); changed = i != HD->cv_type; HD->cv_type = i; fscanf (infile, "short cv_dims;\t %d\t/* dimensions (default 2) */\n", &i); changed = changed || i != HD->cv_dims; HD->cv_dims = i; fscanf (infile, "short cv_hpls;\t %d\t/* hyperplanes (default 1) */\n", &i); changed = changed || i != HD->cv_hpls; HD->cv_hpls = i; fscanf (infile, "short cv_plns;\t %d\t/* planes (default 1) */\n", &i); changed = changed || i != HD->cv_plns; HD->cv_plns = i; fscanf (infile, "short cv_rows;\t %d\t/* rows */\n", &i); changed = changed || i != HD->cv_rows; HD->cv_rows = i; fscanf (infile, "short cv_cols;\t %d\t/* columns */\n", &i); changed = changed || i != HD->cv_cols; HD->cv_cols = i; fscanf (infile, "short cv_bnds;\t %d\t/* bands (default 1) */\n", &i); changed = changed || i != HD->cv_bnds; HD->cv_bnds = i; fscanf (infile, "short cv_bp;\t %d\t/* bits per pixel (default 8) */\n", &i); changed = changed || i != HD->cv_bp; HD->cv_bp = i; fscanf (infile, "\n"); fscanf (infile, "short cv_ebb;\t %d\t/* exponent bits per band (0 or 7) */\n", &i); changed = changed || i != HD->cv_ebb; HD->cv_ebb = i; fscanf (infile, "short cv_sbb;\t %d\t/* significant bits per band (no sign bit)*/\n", &i); changed = changed || i != HD->cv_sbb; HD->cv_sbb = i; fscanf (infile, "short cv_bb;\t %d\t/* bits per band (often redundant) */\n", &i); changed = changed || i != HD->cv_bb; HD->cv_bb = i; fscanf (infile, "short cv_sbp;\t %d\t/* significant bits per pixel (often 8 or 6)*/\n", &i); changed = changed || i != HD->cv_sbp; HD->cv_sbp = i; fscanf (infile, "short cv_ebp;\t %d\t/* exponent bits per pixel (0 or 7) */\n", &i); changed = changed || i != HD->cv_ebp; HD->cv_ebp = i; /* read one fewer character than in phead because of character look-ahead in fscanf, then grab it with getc */ fscanf (infile, "Comment field:"); getc (infile); return (changed); }