/*----------------------------------------------------------------------*\ | DBFrename: Rename fields in a DBF file. | | | | ArcView is able to read and display a shapefile whose corresponding | | dbf has two fields with the same name. However ArcExplorer is not, | | and ArcIMS cannot create a map service using a shapefile whose DBF | | field names are not unique. Consequently a method to change field | | names in a dbf is needed. | | | | This program reads the dbf named on the command line, and attempts | | to change the field names. If an old and new name are specified on | | the command line following the DBF file name, it replaces the old | | field name with the new one. If these names are not specified, it | | prints all of the field names and attributes, and allows the user to | | change the names of any of the fields, within the constraints of the | | DBF format. | | | | If the old name specified is a number, it will be interpreted as the | | index of the field whose name should be replaced by new_name. | | | | Peter N. Schweitzer (U.S. Geological Survey, Reston VA 20192) | \*----------------------------------------------------------------------*/ #include #include #include #define version_id "1.2" #define version_date "5-Oct-2005" #define version_author "Peter N. Schweitzer" #ifdef _WIN32 #define mode "rb+" #else #define mode "r+" #endif static int no_space (char *s) { while (*s) if (isspace(*s)) return (0); else s++; return (1); } static int no_punct (char *s) { /* But allow underscore */ while (*s) if (ispunct(*s) && *s != '_') return (0); else s++; return (1); } int main (int argc, char *argv[]) { char *input_file = NULL; char *old_name = NULL; char *new_name = NULL; FILE *fp; unsigned char header[32]; int i,j,n,fc; unsigned char *buffer; unsigned char name[16], *s; char *e; unsigned char **field_name; unsigned char string[16]; int which,done,changed; if (argc > 1) { input_file = argv[1]; if (fp = fopen (input_file,mode)) { fread (header,32,1,fp); n = header[8] + 256*header[9]; fc = (n - 32)/32; printf ("DBFrename %s %s %s\n",version_id,version_date,version_author); printf ("Number of fields: %d\n",fc); if (field_name = (unsigned char **) malloc (fc * sizeof (char *))) { if (buffer = (unsigned char *) malloc (n-32)) { fread (buffer,n-32,1,fp); for (i=0; i < fc; i++) { s = buffer + i * 32; field_name[i] = s; memcpy (name,s,10); name[10] = 0; printf ("%d: %10s",i,name); printf (" %c",s[11]); printf (" %4d",s[12] + 256*(s[13] + 256*(s[14] + 256*s[15]))); printf (" %2d",s[16]); printf (" %2d",s[17]); printf ("\n"); } /*--------------------------------------------------*\ | Non-interactive mode: just try to make the change | dbfrename input_file old_name new name \*--------------------------------------------------*/ if (argc > 3) { old_name = argv[2]; new_name = argv[3]; changed = 0; if (isdigit (*old_name)) which = atoi (old_name); else for (which=0; which < fc; which++) if (stricmp (old_name,field_name[which]) == 0) break; if (which != fc) /* Name doesn't exist? */ if (strlen (new_name) < 11) /* Name too long? */ if (no_space (new_name)) /* Name contains whitespace? */ if (no_punct (new_name)) { /* Name contains punctuation? */ for (j=0; j < fc; j++) if (j != which) if (stricmp (new_name,field_name[j]) == 0) break; if (j == fc) { /* Name same as another field? */ strcpy (field_name[which],new_name); printf ("Ok, new name for field %d is %s\n",which,new_name); changed = 1; } else printf ("Error: new name matches an existing field %d.\n",j); } else printf ("Error: field names cannot contain punctuation.\n"); else printf ("Error: field names cannot contain whitespace.\n"); else printf ("Error: field names must be 10 characters or less.\n"); else printf ("Error: %s contains no field named %s.\n",input_file,old_name); if (changed) { fseek (fp,0L,SEEK_SET); fwrite (header,1,32,fp); fwrite (buffer,1,n-32,fp); fflush (fp); } exit (changed == 0); } /*--------------------------------------------------*\ | Ask user \*--------------------------------------------------*/ changed = 0; done = 0; while (!done) { printf ("Enter a field number, blank when done.\n"); printf ("Rename which field?: "); fgets (string,16,stdin); which = (int) strtol (string,&e,0); if (e != (char *) string) { printf ("Old name is %s; Enter new name: ",field_name[which]); fgets (string,16,stdin); /* Trim leading and trailing spaces */ s = string + strlen (string) - 1; if (*s == '\n') *s-- = 0; if (*s == '\r') *s-- = 0; while (s >= string && isspace(*s)) *s-- = 0; for (s=string; *s && isspace(*s); s++); strcpy (name,s); /* Check for problems */ if (*name) /* Name empty? */ if (strlen (name) < 11) /* Name too long? */ if (no_space (name)) /* Name contains whitespace? */ if (no_punct (name)) { /* Name contains punctuation? */ for (j=0; j < fc; j++) if (j != which) if (stricmp (name,field_name[j]) == 0) break; if (j == fc) { /* Name same as another field? */ strcpy (field_name[which],name); printf ("Ok, new name for field %d is %s\n",which,name); changed = 1; } else printf ("Sorry, that name matches field %d. Name not changed.\n",j); } else printf ("Sorry, field names cannot contain punctuation. Name not changed.\n"); else printf ("Sorry, field name cannot contain whitespace. Name not changed.\n"); else printf ("Sorry, new name must be 10 characters or less. Name not changed.\n"); else printf ("Sorry, no new name given. Name not changed.\n"); } else done = 1; } /* If changes were made, show user and confirm save. */ if (changed) { printf ("Field names as modified:\n"); for (i=0; i < fc; i++) printf ("%d: %10s\n",i,field_name[i]); printf ("Save these in the file? [y/N]: "); fgets (string,16,stdin); if (toupper(*string) == 'Y') { fseek (fp,0L,SEEK_SET); fwrite (header,1,32,fp); fwrite (buffer,1,n-32,fp); fflush (fp); } } free (buffer); } else { fprintf (stderr,"Error: could not allocate space for field header info (%d bytes)\n",n-32); fclose (fp); exit (1); } free (field_name); } else { fprintf (stderr,"Error: could not allocate space for field name pointers (%d bytes)\n",fc*sizeof(char *)); fclose (fp); exit (1); } fclose (fp); } else { printf ("Error: could not open input file %s\n",input_file); exit (1); } } else { printf ("DBFrename %s %s %s\n",version_id,version_date,version_author); printf (" Rename fields in a DBF file. Useful for fixing identical field names.\n"); printf ("Usage: %s input_file.dbf [old_name|index new_name]\n",argv[0]); printf (" If old and new names are not specified, this program carries out a dialog\n"); printf (" with the user at the command line.\n"); } exit (0); } /*----------------------------------------------------------------------*\ \*----------------------------------------------------------------------*/