On Sun, Nov 15, 2009 at 12:32:35AM +0100, Frans Pop wrote:
> Attached a new version of the most important changes, including 
> improvements from most of your comments. It even seems to work the same as 
> the previous version :-)

Great. :)
 
> Could you go through it once more and maybe look closely at freeing release 
> and other vars again?

Here we go:

> diff --git a/packages/choose-mirror/choose-mirror.c 
> b/packages/choose-mirror/choose-mirror.c
> [...]
> +static int validate_codename(int *status, const char *codename, const char 
> *suite) {
> [...]
> +     struct release_t release;
> +     int ret = 0;
> +
> +     if (get_release(&release, codename)) {
> +             *status = release.status;
> +             if (strcmp(release.suite, suite) != 0)
> +                     *status &= ~IS_VALID;
> +             ret = 1;
> +     }
> +
> +     free(release.name);
> +     free(release.suite);

Not obvious, but this is fine as get_release() starts by setting both to
NULL.  I tend to explicitely start this kind of code by using something
like:
  memset(&release, 0, sizeof (struct release_t))
But that's a matter of choice...

> [...]
> +             /* Check if release can also be gotten using codename */
> +             if ((release->status & IS_VALID) && release->name != NULL &&
> +                 !(release->status & GET_CODENAME)) {
> +                     int cn_status;
> +                     if (validate_codename(&cn_status, release->name, name)) 
> {
> +                             if (cn_status & IS_VALID) {
> +                                     release->status |= (cn_status & 
> GET_CODENAME);
> +                             } else {
> +                                     release->status &= ~IS_VALID;
> +                                     log_invalid_release(name, "Codename");
> +                             }
> +                     }

How about moving the if block inside validate_codename()?  Thus you
would be able to pass whole release that needs to be updated. Once
again, a matter of taste, though.

> [...]
> +     return (release->name != NULL);

No need for the parenthesis.

> [...]
> +static int find_releases(void) {
> [...]

Looks fine.  I would probably not do it that way, but it looks fine.
One thing that is not usually done is the full copy of 'struct
release_t' from 'release' to 'releases'. But that's fine as it is.

> [...]
>  static int choose_suite(void) {
> [...]
> +     char **choices_c, **choices, *list;
> [...]
> +     choices_c = malloc(MAXRELEASES * sizeof(char*));
> +     choices = malloc(MAXRELEASES * sizeof(char*));

Once again, candidates being statically allocated.  And as those arrays
are only used to build a list suitable for debconf_list, you can even
declare the char 'const'.

> +     for (i=0; releases[i].name != NULL; i++) {
> +             char *name;
> +
> +             if (releases[i].status & GET_SUITE)
> +                     name = strdup(releases[i].suite);
> +             else
> +                     name = strdup(releases[i].name);
> +
> +             choices_c[i] = strdup(name);
> +             if (strcmp(name, releases[i].name) != 0)
> +                     asprintf(&choices[i], "%s - %s", releases[i].name, 
> name);
> +             else
> +                     choices[i] = strdup(name);
> +             if (releases[i].status & IS_DEFAULT) {
> +                     debconf_set(debconf, DEBCONF_BASE "suite", name);
> +                     have_default = 1;
> +             }
> +
> +             free(name);
> +     }
>
> > There is probably extra strdup() as well here.
> 
> Not that I can see; 'name' is needed I think. What were you thinking of?
> Can I maybe just do 'choices_c[i] = name' and 'choices[i] = name' instead 
> of duplicating the string?

Exactly. 

> But then I can't free name where I do now, right?

Right. But those strings need to be released when cleaning up
'releases', see below.
 
> +
> +     choices_c[i] = NULL;
> +     list = debconf_list(choices_c);
> +     debconf_subst(debconf, DEBCONF_BASE "suite", "CHOICES-C", list);
> +     choices[i] = NULL;
> +     list = debconf_list(choices);

Previously allocated list has been lost: memory leak. Should be free()'d
after given to debconf_subst().

> [...]
> +int set_codename (void) {
> [...]
> +             for (i=0; releases[i].name != NULL; i++) {
> +                     if (strcmp(releases[i].name, suite) == 0 ||
> +                         strcmp(releases[i].suite, suite) == 0) {
> +                             char *codename;
> +
> +                             if (releases[i].status & GET_CODENAME)
> +                                     codename = releases[i].name;
> +                             else
> +                                     codename = releases[i].suite;
> +                             debconf_set(debconf, DEBCONF_BASE "codename", 
> codename);
> +                             di_log(DI_LOG_LEVEL_INFO, "codename set to: 
> %s", codename);
> +                             free(codename);
> +                             break;

That call to free() actually releases the memory for one string or
another of the strings reachable by 'releases'. See below.

> [...]
>  }

If I got it right, 'releases' become useless after set_codename().  So
all allocated .name and .suite should all free()'d at that point.



> > Are "Releases" lines defined not be hold more than 80 characters?
> > Proper boundary checking seems to be done hereafter, so I'm just
> > wondering.
> 
> Here's a typical Release file:
> http://ftp.nl.debian.org/debian/dists/lenny/Release
> 
> So yes, they can be longer than 80, but not the lines we care about. And as 
> we don't actually read the file, but only a grep of the lines we want, 
> there is no problem.

Thanks for the insight.
 
> > That might be useless given the size of the Release file, but you could
> > exit the while loop as soon as (release.name != NULL && release.suite !=
> > NULL).
> 
> Same. As we grep only the exact two lines we want that's not needed.

I was only suggesting this as an optimisation, but yes, not really
needed.
 
> > get_release() calls validate_codename() and validate_codename() calls
> > get_release(). I like mutually recursive functions in Haskell, but
> > it might be better to avoid them in C, I'd say. ;)
> 
> This I have not changed yet. The idea is that given the tests and the 
> content of Release files, you can never get an endless recursion. 
> validate_codename() should get called one time maximum.
> I will think about this a bit more though.

Actually I had missed the fact that Release files might differ. So
that's far from the worst path to take.

> [...] 
> > It would also need another static variable that would hold the actual
> > number of releases already parsed (and if == 0, then releases have not
> > been parsed yet).
> 
> Not needed IMO. When releases gets used .name is always set to NULL for the 
> last entry as terminator.

You never access the array before filling meaningful value, so it's
indeed fine; but it has been observed that unitialized memory issues can
be hard to debug, hence the suggestion. :)

> [...] 
> > Actually, I wonder about calling that struct 'release_t', as this does
> > not define a full type (_t) but only a struct name. Using 'struct
> > release' might be less confusing.
> 
> Yeah, but it's consistent with how mirror_t is defined...

True.  Same comments actually apply to mirror_t. :)

Cheers,
-- 
Jérémy Bobbio                        .''`. 
lu...@debian.org                    : :Ⓐ  :  # apt-get install anarchism
                                    `. `'` 
                                      `-   

Attachment: signature.asc
Description: Digital signature

Reply via email to