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.