afify / azan (public) (License: MIT) (since 2020-03-08) (hash sha1)
azan is a simple muslim prayers notifier for unix-like systems.
List of commits:
Subject Hash Author Date (UTC)
[init] Initial Commit 2654768d0412e07cf7aa4b78a41b125aeceab82e Hassan Afify 2019-09-04 01:44:23
Commit 2654768d0412e07cf7aa4b78a41b125aeceab82e - [init] Initial Commit
Author: Hassan Afify
Author date (UTC): 2019-09-04 01:44
Committer name: Hassan Afify
Committer date (UTC): 2019-09-04 01:44
Parent(s):
Signing key: 0F6CD1196B2A5658
Tree: 991b6bffea8127131d6d07728d1c5eaea1d8b28a
File Lines added Lines deleted
CODE_STYLE 176 0
LICENSE 21 0
Makefile 24 0
README.md 37 0
TODO 9 0
azan.c 207 0
azan.h 12 0
config.h 11 0
screenshot.png 0 0
File CODE_STYLE added (mode: 100644) (index 0000000..9677131)
1 ===============================================================================
2
3 - Suckless code style
4 https://suckless.org/coding_style/
5
6 - Splint (linter)
7 https://splint.org/
8
9 ===============================================================================
10
11 Style
12
13 Note that the following are guidelines and the most important aspect of style is consistency. Strive to keep your style consistent with the project on which you are working. It is up to the project maintainer to take some liberty in the style guidelines.
14 Recommended Reading
15
16 The following contain good information, some of which is repeated below, some of which is contradicted below.
17
18 https://man.openbsd.org/style
19 http://doc.cat-v.org/bell_labs/pikestyle
20 https://www.kernel.org/doc/Documentation/process/coding-style.rst
21
22 File Layout
23
24 Comment with LICENSE and possibly short explanation of file/tool.
25 Headers
26 Macros
27 Types
28 Function declarations:
29 Include variable names.
30 For short files these can be left out.
31 Group/order in logical manner.
32 Global variables.
33 Function definitions in same order as declarations.
34 main
35
36 C Features
37
38 Use C99 without extensions (ISO/IEC 9899:1999).
39 Use POSIX.1-2008:
40 When using gcc define _POSIX_C_SOURCE 200809L.
41 Alternatively define _XOPEN_SOURCE 700.
42 Do not mix declarations and code.
43 Do not use for loop initial declarations.
44 Use /* */ for comments, not //.
45 Variadic macros are acceptable, but remember:
46 __VA_ARGS__ not a named parameter.
47 Arg list cannot be empty.
48
49 Blocks
50
51 All variable declarations at top of block.
52 { on same line preceded by single space (except functions).
53 } on own line unless continuing statement (if else, do while, ...).
54
55 Use block for single statement if inner statement needs a block.
56
57 for (;;) {
58 if (foo) {
59 bar;
60 baz;
61 }
62 }
63
64 Use another branch of the same statement needs a block:
65
66 if (foo) {
67 bar;
68 } else {
69 baz;
70 qux;
71 }
72
73 Leading Whitespace
74
75 Use tabs for indentation and spaces for alignment. This ensures everything will line up independent of tab size. This means:
76
77 No tabs except beginning of line.
78 Use spaces - not tabs - for multiline macros as the indentation level is 0, where the #define began.
79
80 Functions
81
82 Return type and modifiers on own line.
83 Function name and argument list on next line. This allows to grep for function names simply using grep ^functionname(.
84 Opening { on own line (function definitions are a special case of blocks as they cannot be nested).
85 Functions not used outside translation unit should be declared and defined static.
86
87 Example:
88
89 static void
90 usage(void)
91 {
92 eprintf("usage: %s [file ...]\n", argv0);
93 }
94
95 Variables
96
97 Global variables not used outside translation unit should be declared static.
98 In declaration of pointers the * is adjacent to variable name, not type.
99
100 Keywords
101
102 Use a space after if, for, while, switch (they are not function calls).
103 Do not use a space after the opening ( and before the closing ).
104 Preferably use () with sizeof.
105 Do not use a space with sizeof().
106
107 Switch
108
109 Do not indent cases another level.
110 Comment cases that FALLTHROUGH.
111
112 Example:
113
114 switch (value) {
115 case 0: /* FALLTHROUGH */
116 case 1:
117 case 2:
118 break;
119 default:
120 break;
121 }
122
123 Headers
124
125 Place system/libc headers first in alphabetical order.
126 If headers must be included in a specific order add a comment to explain.
127 Place local headers after an empty line.
128 When writing and using local headers.
129 Try to avoid cyclic header inclusion dependencies.
130 Instead ensure they are included where and when they are needed.
131 Read https://talks.golang.org/2012/splash.article#TOC_5.
132 Read http://plan9.bell-labs.com/sys/doc/comp.html
133
134 User Defined Types
135
136 Do not use type_t naming (it is reserved for POSIX and less readable).
137 Typedef opaque structs.
138 Do not typedef builtin types.
139 Use CamelCase for typedef'd types.
140
141 Line Length
142
143 Keep lines to reasonable length (max 79 characters).
144
145 Tests and Boolean Values
146
147 Do not use C99 bool types (stick to integer types).
148 Otherwise use compound assignment and tests unless the line grows too long:
149
150 if (!(p = malloc(sizeof(*p))))
151 hcf();
152
153 Handling Errors
154
155 When functions return -1 for error test against 0 not -1:
156
157 if (func() < 0)
158 hcf();
159
160 Use goto to unwind and cleanup when necessary instead of multiple nested levels.
161 return or exit early on failures instead of multiple nested levels.
162 Unreachable code should have a NOTREACHED comment.
163 Think long and hard on whether or not you should cleanup on fatal errors. For simple "one-shot" programs (not daemons) it can be OK to not free memory. It is advised to cleanup temporary files however.
164
165 Enums and #define
166
167 Use enums for values that are grouped semantically and #define otherwise:
168
169 #define MAXSZ 4096
170 #define MAGIC1 0xdeadbeef
171
172 enum {
173 DIRECTION_X,
174 DIRECTION_Y,
175 DIRECTION_Z
176 };
File LICENSE added (mode: 100644) (index 0000000..fcc2ca6)
1 MIT License
2
3 Copyright (c) 2019 Suckless Islamic
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
File Makefile added (mode: 100644) (index 0000000..caf9556)
1 # azan
2 # See LICENSE file for copyright and license details.
3 .POSIX:
4
5 SRC = azan.c
6 CC = gcc
7 TARGET= azan
8 IFLAGS= /usr/local/include/
9 LDFLAGS= /usr/local/lib -lcurl -ljson-c
10
11 all:
12 $(CC) $(SRC) -o $(TARGET) -I $(IFLAGS) -L $(LDFLAGS) -Wall -Wextra -pedantic
13
14 clean:
15 rm $(TARGET)
16
17 install: azan
18 cp -f azan /bin
19 chmod 755 /bin/azan
20
21 uninstall:
22 rm -f /bin/azan
23
24 .PHONY: all clean install uninstall
File README.md added (mode: 100644) (index 0000000..5877999)
1 Azan
2 ====
3 [![platform](https://img.shields.io/badge/Platform-Linux-blue.svg)](#)
4 [![license](https://img.shields.io/github/license/SucklessIslamic/azan.svg)](https://github.com/SucklessIslamic/azan/blob/master/LICENSE)
5
6 ![azanimage](/screenshot.png)
7
8 Requirements
9 ------------
10 In order to build azan you need the curl header files.
11
12 Installation
13 ------------
14 ```sh
15 $ git clone https://github.com/SucklessIslamic/azan.git
16 $ cd azan/
17 $ make
18 $ sudo make clean install
19 ```
20
21 Run
22 ---
23 ```sh
24 $ azan
25 ```
26 Configuration
27 -------------
28 The configuration of azan is done by creating a custom config.h
29 and (re)compiling the source code.
30
31 Ask Questions
32 -------------
33 You are welcome to submit questions and bug reports as Github Issues.
34
35 Copyright and License
36 ---------------------
37 Azan is provided under the MIT license.
File TODO added (mode: 100644) (index 0000000..bd4cfc3)
1 This Stage:
2 -----------
3 - replace json lib.
4 - fix splint warnings
5 - check functions return int with < 0
6
7 Next Stages:
8 ------------
9 - arg options (print today prayer times with notify-send)
File azan.c added (mode: 100644) (index 0000000..5cccd7d)
1 /* See LICENSE file for copyright and license details.
2 * Azan is simple Muslim prayers notifier.
3 * print next pray left duration.
4 *
5 * use curl lib to make a get request to azan api
6 * save json file in /tmp dir
7 * each time app runs check if cache file exists if not make get request
8 *
9 * read the cache file, get next pray, print left duration %h:%m
10 * send notification if left duration = 0 minutes
11 *
12 * this app should run each 59s
13 */
14
15 #include <stdio.h>
16 #include <time.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <curl/curl.h>
20 #include <json-c/json.h>
21
22 #include "azan.h"
23 #include "config.h"
24
25 static int
26 file_exist(char *filename)
27 {
28 struct stat buffer;
29 return (stat(filename, &buffer) == 0);
30 }
31
32 static void
33 get_page(const char* url, const char* file_name)
34 {
35
36 // Check connection
37 CURL *curl = curl_easy_init();
38 if(curl) {
39 CURLcode ret;
40 curl_easy_setopt(curl, CURLOPT_URL, "https://google.com/");
41 // curl_easy_setopt(curl, CURLOPT_URL, "test a failed connection");
42 curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
43 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2L);
44 ret = curl_easy_perform(curl);
45
46 // If Connected
47 if(ret == CURLE_OK) {
48 CURL* easyhandle = curl_easy_init();
49 curl_easy_setopt(easyhandle, CURLOPT_URL, url);
50 FILE* file = fopen(file_name, "w");
51 curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, file) ;
52 curl_easy_perform(easyhandle);
53 curl_easy_cleanup(easyhandle);
54 fclose(file);
55 }
56
57 // If Not Connected
58 else{
59 printf("not connected\n");
60 }
61 }
62 }
63
64 static void
65 print_next_pray(time_t t, char *json_file)
66 {
67
68 /* this fuction get as argument a json file
69 opens it and find pray times data
70 create a local array to store data
71 assign data to array
72
73 TODO:
74 1- return values as time type
75 2- pass an array insted of creating local one and no return
76 */
77
78 // Parse the json file
79 FILE *fp;
80 char buffer[2048];
81
82 fp = fopen(json_file,"r");
83 fread(buffer, 2048, 1, fp);
84 fclose(fp);
85
86 struct json_object *parsed_json;
87 struct json_object *data;
88 struct json_object *timings;
89
90 struct json_object *date;
91 struct json_object *json_hijri;
92 struct json_object *json_date;
93
94 // typedef struct {
95 // const struct json_object *json_pray;
96 // const char *string_pray;
97 // const unsigned int int_pray;
98 // const time_t time_pray;
99 // }PrayTime;
100
101 struct json_object *json_pray[5];
102 const char *array_string_pray[5];
103 int array_array_int_pray[5][2];
104 time_t array_time_pray[6];
105 char array_char_pray_name[6] = {'F','D','A','M','I','F'};
106
107 parsed_json = json_tokener_parse(buffer);
108 json_object_object_get_ex(parsed_json, "data" , &data);
109 json_object_object_get_ex(data, "timings", &timings);
110
111 json_object_object_get_ex(data, "date", &date);
112 json_object_object_get_ex(date, "hijri", &json_hijri);
113 json_object_object_get_ex(json_hijri, "date", &json_date);
114
115 // Hijri date
116 const char *hijri = json_object_get_string(json_date);
117
118 json_object_object_get_ex(timings, "Fajr", &json_pray[0]);
119 json_object_object_get_ex(timings, "Dhuhr", &json_pray[1]);
120 json_object_object_get_ex(timings, "Asr", &json_pray[2]);
121 json_object_object_get_ex(timings, "Maghrib", &json_pray[3]);
122 json_object_object_get_ex(timings, "Isha", &json_pray[4]);
123
124 // Handel time and duration
125 // struct tm current_time = *localtime(&t);
126 struct tm dt = *localtime(&t);
127
128 for(int i = 0; i < 5; i++){
129 // Create array of strings from the json object
130 array_string_pray[i] = json_object_get_string(json_pray[i]),
131
132 // Remove : from string parsed from json file
133 // Add to pray_int array
134 sscanf(array_string_pray[i], "%d:%d",
135 &array_array_int_pray[i][0], &array_array_int_pray[i][1]);
136
137 // Change the hours and minutes from int array to time type
138 dt.tm_hour = array_array_int_pray[i][0];
139 dt.tm_min = array_array_int_pray[i][1];
140 array_time_pray[i] = mktime(&dt);
141 };
142
143 // Create next fajr
144 dt.tm_mday = dt.tm_mday+1;
145 dt.tm_hour = array_array_int_pray[0][0];
146 dt.tm_min = array_array_int_pray[0][1];
147 array_time_pray[5] = mktime(&dt);
148
149 for(int i=0; i<6; i++){
150 if(t == array_time_pray[i]){
151 printf("%*.*s 00:00\n", 5, 5, hijri);
152 char command[100], msg[100];
153 strcpy(command,"notify-send ");
154 strcpy(msg,"\"Azan\"");
155 strcat(command,msg);
156 (void) system(command);
157 break;
158 }
159 else if(t < array_time_pray[i]){
160 // duration to next pray
161 int seconds = difftime(array_time_pray[i], t);
162 unsigned int hours = (seconds / (60*60));
163 unsigned int minutes = (seconds / (60)) % 60;
164
165 printf("%*.*s %c %.2d:%.2d\n", 5, 5, hijri,
166 array_char_pray_name[i], hours, minutes); break;
167 };
168 };
169
170 //struct tm tm;
171 // char buf[255];
172 //
173 // strptime("2001-11-13 18:31:01", "%Y-%m-%d %H:%M:%S", &tm);
174 // strftime(buf, sizeof(buf), "%s", &tm);
175 // puts(buf);
176 // exit(EXIT_SUCCESS);
177 }
178
179 int
180 main(void)
181 {
182 time_t t = time(NULL);
183 struct tm tm_variable = *localtime(&t);
184
185 // Cache file name
186 static char cache_file[32];
187 sprintf(cache_file, "/tmp/%.2d-%.2d-%d.json",
188 tm_variable.tm_mday, tm_variable.tm_mon+1,
189 tm_variable.tm_year+1900);
190
191 // Chech if cache does not exists
192 if (!file_exist (cache_file)){
193 static char api_url[128];
194 sprintf(api_url,
195 "http://api.aladhan.com/v1/timingsByCity?city=%s&country=%s&method=8",
196 city, country);
197
198 // Create the cache file
199 get_page(api_url, cache_file);
200 }
201
202 // Read if exists cache file and print next pray left time
203 if (file_exist (cache_file)){
204 print_next_pray(t, cache_file);
205 }
206 return 0;
207 }
File azan.h added (mode: 100644) (index 0000000..f896689)
1 /* See LICENSE file for copyright and license details.*/
2
3 #ifndef AZAN_H
4 #define AZAN_H
5
6 /* function declarations */
7
8 static int file_exist(char *filename);
9 static void get_page(const char* url, const char* file_name);
10 static void print_next_pray(time_t t, char *json_file);
11
12 #endif /* AZAN_H */
File config.h added (mode: 100644) (index 0000000..95cffa0)
1 /* See LICENSE file for copyright and license details.*/
2
3 #ifndef CONFIG_H
4 #define CONFIG_H
5
6 /* variable declarations */
7
8 static const char city[] = "Mekka";
9 static const char country[] = "Saudi%20Arabia";
10
11 #endif /* CONFIG_H */
File screenshot.png added (mode: 100644) (index 0000000..6e13c2d)
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/afify/azan

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/afify/azan

Clone this repository using git:
git clone git://git.rocketgit.com/user/afify/azan

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main