Re: Critique



Andrew Clark wrote:
> I am trying to write a program to open a file and process it
> according to command line parameters. All I need are two simple
> search and replace operations. I invoke the program with an int
> representing a site code and a long representing a date and my
> program should replace some hard-coded text strings in the file
> with the int and the long I pass in. ...

[I've halved the spacing.]

> /* SiteUpdate.c */
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
> #define BUFSIZE 256
> #define SITEMIN 0
> #define SITEMAX 599
> #define STARTDATE 20050401
> #define ENDDATE 29991231

This potentially clashes with reserved EXXXX identifiers from
<errno.h>

> #define IN "SiteUpdate.sql"

#define FIND1 "xxx"
#define FIND2 "yyyyyyyy"

#define FIND1_LEN ((sizeof FIND1) - 1)
#define FIND2_LEN ((sizeof FIND2) - 1)

> int fgetline ( FILE *, char * );

What does this routine buy you over fgets()?

>
> int main ( int argc, char *argv[] )

Why not make this a basic stdin/stdout filter and avoid hardcoding
the input and output files? [The input file at least.]

> {
> int rc = EXIT_FAILURE;
> int site_code = SITEMIN - 1;
> long date = STARTDATE - 1;
>
> if ( 3 == argc )
> {
> site_code = atoi ( argv[1] );

Use the more robust strtoi(argv[1],0,10) in place of atoi.

> if ( site_code < SITEMIN || SITEMAX < site_code )
> {
> fprintf ( stderr, "Site code must be a number between %d and
> %d.\n", SITEMIN, SITEMAX );
> }
> else
> {
> date = atol ( argv[2] );
> if ( date < STARTDATE || ENDDATE < date )
> {
> fprintf ( stderr, "%s\n", "Date must be in the format
> \"YYYYMMDD\" and between" );
> fprintf ( stderr, "%s\n", "today and December 31,
> 2999." );
> }
> else
> {
> char *outfile, code[4];

To add robustness you might do something like...

#define STR(x) #x
#define STRSTR(x) STR(x)

char code[sizeof STRSTR(SITEMAX)];

> FILE *in, *out;
>
> sprintf ( code, "%d", site_code + 400 );
>
> /* subtract 1 for "site" -> "xxx" and */
> /* add one for NUL character */
> outfile = malloc ( strlen ( IN ) - 1 + 1 );
>
> if ( outfile )
> {
> sprintf ( outfile, "%sUpdate.sql", code );

Why is Update.sql a magic literal?

> in = fopen ( IN, "r" );
> out = fopen ( outfile, "w+" );

Why the "+"?

> if ( in && out )
> {
> char buf[BUFSIZE], *p;
> char *temp;
>
> while ( EOF != fgetline ( in, buf ) )
> {
> if ( buf == strstr ( buf, "--" ) )
> {
> /* If the line is a comment, do not */
> /* include it in the final script */
> continue;
> }
> else if ( ( p = strstr ( buf, "xxx" ) ) )
> {
> temp = malloc ( strlen ( buf ) + 1 );
> if ( temp )
> {
> /* Replace "xxx" with site code */
> strcpy ( temp, buf );
> sprintf ( p, "%s", code );
> strcat ( buf, strstr ( temp, "xxx" )
> + strlen ( "xxx" ) );
> fprintf ( out, "%s\n", buf );
>
> free ( temp );
> temp = NULL;
> }

else if ( ( p = strstr ( buf, FIND1 ) ) )
{
fprintf(out, ".*s%s%s\n", (int) (p - buf), buf,
code,
p + FIND1_LEN );
}

> <snip>
> else
> {
> /* Nothing to do, just copy the line */
> fprintf ( out, "%s\n", buf );

Why use a utility routine that strips the newline, only to
put it back manually each time?

> <snip>

You handle comments inconsistently in that a '-- xxx' in the
middle of a line may invoke substitution, but an -- comment
at the very start of a line won't. Also, you ignore /* */
style comments.

I think you can simplify the whole task by scanning character
by character with a simple state machine (ignoring sql comments
as you go). All you need to do is check is whether an 'x' is
followed by two more 'x's, and whether a 'y' is followed by 7
more 'y's. If an 'x' isn't followed by two more, then just
output the number of consecutive 'x's read to that point.

Going off topic, I'm sure you can find existing tools to do
such simple replacements.

Personally, I think even those tools are probably overkill
for the deeper problem at hand.

Since you already have a template file, let's suppose that file
can be abstracted down to...

select 'xxx', 'yyyyyyyy' from dual;

Why not just make your template (Update.sql) file more like...

select '&1', '&2' form dual;

Then your program output is just...

@Update <site> <date>

--
Peter

.



Relevant Pages

  • Re: Critique
    ... > representing a site code and a long representing a date and my ... > with the int and the long I pass in. ... Things will be much clearer if you break operations up. ...
    (comp.lang.c)
  • Re: [MSDOS] Trouble with mouse programming
    ... >> which not only consume a lot of stack but probably also invoke ... All that int 29h does is to invoke the int 10h ... and forgetting a declaration when calling it. ...
    (comp.programming)
  • Re: Tcl crashes and/or hangs in RHEL5?
    ... Compile it into "Invoke", then copy that to "/home/Fred/mydeamon" and name your Tcl code as "/home/Fred/mydeamon.tcl". ... int Tcl_AppInit3{ ... int main(int argc, char * argv) { ...
    (comp.lang.tcl)
  • Critique
    ... site code and a long representing a date and my program should replace ... some hard-coded text strings in the file with the int and the long I pass ... Input file is .\\SiteUpdate.sql"); ...
    (comp.lang.c)
  • Re: what is difference between sizeof and strlen
    ... >>int main; ... >>others are not standard and invoke an undefined behaviour. ... The behavior of void main, for example, is ...
    (comp.lang.c)