git.y1.nz

digest-gopher

Converts gopher to plaintext-with-links.
download: http://git.y1.nz/archives/digest-gopher.tar.gz
README | Files | Log | Refs

main.c

      1 #include <stdio.h> /* printf etc etc */
      2 #include <unistd.h> /* getopt */
      3 
      4 #define MAX_LINE_LENGTH  255
      5 #define PRINT_USAGE  { \
      6 	printf( \
      7 		"Convert gopher to plaintext-with-links." "\n" \
      8 		"Usage: %s [-o OUTFILE] GPHFILE" "\n", \
      9 		argv[0] \
     10 	); \
     11 	return 1; \
     12 }
     13 #define IS_UNASCII(c)  (c != '\t' && c != '\n' && c != '\r')
     14 #define SAFE_FREE(name)  if (name != NULL) free(name);
     15 
     16 /* globals */
     17 FILE *out;
     18 char line[MAX_LINE_LENGTH];
     19 int line_index = 0;
     20 
     21 /**
     22  * Finds the first '\t' in the string, if one is found, replace it with
     23  * '\0' and return a pointer to the next char. If the string null-terminates
     24  * before any '\t' are found, return the pointer to its '\0'.
     25  */
     26 char *
     27 delimit(char *str)
     28 {
     29 	int i = 0;
     30 	for (;;) {
     31 		if (str[i] == '\t') {
     32 			str[i] = '\0';
     33 			return str + (i+1);
     34 		}
     35 		if (str[i] == '\0') return str + i;
     36 		i++;
     37 	}
     38 }
     39 
     40 /** Return a null-terminated string, to indicate what type a line is. */
     41 const char *
     42 get_prefix(char type)
     43 {
     44 	switch (type) {
     45 	case '0': return "txt";
     46 	case '1': return "";
     47 	case '2': return "CSO";
     48 	case '3': return "err";
     49 	case '4': return "mac";
     50 	case '5': return "dos";
     51 	case '6': return "uue";
     52 	case '7': return "srch"; // TODO handle like HTML forms
     53 	case '8': return "teln";
     54 	case '9': return "bin";
     55 	case '+': return "mir";
     56 	case 'T': return "ibm";
     57 	case 'g': return "gif";
     58 	case 'I': return "img";
     59 	case 'h': return "html";
     60 	case 'i': return "";
     61 	default: return "(?)";
     62 	}
     63 }
     64 
     65 void
     66 print_escaped(const char *display)
     67 {
     68 	char ch;
     69 	int i = 0;
     70 	for (;;) {
     71 		ch = display[i++];
     72 		if (ch == '\0') return;
     73 		switch (ch) {
     74 		case '[': fprintf(out, "\\["); break;
     75 		case ']': fprintf(out, "\\]"); break;
     76 		default: fputc(ch, out);
     77 		}
     78 	}
     79 }
     80 
     81 /** Using a null-terminated line written to line, print the line to out. */
     82 int
     83 parse_line()
     84 {
     85 	char type = line[0];
     86 	if (type == '.') return 1;
     87 	const char *prefix = get_prefix(type);
     88 	char *start = line+1;
     89 	char *user_display = start;
     90 	start = delimit(start); char *selector = start;
     91 	start = delimit(start); char *host = start;
     92 	start = delimit(start); char *port = start;
     93 	start = delimit(start);
     94 
     95 	// TODO are there any other non-link-typed lines?
     96 	if (type == 'i') {
     97 		fprintf(out, "   ");
     98 		print_escaped(user_display);
     99 		fprintf(out, "\n");
    100 		return 0;
    101 	}
    102 
    103 	fprintf(out, "[%c ", type);
    104 	print_escaped(user_display);
    105 	fprintf(out, "](");
    106 	if (
    107 		selector[0] == 'U' &&
    108 		selector[1] == 'R' &&
    109 		selector[2] == 'L' &&
    110 		selector[3] == ':'
    111 	)    fprintf(out, "%s", selector + 4);
    112 	else fprintf(out, "gopher://%s:%s/%c%s", host, port, type, selector);
    113 	fprintf(out, ")" "\n");
    114 	return 0;
    115 }
    116 
    117 /**
    118  * Read a file line-by-line and parse one line at a time using the line buffer.
    119  */
    120 int
    121 main(int argc, char *argv[]) 
    122 {
    123 	const char *infile = NULL, *outfile = NULL;
    124 	FILE *in;
    125 	
    126 	int opt;
    127 	for (;;) {
    128 		opt = getopt(argc, argv, "f:o:");
    129 		if (opt == -1) break;
    130 		switch (opt) {
    131 		case 'o': outfile = optarg; break;
    132 		default: PRINT_USAGE;
    133 		}
    134 	}
    135 	if (optind != argc-1) PRINT_USAGE;
    136 
    137 	in = fopen(argv[optind], "r");
    138 	out = outfile == NULL ? stdout : fopen(outfile, "w");
    139 
    140 	char ch;
    141 	for (;;) {
    142 		ch = fgetc(in);
    143 		if (ch == '\r') continue;
    144 		line[line_index++] = ch == '\n' ? '\0' : ch;
    145 		line[line_index] = '\0';
    146 		if (
    147 			line_index == MAX_LINE_LENGTH-1 ||
    148 			ch == '\0' || ch == EOF
    149 		) return 0;
    150 		if (ch == '\n') {
    151 			parse_line();
    152 			line_index = 0;
    153 		}
    154 	}
    155 
    156 	fclose(in); fclose(out);
    157 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.