\n"
"
ftp.gnupg.org:%s
\n"
"
\n",
esc_title);
readme = fopen ("README", "r");
if (opt_readme && (readme = fopen ("README", "r")))
{
fputs ("
\n", stdout);
while (fgets (line, sizeof line, readme))
{
int no_lf = 0;
/* Eat up the rest of an incomplete line. */
if (!*line)
no_lf = 1;
else if (line[strlen (line)-1] != '\n')
{
no_lf = 1;
while ((c = getc (readme)) != EOF && c != '\n')
;
}
/* Replace empty lines with a leading doc by an empty
* line. These lines are used on FTP servers to avoid
* problems with broken FTP cleints. */
if (*line == '.')
{
for (p=line+1; (*p == ' ' || *p == '\t' || *p == '\n'); p++)
;
if (!*p)
{
putchar ('\n');
*line = 0;
}
}
if (*line)
fputs (html_escape_detect_link (line), stdout);
if (no_lf)
putchar ('\n');
}
fputs ("
\n", stdout);
fclose (readme);
}
fputs ("
\n", stdout);
}
else
{
printf ("\n"
"\n"
"
Index of %s\n"
"\n"
"\n"
"
Index of %s
\n"
"
\n",
esc_title, esc_title);
}
}
static void
print_footer (void)
{
if (!opt_html)
return;
if (opt_gpgweb)
{
fputs ("\n"
"\n"
"\n"
"\n"
"\n"
"\n", stdout);
}
else
{
printf ("
\n"
"\n"
"\n");
}
}
-/* Print COUNT directories from the array SORTED. */
+/* Print COUNT directories from the array SORTED.
+ * Note: This function assumes that the CWD is the listed directory. */
static void
print_dirs (finfo_t *sorted, int count, int at_root)
{
int idx;
finfo_t fi;
int any = 0;
+ char *title = NULL;
for (idx=0; idx < count; idx++)
{
fi = sorted[idx];
if (!fi->is_dir)
continue;
if (!any && opt_html)
{
any = 1;
if (opt_gpgweb)
{
fputs ("
Directories
\n"
"
\n"
"
\n", stdout);
if (!at_root)
fputs (" | "
""
"Parent Directory |
\n", stdout);
}
else
{
fputs ("  | "
"Directories |
\n",
stdout);
if (!at_root)
fputs (""
"Parent Directory |
\n", stdout);
}
}
+ free (title);
+ title = NULL;
if (opt_gpgweb)
- printf (" | "
- "%s |
\n",
- html_escape_href (fi->name), html_escape (fi->name));
- else if (opt_html)
- printf ("%s |
\n",
- html_escape_href (fi->name), html_escape (fi->name));
+ {
+ char *fname;
+ FILE *fp;
+
+ fname = xstrconcat (fi->name, "/", ".title", NULL);
+ fp = fopen (fname, "r");
+ free (fname);
+ if (fp)
+ {
+ char line[200];
+
+ if (fgets (line, sizeof line, fp) && *line)
+ {
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ title = xstrdup (html_escape (line));
+ }
+ fclose (fp);
+ }
+ }
+
+ if (opt_html)
+ {
+ if (opt_gpgweb)
+ printf (" | "
+ "%s | ",
+ html_escape_href (fi->name), html_escape (fi->name));
+ else
+ printf ("
%s | ",
+ html_escape_href (fi->name), html_escape (fi->name));
+ if (title)
+ printf ("%s | ", title);
+ fputs ("
\n", stdout);
+ }
else
printf ("D %s\n", fi->name);
}
if (any && opt_gpgweb)
{
fputs ("
\n"
"
\n\n", stdout);
}
else if (opt_gpgweb && !at_root)
{
/* !any - need to print an UP link */
fputs ("
\n", stdout);
}
+
+ free (title);
}
/* Print COUNT files from the array SORTED. */
static void
print_files (finfo_t *sorted, int count)
{
int idx;
finfo_t fi;
int any = 0;
for (idx=0; idx < count; idx++)
{
fi = sorted[idx];
if (!fi->is_reg)
continue;
if (!any && opt_html)
{
any = 1;
if (opt_gpgweb)
{
fputs ("
Files
\n"
"
\n"
"
\n", stdout);
}
else
fputs ("Files |
\n",
stdout);
}
if (opt_gpgweb)
printf (" | "
"%s | "
"%s | %s |
\n",
strstr (fi->name, ".sig")? "document":
strstr (fi->name, ".tar")? "tar" : "document",
html_escape_href (fi->name), html_escape (fi->name),
format_time (fi->mtime), format_size (fi->size));
else if (opt_html)
printf ("%s | "
"%s | %s |
\n",
html_escape_href (fi->name), html_escape (fi->name),
format_time (fi->mtime), format_size (fi->size));
else
printf ("F %s\n", fi->name);
}
if (any && opt_gpgweb)
{
fputs ("
\n"
"
\n\n", stdout);
}
}
/* Scan DIRECTORY and print an index.
* FIXME: This does a chdir and does not preserve the old PWD.
* The fix is to build the full filename beofre stat'ing.
*/
static void
scan_directory (const char *directory, const char *title)
{
DIR *dir;
struct dirent *dentry;
finfo_t fi;
finfo_t finfo = NULL;
finfo_t *sorted;
int count = 0;
int idx;
size_t len;
strlist_t sl;
int at_root = 0;
if (opt_gpgweb)
{
if (!strcmp (title, "/"))
at_root = 1;
}
else if (!strcmp (directory, "/"))
at_root = 1;
dir = opendir (directory);
if (!dir)
{
err ("can't open directory '%s': %s\n", directory, strerror (errno));
return;
}
while (errno=0,(dentry = readdir (dir)))
{
if (*dentry->d_name == '.')
continue; /* Skip self, parent, and hidden directories. */
len = strlen (dentry->d_name);
if (!len)
continue; /* Empty filenames should actually not exist. */
if (dentry->d_name[len-1] == '~')
continue; /* Skip backup files. */
for (sl = opt_exclude; sl; sl = sl->next)
if (!strcmp (sl->d, dentry->d_name))
break;
if (sl)
continue; /* Skip excluded names. */
fi = xcalloc (1, sizeof *fi + strlen (dentry->d_name));
strcpy (fi->name, dentry->d_name);
fi->next = finfo;
finfo = fi;
count++;
}
if (errno)
die ("error reading directory '%s': %s\n", directory, strerror (errno));
closedir (dir);
sorted = xcalloc (count, sizeof *sorted);
for (fi=finfo, idx=0; fi; fi = fi->next)
sorted[idx++] = fi;
inf ("directory '%s' has %d files\n", directory, count);
qsort (sorted, count, sizeof *sorted, sort_finfo);
if (chdir (directory))
die ("cannot chdir to '%s': %s\n", directory, strerror (errno));
for (idx=0; idx < count; idx++)
{
struct stat sb;
fi = sorted[idx];
if (stat (fi->name, &sb))
{
err ("cannot stat '%s': %s\n", fi->name, strerror (errno));
continue;
}
fi->is_dir = !!S_ISDIR(sb.st_mode);
fi->is_reg = !!S_ISREG(sb.st_mode);
fi->size = fi->is_reg? sb.st_size : 0;
fi->mtime = sb.st_mtime;
}
print_header (title);
if (opt_files_first)
{
print_files (sorted, count);
print_dirs (sorted, count, at_root);
}
else
{
print_dirs (sorted, count, at_root);
print_files (sorted, count);
}
print_footer ();
/* We create the index file in the current directory. */
if (opt_index)
{
FILE *indexfp = fopen (opt_index, "w");
if (!indexfp)
die ("error creating '%s' for '%s': %s\n",
opt_index, directory, strerror (errno));
for (idx=0; idx < count; idx++)
{
fi = sorted[idx];
fprintf (indexfp, "%s:%c:%llu:%lu:\n",
percent_escape (fi->name),
fi->is_dir? 'd':
fi->is_reg? 'r': '?',
fi->size,
(unsigned long)fi->mtime);
}
if (ferror (indexfp))
die ("error writing '%s' for '%s': %s\n",
opt_index, directory, strerror (errno));
/* Fixme: Check for close errors. */
fclose (indexfp);
}
free (sorted);
while ((fi = finfo))
{
fi = finfo->next;
free (finfo);
finfo = fi;
}
}
int
main (int argc, char **argv)
{
int last_argc = -1;
strlist_t sl;
if (argc < 1)
die ("Hey, read up on how to use exec(2)\n");
argv++; argc--;
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--version"))
{
fputs (PGMNAME " " VERSION "\n"
"Copyright (C) 2017 g10 Code GmbH\n"
"License GPLv3+: GNU GPL version 3 or later"
"
\n"
"This is free software: you are free to change"
" and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGMNAME " [options] directory [title]\n"
"Print an index for an FTP directory.\n\n"
"Options:\n"
" --version print program version\n"
" --verbose verbose diagnostics\n"
" --debug flyswatter\n"
" --reverse reverse sort order\n"
" --reverse-ver reverse only the version number order\n"
" --files-first print files before directories\n"
" --html output HTML\n"
" --gpgweb output HTML as used at gnupg.org\n"
" --readme include README file\n"
" --index FILE create index FILE\n"
" --exclude NAME ignore file NAME\n"
, stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
opt_verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
opt_debug++;
argc--; argv++;
}
else if (!strcmp (*argv, "--reverse"))
{
opt_reverse = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--reverse-ver"))
{
opt_reverse_ver = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--files-first"))
{
opt_files_first = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--readme"))
{
opt_readme = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--html"))
{
opt_html = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--index"))
{
argc--; argv++;
if (!argc || !**argv)
die ("argument missing for option '%s'\n", argv[-1]);
opt_index = *argv;
argc--; argv++;
}
else if (!strcmp (*argv, "--gpgweb"))
{
opt_gpgweb = opt_html = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--exclude"))
{
argc--; argv++;
if (!argc || !**argv)
die ("argument missing for option '%s'\n", argv[-1]);
sl = xmalloc (sizeof *sl + strlen (*argv));
strcpy (sl->d, *argv);
sl->next = opt_exclude;
opt_exclude = sl;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s' (use --help)\n", *argv);
}
if (argc < 1 || argc > 2)
die ("usage: " PGMNAME " [options] directory [title]\n");
scan_directory (argv[0], argv[1]? argv[1]:argv[0]);
return 0;
}
/*
Local Variables:
compile-command: "cc -Wall -g -o ftp-indexer ftp-indexer.c"
End:
*/