Hello,
I'm writing a small tool in C to extract comments from Z80 assembler sources. I get random segmentation fault, generaly after one successful execution of the binary. lldb shows always the same line involved : 121 at calloc(...).
ndx[i]->text = (char *) calloc(tsz, sizeof(char));
Thanks for your help.
// AX
// my Z80 assembler API extractor
//
// T.Peycru, 2023
// NOTE:
// -----
//
// API extractor will extract all the comment lines following ;;;
//
// The string following the trigger sequence of LF+';'+';'+';'
// is the sorting label of the paragraph of comments
//
// All the files in the current directory and its childhood are scanned
// Output is sorted on the label used to build the TOC (unimplemented)
//
// Output format is Markdown (unimplemented)
//
#define _BSD_SOURCE // required to make this program work on Linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for getcwd()
#include <sys/errno.h> // lets us directly access errno
// includes recommended by "man fts":
#include <sys/types.h>
#include <sys/stat.h>
#include <fts.h>
// max constants
#define C_MAX 4096 // buffer for fgets (one line from asm source file)
#define I_MAX 1000 // number of entries
#define N_MAX 4096 // buffer for entry name
#define T_MAX 32768 // buffer for entry text
// entry counter
int i = 0;
// buffers
// entry structure
struct entry
{
char *name;
char *text;
};
// array of pointers to entries
struct entry *ndx[I_MAX];
//
// function to extract API comments in a given filename
// ----------------------------------------------------
//
// trigger is a CRLF+";;;" followed by entry name (to be sorted in a TOC)
// next lines with comment (beginning with ';') are entry text
// any other line ends the text
//
int ApiExtract(char *argv)
{
// file to parse
FILE *fp = fopen(argv, "r");
if(fp == NULL) return 0;
// state : 0=detect mode, 1=scanning mode
int scanning = 0;
// sizes
int nsz = 0;
int tsz = 0;
// buffers
char chunk[C_MAX];
char nbuf[N_MAX];
char tbuf[T_MAX];
// reset buffers
memset(chunk, 0, C_MAX);
memset(nbuf, 0, N_MAX);
memset(tbuf, 0, T_MAX);
while(fgets(chunk, C_MAX-1, fp) != NULL)
{
if (!scanning)
{
// detect mode, look for trigger
// ;;; trigger sequence for API name
if (chunk[0] == ';' && chunk[1] == ';' && chunk[2] == ';')
{
// trigger found, switch to scanning mode
scanning = 1;
// store name (for TOC and entry sorting)
if (chunk[3]) strncpy(nbuf, &chunk[3], N_MAX-4);
}
}
else
{
// scanning mode
if (chunk[0] == ';')
{
// append line of text
strncat( tbuf, &chunk[1], T_MAX-strlen(tbuf)-1);
}
else
{
// text done, store name and text in a new entry
ndx[i] = (struct entry *) calloc(1, sizeof(struct entry));
// set allocation sizes
if (!(nsz = strlen(nbuf)+1)) return -1;
if (!(tsz = strlen(tbuf)+1)) return -1;
// allocate and store name
ndx[i]->name = (char *) calloc(nsz, sizeof(char));
strncpy( ndx[i]->name, nbuf, N_MAX-1);
// allocate and store text
ndx[i]->text = (char *) calloc(tsz, sizeof(char));
strncpy(ndx[i]->text, tbuf, T_MAX-1);
// clear buffers for next iteration
memset(nbuf, 0, N_MAX);
memset(tbuf, 0, T_MAX);
// next entry
i++;
if (i>I_MAX) return -1;
// switch to detect mode
scanning = 0;
}
}
}
fclose(fp);
return 0;
}
//
// function to process entries and output API
// ------------------------------------------
//
int ApiOutput(void)
{
int l = 0;
// for now, just output to stdout
// TBD : sorting, TOC building, MD format enlighting
while(l<i)
{
printf("%s",ndx[l]->name);
printf("%s",ndx[l]->text);
free(ndx[l]->name);
free(ndx[l]->text);
free(ndx[l]);
ndx[l] = 0;
l++;
}
return 0;
}
//
// main parse current file tree using FTS
// --------------------------------------
//
int main(void)
{
/* An array of paths to traverse. Each path must be null
* terminated and the list must end with a NULL pointer. */
char *paths[] = { ".", NULL };
/* 2nd parameter: An options parameter. Must include either
FTS_PHYSICAL or FTS_LOGICAL---they change how symbolic links
are handled.
The 2nd parameter can also include the FTS_NOCHDIR bit (with a
bitwise OR) which causes fts to skip changing into other
directories. I.e., fts will call chdir() to literally cause
your program to behave as if it is running into another
directory until it exits that directory. See "man fts" for more
information.
Last parameter is a comparator which you can optionally provide
to change the traversal of the filesystem hierarchy.
*/
FTS *ftsp = fts_open(paths, FTS_PHYSICAL, NULL);
if(ftsp == NULL)
{
perror("fts_open");
exit(EXIT_FAILURE);
}
while(1) // call fts_read() enough times to get each file
{
FTSENT *ent = fts_read(ftsp); // get next entry (could be file or directory).
if(ent == NULL)
{
if(errno == 0)
break; // No more items, bail out of while loop
else
{
// fts_read() had an error.
perror("fts_read");
exit(EXIT_FAILURE);
}
}
// Given a "entry", call extractor if it is a file
if (ent->fts_info & FTS_F) // The entry is a file.
{
if (ApiExtract(ent->fts_path))
{
// extraction error
perror("apix");
exit(EXIT_FAILURE);
}
}
}
// output
ApiOutput();
// close fts and check for error closing.
if(fts_close(ftsp) == -1)
perror("fts_close");
return 0;
}