xref: /haiku/src/bin/fortune.c (revision adcf5b05a8ca9e17407aa4640675c3873c9f0a6c)
1 /*
2  * Copyright 2002-2009, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 
16 #include <FindDirectory.h>
17 #include <OS.h>
18 
19 
20 static int
21 choose_file(const char *path)
22 {
23 	struct dirent *dirent;
24 	int count = 0;
25 	int chosen;
26 
27 	DIR *dir = opendir(path);
28 	if (dir == NULL)
29 		return -1;
30 
31 	// count directory entries
32 
33 	while ((dirent = readdir(dir)) != NULL) {
34 		if (dirent->d_name[0] == '.')
35 			continue;
36 
37 		count++;
38 	}
39 
40 	if (count == 0) {
41 		closedir(dir);
42 		return -1;
43 	}
44 	// choose and open entry
45 
46 	chosen = rand() % count;
47 	count = 0;
48 	rewinddir(dir);
49 
50 	while ((dirent = readdir(dir)) != NULL) {
51 		if (dirent->d_name[0] == '.')
52 			continue;
53 
54 		if (chosen <= count) {
55 			char name[PATH_MAX];
56 			int fd;
57 
58 			// build full path
59 			strlcpy(name, path, sizeof(name));
60 			strlcat(name, "/", sizeof(name));
61 			strlcat(name, dirent->d_name, sizeof(name));
62 
63 			fd = open(name, O_RDONLY);
64 			if (fd >= 0) {
65 				closedir(dir);
66 				return fd;
67 			}
68 		}
69 		count++;
70 	}
71 
72 	closedir(dir);
73 	return -1;
74 }
75 
76 
77 int
78 main(int argc, char **argv)
79 {
80 	char path[PATH_MAX] = {'\0'};
81 	const char *file = path;
82 	int fd;
83 	char *buffer;
84 	unsigned start, i;
85 	unsigned count;
86 	struct stat stat;
87 
88 	srand(system_time() % INT_MAX);
89 
90 	if (argc > 1) {
91 		// if there are arguments, choose one randomly
92 		file = argv[1 + (rand() % (argc - 1))];
93 	} else if (find_directory(B_SYSTEM_DATA_DIRECTORY, -1, false, path,
94 			sizeof(path)) == B_OK) {
95 		strlcat(path, "/fortunes", sizeof(path));
96 	}
97 
98 	fd = open(file, O_RDONLY, 0);
99 	if (fd < 0) {
100 		fprintf(stderr, "Couldn't open %s: %s\n", file, strerror(errno));
101 		return 1;
102 	}
103 
104 	if (fstat(fd, &stat) < 0) {
105 		fprintf(stderr, "stat() failed: %s\n", strerror(errno));
106 		return 1;
107 	}
108 
109 	if (S_ISDIR(stat.st_mode)) {
110 		close(fd);
111 
112 		fd = choose_file(file);
113 		if (fd < 0) {
114 			fprintf(stderr, "Could not find any fortune file.\n");
115 			return 1;
116 		}
117 
118 		if (fstat(fd, &stat) < 0) {
119 			fprintf(stderr, "stat() failed: %s\n", strerror(errno));
120 			return 1;
121 		}
122 	}
123 
124 	buffer = malloc(stat.st_size + 1);
125 	if (buffer == NULL) {
126 		fprintf(stderr, "Not enough memory.\n");
127 		return 1;
128 	}
129 
130 	if (read(fd, buffer, stat.st_size) < 0) {
131 		fprintf(stderr, "Could not read from fortune file: %s\n",
132 			strerror(errno));
133 		free(buffer);
134 		return -1;
135 	}
136 
137 	buffer[stat.st_size] = '\0';
138 	close(fd);
139 
140 	// count fortunes
141 
142 	count = 0;
143 	for (i = 0; i < stat.st_size - 2; i++) {
144 		if (!strncmp(buffer + i, "\n%\n", 3)) {
145 			count++;
146 			i += 3;
147 		}
148 	}
149 
150 	if (!count) {
151 		printf("Out of cookies...\n");
152 		free(buffer);
153 		return 0;
154 	}
155 
156 	count = rand() % count;
157 	start = 0;
158 
159 	// find beginning & end
160 	for (i = 0; i < stat.st_size - 2; i++) {
161 		if (!strncmp(buffer + i, "\n%\n", 3)) {
162 			if (count-- <= 0) {
163 				buffer[i] = '\0';
164 				break;
165 			}
166 
167 			i += 3;
168 			start = i;
169 		}
170 	}
171 
172 	puts(buffer + start);
173 	free(buffer);
174 	return 0;
175 }
176