xref: /haiku/src/bin/fortune.c (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 		return -1;
134 	}
135 
136 	buffer[stat.st_size] = '\0';
137 	close(fd);
138 
139 	// count fortunes
140 
141 	count = 0;
142 	for (i = 0; i < stat.st_size - 2; i++) {
143 		if (!strncmp(buffer + i, "\n%\n", 3)) {
144 			count++;
145 			i += 3;
146 		}
147 	}
148 
149 	if (!count) {
150 		printf("Out of cookies...\n");
151 		return 0;
152 	}
153 
154 	count = rand() % count;
155 	start = 0;
156 
157 	// find beginning & end
158 	for (i = 0; i < stat.st_size - 2; i++) {
159 		if (!strncmp(buffer + i, "\n%\n", 3)) {
160 			if (count-- <= 0) {
161 				buffer[i] = '\0';
162 				break;
163 			}
164 
165 			i += 3;
166 			start = i;
167 		}
168 	}
169 
170 	puts(buffer + start);
171 	return 0;
172 }
173